Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.28-rc3 507 lines 15 kB view raw
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);