X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Forinoco_pci.c;fp=drivers%2Fnet%2Fwireless%2Forinoco_pci.c;h=5362c214fc8e299da8bd0003f5d2bd76ab9fea6f;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=1759c543fbee9bc6da6ed8bc2572cd765edc12e5;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco_pci.c index 1759c543f..5362c214f 100644 --- a/drivers/net/wireless/orinoco_pci.c +++ b/drivers/net/wireless/orinoco_pci.c @@ -1,11 +1,11 @@ /* orinoco_pci.c * - * Driver for Prism 2.5/3 devices that have a direct PCI interface - * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge). - * The card contains only one PCI region, which contains all the usual - * hermes registers, as well as the COR register. + * Driver for Prism II devices that have a direct PCI interface + * (i.e., not in a Pcmcia or PLX bridge) + * + * Specifically here we're talking about the Linksys WMP11 * - * Current maintainers are: + * Current maintainers (as of 29 September 2003) are: * Pavel Roskin * and David Gibson * @@ -41,9 +41,58 @@ * under either the MPL or the GPL. */ +/* + * Theory of operation... + * ------------------- + * Maybe you had a look in orinoco_plx. Well, this is totally different... + * + * The card contains only one PCI region, which contains all the usual + * hermes registers. + * + * The driver will memory map this region in normal memory. Because + * the hermes registers are mapped in normal memory and not in ISA I/O + * post space, we can't use the usual inw/outw macros and we need to + * use readw/writew. + * This slight difference force us to compile our own version of + * hermes.c with the register access macro changed. That's a bit + * hackish but works fine. + * + * Note that the PCI region is pretty big (4K). That's much more than + * the usual set of hermes register (0x0 -> 0x3E). I've got a strong + * suspicion that the whole memory space of the adapter is in fact in + * this region. Accessing directly the adapter memory instead of going + * through the usual register would speed up significantely the + * operations... + * + * Finally, the card looks like this : +----------------------- + Bus 0, device 14, function 0: + Network controller: PCI device 1260:3873 (Harris Semiconductor) (rev 1). + IRQ 11. + Master Capable. Latency=248. + Prefetchable 32 bit memory at 0xffbcc000 [0xffbccfff]. +----------------------- +00:0e.0 Network controller: Harris Semiconductor: Unknown device 3873 (rev 01) + Subsystem: Unknown device 1737:3874 + Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- + Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- SERR- #include #include #include @@ -51,14 +100,12 @@ #include #include "orinoco.h" -#include "orinoco_pci.h" -/* Offset of the COR register of the PCI card */ +/* All the magic there is from wlan-ng */ +/* Magic offset of the reset register of the PCI card */ #define HERMES_PCI_COR (0x26) - -/* Bitmask to reset the card */ +/* Magic bitmask to reset the card */ #define HERMES_PCI_COR_MASK (0x0080) - /* Magic timeouts for doing the reset. * Those times are straight from wlan-ng, and it is claimed that they * are necessary. Alan will kill me. Take your time and grab a coffee. */ @@ -66,8 +113,13 @@ #define HERMES_PCI_COR_OFFT (500) /* ms */ #define HERMES_PCI_COR_BUSYT (500) /* ms */ +/* Orinoco PCI specific data */ +struct orinoco_pci_card { + void __iomem *pci_ioaddr; +}; + /* - * Do a soft reset of the card using the Configuration Option Register + * Do a soft reset of the PCI card using the Configuration Option Register * We need this to get going... * This is the part of the code that is strongly inspired from wlan-ng * @@ -79,13 +131,14 @@ * Note bis : Don't try to access HERMES_CMD during the reset phase. * It just won't work ! */ -static int orinoco_pci_cor_reset(struct orinoco_private *priv) +static int +orinoco_pci_cor_reset(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; - unsigned long timeout; - u16 reg; + unsigned long timeout; + u16 reg; - /* Assert the reset until the card notices */ + /* Assert the reset until the card notice */ hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); mdelay(HERMES_PCI_COR_ONT); @@ -110,14 +163,19 @@ static int orinoco_pci_cor_reset(struct orinoco_private *priv) return 0; } +/* + * Initialise a card. Mostly similar to PLX code. + */ static int orinoco_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - int err; - struct orinoco_private *priv; + int err = 0; + unsigned long pci_iorange; + u16 __iomem *pci_ioaddr = NULL; + unsigned long pci_iolen; + struct orinoco_private *priv = NULL; struct orinoco_pci_card *card; - struct net_device *dev; - void __iomem *hermes_io; + struct net_device *dev = NULL; err = pci_enable_device(pdev); if (err) { @@ -126,41 +184,50 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, } err = pci_request_regions(pdev, DRIVER_NAME); - if (err) { + if (err != 0) { printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); goto fail_resources; } - hermes_io = pci_iomap(pdev, 0, 0); - if (!hermes_io) { - printk(KERN_ERR PFX "Cannot remap chipset registers\n"); - err = -EIO; - goto fail_map_hermes; + /* Resource 0 is mapped to the hermes registers */ + pci_iorange = pci_resource_start(pdev, 0); + pci_iolen = pci_resource_len(pdev, 0); + pci_ioaddr = ioremap(pci_iorange, pci_iolen); + if (!pci_iorange) { + printk(KERN_ERR PFX "Cannot remap hardware registers\n"); + goto fail_map; } /* Allocate network device */ dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset); - if (!dev) { - printk(KERN_ERR PFX "Cannot allocate network device\n"); + if (! dev) { err = -ENOMEM; goto fail_alloc; } priv = netdev_priv(dev); card = priv->card; + card->pci_ioaddr = pci_ioaddr; + dev->mem_start = pci_iorange; + dev->mem_end = pci_iorange + pci_iolen - 1; SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); + hermes_struct_init(&priv->hw, pci_ioaddr, HERMES_32BIT_REGSPACING); + + printk(KERN_DEBUG PFX "Detected device %s, mem:0x%lx-0x%lx, irq %d\n", + pci_name(pdev), dev->mem_start, dev->mem_end, pdev->irq); - err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, + err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, dev); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; goto fail_irq; } + dev->irq = pdev->irq; + /* Perform a COR reset to start the card */ err = orinoco_pci_cor_reset(priv); if (err) { printk(KERN_ERR PFX "Initial reset failed\n"); @@ -169,13 +236,11 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, err = register_netdev(dev); if (err) { - printk(KERN_ERR PFX "Cannot register network device\n"); + printk(KERN_ERR PFX "Failed to register net device\n"); goto fail; } pci_set_drvdata(pdev, dev); - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s\n", dev->name, - pci_name(pdev)); return 0; @@ -187,9 +252,9 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, free_orinocodev(dev); fail_alloc: - pci_iounmap(pdev, hermes_io); + iounmap(pci_ioaddr); - fail_map_hermes: + fail_map: pci_release_regions(pdev); fail_resources: @@ -202,17 +267,87 @@ static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_pci_card *card = priv->card; unregister_netdev(dev); - free_irq(pdev->irq, dev); + free_irq(dev->irq, dev); pci_set_drvdata(pdev, NULL); free_orinocodev(dev); - pci_iounmap(pdev, priv->hw.iobase); + iounmap(card->pci_ioaddr); pci_release_regions(pdev); pci_disable_device(pdev); } -static struct pci_device_id orinoco_pci_id_table[] = { +static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct orinoco_private *priv = netdev_priv(dev); + unsigned long flags; + int err; + + + err = orinoco_lock(priv, &flags); + if (err) { + printk(KERN_ERR "%s: hw_unavailable on orinoco_pci_suspend\n", + dev->name); + return err; + } + + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: orinoco_pci_suspend(): Error %d downing interface\n", + dev->name, err); + + netif_device_detach(dev); + + priv->hw_unavailable++; + + orinoco_unlock(priv, &flags); + + pci_save_state(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int orinoco_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct orinoco_private *priv = netdev_priv(dev); + unsigned long flags; + int err; + + printk(KERN_DEBUG "%s: Orinoco-PCI waking up\n", dev->name); + + pci_set_power_state(pdev, 0); + pci_restore_state(pdev); + + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware on orinoco_pci_resume()\n", + dev->name, err); + return err; + } + + spin_lock_irqsave(&priv->lock, flags); + + netif_device_attach(dev); + + priv->hw_unavailable--; + + if (priv->open && (! priv->hw_unavailable)) { + err = __orinoco_up(dev); + if (err) + printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n", + dev->name, err); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static struct pci_device_id orinoco_pci_pci_id_table[] = { /* Intersil Prism 3 */ {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, /* Intersil Prism 2.5 */ @@ -222,11 +357,11 @@ static struct pci_device_id orinoco_pci_id_table[] = { {0,}, }; -MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table); +MODULE_DEVICE_TABLE(pci, orinoco_pci_pci_id_table); static struct pci_driver orinoco_pci_driver = { .name = DRIVER_NAME, - .id_table = orinoco_pci_id_table, + .id_table = orinoco_pci_pci_id_table, .probe = orinoco_pci_init_one, .remove = __devexit_p(orinoco_pci_remove_one), .suspend = orinoco_pci_suspend,