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.17-rc2 399 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#include <linux/module.h> 97#include <linux/kernel.h> 98#include <linux/init.h> 99#include <linux/delay.h> 100#include <linux/pci.h> 101 102#include "orinoco.h" 103 104/* All the magic there is from wlan-ng */ 105/* Magic offset of the reset register of the PCI card */ 106#define HERMES_PCI_COR (0x26) 107/* Magic bitmask to reset the card */ 108#define HERMES_PCI_COR_MASK (0x0080) 109/* Magic timeouts for doing the reset. 110 * Those times are straight from wlan-ng, and it is claimed that they 111 * are necessary. Alan will kill me. Take your time and grab a coffee. */ 112#define HERMES_PCI_COR_ONT (250) /* ms */ 113#define HERMES_PCI_COR_OFFT (500) /* ms */ 114#define HERMES_PCI_COR_BUSYT (500) /* ms */ 115 116/* Orinoco PCI specific data */ 117struct orinoco_pci_card { 118 void __iomem *pci_ioaddr; 119}; 120 121/* 122 * Do a soft reset of the PCI card using the Configuration Option Register 123 * We need this to get going... 124 * This is the part of the code that is strongly inspired from wlan-ng 125 * 126 * Note : This code is done with irq enabled. This mean that many 127 * interrupts will occur while we are there. This is why we use the 128 * jiffies to regulate time instead of a straight mdelay(). Usually we 129 * need only around 245 iteration of the loop to do 250 ms delay. 130 * 131 * Note bis : Don't try to access HERMES_CMD during the reset phase. 132 * It just won't work ! 133 */ 134static int 135orinoco_pci_cor_reset(struct orinoco_private *priv) 136{ 137 hermes_t *hw = &priv->hw; 138 unsigned long timeout; 139 u16 reg; 140 141 /* Assert the reset until the card notice */ 142 hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); 143 mdelay(HERMES_PCI_COR_ONT); 144 145 /* Give time for the card to recover from this hard effort */ 146 hermes_write_regn(hw, PCI_COR, 0x0000); 147 mdelay(HERMES_PCI_COR_OFFT); 148 149 /* The card is ready when it's no longer busy */ 150 timeout = jiffies + (HERMES_PCI_COR_BUSYT * HZ / 1000); 151 reg = hermes_read_regn(hw, CMD); 152 while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { 153 mdelay(1); 154 reg = hermes_read_regn(hw, CMD); 155 } 156 157 /* Still busy? */ 158 if (reg & HERMES_CMD_BUSY) { 159 printk(KERN_ERR PFX "Busy timeout\n"); 160 return -ETIMEDOUT; 161 } 162 163 return 0; 164} 165 166/* 167 * Initialise a card. Mostly similar to PLX code. 168 */ 169static int orinoco_pci_init_one(struct pci_dev *pdev, 170 const struct pci_device_id *ent) 171{ 172 int err = 0; 173 unsigned long pci_iorange; 174 u16 __iomem *pci_ioaddr = NULL; 175 unsigned long pci_iolen; 176 struct orinoco_private *priv = NULL; 177 struct orinoco_pci_card *card; 178 struct net_device *dev = NULL; 179 180 err = pci_enable_device(pdev); 181 if (err) { 182 printk(KERN_ERR PFX "Cannot enable PCI device\n"); 183 return err; 184 } 185 186 err = pci_request_regions(pdev, DRIVER_NAME); 187 if (err != 0) { 188 printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); 189 goto fail_resources; 190 } 191 192 /* Resource 0 is mapped to the hermes registers */ 193 pci_iorange = pci_resource_start(pdev, 0); 194 pci_iolen = pci_resource_len(pdev, 0); 195 pci_ioaddr = ioremap(pci_iorange, pci_iolen); 196 if (!pci_iorange) { 197 printk(KERN_ERR PFX "Cannot remap hardware registers\n"); 198 goto fail_map; 199 } 200 201 /* Allocate network device */ 202 dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset); 203 if (! dev) { 204 err = -ENOMEM; 205 goto fail_alloc; 206 } 207 208 priv = netdev_priv(dev); 209 card = priv->card; 210 card->pci_ioaddr = pci_ioaddr; 211 dev->mem_start = pci_iorange; 212 dev->mem_end = pci_iorange + pci_iolen - 1; 213 SET_MODULE_OWNER(dev); 214 SET_NETDEV_DEV(dev, &pdev->dev); 215 216 hermes_struct_init(&priv->hw, pci_ioaddr, HERMES_32BIT_REGSPACING); 217 218 printk(KERN_DEBUG PFX "Detected device %s, mem:0x%lx-0x%lx, irq %d\n", 219 pci_name(pdev), dev->mem_start, dev->mem_end, pdev->irq); 220 221 err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, 222 dev->name, dev); 223 if (err) { 224 printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); 225 err = -EBUSY; 226 goto fail_irq; 227 } 228 dev->irq = pdev->irq; 229 230 /* Perform a COR reset to start the card */ 231 err = orinoco_pci_cor_reset(priv); 232 if (err) { 233 printk(KERN_ERR PFX "Initial reset failed\n"); 234 goto fail; 235 } 236 237 err = register_netdev(dev); 238 if (err) { 239 printk(KERN_ERR PFX "Failed to register net device\n"); 240 goto fail; 241 } 242 243 pci_set_drvdata(pdev, dev); 244 245 return 0; 246 247 fail: 248 free_irq(pdev->irq, dev); 249 250 fail_irq: 251 pci_set_drvdata(pdev, NULL); 252 free_orinocodev(dev); 253 254 fail_alloc: 255 iounmap(pci_ioaddr); 256 257 fail_map: 258 pci_release_regions(pdev); 259 260 fail_resources: 261 pci_disable_device(pdev); 262 263 return err; 264} 265 266static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev) 267{ 268 struct net_device *dev = pci_get_drvdata(pdev); 269 struct orinoco_private *priv = netdev_priv(dev); 270 struct orinoco_pci_card *card = priv->card; 271 272 unregister_netdev(dev); 273 free_irq(dev->irq, dev); 274 pci_set_drvdata(pdev, NULL); 275 free_orinocodev(dev); 276 iounmap(card->pci_ioaddr); 277 pci_release_regions(pdev); 278 pci_disable_device(pdev); 279} 280 281static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) 282{ 283 struct net_device *dev = pci_get_drvdata(pdev); 284 struct orinoco_private *priv = netdev_priv(dev); 285 unsigned long flags; 286 int err; 287 288 289 err = orinoco_lock(priv, &flags); 290 if (err) { 291 printk(KERN_ERR "%s: hw_unavailable on orinoco_pci_suspend\n", 292 dev->name); 293 return err; 294 } 295 296 err = __orinoco_down(dev); 297 if (err) 298 printk(KERN_WARNING "%s: orinoco_pci_suspend(): Error %d downing interface\n", 299 dev->name, err); 300 301 netif_device_detach(dev); 302 303 priv->hw_unavailable++; 304 305 orinoco_unlock(priv, &flags); 306 307 pci_save_state(pdev); 308 pci_set_power_state(pdev, PCI_D3hot); 309 310 return 0; 311} 312 313static int orinoco_pci_resume(struct pci_dev *pdev) 314{ 315 struct net_device *dev = pci_get_drvdata(pdev); 316 struct orinoco_private *priv = netdev_priv(dev); 317 unsigned long flags; 318 int err; 319 320 printk(KERN_DEBUG "%s: Orinoco-PCI waking up\n", dev->name); 321 322 pci_set_power_state(pdev, 0); 323 pci_restore_state(pdev); 324 325 err = orinoco_reinit_firmware(dev); 326 if (err) { 327 printk(KERN_ERR "%s: Error %d re-initializing firmware on orinoco_pci_resume()\n", 328 dev->name, err); 329 return err; 330 } 331 332 spin_lock_irqsave(&priv->lock, flags); 333 334 netif_device_attach(dev); 335 336 priv->hw_unavailable--; 337 338 if (priv->open && (! priv->hw_unavailable)) { 339 err = __orinoco_up(dev); 340 if (err) 341 printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n", 342 dev->name, err); 343 } 344 345 spin_unlock_irqrestore(&priv->lock, flags); 346 347 return 0; 348} 349 350static struct pci_device_id orinoco_pci_pci_id_table[] = { 351 /* Intersil Prism 3 */ 352 {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, 353 /* Intersil Prism 2.5 */ 354 {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, 355 /* Samsung MagicLAN SWL-2210P */ 356 {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, 357 {0,}, 358}; 359 360MODULE_DEVICE_TABLE(pci, orinoco_pci_pci_id_table); 361 362static struct pci_driver orinoco_pci_driver = { 363 .name = DRIVER_NAME, 364 .id_table = orinoco_pci_pci_id_table, 365 .probe = orinoco_pci_init_one, 366 .remove = __devexit_p(orinoco_pci_remove_one), 367 .suspend = orinoco_pci_suspend, 368 .resume = orinoco_pci_resume, 369}; 370 371static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION 372 " (Pavel Roskin <proski@gnu.org>," 373 " David Gibson <hermes@gibson.dropbear.id.au> &" 374 " Jean Tourrilhes <jt@hpl.hp.com>)"; 375MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & David Gibson <hermes@gibson.dropbear.id.au>"); 376MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); 377MODULE_LICENSE("Dual MPL/GPL"); 378 379static int __init orinoco_pci_init(void) 380{ 381 printk(KERN_DEBUG "%s\n", version); 382 return pci_module_init(&orinoco_pci_driver); 383} 384 385static void __exit orinoco_pci_exit(void) 386{ 387 pci_unregister_driver(&orinoco_pci_driver); 388} 389 390module_init(orinoco_pci_init); 391module_exit(orinoco_pci_exit); 392 393/* 394 * Local variables: 395 * c-indent-level: 8 396 * c-basic-offset: 8 397 * tab-width: 8 398 * End: 399 */