linux/drivers/net/wireless/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 orinoco.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 orinoco.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/cs_types.h>
  29#include <pcmcia/cs.h>
  30#include <pcmcia/cistpl.h>
  31#include <pcmcia/cisreg.h>
  32#include <pcmcia/ds.h>
  33
  34#include "orinoco.h"
  35
  36/********************************************************************/
  37/* Module stuff                                                     */
  38/********************************************************************/
  39
  40MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
  41MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
  42MODULE_LICENSE("Dual MPL/GPL");
  43
  44/* Module parameters */
  45
  46/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
  47 * don't have any CIS entry for it. This workaround it... */
  48static int ignore_cis_vcc; /* = 0 */
  49module_param(ignore_cis_vcc, int, 0);
  50MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
  51
  52/********************************************************************/
  53/* Data structures                                                  */
  54/********************************************************************/
  55
  56/* PCMCIA specific device information (goes in the card field of
  57 * struct orinoco_private */
  58struct orinoco_pccard {
  59        struct pcmcia_device    *p_dev;
  60        dev_node_t node;
  61};
  62
  63/********************************************************************/
  64/* Function prototypes                                              */
  65/********************************************************************/
  66
  67static int spectrum_cs_config(struct pcmcia_device *link);
  68static void spectrum_cs_release(struct pcmcia_device *link);
  69
  70/* Constants for the CISREG_CCSR register */
  71#define HCR_RUN         0x07    /* run firmware after reset */
  72#define HCR_IDLE        0x0E    /* don't run firmware after reset */
  73#define HCR_MEM16       0x10    /* memory width bit, should be preserved */
  74
  75
  76#define CS_CHECK(fn, ret) \
  77  do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
  78
  79/*
  80 * Reset the card using configuration registers COR and CCSR.
  81 * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
  82 */
  83static int
  84spectrum_reset(struct pcmcia_device *link, int idle)
  85{
  86        int last_ret, last_fn;
  87        conf_reg_t reg;
  88        u_int save_cor;
  89
  90        /* Doing it if hardware is gone is guaranteed crash */
  91        if (!pcmcia_dev_present(link))
  92                return -ENODEV;
  93
  94        /* Save original COR value */
  95        reg.Function = 0;
  96        reg.Action = CS_READ;
  97        reg.Offset = CISREG_COR;
  98        CS_CHECK(AccessConfigurationRegister,
  99                 pcmcia_access_configuration_register(link, &reg));
 100        save_cor = reg.Value;
 101
 102        /* Soft-Reset card */
 103        reg.Action = CS_WRITE;
 104        reg.Offset = CISREG_COR;
 105        reg.Value = (save_cor | COR_SOFT_RESET);
 106        CS_CHECK(AccessConfigurationRegister,
 107                 pcmcia_access_configuration_register(link, &reg));
 108        udelay(1000);
 109
 110        /* Read CCSR */
 111        reg.Action = CS_READ;
 112        reg.Offset = CISREG_CCSR;
 113        CS_CHECK(AccessConfigurationRegister,
 114                 pcmcia_access_configuration_register(link, &reg));
 115
 116        /*
 117         * Start or stop the firmware.  Memory width bit should be
 118         * preserved from the value we've just read.
 119         */
 120        reg.Action = CS_WRITE;
 121        reg.Offset = CISREG_CCSR;
 122        reg.Value = (idle ? HCR_IDLE : HCR_RUN) | (reg.Value & HCR_MEM16);
 123        CS_CHECK(AccessConfigurationRegister,
 124                 pcmcia_access_configuration_register(link, &reg));
 125        udelay(1000);
 126
 127        /* Restore original COR configuration index */
 128        reg.Action = CS_WRITE;
 129        reg.Offset = CISREG_COR;
 130        reg.Value = (save_cor & ~COR_SOFT_RESET);
 131        CS_CHECK(AccessConfigurationRegister,
 132                 pcmcia_access_configuration_register(link, &reg));
 133        udelay(1000);
 134        return 0;
 135
 136      cs_failed:
 137        cs_error(link, last_fn, last_ret);
 138        return -ENODEV;
 139}
 140
 141/********************************************************************/
 142/* Device methods                                                   */
 143/********************************************************************/
 144
 145static int
 146spectrum_cs_hard_reset(struct orinoco_private *priv)
 147{
 148        struct orinoco_pccard *card = priv->card;
 149        struct pcmcia_device *link = card->p_dev;
 150
 151        /* Soft reset using COR and HCR */
 152        spectrum_reset(link, 0);
 153
 154        return 0;
 155}
 156
 157static int
 158spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
 159{
 160        struct orinoco_pccard *card = priv->card;
 161        struct pcmcia_device *link = card->p_dev;
 162
 163        return spectrum_reset(link, idle);
 164}
 165
 166/********************************************************************/
 167/* PCMCIA stuff                                                     */
 168/********************************************************************/
 169
 170/*
 171 * This creates an "instance" of the driver, allocating local data
 172 * structures for one device.  The device is registered with Card
 173 * Services.
 174 * 
 175 * The dev_link structure is initialized, but we don't actually
 176 * configure the card at this point -- we wait until we receive a card
 177 * insertion event.  */
 178static int
 179spectrum_cs_probe(struct pcmcia_device *link)
 180{
 181        struct net_device *dev;
 182        struct orinoco_private *priv;
 183        struct orinoco_pccard *card;
 184
 185        dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
 186                               spectrum_cs_hard_reset,
 187                               spectrum_cs_stop_firmware);
 188        if (! dev)
 189                return -ENOMEM;
 190        priv = netdev_priv(dev);
 191        card = priv->card;
 192
 193        /* Link both structures together */
 194        card->p_dev = link;
 195        link->priv = dev;
 196
 197        /* Interrupt setup */
 198        link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
 199        link->irq.IRQInfo1 = IRQ_LEVEL_ID;
 200        link->irq.Handler = orinoco_interrupt;
 201        link->irq.Instance = dev; 
 202
 203        /* General socket configuration defaults can go here.  In this
 204         * client, we assume very little, and rely on the CIS for
 205         * almost everything.  In most clients, many details (i.e.,
 206         * number, sizes, and attributes of IO windows) are fixed by
 207         * the nature of the device, and can be hard-wired here. */
 208        link->conf.Attributes = 0;
 209        link->conf.IntType = INT_MEMORY_AND_IO;
 210
 211        return spectrum_cs_config(link);
 212}                               /* spectrum_cs_attach */
 213
 214/*
 215 * This deletes a driver "instance".  The device is de-registered with
 216 * Card Services.  If it has been released, all local data structures
 217 * are freed.  Otherwise, the structures will be freed when the device
 218 * is released.
 219 */
 220static void spectrum_cs_detach(struct pcmcia_device *link)
 221{
 222        struct net_device *dev = link->priv;
 223
 224        if (link->dev_node)
 225                unregister_netdev(dev);
 226
 227        spectrum_cs_release(link);
 228
 229        free_orinocodev(dev);
 230}                               /* spectrum_cs_detach */
 231
 232/*
 233 * spectrum_cs_config() is scheduled to run after a CARD_INSERTION
 234 * event is received, to configure the PCMCIA socket, and to make the
 235 * device available to the system.
 236 */
 237
 238static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
 239                                    cistpl_cftable_entry_t *cfg,
 240                                    cistpl_cftable_entry_t *dflt,
 241                                    unsigned int vcc,
 242                                    void *priv_data)
 243{
 244        if (cfg->index == 0)
 245                goto next_entry;
 246
 247        /* Use power settings for Vcc and Vpp if present */
 248        /* Note that the CIS values need to be rescaled */
 249        if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
 250                if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
 251                        DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n",  vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
 252                        if (!ignore_cis_vcc)
 253                                goto next_entry;
 254                }
 255        } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
 256                if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
 257                        DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n",  vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
 258                        if (!ignore_cis_vcc)
 259                                goto next_entry;
 260                }
 261        }
 262
 263        if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
 264                p_dev->conf.Vpp =
 265                        cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
 266        else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
 267                p_dev->conf.Vpp =
 268                        dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
 269
 270        /* Do we need to allocate an interrupt? */
 271        p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
 272
 273        /* IO window settings */
 274        p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
 275        if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
 276                cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
 277                p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
 278                if (!(io->flags & CISTPL_IO_8BIT))
 279                        p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
 280                if (!(io->flags & CISTPL_IO_16BIT))
 281                        p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
 282                p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
 283                p_dev->io.BasePort1 = io->win[0].base;
 284                p_dev->io.NumPorts1 = io->win[0].len;
 285                if (io->nwin > 1) {
 286                        p_dev->io.Attributes2 = p_dev->io.Attributes1;
 287                        p_dev->io.BasePort2 = io->win[1].base;
 288                        p_dev->io.NumPorts2 = io->win[1].len;
 289                }
 290
 291                /* This reserves IO space but doesn't actually enable it */
 292                if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
 293                        goto next_entry;
 294        }
 295        return 0;
 296
 297next_entry:
 298        pcmcia_disable_device(p_dev);
 299        return -ENODEV;
 300};
 301
 302static int
 303spectrum_cs_config(struct pcmcia_device *link)
 304{
 305        struct net_device *dev = link->priv;
 306        struct orinoco_private *priv = netdev_priv(dev);
 307        struct orinoco_pccard *card = priv->card;
 308        hermes_t *hw = &priv->hw;
 309        int last_fn, last_ret;
 310        void __iomem *mem;
 311
 312        /*
 313         * In this loop, we scan the CIS for configuration table
 314         * entries, each of which describes a valid card
 315         * configuration, including voltage, IO window, memory window,
 316         * and interrupt settings.
 317         *
 318         * We make no assumptions about the card to be configured: we
 319         * use just the information available in the CIS.  In an ideal
 320         * world, this would work for any PCMCIA card, but it requires
 321         * a complete and accurate CIS.  In practice, a driver usually
 322         * "knows" most of these things without consulting the CIS,
 323         * and most client drivers will only use the CIS to fill in
 324         * implementation-defined details.
 325         */
 326        last_ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
 327        if (last_ret) {
 328                if (!ignore_cis_vcc)
 329                        printk(KERN_ERR PFX "GetNextTuple(): No matching "
 330                               "CIS configuration.  Maybe you need the "
 331                               "ignore_cis_vcc=1 parameter.\n");
 332                cs_error(link, RequestIO, last_ret);
 333                goto failed;
 334        }
 335
 336        /*
 337         * Allocate an interrupt line.  Note that this does not assign
 338         * a handler to the interrupt, unless the 'Handler' member of
 339         * the irq structure is initialized.
 340         */
 341        CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
 342
 343        /* We initialize the hermes structure before completing PCMCIA
 344         * configuration just in case the interrupt handler gets
 345         * called. */
 346        mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
 347        if (!mem)
 348                goto cs_failed;
 349
 350        hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
 351
 352        /*
 353         * This actually configures the PCMCIA socket -- setting up
 354         * the I/O windows and the interrupt mapping, and putting the
 355         * card and host interface into "Memory and IO" mode.
 356         */
 357        CS_CHECK(RequestConfiguration,
 358                 pcmcia_request_configuration(link, &link->conf));
 359
 360        /* Ok, we have the configuration, prepare to register the netdev */
 361        dev->base_addr = link->io.BasePort1;
 362        dev->irq = link->irq.AssignedIRQ;
 363        card->node.major = card->node.minor = 0;
 364
 365        /* Reset card */
 366        if (spectrum_cs_hard_reset(priv) != 0) {
 367                goto failed;
 368        }
 369
 370        SET_NETDEV_DEV(dev, &handle_to_dev(link));
 371        /* Tell the stack we exist */
 372        if (register_netdev(dev) != 0) {
 373                printk(KERN_ERR PFX "register_netdev() failed\n");
 374                goto failed;
 375        }
 376
 377        /* At this point, the dev_node_t structure(s) needs to be
 378         * initialized and arranged in a linked list at link->dev_node. */
 379        strcpy(card->node.dev_name, dev->name);
 380        link->dev_node = &card->node; /* link->dev_node being non-NULL is also
 381                                    used to indicate that the
 382                                    net_device has been registered */
 383
 384        /* Finally, report what we've done */
 385        printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io "
 386               "0x%04x-0x%04x\n", dev->name, dev->dev.parent->bus_id,
 387               link->irq.AssignedIRQ, link->io.BasePort1,
 388               link->io.BasePort1 + link->io.NumPorts1 - 1);
 389
 390        return 0;
 391
 392 cs_failed:
 393        cs_error(link, last_fn, last_ret);
 394
 395 failed:
 396        spectrum_cs_release(link);
 397        return -ENODEV;
 398}                               /* spectrum_cs_config */
 399
 400/*
 401 * After a card is removed, spectrum_cs_release() will unregister the
 402 * device, and release the PCMCIA configuration.  If the device is
 403 * still open, this will be postponed until it is closed.
 404 */
 405static void
 406spectrum_cs_release(struct pcmcia_device *link)
 407{
 408        struct net_device *dev = link->priv;
 409        struct orinoco_private *priv = netdev_priv(dev);
 410        unsigned long flags;
 411
 412        /* We're committed to taking the device away now, so mark the
 413         * hardware as unavailable */
 414        spin_lock_irqsave(&priv->lock, flags);
 415        priv->hw_unavailable++;
 416        spin_unlock_irqrestore(&priv->lock, flags);
 417
 418        pcmcia_disable_device(link);
 419        if (priv->hw.iobase)
 420                ioport_unmap(priv->hw.iobase);
 421}                               /* spectrum_cs_release */
 422
 423
 424static int
 425spectrum_cs_suspend(struct pcmcia_device *link)
 426{
 427        struct net_device *dev = link->priv;
 428        struct orinoco_private *priv = netdev_priv(dev);
 429        unsigned long flags;
 430        int err = 0;
 431
 432        /* Mark the device as stopped, to block IO until later */
 433        spin_lock_irqsave(&priv->lock, flags);
 434
 435        err = __orinoco_down(dev);
 436        if (err)
 437                printk(KERN_WARNING "%s: Error %d downing interface\n",
 438                       dev->name, err);
 439
 440        netif_device_detach(dev);
 441        priv->hw_unavailable++;
 442
 443        spin_unlock_irqrestore(&priv->lock, flags);
 444
 445        return err;
 446}
 447
 448static int
 449spectrum_cs_resume(struct pcmcia_device *link)
 450{
 451        struct net_device *dev = link->priv;
 452        struct orinoco_private *priv = netdev_priv(dev);
 453
 454        netif_device_attach(dev);
 455        priv->hw_unavailable--;
 456        schedule_work(&priv->reset_work);
 457
 458        return 0;
 459}
 460
 461
 462/********************************************************************/
 463/* Module initialization                                            */
 464/********************************************************************/
 465
 466/* Can't be declared "const" or the whole __initdata section will
 467 * become const */
 468static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
 469        " (Pavel Roskin <proski@gnu.org>,"
 470        " David Gibson <hermes@gibson.dropbear.id.au>, et al)";
 471
 472static struct pcmcia_device_id spectrum_cs_ids[] = {
 473        PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
 474        PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
 475        PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
 476        PCMCIA_DEVICE_NULL,
 477};
 478MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
 479
 480static struct pcmcia_driver orinoco_driver = {
 481        .owner          = THIS_MODULE,
 482        .drv            = {
 483                .name   = DRIVER_NAME,
 484        },
 485        .probe          = spectrum_cs_probe,
 486        .remove         = spectrum_cs_detach,
 487        .suspend        = spectrum_cs_suspend,
 488        .resume         = spectrum_cs_resume,
 489        .id_table       = spectrum_cs_ids,
 490};
 491
 492static int __init
 493init_spectrum_cs(void)
 494{
 495        printk(KERN_DEBUG "%s\n", version);
 496
 497        return pcmcia_register_driver(&orinoco_driver);
 498}
 499
 500static void __exit
 501exit_spectrum_cs(void)
 502{
 503        pcmcia_unregister_driver(&orinoco_driver);
 504}
 505
 506module_init(init_spectrum_cs);
 507module_exit(exit_spectrum_cs);
 508
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.