18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#define PRISM2_PCI 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on 58c2ecf20Sopenharmony_ci * driver patches from Reyk Floeter <reyk@vantronix.net> and 68c2ecf20Sopenharmony_ci * Andy Warner <andyw@pobox.com> */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/if.h> 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 148c2ecf20Sopenharmony_ci#include <linux/wireless.h> 158c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/ioport.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <asm/io.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "hostap_wlan.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic char *dev_info = "hostap_pci"; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen"); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " 298c2ecf20Sopenharmony_ci "PCI cards."); 308c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* struct local_info::hw_priv */ 358c2ecf20Sopenharmony_cistruct hostap_pci_priv { 368c2ecf20Sopenharmony_ci void __iomem *mem_start; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* FIX: do we need mb/wmb/rmb with memory operations? */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct pci_device_id prism2_pci_id_table[] = { 448c2ecf20Sopenharmony_ci /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */ 458c2ecf20Sopenharmony_ci { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, 468c2ecf20Sopenharmony_ci /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ 478c2ecf20Sopenharmony_ci { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, 488c2ecf20Sopenharmony_ci /* Samsung MagicLAN SWL-2210P */ 498c2ecf20Sopenharmony_ci { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, 508c2ecf20Sopenharmony_ci { 0 } 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#ifdef PRISM2_IO_DEBUG 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct hostap_interface *iface; 598c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 608c2ecf20Sopenharmony_ci local_info_t *local; 618c2ecf20Sopenharmony_ci unsigned long flags; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 648c2ecf20Sopenharmony_ci local = iface->local; 658c2ecf20Sopenharmony_ci hw_priv = local->hw_priv; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 688c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); 698c2ecf20Sopenharmony_ci writeb(v, hw_priv->mem_start + a); 708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline u8 hfa384x_inb_debug(struct net_device *dev, int a) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct hostap_interface *iface; 768c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 778c2ecf20Sopenharmony_ci local_info_t *local; 788c2ecf20Sopenharmony_ci unsigned long flags; 798c2ecf20Sopenharmony_ci u8 v; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 828c2ecf20Sopenharmony_ci local = iface->local; 838c2ecf20Sopenharmony_ci hw_priv = local->hw_priv; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 868c2ecf20Sopenharmony_ci v = readb(hw_priv->mem_start + a); 878c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); 888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 898c2ecf20Sopenharmony_ci return v; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct hostap_interface *iface; 958c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 968c2ecf20Sopenharmony_ci local_info_t *local; 978c2ecf20Sopenharmony_ci unsigned long flags; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1008c2ecf20Sopenharmony_ci local = iface->local; 1018c2ecf20Sopenharmony_ci hw_priv = local->hw_priv; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1048c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); 1058c2ecf20Sopenharmony_ci writew(v, hw_priv->mem_start + a); 1068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline u16 hfa384x_inw_debug(struct net_device *dev, int a) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1128c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 1138c2ecf20Sopenharmony_ci local_info_t *local; 1148c2ecf20Sopenharmony_ci unsigned long flags; 1158c2ecf20Sopenharmony_ci u16 v; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1188c2ecf20Sopenharmony_ci local = iface->local; 1198c2ecf20Sopenharmony_ci hw_priv = local->hw_priv; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 1228c2ecf20Sopenharmony_ci v = readw(hw_priv->mem_start + a); 1238c2ecf20Sopenharmony_ci prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); 1248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 1258c2ecf20Sopenharmony_ci return v; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) 1298c2ecf20Sopenharmony_ci#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) 1308c2ecf20Sopenharmony_ci#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) 1318c2ecf20Sopenharmony_ci#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) 1328c2ecf20Sopenharmony_ci#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), le16_to_cpu((v))) 1338c2ecf20Sopenharmony_ci#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw_debug(dev, (a))) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#else /* PRISM2_IO_DEBUG */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline void hfa384x_outb(struct net_device *dev, int a, u8 v) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1408c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 1418c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1428c2ecf20Sopenharmony_ci hw_priv = iface->local->hw_priv; 1438c2ecf20Sopenharmony_ci writeb(v, hw_priv->mem_start + a); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline u8 hfa384x_inb(struct net_device *dev, int a) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1498c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 1508c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1518c2ecf20Sopenharmony_ci hw_priv = iface->local->hw_priv; 1528c2ecf20Sopenharmony_ci return readb(hw_priv->mem_start + a); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic inline void hfa384x_outw(struct net_device *dev, int a, u16 v) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1588c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 1598c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1608c2ecf20Sopenharmony_ci hw_priv = iface->local->hw_priv; 1618c2ecf20Sopenharmony_ci writew(v, hw_priv->mem_start + a); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic inline u16 hfa384x_inw(struct net_device *dev, int a) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1678c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 1688c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1698c2ecf20Sopenharmony_ci hw_priv = iface->local->hw_priv; 1708c2ecf20Sopenharmony_ci return readw(hw_priv->mem_start + a); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v)) 1748c2ecf20Sopenharmony_ci#define HFA384X_INB(a) hfa384x_inb(dev, (a)) 1758c2ecf20Sopenharmony_ci#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v)) 1768c2ecf20Sopenharmony_ci#define HFA384X_INW(a) hfa384x_inw(dev, (a)) 1778c2ecf20Sopenharmony_ci#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), le16_to_cpu((v))) 1788c2ecf20Sopenharmony_ci#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw(dev, (a))) 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#endif /* PRISM2_IO_DEBUG */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, 1848c2ecf20Sopenharmony_ci int len) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci u16 d_off; 1878c2ecf20Sopenharmony_ci __le16 *pos; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; 1908c2ecf20Sopenharmony_ci pos = (__le16 *) buf; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci for ( ; len > 1; len -= 2) 1938c2ecf20Sopenharmony_ci *pos++ = HFA384X_INW_DATA(d_off); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (len & 1) 1968c2ecf20Sopenharmony_ci *((char *) pos) = HFA384X_INB(d_off); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci u16 d_off; 2058c2ecf20Sopenharmony_ci __le16 *pos; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; 2088c2ecf20Sopenharmony_ci pos = (__le16 *) buf; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for ( ; len > 1; len -= 2) 2118c2ecf20Sopenharmony_ci HFA384X_OUTW_DATA(*pos++, d_off); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (len & 1) 2148c2ecf20Sopenharmony_ci HFA384X_OUTB(*((char *) pos), d_off); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* FIX: This might change at some point.. */ 2218c2ecf20Sopenharmony_ci#include "hostap_hw.c" 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void prism2_pci_cor_sreset(local_info_t *local) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 2268c2ecf20Sopenharmony_ci u16 reg; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci reg = HFA384X_INB(HFA384X_PCICOR_OFF); 2298c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* linux-wlan-ng uses extremely long hold and settle times for 2328c2ecf20Sopenharmony_ci * COR sreset. A comment in the driver code mentions that the long 2338c2ecf20Sopenharmony_ci * delays appear to be necessary. However, at least IBM 22P6901 seems 2348c2ecf20Sopenharmony_ci * to work fine with shorter delays. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Longer delays can be configured by uncommenting following line: */ 2378c2ecf20Sopenharmony_ci/* #define PRISM2_PCI_USE_LONG_DELAYS */ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci#ifdef PRISM2_PCI_USE_LONG_DELAYS 2408c2ecf20Sopenharmony_ci int i; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); 2438c2ecf20Sopenharmony_ci mdelay(250); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); 2468c2ecf20Sopenharmony_ci mdelay(500); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ 2498c2ecf20Sopenharmony_ci i = 2000000 / 10; 2508c2ecf20Sopenharmony_ci while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) 2518c2ecf20Sopenharmony_ci udelay(10); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci#else /* PRISM2_PCI_USE_LONG_DELAYS */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); 2568c2ecf20Sopenharmony_ci mdelay(2); 2578c2ecf20Sopenharmony_ci HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); 2588c2ecf20Sopenharmony_ci mdelay(2); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#endif /* PRISM2_PCI_USE_LONG_DELAYS */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { 2638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void prism2_pci_genesis_reset(local_info_t *local, int hcr) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF); 2738c2ecf20Sopenharmony_ci mdelay(10); 2748c2ecf20Sopenharmony_ci HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF); 2758c2ecf20Sopenharmony_ci mdelay(10); 2768c2ecf20Sopenharmony_ci HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF); 2778c2ecf20Sopenharmony_ci mdelay(10); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic struct prism2_helper_functions prism2_pci_funcs = 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci .card_present = NULL, 2848c2ecf20Sopenharmony_ci .cor_sreset = prism2_pci_cor_sreset, 2858c2ecf20Sopenharmony_ci .genesis_reset = prism2_pci_genesis_reset, 2868c2ecf20Sopenharmony_ci .hw_type = HOSTAP_HW_PCI, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int prism2_pci_probe(struct pci_dev *pdev, 2918c2ecf20Sopenharmony_ci const struct pci_device_id *id) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci unsigned long phymem; 2948c2ecf20Sopenharmony_ci void __iomem *mem = NULL; 2958c2ecf20Sopenharmony_ci local_info_t *local = NULL; 2968c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 2978c2ecf20Sopenharmony_ci static int cards_found /* = 0 */; 2988c2ecf20Sopenharmony_ci int irq_registered = 0; 2998c2ecf20Sopenharmony_ci struct hostap_interface *iface; 3008c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); 3038c2ecf20Sopenharmony_ci if (hw_priv == NULL) 3048c2ecf20Sopenharmony_ci return -ENOMEM; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) 3078c2ecf20Sopenharmony_ci goto err_out_free; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci phymem = pci_resource_start(pdev, 0); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { 3128c2ecf20Sopenharmony_ci printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); 3138c2ecf20Sopenharmony_ci goto err_out_disable; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci mem = pci_ioremap_bar(pdev, 0); 3178c2ecf20Sopenharmony_ci if (mem == NULL) { 3188c2ecf20Sopenharmony_ci printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; 3198c2ecf20Sopenharmony_ci goto fail; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci dev = prism2_init_local_data(&prism2_pci_funcs, cards_found, 3238c2ecf20Sopenharmony_ci &pdev->dev); 3248c2ecf20Sopenharmony_ci if (dev == NULL) 3258c2ecf20Sopenharmony_ci goto fail; 3268c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 3278c2ecf20Sopenharmony_ci local = iface->local; 3288c2ecf20Sopenharmony_ci local->hw_priv = hw_priv; 3298c2ecf20Sopenharmony_ci cards_found++; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci dev->irq = pdev->irq; 3328c2ecf20Sopenharmony_ci hw_priv->mem_start = mem; 3338c2ecf20Sopenharmony_ci dev->base_addr = (unsigned long) mem; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci prism2_pci_cor_sreset(local); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name, 3408c2ecf20Sopenharmony_ci dev)) { 3418c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: request_irq failed\n", dev->name); 3428c2ecf20Sopenharmony_ci goto fail; 3438c2ecf20Sopenharmony_ci } else 3448c2ecf20Sopenharmony_ci irq_registered = 1; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!local->pri_only && prism2_hw_config(dev, 1)) { 3478c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: hardware initialization failed\n", 3488c2ecf20Sopenharmony_ci dev_info); 3498c2ecf20Sopenharmony_ci goto fail; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " 3538c2ecf20Sopenharmony_ci "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return hostap_hw_ready(dev); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci fail: 3588c2ecf20Sopenharmony_ci if (irq_registered && dev) 3598c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (mem) 3628c2ecf20Sopenharmony_ci iounmap(mem); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci release_mem_region(phymem, pci_resource_len(pdev, 0)); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci err_out_disable: 3678c2ecf20Sopenharmony_ci pci_disable_device(pdev); 3688c2ecf20Sopenharmony_ci prism2_free_local_data(dev); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci err_out_free: 3718c2ecf20Sopenharmony_ci kfree(hw_priv); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return -ENODEV; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic void prism2_pci_remove(struct pci_dev *pdev) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct net_device *dev; 3808c2ecf20Sopenharmony_ci struct hostap_interface *iface; 3818c2ecf20Sopenharmony_ci void __iomem *mem_start; 3828c2ecf20Sopenharmony_ci struct hostap_pci_priv *hw_priv; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci dev = pci_get_drvdata(pdev); 3858c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 3868c2ecf20Sopenharmony_ci hw_priv = iface->local->hw_priv; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* Reset the hardware, and ensure interrupts are disabled. */ 3898c2ecf20Sopenharmony_ci prism2_pci_cor_sreset(iface->local); 3908c2ecf20Sopenharmony_ci hfa384x_disable_interrupts(dev); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (dev->irq) 3938c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci mem_start = hw_priv->mem_start; 3968c2ecf20Sopenharmony_ci prism2_free_local_data(dev); 3978c2ecf20Sopenharmony_ci kfree(hw_priv); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci iounmap(mem_start); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 4028c2ecf20Sopenharmony_ci pci_resource_len(pdev, 0)); 4038c2ecf20Sopenharmony_ci pci_disable_device(pdev); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int __maybe_unused prism2_pci_suspend(struct device *dev_d) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (netif_running(dev)) { 4118c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4128c2ecf20Sopenharmony_ci netif_device_detach(dev); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci prism2_suspend(dev); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int __maybe_unused prism2_pci_resume(struct device *dev_d) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci prism2_hw_config(dev, 0); 4248c2ecf20Sopenharmony_ci if (netif_running(dev)) { 4258c2ecf20Sopenharmony_ci netif_device_attach(dev); 4268c2ecf20Sopenharmony_ci netif_start_queue(dev); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, prism2_pci_id_table); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(prism2_pci_pm_ops, 4358c2ecf20Sopenharmony_ci prism2_pci_suspend, 4368c2ecf20Sopenharmony_ci prism2_pci_resume); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic struct pci_driver prism2_pci_driver = { 4398c2ecf20Sopenharmony_ci .name = "hostap_pci", 4408c2ecf20Sopenharmony_ci .id_table = prism2_pci_id_table, 4418c2ecf20Sopenharmony_ci .probe = prism2_pci_probe, 4428c2ecf20Sopenharmony_ci .remove = prism2_pci_remove, 4438c2ecf20Sopenharmony_ci .driver.pm = &prism2_pci_pm_ops, 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cimodule_pci_driver(prism2_pci_driver); 447