Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / net / wireless / prism54 / islpci_dev.c
index e1545f6..5ddf295 100644 (file)
@@ -1,4 +1,4 @@
-/*  $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_dev.c,v 1.68 2004/02/28 03:06:07 mcgrof Exp $
+/*
  *  
  *  Copyright (C) 2002 Intersil Americas Inc.
  *  Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
@@ -19,7 +19,6 @@
  *
  */
 
-#include <linux/version.h>
 #include <linux/module.h>
 
 #include <linux/netdevice.h>
@@ -30,6 +29,7 @@
 
 #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
@@ -65,24 +187,25 @@ islpci_interrupt(int irq, void *config, struct pt_regs *regs)
        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);
@@ -93,6 +216,9 @@ islpci_interrupt(int irq, void *config, struct pt_regs *regs)
        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);
 
@@ -220,6 +346,12 @@ islpci_interrupt(int irq, void *config, struct pt_regs *regs)
                        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 */
@@ -247,8 +379,6 @@ islpci_open(struct net_device *ndev)
        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) {
@@ -274,10 +404,10 @@ islpci_close(struct net_device *ndev)
        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);
@@ -289,7 +419,7 @@ prism54_bring_down(islpci_private *priv)
         * 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);
@@ -308,8 +438,7 @@ prism54_bring_down(islpci_private *priv)
        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;
 }
@@ -324,14 +453,7 @@ islpci_upload_fw(islpci_private *priv)
 
        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",
@@ -341,8 +463,7 @@ islpci_upload_fw(islpci_private *priv)
                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);
@@ -357,15 +478,8 @@ islpci_reset_if(islpci_private *priv)
        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);
@@ -375,7 +489,7 @@ islpci_reset_if(islpci_private *priv)
                /* 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;
@@ -385,21 +499,16 @@ islpci_reset_if(islpci_private *priv)
                /* 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);
 
@@ -410,10 +519,18 @@ islpci_reset_if(islpci_private *priv)
         * 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;
 }
 
@@ -448,9 +565,9 @@ islpci_reset(islpci_private *priv, int reload_firmware)
        /* 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++) {
@@ -474,27 +591,27 @@ islpci_reset(islpci_private *priv, int reload_firmware)
        /* 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;
@@ -503,7 +620,7 @@ islpci_statistics(struct net_device *ndev)
 /******************************************************************************
     Network device configuration functions
 ******************************************************************************/
-int
+static int
 islpci_alloc_memory(islpci_private *priv)
 {
        int counter;
@@ -580,6 +697,7 @@ islpci_alloc_memory(islpci_private *priv)
                        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;
 
@@ -614,7 +732,7 @@ islpci_free_memory(islpci_private *priv)
 
        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)
@@ -623,10 +741,10 @@ islpci_free_memory(islpci_private *priv)
                                    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++) {
@@ -635,8 +753,7 @@ islpci_free_memory(islpci_private *priv)
                        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;
         }
@@ -652,7 +769,7 @@ islpci_free_memory(islpci_private *priv)
 
                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 */
@@ -694,7 +811,6 @@ islpci_setup(struct pci_dev *pdev)
        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;
@@ -715,9 +831,13 @@ islpci_setup(struct pci_dev *pdev)
        priv = netdev_priv(ndev);
        priv->ndev = ndev;
        priv->pdev = pdev;
-
+       priv->monitor_type = ARPHRD_IEEE80211;
        priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ?
-               ARPHRD_IEEE80211: ARPHRD_ETHER;
+               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;
@@ -743,23 +863,25 @@ islpci_setup(struct pci_dev *pdev)
        /* initialize workqueue's */
        INIT_WORK(&priv->stats_work,
                  (void (*)(void *)) prism54_update_stats, priv);
-
        priv->stats_timestamp = 0;
 
+       INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake, priv);
+       priv->reset_task_pending = 0;
+
        /* allocate various memory areas */
        if (islpci_alloc_memory(priv))
                goto do_free_netdev;
 
        /* 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;
@@ -776,9 +898,9 @@ islpci_setup(struct pci_dev *pdev)
       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;
 }