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