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.13-rc6 417 lines 12 kB view raw
1/* orinoco_pci.c 2 * 3 * Driver for Prism II devices that have a direct PCI interface 4 * (i.e., not in a Pcmcia or PLX bridge) 5 * 6 * Specifically here we're talking about the Linksys WMP11 7 * 8 * Current maintainers (as of 29 September 2003) are: 9 * Pavel Roskin <proski AT gnu.org> 10 * and David Gibson <hermes AT gibson.dropbear.id.au> 11 * 12 * Some of this code is borrowed from orinoco_plx.c 13 * Copyright (C) 2001 Daniel Barlow <dan AT telent.net> 14 * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing 15 * has been copied from it. linux-wlan-ng-0.1.10 is originally : 16 * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. 17 * This file originally written by: 18 * Copyright (C) 2001 Jean Tourrilhes <jt AT hpl.hp.com> 19 * And is now maintained by: 20 * (C) Copyright David Gibson, IBM Corp. 2002-2003. 21 * 22 * The contents of this file are subject to the Mozilla Public License 23 * Version 1.1 (the "License"); you may not use this file except in 24 * compliance with the License. You may obtain a copy of the License 25 * at http://www.mozilla.org/MPL/ 26 * 27 * Software distributed under the License is distributed on an "AS IS" 28 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 29 * the License for the specific language governing rights and 30 * limitations under the License. 31 * 32 * Alternatively, the contents of this file may be used under the 33 * terms of the GNU General Public License version 2 (the "GPL"), in 34 * which case the provisions of the GPL are applicable instead of the 35 * above. If you wish to allow the use of your version of this file 36 * only under the terms of the GPL and not to allow others to use your 37 * version of this file under the MPL, indicate your decision by 38 * deleting the provisions above and replace them with the notice and 39 * other provisions required by the GPL. If you do not delete the 40 * provisions above, a recipient may use your version of this file 41 * under either the MPL or the GPL. 42 */ 43 44/* 45 * Theory of operation... 46 * ------------------- 47 * Maybe you had a look in orinoco_plx. Well, this is totally different... 48 * 49 * The card contains only one PCI region, which contains all the usual 50 * hermes registers. 51 * 52 * The driver will memory map this region in normal memory. Because 53 * the hermes registers are mapped in normal memory and not in ISA I/O 54 * post space, we can't use the usual inw/outw macros and we need to 55 * use readw/writew. 56 * This slight difference force us to compile our own version of 57 * hermes.c with the register access macro changed. That's a bit 58 * hackish but works fine. 59 * 60 * Note that the PCI region is pretty big (4K). That's much more than 61 * the usual set of hermes register (0x0 -> 0x3E). I've got a strong 62 * suspicion that the whole memory space of the adapter is in fact in 63 * this region. Accessing directly the adapter memory instead of going 64 * through the usual register would speed up significantely the 65 * operations... 66 * 67 * Finally, the card looks like this : 68----------------------- 69 Bus 0, device 14, function 0: 70 Network controller: PCI device 1260:3873 (Harris Semiconductor) (rev 1). 71 IRQ 11. 72 Master Capable. Latency=248. 73 Prefetchable 32 bit memory at 0xffbcc000 [0xffbccfff]. 74----------------------- 7500:0e.0 Network controller: Harris Semiconductor: Unknown device 3873 (rev 01) 76 Subsystem: Unknown device 1737:3874 77 Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- 78 Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- 79 Latency: 248 set, cache line size 08 80 Interrupt: pin A routed to IRQ 11 81 Region 0: Memory at ffbcc000 (32-bit, prefetchable) [size=4K] 82 Capabilities: [dc] Power Management version 2 83 Flags: PMEClk- AuxPwr- DSI- D1+ D2+ PME+ 84 Status: D0 PME-Enable- DSel=0 DScale=0 PME- 85----------------------- 86 * 87 * That's all.. 88 * 89 * Jean II 90 */ 91 92#define DRIVER_NAME "orinoco_pci" 93#define PFX DRIVER_NAME ": " 94 95#include <linux/config.h> 96 97#include <linux/module.h> 98#include <linux/kernel.h> 99#include <linux/init.h> 100#include <linux/sched.h> 101#include <linux/ptrace.h> 102#include <linux/slab.h> 103#include <linux/string.h> 104#include <linux/timer.h> 105#include <linux/ioport.h> 106#include <linux/netdevice.h> 107#include <linux/if_arp.h> 108#include <linux/etherdevice.h> 109#include <linux/list.h> 110#include <linux/pci.h> 111#include <linux/fcntl.h> 112 113#include <asm/uaccess.h> 114#include <asm/io.h> 115#include <asm/system.h> 116 117#include "hermes.h" 118#include "orinoco.h" 119 120/* All the magic there is from wlan-ng */ 121/* Magic offset of the reset register of the PCI card */ 122#define HERMES_PCI_COR (0x26) 123/* Magic bitmask to reset the card */ 124#define HERMES_PCI_COR_MASK (0x0080) 125/* Magic timeouts for doing the reset. 126 * Those times are straight from wlan-ng, and it is claimed that they 127 * are necessary. Alan will kill me. Take your time and grab a coffee. */ 128#define HERMES_PCI_COR_ONT (250) /* ms */ 129#define HERMES_PCI_COR_OFFT (500) /* ms */ 130#define HERMES_PCI_COR_BUSYT (500) /* ms */ 131 132/* Orinoco PCI specific data */ 133struct orinoco_pci_card { 134 void __iomem *pci_ioaddr; 135}; 136 137/* 138 * Do a soft reset of the PCI card using the Configuration Option Register 139 * We need this to get going... 140 * This is the part of the code that is strongly inspired from wlan-ng 141 * 142 * Note : This code is done with irq enabled. This mean that many 143 * interrupts will occur while we are there. This is why we use the 144 * jiffies to regulate time instead of a straight mdelay(). Usually we 145 * need only around 245 iteration of the loop to do 250 ms delay. 146 * 147 * Note bis : Don't try to access HERMES_CMD during the reset phase. 148 * It just won't work ! 149 */ 150static int 151orinoco_pci_cor_reset(struct orinoco_private *priv) 152{ 153 hermes_t *hw = &priv->hw; 154 unsigned long timeout; 155 u16 reg; 156 157 /* Assert the reset until the card notice */ 158 hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); 159 mdelay(HERMES_PCI_COR_ONT); 160 161 /* Give time for the card to recover from this hard effort */ 162 hermes_write_regn(hw, PCI_COR, 0x0000); 163 mdelay(HERMES_PCI_COR_OFFT); 164 165 /* The card is ready when it's no longer busy */ 166 timeout = jiffies + (HERMES_PCI_COR_BUSYT * HZ / 1000); 167 reg = hermes_read_regn(hw, CMD); 168 while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { 169 mdelay(1); 170 reg = hermes_read_regn(hw, CMD); 171 } 172 173 /* Still busy? */ 174 if (reg & HERMES_CMD_BUSY) { 175 printk(KERN_ERR PFX "Busy timeout\n"); 176 return -ETIMEDOUT; 177 } 178 179 return 0; 180} 181 182/* 183 * Initialise a card. Mostly similar to PLX code. 184 */ 185static int orinoco_pci_init_one(struct pci_dev *pdev, 186 const struct pci_device_id *ent) 187{ 188 int err = 0; 189 unsigned long pci_iorange; 190 u16 __iomem *pci_ioaddr = NULL; 191 unsigned long pci_iolen; 192 struct orinoco_private *priv = NULL; 193 struct orinoco_pci_card *card; 194 struct net_device *dev = NULL; 195 196 err = pci_enable_device(pdev); 197 if (err) { 198 printk(KERN_ERR PFX "Cannot enable PCI device\n"); 199 return err; 200 } 201 202 err = pci_request_regions(pdev, DRIVER_NAME); 203 if (err != 0) { 204 printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); 205 goto fail_resources; 206 } 207 208 /* Resource 0 is mapped to the hermes registers */ 209 pci_iorange = pci_resource_start(pdev, 0); 210 pci_iolen = pci_resource_len(pdev, 0); 211 pci_ioaddr = ioremap(pci_iorange, pci_iolen); 212 if (!pci_iorange) { 213 printk(KERN_ERR PFX "Cannot remap hardware registers\n"); 214 goto fail_map; 215 } 216 217 /* Allocate network device */ 218 dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset); 219 if (! dev) { 220 err = -ENOMEM; 221 goto fail_alloc; 222 } 223 224 priv = netdev_priv(dev); 225 card = priv->card; 226 card->pci_ioaddr = pci_ioaddr; 227 dev->mem_start = pci_iorange; 228 dev->mem_end = pci_iorange + pci_iolen - 1; 229 SET_MODULE_OWNER(dev); 230 SET_NETDEV_DEV(dev, &pdev->dev); 231 232 hermes_struct_init(&priv->hw, pci_ioaddr, HERMES_32BIT_REGSPACING); 233 234 printk(KERN_DEBUG PFX "Detected device %s, mem:0x%lx-0x%lx, irq %d\n", 235 pci_name(pdev), dev->mem_start, dev->mem_end, pdev->irq); 236 237 err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, 238 dev->name, dev); 239 if (err) { 240 printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); 241 err = -EBUSY; 242 goto fail_irq; 243 } 244 dev->irq = pdev->irq; 245 246 /* Perform a COR reset to start the card */ 247 err = orinoco_pci_cor_reset(priv); 248 if (err) { 249 printk(KERN_ERR PFX "Initial reset failed\n"); 250 goto fail; 251 } 252 253 err = register_netdev(dev); 254 if (err) { 255 printk(KERN_ERR PFX "Failed to register net device\n"); 256 goto fail; 257 } 258 259 pci_set_drvdata(pdev, dev); 260 261 return 0; 262 263 fail: 264 free_irq(pdev->irq, dev); 265 266 fail_irq: 267 pci_set_drvdata(pdev, NULL); 268 free_orinocodev(dev); 269 270 fail_alloc: 271 iounmap(pci_ioaddr); 272 273 fail_map: 274 pci_release_regions(pdev); 275 276 fail_resources: 277 pci_disable_device(pdev); 278 279 return err; 280} 281 282static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev) 283{ 284 struct net_device *dev = pci_get_drvdata(pdev); 285 struct orinoco_private *priv = netdev_priv(dev); 286 struct orinoco_pci_card *card = priv->card; 287 288 unregister_netdev(dev); 289 free_irq(dev->irq, dev); 290 pci_set_drvdata(pdev, NULL); 291 free_orinocodev(dev); 292 iounmap(card->pci_ioaddr); 293 pci_release_regions(pdev); 294 pci_disable_device(pdev); 295} 296 297static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) 298{ 299 struct net_device *dev = pci_get_drvdata(pdev); 300 struct orinoco_private *priv = netdev_priv(dev); 301 unsigned long flags; 302 int err; 303 304 printk(KERN_DEBUG "%s: Orinoco-PCI entering sleep mode (state=%d)\n", 305 dev->name, state); 306 307 err = orinoco_lock(priv, &flags); 308 if (err) { 309 printk(KERN_ERR "%s: hw_unavailable on orinoco_pci_suspend\n", 310 dev->name); 311 return err; 312 } 313 314 err = __orinoco_down(dev); 315 if (err) 316 printk(KERN_WARNING "%s: orinoco_pci_suspend(): Error %d downing interface\n", 317 dev->name, err); 318 319 netif_device_detach(dev); 320 321 priv->hw_unavailable++; 322 323 orinoco_unlock(priv, &flags); 324 325 pci_save_state(pdev); 326 pci_set_power_state(pdev, PCI_D3hot); 327 328 return 0; 329} 330 331static int orinoco_pci_resume(struct pci_dev *pdev) 332{ 333 struct net_device *dev = pci_get_drvdata(pdev); 334 struct orinoco_private *priv = netdev_priv(dev); 335 unsigned long flags; 336 int err; 337 338 printk(KERN_DEBUG "%s: Orinoco-PCI waking up\n", dev->name); 339 340 pci_set_power_state(pdev, 0); 341 pci_restore_state(pdev); 342 343 err = orinoco_reinit_firmware(dev); 344 if (err) { 345 printk(KERN_ERR "%s: Error %d re-initializing firmware on orinoco_pci_resume()\n", 346 dev->name, err); 347 return err; 348 } 349 350 spin_lock_irqsave(&priv->lock, flags); 351 352 netif_device_attach(dev); 353 354 priv->hw_unavailable--; 355 356 if (priv->open && (! priv->hw_unavailable)) { 357 err = __orinoco_up(dev); 358 if (err) 359 printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n", 360 dev->name, err); 361 } 362 363 spin_unlock_irqrestore(&priv->lock, flags); 364 365 return 0; 366} 367 368static struct pci_device_id orinoco_pci_pci_id_table[] = { 369 /* Intersil Prism 3 */ 370 {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, 371 /* Intersil Prism 2.5 */ 372 {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, 373 /* Samsung MagicLAN SWL-2210P */ 374 {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, 375 {0,}, 376}; 377 378MODULE_DEVICE_TABLE(pci, orinoco_pci_pci_id_table); 379 380static struct pci_driver orinoco_pci_driver = { 381 .name = DRIVER_NAME, 382 .id_table = orinoco_pci_pci_id_table, 383 .probe = orinoco_pci_init_one, 384 .remove = __devexit_p(orinoco_pci_remove_one), 385 .suspend = orinoco_pci_suspend, 386 .resume = orinoco_pci_resume, 387}; 388 389static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION 390 " (Pavel Roskin <proski@gnu.org>," 391 " David Gibson <hermes@gibson.dropbear.id.au> &" 392 " Jean Tourrilhes <jt@hpl.hp.com>)"; 393MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & David Gibson <hermes@gibson.dropbear.id.au>"); 394MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); 395MODULE_LICENSE("Dual MPL/GPL"); 396 397static int __init orinoco_pci_init(void) 398{ 399 printk(KERN_DEBUG "%s\n", version); 400 return pci_module_init(&orinoco_pci_driver); 401} 402 403static void __exit orinoco_pci_exit(void) 404{ 405 pci_unregister_driver(&orinoco_pci_driver); 406} 407 408module_init(orinoco_pci_init); 409module_exit(orinoco_pci_exit); 410 411/* 412 * Local variables: 413 * c-indent-level: 8 414 * c-basic-offset: 8 415 * tab-width: 8 416 * End: 417 */