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