linux/drivers/net/wireless/orinoco/spectrum_cs.c
<<
>>
Prefs
   1/*
   2 * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
   3 * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
   4 * Communications and Intel PRO/Wireless 2011B.
   5 *
   6 * The driver implements Symbol firmware download.  The rest is handled
   7 * in hermes.c and main.c.
   8 *
   9 * Utilities for downloading the Symbol firmware are available at
  10 * http://sourceforge.net/projects/orinoco/
  11 *
  12 * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
  13 * Portions based on orinoco_cs.c:
  14 *      Copyright (C) David Gibson, Linuxcare Australia
  15 * Portions based on Spectrum24tDnld.c from original spectrum24 driver:
  16 *      Copyright (C) Symbol Technologies.
  17 *
  18 * See copyright notice in file main.c.
  19 */
  20
  21#define DRIVER_NAME "spectrum_cs"
  22#define PFX DRIVER_NAME ": "
  23
  24#include <linux/module.h>
  25#include <linux/kernel.h>
  26#include <linux/delay.h>
  27#include <pcmcia/cistpl.h>
  28#include <pcmcia/cisreg.h>
  29#include <pcmcia/ds.h>
  30
  31#include "orinoco.h"
  32
  33/********************************************************************/
  34/* Module stuff                                                     */
  35/********************************************************************/
  36
  37MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
  38MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
  39MODULE_LICENSE("Dual MPL/GPL");
  40
  41/* Module parameters */
  42
  43/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
  44 * don't have any CIS entry for it. This workaround it... */
  45static int ignore_cis_vcc; /* = 0 */
  46module_param(ignore_cis_vcc, int, 0);
  47MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
  48
  49/********************************************************************/
  50/* Data structures                                                  */
  51/********************************************************************/
  52
  53/* PCMCIA specific device information (goes in the card field of
  54 * struct orinoco_private */
  55struct orinoco_pccard {
  56        struct pcmcia_device    *p_dev;
  57};
  58
  59/********************************************************************/
  60/* Function prototypes                                              */
  61/********************************************************************/
  62
  63static int spectrum_cs_config(struct pcmcia_device *link);
  64static void spectrum_cs_release(struct pcmcia_device *link);
  65
  66/* Constants for the CISREG_CCSR register */
  67#define HCR_RUN         0x07    /* run firmware after reset */
  68#define HCR_IDLE        0x0E    /* don't run firmware after reset */
  69#define HCR_MEM16       0x10    /* memory width bit, should be preserved */
  70
  71
  72/*
  73 * Reset the card using configuration registers COR and CCSR.
  74 * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
  75 */
  76static int
  77spectrum_reset(struct pcmcia_device *link, int idle)
  78{
  79        int ret;
  80        u8 save_cor;
  81        u8 ccsr;
  82
  83        /* Doing it if hardware is gone is guaranteed crash */
  84        if (!pcmcia_dev_present(link))
  85                return -ENODEV;
  86
  87        /* Save original COR value */
  88        ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor);
  89        if (ret)
  90                goto failed;
  91
  92        /* Soft-Reset card */
  93        ret = pcmcia_write_config_byte(link, CISREG_COR,
  94                                (save_cor | COR_SOFT_RESET));
  95        if (ret)
  96                goto failed;
  97        udelay(1000);
  98
  99        /* Read CCSR */
 100        ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr);
 101        if (ret)
 102                goto failed;
 103
 104        /*
 105         * Start or stop the firmware.  Memory width bit should be
 106         * preserved from the value we've just read.
 107         */
 108        ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16);
 109        ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr);
 110        if (ret)
 111                goto failed;
 112        udelay(1000);
 113
 114        /* Restore original COR configuration index */
 115        ret = pcmcia_write_config_byte(link, CISREG_COR,
 116                                (save_cor & ~COR_SOFT_RESET));
 117        if (ret)
 118                goto failed;
 119        udelay(1000);
 120        return 0;
 121
 122failed:
 123        return -ENODEV;
 124}
 125
 126/********************************************************************/
 127/* Device methods                                                   */
 128/********************************************************************/
 129
 130static int
 131spectrum_cs_hard_reset(struct orinoco_private *priv)
 132{
 133        struct orinoco_pccard *card = priv->card;
 134        struct pcmcia_device *link = card->p_dev;
 135
 136        /* Soft reset using COR and HCR */
 137        spectrum_reset(link, 0);
 138
 139        return 0;
 140}
 141
 142static int
 143spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
 144{
 145        struct orinoco_pccard *card = priv->card;
 146        struct pcmcia_device *link = card->p_dev;
 147
 148        return spectrum_reset(link, idle);
 149}
 150
 151/********************************************************************/
 152/* PCMCIA stuff                                                     */
 153/********************************************************************/
 154
 155static int
 156spectrum_cs_probe(struct pcmcia_device *link)
 157{
 158        struct orinoco_private *priv;
 159        struct orinoco_pccard *card;
 160
 161        priv = alloc_orinocodev(sizeof(*card), &link->dev,
 162                                spectrum_cs_hard_reset,
 163                                spectrum_cs_stop_firmware);
 164        if (!priv)
 165                return -ENOMEM;
 166        card = priv->card;
 167
 168        /* Link both structures together */
 169        card->p_dev = link;
 170        link->priv = priv;
 171
 172        return spectrum_cs_config(link);
 173}                               /* spectrum_cs_attach */
 174
 175static void spectrum_cs_detach(struct pcmcia_device *link)
 176{
 177        struct orinoco_private *priv = link->priv;
 178
 179        orinoco_if_del(priv);
 180
 181        spectrum_cs_release(link);
 182
 183        free_orinocodev(priv);
 184}                               /* spectrum_cs_detach */
 185
 186static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
 187                                    void *priv_data)
 188{
 189        if (p_dev->config_index == 0)
 190                return -EINVAL;
 191
 192        return pcmcia_request_io(p_dev);
 193};
 194
 195static int
 196spectrum_cs_config(struct pcmcia_device *link)
 197{
 198        struct orinoco_private *priv = link->priv;
 199        struct hermes *hw = &priv->hw;
 200        int ret;
 201        void __iomem *mem;
 202
 203        link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC |
 204                CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
 205        if (ignore_cis_vcc)
 206                link->config_flags &= ~CONF_AUTO_CHECK_VCC;
 207        ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
 208        if (ret) {
 209                if (!ignore_cis_vcc)
 210                        printk(KERN_ERR PFX "GetNextTuple(): No matching "
 211                               "CIS configuration.  Maybe you need the "
 212                               "ignore_cis_vcc=1 parameter.\n");
 213                goto failed;
 214        }
 215
 216        mem = ioport_map(link->resource[0]->start,
 217                        resource_size(link->resource[0]));
 218        if (!mem)
 219                goto failed;
 220
 221        /* We initialize the hermes structure before completing PCMCIA
 222         * configuration just in case the interrupt handler gets
 223         * called. */
 224        hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
 225        hw->eeprom_pda = true;
 226
 227        ret = pcmcia_request_irq(link, orinoco_interrupt);
 228        if (ret)
 229                goto failed;
 230
 231        ret = pcmcia_enable_device(link);
 232        if (ret)
 233                goto failed;
 234
 235        /* Reset card */
 236        if (spectrum_cs_hard_reset(priv) != 0)
 237                goto failed;
 238
 239        /* Initialise the main driver */
 240        if (orinoco_init(priv) != 0) {
 241                printk(KERN_ERR PFX "orinoco_init() failed\n");
 242                goto failed;
 243        }
 244
 245        /* Register an interface with the stack */
 246        if (orinoco_if_add(priv, link->resource[0]->start,
 247                           link->irq, NULL) != 0) {
 248                printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 249                goto failed;
 250        }
 251
 252        return 0;
 253
 254 failed:
 255        spectrum_cs_release(link);
 256        return -ENODEV;
 257}                               /* spectrum_cs_config */
 258
 259static void
 260spectrum_cs_release(struct pcmcia_device *link)
 261{
 262        struct orinoco_private *priv = link->priv;
 263        unsigned long flags;
 264
 265        /* We're committed to taking the device away now, so mark the
 266         * hardware as unavailable */
 267        priv->hw.ops->lock_irqsave(&priv->lock, &flags);
 268        priv->hw_unavailable++;
 269        priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
 270
 271        pcmcia_disable_device(link);
 272        if (priv->hw.iobase)
 273                ioport_unmap(priv->hw.iobase);
 274}                               /* spectrum_cs_release */
 275
 276
 277static int
 278spectrum_cs_suspend(struct pcmcia_device *link)
 279{
 280        struct orinoco_private *priv = link->priv;
 281        int err = 0;
 282
 283        /* Mark the device as stopped, to block IO until later */
 284        orinoco_down(priv);
 285
 286        return err;
 287}
 288
 289static int
 290spectrum_cs_resume(struct pcmcia_device *link)
 291{
 292        struct orinoco_private *priv = link->priv;
 293        int err = orinoco_up(priv);
 294
 295        return err;
 296}
 297
 298
 299/********************************************************************/
 300/* Module initialization                                            */
 301/********************************************************************/
 302
 303static const struct pcmcia_device_id spectrum_cs_ids[] = {
 304        PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
 305        PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
 306        PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
 307        PCMCIA_DEVICE_NULL,
 308};
 309MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
 310
 311static struct pcmcia_driver orinoco_driver = {
 312        .owner          = THIS_MODULE,
 313        .name           = DRIVER_NAME,
 314        .probe          = spectrum_cs_probe,
 315        .remove         = spectrum_cs_detach,
 316        .suspend        = spectrum_cs_suspend,
 317        .resume         = spectrum_cs_resume,
 318        .id_table       = spectrum_cs_ids,
 319};
 320module_pcmcia_driver(orinoco_driver);
 321