/*
- *
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
* Copyright (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
*
*/
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <asm/io.h>
+#include "prismcompat.h"
#include "isl_38xx.h"
#include "isl_ioctl.h"
#include "islpci_dev.h"
#include "islpci_eth.h"
#include "oid_mgt.h"
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
-#define prism54_synchronize_irq(irq) synchronize_irq()
-#else
-#define prism54_synchronize_irq(irq) synchronize_irq(irq)
-#endif
-
#define ISL3877_IMAGE_FILE "isl3877"
+#define ISL3886_IMAGE_FILE "isl3886"
#define ISL3890_IMAGE_FILE "isl3890"
+static int prism54_bring_down(islpci_private *);
+static int islpci_alloc_memory(islpci_private *);
+static struct net_device_stats *islpci_statistics(struct net_device *);
+
/* Temporary dummy MAC address to use until firmware is loaded.
* The idea there is that some tools (such as nameif) may query
* the MAC address before the netdev is 'open'. By using a valid
* Of course, this is not the final/real MAC address. It doesn't
* matter, as you are suppose to be able to change it anytime via
* ndev->set_mac_address. Jean II */
-const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 };
+static const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 };
+
+static int
+isl_upload_firmware(islpci_private *priv)
+{
+ u32 reg, rc;
+ void __iomem *device_base = priv->device_base;
+
+ /* clear the RAMBoot and the Reset bit */
+ reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ reg &= ~ISL38XX_CTRL_STAT_RAMBOOT;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* set the Reset bit without reading the register ! */
+ reg |= ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* clear the Reset bit */
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ wmb();
+
+ /* wait a while for the device to reboot */
+ mdelay(50);
+
+ {
+ const struct firmware *fw_entry = NULL;
+ long fw_len;
+ const u32 *fw_ptr;
+
+ rc = request_firmware(&fw_entry, priv->firmware, PRISM_FW_PDEV);
+ if (rc) {
+ printk(KERN_ERR
+ "%s: request_firmware() failed for '%s'\n",
+ "prism54", priv->firmware);
+ return rc;
+ }
+ /* prepare the Direct Memory Base register */
+ reg = ISL38XX_DEV_FIRMWARE_ADDRES;
+
+ fw_ptr = (u32 *) fw_entry->data;
+ fw_len = fw_entry->size;
+
+ if (fw_len % 4) {
+ printk(KERN_ERR
+ "%s: firmware '%s' size is not multiple of 32bit, aborting!\n",
+ "prism54", priv->firmware);
+ release_firmware(fw_entry);
+ return -EILSEQ; /* Illegal byte sequence */;
+ }
+
+ while (fw_len > 0) {
+ long _fw_len =
+ (fw_len >
+ ISL38XX_MEMORY_WINDOW_SIZE) ?
+ ISL38XX_MEMORY_WINDOW_SIZE : fw_len;
+ u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN;
+
+ /* set the cards base address for writting the data */
+ isl38xx_w32_flush(device_base, reg,
+ ISL38XX_DIR_MEM_BASE_REG);
+ wmb(); /* be paranoid */
+
+ /* increment the write address for next iteration */
+ reg += _fw_len;
+ fw_len -= _fw_len;
+
+ /* write the data to the Direct Memory Window 32bit-wise */
+ /* memcpy_toio() doesn't guarantee 32bit writes :-| */
+ while (_fw_len > 0) {
+ /* use non-swapping writel() */
+ __raw_writel(*fw_ptr, dev_fw_ptr);
+ fw_ptr++, dev_fw_ptr++;
+ _fw_len -= 4;
+ }
+
+ /* flush PCI posting */
+ (void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH);
+ wmb(); /* be paranoid again */
+
+ BUG_ON(_fw_len != 0);
+ }
+
+ BUG_ON(fw_len != 0);
+
+ /* Firmware version is at offset 40 (also for "newmac") */
+ printk(KERN_DEBUG "%s: firmware version: %.8s\n",
+ priv->ndev->name, fw_entry->data + 40);
+
+ release_firmware(fw_entry);
+ }
+
+ /* now reset the device
+ * clear the Reset & ClkRun bit, set the RAMBoot bit */
+ reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
+ reg &= ~ISL38XX_CTRL_STAT_CLKRUN;
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ reg |= ISL38XX_CTRL_STAT_RAMBOOT;
+ isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG);
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* set the reset bit latches the host override and RAMBoot bits
+ * into the device for operation when the reset bit is reset */
+ reg |= ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ /* don't do flush PCI posting here! */
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ /* clear the reset bit should start the whole circus */
+ reg &= ~ISL38XX_CTRL_STAT_RESET;
+ writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
+ /* don't do flush PCI posting here! */
+ wmb();
+ udelay(ISL38XX_WRITEIO_DELAY);
+
+ return 0;
+}
/******************************************************************************
Device Interrupt Handler
******************************************************************************/
irqreturn_t
-islpci_interrupt(int irq, void *config, struct pt_regs *regs)
+islpci_interrupt(int irq, void *config)
{
u32 reg;
islpci_private *priv = config;
struct net_device *ndev = priv->ndev;
- void *device = priv->device_base;
+ void __iomem *device = priv->device_base;
int powerstate = ISL38XX_PSM_POWERSAVE_STATE;
+ /* lock the interrupt handler */
+ spin_lock(&priv->slock);
+
/* received an interrupt request on a shared IRQ line
* first check whether the device is in sleep mode */
reg = readl(device + ISL38XX_CTRL_STAT_REG);
if (reg & ISL38XX_CTRL_STAT_SLEEPMODE)
/* device is in sleep mode, IRQ was generated by someone else */
{
- printk(KERN_DEBUG "Assuming someone else called the IRQ\n");
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
+#endif
+ spin_unlock(&priv->slock);
return IRQ_NONE;
}
- if (islpci_get_state(priv) != PRV_STATE_SLEEP)
- powerstate = ISL38XX_PSM_ACTIVE_STATE;
-
- /* lock the interrupt handler */
- spin_lock(&priv->slock);
/* check whether there is any source of interrupt on the device */
reg = readl(device + ISL38XX_INT_IDENT_REG);
reg &= ISL38XX_INT_SOURCES;
if (reg != 0) {
+ if (islpci_get_state(priv) != PRV_STATE_SLEEP)
+ powerstate = ISL38XX_PSM_ACTIVE_STATE;
+
/* reset the request bits in the Identification register */
isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG);
isl38xx_handle_wakeup(priv->control_block,
&powerstate, priv->device_base);
}
+ } else {
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");
+#endif
+ spin_unlock(&priv->slock);
+ return IRQ_NONE;
}
/* sleep -> ready */
u32 rc;
islpci_private *priv = netdev_priv(ndev);
- printk(KERN_DEBUG "%s: islpci_open()\n", ndev->name);
-
/* reset data structures, upload firmware and reset device */
rc = islpci_reset(priv,1);
if (rc) {
return prism54_bring_down(priv);
}
-int
+static int
prism54_bring_down(islpci_private *priv)
{
- void *device_base = priv->device_base;
+ void __iomem *device_base = priv->device_base;
u32 reg;
/* we are going to shutdown the device */
islpci_set_state(priv, PRV_STATE_PREBOOT);
/* disable all device interrupts in case they weren't */
- isl38xx_disable_interrupts(priv->device_base);
+ isl38xx_disable_interrupts(priv->device_base);
/* For safety reasons, we may want to ensure that no DMA transfer is
* currently in progress by emptying the TX and RX queues. */
/* wait until interrupts have finished executing on other CPUs */
- prism54_synchronize_irq(priv->pdev->irq);
+ synchronize_irq(priv->pdev->irq);
reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT);
wmb();
/* wait a while for the device to reset */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(50*HZ/1000);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(50));
return 0;
}
printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name);
- rc = isl38xx_upload_firmware(priv->firmware,
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,75))
- &priv->pdev->dev,
-#else
- pci_name(priv->pdev),
-#endif
- priv->device_base,
- priv->device_host_address);
+ rc = isl_upload_firmware(priv);
if (rc) {
/* error uploading the firmware */
printk(KERN_ERR "%s: could not upload firmware ('%s')\n",
return rc;
}
- printk(KERN_DEBUG
- "%s: firmware uploaded done, now triggering reset...\n",
+ printk(KERN_DEBUG "%s: firmware upload complete\n",
priv->ndev->name);
islpci_set_state(priv, PRV_STATE_POSTBOOT);
int result = -ETIME;
int count;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
- /* This is 2.6 specific, nicer, shorter, but not in 2.4 yet */
DEFINE_WAIT(wait);
prepare_to_wait(&priv->reset_done, &wait, TASK_UNINTERRUPTIBLE);
-#else
- DECLARE_WAITQUEUE(wait, current);
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&priv->reset_done, &wait);
-#endif
-
+
/* now the last step is to reset the interface */
isl38xx_interface_reset(priv->device_base, priv->device_host_address);
islpci_set_state(priv, PRV_STATE_PREINIT);
for(count = 0; count < 2 && result; count++) {
/* The software reset acknowledge needs about 220 msec here.
* Be conservative and wait for up to one second. */
-
- remaining = schedule_timeout(HZ);
+
+ remaining = schedule_timeout_uninterruptible(HZ);
if(remaining > 0) {
result = 0;
break;
}
- /* If we're here it's because our IRQ hasn't yet gone through.
+ /* If we're here it's because our IRQ hasn't yet gone through.
* Retry a bit more...
*/
- printk(KERN_ERR "%s: device soft reset timed out\n",
- priv->ndev->name);
-
+ printk(KERN_ERR "%s: no 'reset complete' IRQ seen - retrying\n",
+ priv->ndev->name);
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
- /* 2.6 specific too */
finish_wait(&priv->reset_done, &wait);
-#else
- remove_wait_queue(&priv->reset_done, &wait);
- set_current_state(TASK_RUNNING);
-#endif
- if(result)
+ if (result) {
+ printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name);
return result;
+ }
islpci_set_state(priv, PRV_STATE_INIT);
/* Now that the device is 100% up, let's allow
* for the other interrupts --
- * NOTE: this is not *yet* true since we've only allowed the
+ * NOTE: this is not *yet* true since we've only allowed the
* INIT interrupt on the IRQ line. We can perhaps poll
* the IRQ line until we know for sure the reset went through */
isl38xx_enable_common_interrupts(priv->device_base);
- prism54_mib_init_work(priv);
+ down_write(&priv->mib_sem);
+ result = mgt_commit(priv);
+ if (result) {
+ printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name);
+ up_write(&priv->mib_sem);
+ return result;
+ }
+ up_write(&priv->mib_sem);
islpci_set_state(priv, PRV_STATE_READY);
+ printk(KERN_DEBUG "%s: interface reset complete\n", priv->ndev->name);
return 0;
}
/* reset the mgmt receive queue */
for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
isl38xx_fragment *frag = &cb->rx_data_mgmt[counter];
- frag->size = MGMT_FRAME_SIZE;
+ frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
frag->flags = 0;
- frag->address = priv->mgmt_rx[counter].pci_addr;
+ frag->address = cpu_to_le32(priv->mgmt_rx[counter].pci_addr);
}
for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
/* now that the data structures are cleaned up, upload
* firmware and reset interface */
rc = islpci_upload_fw(priv);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR "%s: islpci_reset: failure\n",
+ priv->ndev->name);
return rc;
+ }
}
/* finally reset interface */
rc = islpci_reset_if(priv);
- if (!rc) /* If successful */
- return rc;
-
- printk(KERN_DEBUG "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n");
+ if (rc)
+ printk(KERN_ERR "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n");
return rc;
-
}
-struct net_device_stats *
+static struct net_device_stats *
islpci_statistics(struct net_device *ndev)
{
islpci_private *priv = netdev_priv(ndev);
#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_statistics \n");
+ DEBUG(SHOW_FUNCTION_CALLS, "islpci_statistics\n");
#endif
return &priv->statistics;
/******************************************************************************
Network device configuration functions
******************************************************************************/
-int
+static int
islpci_alloc_memory(islpci_private *priv)
{
int counter;
skb = NULL;
goto out_free;
}
+ skb_reserve(skb, (4 - (long) skb->data) & 0x03);
/* add the new allocated sk_buff to the buffer array */
priv->data_low_rx[counter] = skb;
}
prism54_acl_init(&priv->acl);
- prism54_wpa_ie_init(priv);
- if (mgt_init(priv))
+ prism54_wpa_bss_ie_init(priv);
+ if (mgt_init(priv))
goto out_free;
return 0;
if (priv->device_base)
iounmap(priv->device_base);
- priv->device_base = 0;
+ priv->device_base = NULL;
/* free consistent DMA area... */
if (priv->driver_mem_address)
priv->device_host_address);
/* clear some dangling pointers */
- priv->driver_mem_address = 0;
+ priv->driver_mem_address = NULL;
priv->device_host_address = 0;
priv->device_psm_buffer = 0;
- priv->control_block = 0;
+ priv->control_block = NULL;
/* clean up mgmt rx buffers */
for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
pci_unmap_single(priv->pdev, buf->pci_addr,
buf->size, PCI_DMA_FROMDEVICE);
buf->pci_addr = 0;
- if (buf->mem)
- kfree(buf->mem);
+ kfree(buf->mem);
buf->size = 0;
buf->mem = NULL;
}
if (priv->data_low_rx[counter])
dev_kfree_skb(priv->data_low_rx[counter]);
- priv->data_low_rx[counter] = 0;
+ priv->data_low_rx[counter] = NULL;
}
/* Free the acces control list and the WPA list */
prism54_acl_clean(&priv->acl);
- prism54_wpa_ie_clean(priv);
+ prism54_wpa_bss_ie_clean(priv);
mgt_clean(priv);
return 0;
ndev->open = &islpci_open;
ndev->stop = &islpci_close;
ndev->get_stats = &islpci_statistics;
- ndev->get_wireless_stats = &prism54_get_wireless_stats;
ndev->do_ioctl = &prism54_ioctl;
ndev->wireless_handlers =
(struct iw_handler_def *) &prism54_handler_def;
priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ?
priv->monitor_type : ARPHRD_ETHER;
+ /* Add pointers to enable iwspy support. */
+ priv->wireless_data.spy_data = &priv->spy_data;
+ ndev->wireless_data = &priv->wireless_data;
+
/* save the start and end address of the PCI memory area */
ndev->mem_start = (unsigned long) priv->device_base;
ndev->mem_end = ndev->mem_start + ISL38XX_PCI_MEM_SIZE;
priv->state_off = 1;
/* initialize workqueue's */
- INIT_WORK(&priv->stats_work,
- (void (*)(void *)) prism54_update_stats, priv);
+ INIT_WORK(&priv->stats_work, prism54_update_stats);
priv->stats_timestamp = 0;
- INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake, priv);
+ INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake);
priv->reset_task_pending = 0;
/* allocate various memory areas */
/* select the firmware file depending on the device id */
switch (pdev->device) {
- case PCIDEVICE_ISL3890:
- case PCIDEVICE_3COM6001:
- strcpy(priv->firmware, ISL3890_IMAGE_FILE);
- break;
- case PCIDEVICE_ISL3877:
+ case 0x3877:
strcpy(priv->firmware, ISL3877_IMAGE_FILE);
break;
+ case 0x3886:
+ strcpy(priv->firmware, ISL3886_IMAGE_FILE);
+ break;
+
default:
strcpy(priv->firmware, ISL3890_IMAGE_FILE);
break;
do_islpci_free_memory:
islpci_free_memory(priv);
do_free_netdev:
- pci_set_drvdata(pdev, 0);
+ pci_set_drvdata(pdev, NULL);
free_netdev(ndev);
- priv = 0;
+ priv = NULL;
return NULL;
}