#define WLAN_HOSTIF WLAN_PCI
#include "hfa384x.c"
#include "prism2mgmt.c"
#include "prism2mib.c"
#include "prism2sta.c"

#define PCI_SIZE		0x1000		/* Memory size - 4K bytes */

/* ISL3874A 11Mb/s WLAN controller */
#define PCIVENDOR_INTERSIL	0x1260UL
#define PCIDEVICE_ISL3874	0x3873UL /* [MSM] yeah I know...the ID says 
					    3873. Trust me, it's a 3874. */

/* Samsung SWL-2210P 11Mb/s WLAN controller (uses ISL3874A) */
#define PCIVENDOR_SAMSUNG      0x167dUL
#define PCIDEVICE_SWL_2210P    0xa000UL

#define PCIVENDOR_NETGEAR      0x1385UL /* for MA311 */

/* PCI Class & Sub-Class code, Network-'Other controller' */
#define PCI_CLASS_NETWORK_OTHERS 0x280


/*----------------------------------------------------------------
* prism2sta_probe_pci
*
* Probe routine called when a PCI device w/ matching ID is found. 
* The ISL3874 implementation uses the following map:
*   BAR0: Prism2.x registers memory mapped, size=4k
* Here's the sequence:
*   - Allocate the PCI resources.  
*   - Read the PCMCIA attribute memory to make sure we have a WLAN card
*   - Reset the MAC 
*   - 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_pci(
	struct pci_dev *pdev, 
	const struct pci_device_id *id)
{
	int		result;
	phys_t		phymem = 0;
	void		__iomem *mem = NULL;
        wlandevice_t    *wlandev = NULL;
	hfa384x_t	*hw = NULL;

	DBFENTER;

	/* Enable the pci device */
	if (pci_enable_device(pdev)) {
		WLAN_LOG_ERROR("%s: pci_enable_device() failed.\n", dev_info);
		result = -EIO;
		goto fail;
	}

	/* Figure out our resources */
	phymem = pci_resource_start(pdev, 0);

        if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) {
		printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n");
		result = -EIO;
		goto fail;
        }

	mem = ioremap(phymem, PCI_SIZE);
	if ( mem == 0 ) {
		WLAN_LOG_ERROR("%s: ioremap() failed.\n", dev_info);
		result = -EIO;
		goto fail;
	}

	/* Log the device */
        WLAN_LOG_INFO("A Prism2.5 PCI device found, "
		"phymem:0x%llx, irq:%d, mem:0x%p\n", 
		(unsigned long long)phymem, pdev->irq, mem);
	
	if ((wlandev = create_wlan()) == NULL) {
		WLAN_LOG_ERROR("%s: Memory allocation failure.\n", dev_info);
		result = -EIO;
		goto fail;
	}
	hw = wlandev->priv;
	
	if ( wlan_setup(wlandev) != 0 ) {
		WLAN_LOG_ERROR("%s: wlan_setup() failed.\n", dev_info);
		result = -EIO;
		goto fail;
	}

	/* Setup netdevice's ability to report resources 
	 * Note: the netdevice was allocated by wlan_setup()
	 */
        wlandev->netdev->irq = pdev->irq;
        wlandev->netdev->mem_start = (unsigned long) mem;
        wlandev->netdev->mem_end = wlandev->netdev->mem_start + 
		pci_resource_len(pdev, 0);

	/* Initialize the hw data */
        hfa384x_create(hw, wlandev->netdev->irq, 0, mem);
	hw->wlandev = wlandev;

	SET_MODULE_OWNER(wlandev->netdev);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
       SET_NETDEV_DEV(wlandev->netdev, &(pdev->dev));
#endif

#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 fail;
	}		
#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) {
			WLAN_LOG_ERROR(
				"%s: hfa384x_corereset() failed.\n", 
				dev_info);
			hfa384x_destroy(hw);
			result = -EIO;
			goto fail;
		}
	}

        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, 
		    IRQF_SHARED, wlandev->name, wlandev);

	wlandev->msdstate = WLAN_MSD_HWPRESENT;

	/* Register the wlandev, this gets us a name and registers the
	 * linux netdevice.
	 */
        if ( register_wlandev(wlandev) != 0 ) {
		WLAN_LOG_ERROR("%s: register_wlandev() failed.\n", dev_info);
		result = -EIO;
		goto fail;
        }

	if (wlan_wext_write)
		prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable);

	result = 0;
	goto done;

 fail:
	pci_set_drvdata(pdev, NULL);
	if (wlandev)	kfree(wlandev);
	if (hw)		kfree(hw);
        if (mem)        iounmap(mem);
	pci_release_regions(pdev);
        pci_disable_device(pdev);

 done:
	p80211_allow_ioctls(wlandev);
	DBFEXIT;
	return result;
}

static void __devexit prism2sta_remove_pci(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 pci_id_tbl[] = {
	{
		PCIVENDOR_INTERSIL, PCIDEVICE_ISL3874,	
		PCI_ANY_ID, PCI_ANY_ID,
		0, 0, 
		/* Driver data, we just put the name here */
		(unsigned long)"Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller"
	},
	{
		PCIVENDOR_INTERSIL, 0x3872,	
		PCI_ANY_ID, PCI_ANY_ID,
		0, 0, 
		/* Driver data, we just put the name here */
		(unsigned long)"Intersil Prism2.5 ISL3872 11Mb/s WLAN Controller"
	},
        { 
               PCIVENDOR_SAMSUNG, PCIDEVICE_SWL_2210P,
               PCI_ANY_ID, PCI_ANY_ID,
               0, 0,
               /* Driver data, we just put the name here */
               (unsigned long)"Samsung MagicLAN SWL-2210P 11Mb/s WLAN Controller"
	},
	{ /* for NetGear MA311 */
		PCIVENDOR_NETGEAR, 0x3872,
		PCI_ANY_ID, PCI_ANY_ID,
		0, 0,
		/* Driver data, we just put the name here */
		(unsigned long)"Netgear MA311 WLAN Controller"
	},
	{
		0, 0, 0, 0, 0, 0, 0
	}
};

MODULE_DEVICE_TABLE(pci, pci_id_tbl);

/* Function declared here because of ptr reference below */
static int  __devinit prism2sta_probe_pci(struct pci_dev *pdev, 
				const struct pci_device_id *id);
static void  __devexit prism2sta_remove_pci(struct pci_dev *pdev);

static struct pci_driver prism2_pci_drv_id = {
        .name = "prism2_pci",
        .id_table = pci_id_tbl,
        .probe = prism2sta_probe_pci,
        .remove = prism2sta_remove_pci,
#ifdef CONFIG_PM
        .suspend = prism2sta_suspend_pci,
        .resume = prism2sta_resume_pci,
#endif
};

#ifdef MODULE

static int __init prism2pci_init(void)
{
        WLAN_LOG_NOTICE("%s Loaded\n", version);
	return pci_register_driver(&prism2_pci_drv_id);
};

static void __exit prism2pci_cleanup(void)
{
	pci_unregister_driver(&prism2_pci_drv_id);
};

module_init(prism2pci_init);
module_exit(prism2pci_cleanup);

#endif

int hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis)
{
	int		result = 0;
	unsigned long	timeout;
	UINT16	reg;
	DBFENTER;

	/* Assert reset and wait awhile 
	 * (note: these delays are _really_ long, but they appear to be
	 *        necessary.)
	 */
	hfa384x_setreg(hw, 0xc5, HFA384x_PCICOR);
	timeout = jiffies + HZ/4;
	while(time_before(jiffies, timeout)) udelay(5);

	if (genesis) {
		hfa384x_setreg(hw, genesis, HFA384x_PCIHCR);
		timeout = jiffies + HZ/4;
		while(time_before(jiffies, timeout)) udelay(5);
	}

	/* Clear the reset and wait some more 
	 */
	hfa384x_setreg(hw, 0x45, HFA384x_PCICOR);
	timeout = jiffies + HZ/2;
	while(time_before(jiffies, timeout)) udelay(5);	

	/* Wait for f/w to complete initialization (CMD:BUSY == 0) 
	 */
	timeout = jiffies + 2*HZ;
	reg = hfa384x_getreg(hw, HFA384x_CMD);
	while ( HFA384x_CMD_ISBUSY(reg) && time_before( jiffies, timeout) ) {
		reg = hfa384x_getreg(hw, HFA384x_CMD);
		udelay(10);
	}
	if (HFA384x_CMD_ISBUSY(reg)) {
		WLAN_LOG_WARNING("corereset: Timed out waiting for cmd register.\n");
		result=1;
	}
	DBFEXIT;
	return result;
}
