333 lines
8.1 KiB
C
333 lines
8.1 KiB
C
#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;
|
|
|
|
/* Register the wlandev, this gets us a name and registers the
|
|
* linux netdevice.
|
|
*/
|
|
SET_MODULE_OWNER(wlandev->netdev);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
|
|
SET_NETDEV_DEV(wlandev->netdev, &(pdev->dev));
|
|
#endif
|
|
if ( register_wlandev(wlandev) != 0 ) {
|
|
WLAN_LOG_ERROR("%s: register_wlandev() failed.\n", dev_info);
|
|
result = -EIO;
|
|
goto fail;
|
|
}
|
|
|
|
#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);
|
|
unregister_wlandev(wlandev);
|
|
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,
|
|
SA_SHIRQ, wlandev->name, wlandev);
|
|
|
|
wlandev->msdstate = WLAN_MSD_HWPRESENT;
|
|
|
|
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:
|
|
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_module_init(&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;
|
|
}
|