diff options
Diffstat (limited to 'drivers/staging/wlan-ng/prism2_plx.c')
-rw-r--r-- | drivers/staging/wlan-ng/prism2_plx.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/drivers/staging/wlan-ng/prism2_plx.c b/drivers/staging/wlan-ng/prism2_plx.c new file mode 100644 index 0000000..320443f --- /dev/null +++ b/drivers/staging/wlan-ng/prism2_plx.c @@ -0,0 +1,472 @@ +#define WLAN_HOSTIF WLAN_PLX +#include "hfa384x.c" +#include "prism2mgmt.c" +#include "prism2mib.c" +#include "prism2sta.c" + +#define PLX_ATTR_SIZE 0x1000 /* Attribute memory size - 4K bytes */ +#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */ +#define COR_VALUE 0x41 /* Enable PC card with irq in level trigger */ +#define PLX_INTCSR 0x4c /* Interrupt Control and Status Register */ +#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */ +#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ + +/* 3Com 3CRW777A (PLX) board ID */ +#define PCIVENDOR_3COM 0x10B7 +#define PCIDEVICE_AIRCONNECT 0x7770 + +/* Eumitcom PCI WL11000 PCI Adapter (PLX) board device+vendor ID */ +#define PCIVENDOR_EUMITCOM 0x1638UL +#define PCIDEVICE_WL11000 0x1100UL + +/* Global Sun Tech GL24110P PCI Adapter (PLX) board device+vendor ID */ +#define PCIVENDOR_GLOBALSUN 0x16abUL +#define PCIDEVICE_GL24110P 0x1101UL +#define PCIDEVICE_GL24110P_ALT 0x1102UL + +/* Netgear MA301 PCI Adapter (PLX) board device+vendor ID */ +#define PCIVENDOR_NETGEAR 0x1385UL +#define PCIDEVICE_MA301 0x4100UL + +/* US Robotics USR2410 PCI Adapter (PLX) board device+vendor ID */ +#define PCIVENDOR_USROBOTICS 0x16ecUL +#define PCIDEVICE_USR2410 0x3685UL + +/* Linksys WPC11 card with the WDT11 adapter (PLX) board device+vendor ID */ +#define PCIVENDOR_Linksys 0x16abUL +#define PCIDEVICE_Wpc11Wdt11 0x1102UL + +/* National Datacomm Corp SOHOware Netblaster II PCI */ +#define PCIVENDOR_NDC 0x15e8UL +#define PCIDEVICE_NCP130_PLX 0x0130UL +#define PCIDEVICE_NCP130_ASIC 0x0131UL + +/* NDC NCP130_PLX is also sold by Corega. Their name is CGWLPCIA11 */ +#define PCIVENDOR_COREGA PCIVENDOR_NDC +#define PCIDEVICE_CGWLPCIA11 PCIDEVICE_NCP130_PLX + +/* PCI Class & Sub-Class code, Network-'Other controller' */ +#define PCI_CLASS_NETWORK_OTHERS 0x280 + +/*---------------------------------------------------------------- +* prism2sta_probe_plx +* +* Probe routine called when a PCI device w/ matching ID is found. +* This PLX implementation uses the following map: +* BAR0: Unused +* BAR1: ???? +* BAR2: PCMCIA attribute memory +* BAR3: PCMCIA i/o space +* Here's the sequence: +* - Allocate the PCI resources. +* - Read the PCMCIA attribute memory to make sure we have a WLAN card +* - Reset the MAC using the PCMCIA COR +* - Initialize the netdev and wlan data +* - Initialize the MAC +* +* Arguments: +* pdev ptr to pci device structure containing info about +* pci configuration. +* id ptr to the device id entry that matched this device. +* +* Returns: +* zero - success +* negative - failed +* +* Side effects: +* +* +* Call context: +* process thread +* +----------------------------------------------------------------*/ +static int __devinit +prism2sta_probe_plx( + struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int result; + phys_t pccard_ioaddr; + phys_t pccard_attr_mem; + unsigned int pccard_attr_len; + void __iomem *attr_mem = NULL; + UINT32 plx_addr; + wlandevice_t *wlandev = NULL; + hfa384x_t *hw = NULL; + int reg; + u32 regic; + + if (pci_enable_device(pdev)) + return -EIO; + + /* TMC7160 boards are special */ + if ((pdev->vendor == PCIVENDOR_NDC) && + (pdev->device == PCIDEVICE_NCP130_ASIC)) { + unsigned long delay; + + pccard_attr_mem = 0; + pccard_ioaddr = pci_resource_start(pdev, 1); + + outb(0x45, pccard_ioaddr); + delay = jiffies + 1*HZ; + while (time_before(jiffies, delay)); + + if (inb(pccard_ioaddr) != 0x45) { + WLAN_LOG_ERROR("Initialize the TMC7160 failed. (0x%x)\n", inb(pccard_ioaddr)); + return -EIO; + } + + pccard_ioaddr = pci_resource_start(pdev, 2); + prism2_doreset = 0; + + WLAN_LOG_INFO("NDC NCP130 with TMC716(ASIC) PCI interface device found at io:0x%x, irq:%d\n", pccard_ioaddr, pdev->irq); + goto init; + } + + /* Collect the resource requirements */ + pccard_attr_mem = pci_resource_start(pdev, 2); + pccard_attr_len = pci_resource_len(pdev, 2); + if (pccard_attr_len < PLX_MIN_ATTR_LEN) + return -EIO; + + pccard_ioaddr = pci_resource_start(pdev, 3); + + /* bjoern: We need to tell the card to enable interrupts, in + * case the serial eprom didn't do this already. See the + * PLX9052 data book, p8-1 and 8-24 for reference. + * [MSM]: This bit of code came from the orinoco_cs driver. + */ + plx_addr = pci_resource_start(pdev, 1); + + regic = 0; + regic = inl(plx_addr+PLX_INTCSR); + if(regic & PLX_INTCSR_INTEN) { + WLAN_LOG_DEBUG(1, + "%s: Local Interrupt already enabled\n", dev_info); + } else { + regic |= PLX_INTCSR_INTEN; + outl(regic, plx_addr+PLX_INTCSR); + regic = inl(plx_addr+PLX_INTCSR); + if(!(regic & PLX_INTCSR_INTEN)) { + WLAN_LOG_ERROR( + "%s: Couldn't enable Local Interrupts\n", + dev_info); + return -EIO; + } + } + + /* These assignments are here in case of future mappings for + * io space and irq that might be similar to ioremap + */ + if (!request_mem_region(pccard_attr_mem, pci_resource_len(pdev, 2), "Prism2")) { + WLAN_LOG_ERROR("%s: Couldn't reserve PCI memory region\n", dev_info); + return -EIO; + } + + attr_mem = ioremap(pccard_attr_mem, pccard_attr_len); + + WLAN_LOG_INFO("A PLX PCI/PCMCIA interface device found, " + "phymem:0x%llx, phyio=0x%x, irq:%d, " + "mem: 0x%lx\n", + (unsigned long long)pccard_attr_mem, pccard_ioaddr, pdev->irq, + (unsigned long)attr_mem); + + /* Verify whether PC card is present. + * [MSM] This needs improvement, the right thing to do is + * probably to walk the CIS looking for the vendor and product + * IDs. It would be nice if this could be tied in with the + * etc/pcmcia/wlan-ng.conf file. Any volunteers? ;-) + */ + if ( + readb(attr_mem + 0) != 0x01 || readb(attr_mem + 2) != 0x03 || + readb(attr_mem + 4) != 0x00 || readb(attr_mem + 6) != 0x00 || + readb(attr_mem + 8) != 0xFF || readb(attr_mem + 10) != 0x17 || + readb(attr_mem + 12) != 0x04 || readb(attr_mem + 14) != 0x67) { + WLAN_LOG_ERROR("Prism2 PC card CIS is invalid.\n"); + return -EIO; + } + WLAN_LOG_INFO("A PCMCIA WLAN adapter was found.\n"); + + /* Write COR to enable PC card */ + writeb(COR_VALUE, attr_mem + COR_OFFSET); + reg = readb(attr_mem + COR_OFFSET); + + init: + + /* + * Now do everything the same as a PCI device + * [MSM] TODO: We could probably factor this out of pcmcia/pci/plx + * and perhaps usb. Perhaps a task for another day....... + */ + + if ((wlandev = create_wlan()) == NULL) { + WLAN_LOG_ERROR("%s: Memory allocation failure.\n", dev_info); + result = -EIO; + goto failed; + } + + hw = wlandev->priv; + + if ( wlan_setup(wlandev) != 0 ) { + WLAN_LOG_ERROR("%s: wlan_setup() failed.\n", dev_info); + result = -EIO; + goto failed; + } + + /* Setup netdevice's ability to report resources + * Note: the netdevice was allocated by wlan_setup() + */ + wlandev->netdev->irq = pdev->irq; + wlandev->netdev->base_addr = pccard_ioaddr; + wlandev->netdev->mem_start = (unsigned long)attr_mem; + wlandev->netdev->mem_end = (unsigned long)attr_mem + pci_resource_len(pdev, 0); + + /* Initialize the hw data */ + hfa384x_create(hw, wlandev->netdev->irq, pccard_ioaddr, attr_mem); + hw->wlandev = wlandev; + + /* Register the wlandev, this gets us a name and registers the + * linux netdevice. + */ + SET_MODULE_OWNER(wlandev->netdev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) + SET_NETDEV_DEV(wlandev->netdev, &(pdev->dev)); +#endif + if ( register_wlandev(wlandev) != 0 ) { + WLAN_LOG_ERROR("%s: register_wlandev() failed.\n", dev_info); + result = -EIO; + goto failed; + } + +#if 0 + /* TODO: Move this and an irq test into an hfa384x_testif() routine. + */ + outw(PRISM2STA_MAGIC, HFA384x_SWSUPPORT(wlandev->netdev->base_addr)); + reg=inw( HFA384x_SWSUPPORT(wlandev->netdev->base_addr)); + if ( reg != PRISM2STA_MAGIC ) { + WLAN_LOG_ERROR("MAC register access test failed!\n"); + result = -EIO; + goto failed; + } +#endif + + /* Do a chip-level reset on the MAC */ + if (prism2_doreset) { + result = hfa384x_corereset(hw, + prism2_reset_holdtime, + prism2_reset_settletime, 0); + if (result != 0) { + unregister_wlandev(wlandev); + hfa384x_destroy(hw); + WLAN_LOG_ERROR( + "%s: hfa384x_corereset() failed.\n", + dev_info); + result = -EIO; + goto failed; + } + } + + pci_set_drvdata(pdev, wlandev); + + /* Shouldn't actually hook up the IRQ until we + * _know_ things are alright. A test routine would help. + */ + request_irq(wlandev->netdev->irq, hfa384x_interrupt, + SA_SHIRQ, wlandev->name, wlandev); + + wlandev->msdstate = WLAN_MSD_HWPRESENT; + + result = 0; + + goto done; + + failed: + + pci_set_drvdata(pdev, NULL); + if (wlandev) kfree(wlandev); + if (hw) kfree(hw); + if (attr_mem) iounmap(attr_mem); + pci_release_regions(pdev); + pci_disable_device(pdev); + + done: + DBFEXIT; + return result; +} + +static void __devexit prism2sta_remove_plx(struct pci_dev *pdev) +{ + wlandevice_t *wlandev; + hfa384x_t *hw; + + wlandev = (wlandevice_t *) pci_get_drvdata(pdev); + hw = wlandev->priv; + + p80211netdev_hwremoved(wlandev); + + /* reset hardware */ + prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable); + + if (pdev->irq) + free_irq(pdev->irq, wlandev); + + unregister_wlandev(wlandev); + + /* free local stuff */ + if (hw) { + hfa384x_destroy(hw); + kfree(hw); + } + + iounmap((void __iomem *)wlandev->netdev->mem_start); + wlan_unsetup(wlandev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + kfree(wlandev); +} + +static struct pci_device_id plx_id_tbl[] = { + { + PCIVENDOR_EUMITCOM, PCIDEVICE_WL11000, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"Eumitcom WL11000 PCI(PLX) card" + }, + { + PCIVENDOR_GLOBALSUN, PCIDEVICE_GL24110P, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"Global Sun Tech GL24110P PCI(PLX) card" + }, + { + PCIVENDOR_GLOBALSUN, PCIDEVICE_GL24110P_ALT, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"Global Sun Tech GL24110P PCI(PLX) card" + }, + { + PCIVENDOR_NETGEAR, PCIDEVICE_MA301, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"Global Sun Tech GL24110P PCI(PLX) card" + }, + { + PCIVENDOR_USROBOTICS, PCIDEVICE_USR2410, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"US Robotics USR2410 PCI(PLX) card" + }, + { + PCIVENDOR_Linksys, PCIDEVICE_Wpc11Wdt11, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"Linksys WPC11 with WDT11 PCI(PLX) adapter" + }, + { + PCIVENDOR_NDC, PCIDEVICE_NCP130_PLX, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"NDC Netblaster II PCI(PLX)" + }, + { + PCIVENDOR_NDC, PCIDEVICE_NCP130_ASIC, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"NDC Netblaster II PCI(TMC7160)" + }, + { + PCIVENDOR_3COM, PCIDEVICE_AIRCONNECT, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + /* Driver data, we just put the name here */ + (unsigned long)"3Com AirConnect PCI 802.11b 11Mb/s WLAN Controller" + }, + { + 0, 0, 0, 0, 0, 0, 0 + } +}; + +MODULE_DEVICE_TABLE(pci, plx_id_tbl); + +/* Function declared here because of ptr reference below */ +static int __devinit prism2sta_probe_plx(struct pci_dev *pdev, + const struct pci_device_id *); +static void __devexit prism2sta_remove_plx(struct pci_dev *pdev); + +static struct pci_driver prism2_plx_drv_id = { + .name = "prism2_plx", + .id_table = plx_id_tbl, + .probe = prism2sta_probe_plx, + .remove = prism2sta_remove_plx, +#ifdef CONFIG_PM + .suspend = prism2sta_suspend_pci, + .resume = prism2sta_resume_pci, +#endif +}; + +#ifdef MODULE + +static int __init prism2plx_init(void) +{ + WLAN_LOG_NOTICE("%s Loaded\n", version); + return pci_module_init(&prism2_plx_drv_id); +}; + +static void __exit prism2plx_cleanup(void) +{ + pci_unregister_driver(&prism2_plx_drv_id); +}; + +module_init(prism2plx_init); +module_exit(prism2plx_cleanup); + +#endif // MODULE + + +int hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis) +{ + int result = 0; + +#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */ +#define COR_VALUE 0x41 /* Enable PC card with irq in level trigger */ + +#define HCR_OFFSET 0x3e2 /* HCR attribute offset of Prism2 PC card */ + + UINT8 corsave; + DBFENTER; + + WLAN_LOG_DEBUG(3, "Doing reset via direct COR access.\n"); + + /* Collect COR */ + corsave = readb(hw->membase + COR_OFFSET); + /* Write reset bit (BIT7) */ + writeb(corsave | BIT7, hw->membase + COR_OFFSET); + /* Hold for holdtime */ + mdelay(holdtime); + + if (genesis) { + writeb(genesis, hw->membase + HCR_OFFSET); + /* Hold for holdtime */ + mdelay(holdtime); + } + + /* Clear reset bit */ + writeb(corsave & ~BIT7, hw->membase + COR_OFFSET); + /* Wait for settletime */ + mdelay(settletime); + /* Set non-reset bits back what they were */ + writeb(corsave, hw->membase + COR_OFFSET); + DBFEXIT; + return result; +} |