18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#define PRISM2_PLX 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is 58c2ecf20Sopenharmony_ci * based on: 68c2ecf20Sopenharmony_ci * - Host AP driver patch from james@madingley.org 78c2ecf20Sopenharmony_ci * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/if.h> 138c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 178c2ecf20Sopenharmony_ci#include <linux/wireless.h> 188c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/ioport.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <asm/io.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "hostap_wlan.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic char *dev_info = "hostap_plx"; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen"); 318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " 328c2ecf20Sopenharmony_ci "cards (PLX)."); 338c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int ignore_cis; 388c2ecf20Sopenharmony_cimodule_param(ignore_cis, int, 0444); 398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS"); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* struct local_info::hw_priv */ 438c2ecf20Sopenharmony_cistruct hostap_plx_priv { 448c2ecf20Sopenharmony_ci void __iomem *attr_mem; 458c2ecf20Sopenharmony_ci unsigned int cor_offset; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ 508c2ecf20Sopenharmony_ci#define COR_SRESET 0x80 518c2ecf20Sopenharmony_ci#define COR_LEVLREQ 0x40 528c2ecf20Sopenharmony_ci#define COR_ENABLE_FUNC 0x01 538c2ecf20Sopenharmony_ci/* PCI Configuration Registers */ 548c2ecf20Sopenharmony_ci#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */ 558c2ecf20Sopenharmony_ci/* Local Configuration Registers */ 568c2ecf20Sopenharmony_ci#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */ 578c2ecf20Sopenharmony_ci#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */ 588c2ecf20Sopenharmony_ci#define PLX_CNTRL 0x50 598c2ecf20Sopenharmony_ci#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const struct pci_device_id prism2_plx_id_table[] = { 658c2ecf20Sopenharmony_ci PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), 668c2ecf20Sopenharmony_ci PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), 678c2ecf20Sopenharmony_ci PLXDEV(0x126c, 0x8030, "Nortel emobility"), 688c2ecf20Sopenharmony_ci PLXDEV(0x1562, 0x0001, "Symbol LA-4123"), 698c2ecf20Sopenharmony_ci PLXDEV(0x1385, 0x4100, "Netgear MA301"), 708c2ecf20Sopenharmony_ci PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), 718c2ecf20Sopenharmony_ci PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), 728c2ecf20Sopenharmony_ci PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), 738c2ecf20Sopenharmony_ci PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"), 748c2ecf20Sopenharmony_ci PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), 758c2ecf20Sopenharmony_ci PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), 768c2ecf20Sopenharmony_ci PLXDEV(0x16ab, 0x1103, "Longshine 8031"), 778c2ecf20Sopenharmony_ci PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"), 788c2ecf20Sopenharmony_ci PLXDEV(0xec80, 0xec00, "Belkin F5D6000"), 798c2ecf20Sopenharmony_ci { 0 } 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid 848c2ecf20Sopenharmony_ci * is not listed here, you will need to add it here to get the driver 858c2ecf20Sopenharmony_ci * initialized. */ 868c2ecf20Sopenharmony_cistatic struct prism2_plx_manfid { 878c2ecf20Sopenharmony_ci u16 manfid1, manfid2; 888c2ecf20Sopenharmony_ci} prism2_plx_known_manfids[] = { 898c2ecf20Sopenharmony_ci { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */, 908c2ecf20Sopenharmony_ci { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */, 918c2ecf20Sopenharmony_ci { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */, 928c2ecf20Sopenharmony_ci { 0x0126, 0x8000 } /* Proxim RangeLAN */, 938c2ecf20Sopenharmony_ci { 0x0138, 0x0002 } /* Compaq WL100 */, 948c2ecf20Sopenharmony_ci { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */, 958c2ecf20Sopenharmony_ci { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */, 968c2ecf20Sopenharmony_ci { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */, 978c2ecf20Sopenharmony_ci { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */, 988c2ecf20Sopenharmony_ci { 0x028a, 0x0002 } /* D-Link DRC-650 */, 998c2ecf20Sopenharmony_ci { 0x0250, 0x0002 } /* Samsung SWL2000-N */, 1008c2ecf20Sopenharmony_ci { 0xc250, 0x0002 } /* EMTAC A2424i */, 1018c2ecf20Sopenharmony_ci { 0xd601, 0x0002 } /* Z-Com XI300 */, 1028c2ecf20Sopenharmony_ci { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, 1038c2ecf20Sopenharmony_ci { 0, 0} 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#ifdef PRISM2_IO_DEBUG 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1128c2ecf20Sopenharmony_ci local_info_t *local; 1138c2ecf20Sopenharmony_ci unsigned long flags; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1168c2ecf20Sopenharmony_ci local = iface->local; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1198c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); 1208c2ecf20Sopenharmony_ci outb(v, dev->base_addr + a); 1218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic inline u8 hfa384x_inb_debug(struct net_device *dev, int a) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1278c2ecf20Sopenharmony_ci local_info_t *local; 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci u8 v; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1328c2ecf20Sopenharmony_ci local = iface->local; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1358c2ecf20Sopenharmony_ci v = inb(dev->base_addr + a); 1368c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); 1378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1388c2ecf20Sopenharmony_ci return v; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1448c2ecf20Sopenharmony_ci local_info_t *local; 1458c2ecf20Sopenharmony_ci unsigned long flags; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1488c2ecf20Sopenharmony_ci local = iface->local; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1518c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); 1528c2ecf20Sopenharmony_ci outw(v, dev->base_addr + a); 1538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic inline u16 hfa384x_inw_debug(struct net_device *dev, int a) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1598c2ecf20Sopenharmony_ci local_info_t *local; 1608c2ecf20Sopenharmony_ci unsigned long flags; 1618c2ecf20Sopenharmony_ci u16 v; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1648c2ecf20Sopenharmony_ci local = iface->local; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1678c2ecf20Sopenharmony_ci v = inw(dev->base_addr + a); 1688c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); 1698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1708c2ecf20Sopenharmony_ci return v; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic inline void hfa384x_outsw_debug(struct net_device *dev, int a, 1748c2ecf20Sopenharmony_ci u8 *buf, int wc) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1778c2ecf20Sopenharmony_ci local_info_t *local; 1788c2ecf20Sopenharmony_ci unsigned long flags; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1818c2ecf20Sopenharmony_ci local = iface->local; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1848c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); 1858c2ecf20Sopenharmony_ci outsw(dev->base_addr + a, buf, wc); 1868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic inline void hfa384x_insw_debug(struct net_device *dev, int a, 1908c2ecf20Sopenharmony_ci u8 *buf, int wc) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1938c2ecf20Sopenharmony_ci local_info_t *local; 1948c2ecf20Sopenharmony_ci unsigned long flags; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1978c2ecf20Sopenharmony_ci local = iface->local; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 2008c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); 2018c2ecf20Sopenharmony_ci insw(dev->base_addr + a, buf, wc); 2028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) 2068c2ecf20Sopenharmony_ci#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) 2078c2ecf20Sopenharmony_ci#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) 2088c2ecf20Sopenharmony_ci#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) 2098c2ecf20Sopenharmony_ci#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) 2108c2ecf20Sopenharmony_ci#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci#else /* PRISM2_IO_DEBUG */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) 2158c2ecf20Sopenharmony_ci#define HFA384X_INB(a) inb(dev->base_addr + (a)) 2168c2ecf20Sopenharmony_ci#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) 2178c2ecf20Sopenharmony_ci#define HFA384X_INW(a) inw(dev->base_addr + (a)) 2188c2ecf20Sopenharmony_ci#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) 2198c2ecf20Sopenharmony_ci#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#endif /* PRISM2_IO_DEBUG */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, 2258c2ecf20Sopenharmony_ci int len) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci u16 d_off; 2288c2ecf20Sopenharmony_ci u16 *pos; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; 2318c2ecf20Sopenharmony_ci pos = (u16 *) buf; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (len / 2) 2348c2ecf20Sopenharmony_ci HFA384X_INSW(d_off, buf, len / 2); 2358c2ecf20Sopenharmony_ci pos += len / 2; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (len & 1) 2388c2ecf20Sopenharmony_ci *((char *) pos) = HFA384X_INB(d_off); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci u16 d_off; 2478c2ecf20Sopenharmony_ci u16 *pos; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; 2508c2ecf20Sopenharmony_ci pos = (u16 *) buf; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (len / 2) 2538c2ecf20Sopenharmony_ci HFA384X_OUTSW(d_off, buf, len / 2); 2548c2ecf20Sopenharmony_ci pos += len / 2; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (len & 1) 2578c2ecf20Sopenharmony_ci HFA384X_OUTB(*((char *) pos), d_off); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* FIX: This might change at some point.. */ 2648c2ecf20Sopenharmony_ci#include "hostap_hw.c" 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void prism2_plx_cor_sreset(local_info_t *local) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci unsigned char corsave; 2708c2ecf20Sopenharmony_ci struct hostap_plx_priv *hw_priv = local->hw_priv; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n", 2738c2ecf20Sopenharmony_ci dev_info); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Set sreset bit of COR and clear it after hold time */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (hw_priv->attr_mem == NULL) { 2788c2ecf20Sopenharmony_ci /* TMD7160 - COR at card's first I/O addr */ 2798c2ecf20Sopenharmony_ci corsave = inb(hw_priv->cor_offset); 2808c2ecf20Sopenharmony_ci outb(corsave | COR_SRESET, hw_priv->cor_offset); 2818c2ecf20Sopenharmony_ci mdelay(2); 2828c2ecf20Sopenharmony_ci outb(corsave & ~COR_SRESET, hw_priv->cor_offset); 2838c2ecf20Sopenharmony_ci mdelay(2); 2848c2ecf20Sopenharmony_ci } else { 2858c2ecf20Sopenharmony_ci /* PLX9052 */ 2868c2ecf20Sopenharmony_ci corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); 2878c2ecf20Sopenharmony_ci writeb(corsave | COR_SRESET, 2888c2ecf20Sopenharmony_ci hw_priv->attr_mem + hw_priv->cor_offset); 2898c2ecf20Sopenharmony_ci mdelay(2); 2908c2ecf20Sopenharmony_ci writeb(corsave & ~COR_SRESET, 2918c2ecf20Sopenharmony_ci hw_priv->attr_mem + hw_priv->cor_offset); 2928c2ecf20Sopenharmony_ci mdelay(2); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void prism2_plx_genesis_reset(local_info_t *local, int hcr) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci unsigned char corsave; 3008c2ecf20Sopenharmony_ci struct hostap_plx_priv *hw_priv = local->hw_priv; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (hw_priv->attr_mem == NULL) { 3038c2ecf20Sopenharmony_ci /* TMD7160 - COR at card's first I/O addr */ 3048c2ecf20Sopenharmony_ci corsave = inb(hw_priv->cor_offset); 3058c2ecf20Sopenharmony_ci outb(corsave | COR_SRESET, hw_priv->cor_offset); 3068c2ecf20Sopenharmony_ci mdelay(10); 3078c2ecf20Sopenharmony_ci outb(hcr, hw_priv->cor_offset + 2); 3088c2ecf20Sopenharmony_ci mdelay(10); 3098c2ecf20Sopenharmony_ci outb(corsave & ~COR_SRESET, hw_priv->cor_offset); 3108c2ecf20Sopenharmony_ci mdelay(10); 3118c2ecf20Sopenharmony_ci } else { 3128c2ecf20Sopenharmony_ci /* PLX9052 */ 3138c2ecf20Sopenharmony_ci corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); 3148c2ecf20Sopenharmony_ci writeb(corsave | COR_SRESET, 3158c2ecf20Sopenharmony_ci hw_priv->attr_mem + hw_priv->cor_offset); 3168c2ecf20Sopenharmony_ci mdelay(10); 3178c2ecf20Sopenharmony_ci writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2); 3188c2ecf20Sopenharmony_ci mdelay(10); 3198c2ecf20Sopenharmony_ci writeb(corsave & ~COR_SRESET, 3208c2ecf20Sopenharmony_ci hw_priv->attr_mem + hw_priv->cor_offset); 3218c2ecf20Sopenharmony_ci mdelay(10); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic struct prism2_helper_functions prism2_plx_funcs = 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci .card_present = NULL, 3298c2ecf20Sopenharmony_ci .cor_sreset = prism2_plx_cor_sreset, 3308c2ecf20Sopenharmony_ci .genesis_reset = prism2_plx_genesis_reset, 3318c2ecf20Sopenharmony_ci .hw_type = HOSTAP_HW_PLX, 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len, 3368c2ecf20Sopenharmony_ci unsigned int *cor_offset, 3378c2ecf20Sopenharmony_ci unsigned int *cor_index) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci#define CISTPL_CONFIG 0x1A 3408c2ecf20Sopenharmony_ci#define CISTPL_MANFID 0x20 3418c2ecf20Sopenharmony_ci#define CISTPL_END 0xFF 3428c2ecf20Sopenharmony_ci#define CIS_MAX_LEN 256 3438c2ecf20Sopenharmony_ci u8 *cis; 3448c2ecf20Sopenharmony_ci int i, pos; 3458c2ecf20Sopenharmony_ci unsigned int rmsz, rasz, manfid1, manfid2; 3468c2ecf20Sopenharmony_ci struct prism2_plx_manfid *manfid; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL); 3498c2ecf20Sopenharmony_ci if (cis == NULL) 3508c2ecf20Sopenharmony_ci return -ENOMEM; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* read CIS; it is in even offsets in the beginning of attr_mem */ 3538c2ecf20Sopenharmony_ci for (i = 0; i < CIS_MAX_LEN; i++) 3548c2ecf20Sopenharmony_ci cis[i] = readb(attr_mem + 2 * i); 3558c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: CIS: %6ph ...\n", dev_info, cis); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* set reasonable defaults for Prism2 cards just in case CIS parsing 3588c2ecf20Sopenharmony_ci * fails */ 3598c2ecf20Sopenharmony_ci *cor_offset = 0x3e0; 3608c2ecf20Sopenharmony_ci *cor_index = 0x01; 3618c2ecf20Sopenharmony_ci manfid1 = manfid2 = 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci pos = 0; 3648c2ecf20Sopenharmony_ci while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) { 3658c2ecf20Sopenharmony_ci if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN) 3668c2ecf20Sopenharmony_ci goto cis_error; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci switch (cis[pos]) { 3698c2ecf20Sopenharmony_ci case CISTPL_CONFIG: 3708c2ecf20Sopenharmony_ci if (cis[pos + 1] < 2) 3718c2ecf20Sopenharmony_ci goto cis_error; 3728c2ecf20Sopenharmony_ci rmsz = (cis[pos + 2] & 0x3c) >> 2; 3738c2ecf20Sopenharmony_ci rasz = cis[pos + 2] & 0x03; 3748c2ecf20Sopenharmony_ci if (4 + rasz + rmsz > cis[pos + 1]) 3758c2ecf20Sopenharmony_ci goto cis_error; 3768c2ecf20Sopenharmony_ci *cor_index = cis[pos + 3] & 0x3F; 3778c2ecf20Sopenharmony_ci *cor_offset = 0; 3788c2ecf20Sopenharmony_ci for (i = 0; i <= rasz; i++) 3798c2ecf20Sopenharmony_ci *cor_offset += cis[pos + 4 + i] << (8 * i); 3808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: cor_index=0x%x " 3818c2ecf20Sopenharmony_ci "cor_offset=0x%x\n", dev_info, 3828c2ecf20Sopenharmony_ci *cor_index, *cor_offset); 3838c2ecf20Sopenharmony_ci if (*cor_offset > attr_len) { 3848c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: COR offset not within " 3858c2ecf20Sopenharmony_ci "attr_mem\n", dev_info); 3868c2ecf20Sopenharmony_ci kfree(cis); 3878c2ecf20Sopenharmony_ci return -1; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci case CISTPL_MANFID: 3928c2ecf20Sopenharmony_ci if (cis[pos + 1] < 4) 3938c2ecf20Sopenharmony_ci goto cis_error; 3948c2ecf20Sopenharmony_ci manfid1 = cis[pos + 2] + (cis[pos + 3] << 8); 3958c2ecf20Sopenharmony_ci manfid2 = cis[pos + 4] + (cis[pos + 5] << 8); 3968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n", 3978c2ecf20Sopenharmony_ci dev_info, manfid1, manfid2); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci pos += cis[pos + 1] + 2; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END) 4058c2ecf20Sopenharmony_ci goto cis_error; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++) 4088c2ecf20Sopenharmony_ci if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) { 4098c2ecf20Sopenharmony_ci kfree(cis); 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is" 4148c2ecf20Sopenharmony_ci " not supported card\n", dev_info, manfid1, manfid2); 4158c2ecf20Sopenharmony_ci goto fail; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci cis_error: 4188c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: invalid CIS data\n", dev_info); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci fail: 4218c2ecf20Sopenharmony_ci kfree(cis); 4228c2ecf20Sopenharmony_ci if (ignore_cis) { 4238c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: ignore_cis parameter set - ignoring " 4248c2ecf20Sopenharmony_ci "errors during CIS verification\n", dev_info); 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci return -1; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int prism2_plx_probe(struct pci_dev *pdev, 4328c2ecf20Sopenharmony_ci const struct pci_device_id *id) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci unsigned int pccard_ioaddr, plx_ioaddr; 4358c2ecf20Sopenharmony_ci unsigned long pccard_attr_mem; 4368c2ecf20Sopenharmony_ci unsigned int pccard_attr_len; 4378c2ecf20Sopenharmony_ci void __iomem *attr_mem = NULL; 4388c2ecf20Sopenharmony_ci unsigned int cor_offset = 0, cor_index = 0; 4398c2ecf20Sopenharmony_ci u32 reg; 4408c2ecf20Sopenharmony_ci local_info_t *local = NULL; 4418c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 4428c2ecf20Sopenharmony_ci struct hostap_interface *iface; 4438c2ecf20Sopenharmony_ci static int cards_found /* = 0 */; 4448c2ecf20Sopenharmony_ci int irq_registered = 0; 4458c2ecf20Sopenharmony_ci int tmd7160; 4468c2ecf20Sopenharmony_ci struct hostap_plx_priv *hw_priv; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); 4498c2ecf20Sopenharmony_ci if (hw_priv == NULL) 4508c2ecf20Sopenharmony_ci return -ENOMEM; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) 4538c2ecf20Sopenharmony_ci goto err_out_free; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* National Datacomm NCP130 based on TMD7160, not PLX9052. */ 4568c2ecf20Sopenharmony_ci tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci plx_ioaddr = pci_resource_start(pdev, 1); 4598c2ecf20Sopenharmony_ci pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (tmd7160) { 4628c2ecf20Sopenharmony_ci /* TMD7160 */ 4638c2ecf20Sopenharmony_ci attr_mem = NULL; /* no access to PC Card attribute memory */ 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, " 4668c2ecf20Sopenharmony_ci "irq=%d, pccard_io=0x%x\n", 4678c2ecf20Sopenharmony_ci plx_ioaddr, pdev->irq, pccard_ioaddr); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci cor_offset = plx_ioaddr; 4708c2ecf20Sopenharmony_ci cor_index = 0x04; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr); 4738c2ecf20Sopenharmony_ci mdelay(1); 4748c2ecf20Sopenharmony_ci reg = inb(plx_ioaddr); 4758c2ecf20Sopenharmony_ci if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) { 4768c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Error setting COR (expected=" 4778c2ecf20Sopenharmony_ci "0x%02x, was=0x%02x)\n", dev_info, 4788c2ecf20Sopenharmony_ci cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg); 4798c2ecf20Sopenharmony_ci goto fail; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci /* PLX9052 */ 4838c2ecf20Sopenharmony_ci pccard_attr_mem = pci_resource_start(pdev, 2); 4848c2ecf20Sopenharmony_ci pccard_attr_len = pci_resource_len(pdev, 2); 4858c2ecf20Sopenharmony_ci if (pccard_attr_len < PLX_MIN_ATTR_LEN) 4868c2ecf20Sopenharmony_ci goto fail; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci attr_mem = ioremap(pccard_attr_mem, pccard_attr_len); 4908c2ecf20Sopenharmony_ci if (attr_mem == NULL) { 4918c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: cannot remap attr_mem\n", 4928c2ecf20Sopenharmony_ci dev_info); 4938c2ecf20Sopenharmony_ci goto fail; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: " 4978c2ecf20Sopenharmony_ci "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n", 4988c2ecf20Sopenharmony_ci pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (prism2_plx_check_cis(attr_mem, pccard_attr_len, 5018c2ecf20Sopenharmony_ci &cor_offset, &cor_index)) { 5028c2ecf20Sopenharmony_ci printk(KERN_INFO "Unknown PC Card CIS - not a " 5038c2ecf20Sopenharmony_ci "Prism2/2.5 card?\n"); 5048c2ecf20Sopenharmony_ci goto fail; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 " 5088c2ecf20Sopenharmony_ci "adapter\n"); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Write COR to enable PC Card */ 5118c2ecf20Sopenharmony_ci writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, 5128c2ecf20Sopenharmony_ci attr_mem + cor_offset); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Enable PCI interrupts if they are not already enabled */ 5158c2ecf20Sopenharmony_ci reg = inl(plx_ioaddr + PLX_INTCSR); 5168c2ecf20Sopenharmony_ci printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg); 5178c2ecf20Sopenharmony_ci if (!(reg & PLX_INTCSR_PCI_INTEN)) { 5188c2ecf20Sopenharmony_ci outl(reg | PLX_INTCSR_PCI_INTEN, 5198c2ecf20Sopenharmony_ci plx_ioaddr + PLX_INTCSR); 5208c2ecf20Sopenharmony_ci if (!(inl(plx_ioaddr + PLX_INTCSR) & 5218c2ecf20Sopenharmony_ci PLX_INTCSR_PCI_INTEN)) { 5228c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Could not enable " 5238c2ecf20Sopenharmony_ci "Local Interrupts\n", dev_info); 5248c2ecf20Sopenharmony_ci goto fail; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci reg = inl(plx_ioaddr + PLX_CNTRL); 5298c2ecf20Sopenharmony_ci printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM " 5308c2ecf20Sopenharmony_ci "present=%d)\n", 5318c2ecf20Sopenharmony_ci reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0); 5328c2ecf20Sopenharmony_ci /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is 5338c2ecf20Sopenharmony_ci * not present; but are there really such cards in use(?) */ 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci dev = prism2_init_local_data(&prism2_plx_funcs, cards_found, 5378c2ecf20Sopenharmony_ci &pdev->dev); 5388c2ecf20Sopenharmony_ci if (dev == NULL) 5398c2ecf20Sopenharmony_ci goto fail; 5408c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 5418c2ecf20Sopenharmony_ci local = iface->local; 5428c2ecf20Sopenharmony_ci local->hw_priv = hw_priv; 5438c2ecf20Sopenharmony_ci cards_found++; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci dev->irq = pdev->irq; 5468c2ecf20Sopenharmony_ci dev->base_addr = pccard_ioaddr; 5478c2ecf20Sopenharmony_ci hw_priv->attr_mem = attr_mem; 5488c2ecf20Sopenharmony_ci hw_priv->cor_offset = cor_offset; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name, 5538c2ecf20Sopenharmony_ci dev)) { 5548c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: request_irq failed\n", dev->name); 5558c2ecf20Sopenharmony_ci goto fail; 5568c2ecf20Sopenharmony_ci } else 5578c2ecf20Sopenharmony_ci irq_registered = 1; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (prism2_hw_config(dev, 1)) { 5608c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: hardware initialization failed\n", 5618c2ecf20Sopenharmony_ci dev_info); 5628c2ecf20Sopenharmony_ci goto fail; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return hostap_hw_ready(dev); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci fail: 5688c2ecf20Sopenharmony_ci if (irq_registered && dev) 5698c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (attr_mem) 5728c2ecf20Sopenharmony_ci iounmap(attr_mem); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5758c2ecf20Sopenharmony_ci prism2_free_local_data(dev); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci err_out_free: 5788c2ecf20Sopenharmony_ci kfree(hw_priv); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return -ENODEV; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void prism2_plx_remove(struct pci_dev *pdev) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct net_device *dev; 5878c2ecf20Sopenharmony_ci struct hostap_interface *iface; 5888c2ecf20Sopenharmony_ci struct hostap_plx_priv *hw_priv; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci dev = pci_get_drvdata(pdev); 5918c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 5928c2ecf20Sopenharmony_ci hw_priv = iface->local->hw_priv; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Reset the hardware, and ensure interrupts are disabled. */ 5958c2ecf20Sopenharmony_ci prism2_plx_cor_sreset(iface->local); 5968c2ecf20Sopenharmony_ci hfa384x_disable_interrupts(dev); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (hw_priv->attr_mem) 5998c2ecf20Sopenharmony_ci iounmap(hw_priv->attr_mem); 6008c2ecf20Sopenharmony_ci if (dev->irq) 6018c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci prism2_free_local_data(dev); 6048c2ecf20Sopenharmony_ci kfree(hw_priv); 6058c2ecf20Sopenharmony_ci pci_disable_device(pdev); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, prism2_plx_id_table); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic struct pci_driver prism2_plx_driver = { 6128c2ecf20Sopenharmony_ci .name = "hostap_plx", 6138c2ecf20Sopenharmony_ci .id_table = prism2_plx_id_table, 6148c2ecf20Sopenharmony_ci .probe = prism2_plx_probe, 6158c2ecf20Sopenharmony_ci .remove = prism2_plx_remove, 6168c2ecf20Sopenharmony_ci}; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cimodule_pci_driver(prism2_plx_driver); 619