📄 islpci_dev.c
字号:
/* * 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> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/module.h>#include <linux/netdevice.h>#include <linux/ethtool.h>#include <linux/pci.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/if_arp.h>#include <asm/io.h>#include "prismcompat.h"#include "isl_38xx.h"#include "isl_ioctl.h"#include "islpci_dev.h"#include "islpci_mgt.h"#include "islpci_eth.h"#include "oid_mgt.h"#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 * OUI prefix, they can process the netdev properly. * 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 */static const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 };static intisl_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 card's base address for writing 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_tislpci_interrupt(int irq, void *config){ u32 reg; islpci_private *priv = config; struct net_device *ndev = priv->ndev; 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 */ {#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n");#endif spin_unlock(&priv->slock); return IRQ_NONE; } /* check whether there is any source of interrupt on the device */ reg = readl(device + ISL38XX_INT_IDENT_REG); /* also check the contents of the Interrupt Enable Register, because this * will filter out interrupt sources from other devices on the same irq ! */ reg &= readl(device + ISL38XX_INT_EN_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);#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_FUNCTION_CALLS, "IRQ: Identification register 0x%p 0x%x \n", device, reg);#endif /* check for each bit in the register separately */ if (reg & ISL38XX_INT_IDENT_UPDATE) {#if VERBOSE > SHOW_ERROR_MESSAGES /* Queue has been updated */ DEBUG(SHOW_TRACING, "IRQ: Update flag \n"); DEBUG(SHOW_QUEUE_INDEXES, "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n", le32_to_cpu(priv->control_block-> driver_curr_frag[0]), le32_to_cpu(priv->control_block-> driver_curr_frag[1]), le32_to_cpu(priv->control_block-> driver_curr_frag[2]), le32_to_cpu(priv->control_block-> driver_curr_frag[3]), le32_to_cpu(priv->control_block-> driver_curr_frag[4]), le32_to_cpu(priv->control_block-> driver_curr_frag[5]) ); DEBUG(SHOW_QUEUE_INDEXES, "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n", le32_to_cpu(priv->control_block-> device_curr_frag[0]), le32_to_cpu(priv->control_block-> device_curr_frag[1]), le32_to_cpu(priv->control_block-> device_curr_frag[2]), le32_to_cpu(priv->control_block-> device_curr_frag[3]), le32_to_cpu(priv->control_block-> device_curr_frag[4]), le32_to_cpu(priv->control_block-> device_curr_frag[5]) );#endif /* cleanup the data low transmit queue */ islpci_eth_cleanup_transmit(priv, priv->control_block); /* device is in active state, update the * powerstate flag if necessary */ powerstate = ISL38XX_PSM_ACTIVE_STATE; /* check all three queues in priority order * call the PIMFOR receive function until the * queue is empty */ if (isl38xx_in_queue(priv->control_block, ISL38XX_CB_RX_MGMTQ) != 0) {#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Received frame in Management Queue\n");#endif islpci_mgt_receive(ndev); islpci_mgt_cleanup_transmit(ndev); /* Refill slots in receive queue */ islpci_mgmt_rx_fill(ndev); /* no need to trigger the device, next islpci_mgt_transaction does it */ } while (isl38xx_in_queue(priv->control_block, ISL38XX_CB_RX_DATA_LQ) != 0) {#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Received frame in Data Low Queue \n");#endif islpci_eth_receive(priv); } /* check whether the data transmit queues were full */ if (priv->data_low_tx_full) { /* check whether the transmit is not full anymore */ if (ISL38XX_CB_TX_QSIZE - isl38xx_in_queue(priv->control_block, ISL38XX_CB_TX_DATA_LQ) >= ISL38XX_MIN_QTHRESHOLD) { /* nope, the driver is ready for more network frames */ netif_wake_queue(priv->ndev); /* reset the full flag */ priv->data_low_tx_full = 0; } } } if (reg & ISL38XX_INT_IDENT_INIT) { /* Device has been initialized */#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Init flag, device initialized \n");#endif wake_up(&priv->reset_done); } if (reg & ISL38XX_INT_IDENT_SLEEP) { /* Device intends to move to powersave state */#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Sleep flag \n");#endif isl38xx_handle_sleep_request(priv->control_block, &powerstate, priv->device_base); } if (reg & ISL38XX_INT_IDENT_WAKEUP) { /* Device has been woken up to active state */#if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Wakeup flag \n");#endif 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 */ if (islpci_get_state(priv) == PRV_STATE_SLEEP && powerstate == ISL38XX_PSM_ACTIVE_STATE) islpci_set_state(priv, PRV_STATE_READY); /* !sleep -> sleep */ if (islpci_get_state(priv) != PRV_STATE_SLEEP && powerstate == ISL38XX_PSM_POWERSAVE_STATE) islpci_set_state(priv, PRV_STATE_SLEEP); /* unlock the interrupt handler */ spin_unlock(&priv->slock); return IRQ_HANDLED;}/****************************************************************************** Network Interface Control & Statistical functions******************************************************************************/static intislpci_open(struct net_device *ndev){ u32 rc; islpci_private *priv = netdev_priv(ndev); /* reset data structures, upload firmware and reset device */ rc = islpci_reset(priv,1); if (rc) { prism54_bring_down(priv); return rc; /* Returns informative message */ } netif_start_queue(ndev);/* netif_mark_up( ndev ); */ return 0;}static intislpci_close(struct net_device *ndev){ islpci_private *priv = netdev_priv(ndev); printk(KERN_DEBUG "%s: islpci_close ()\n", ndev->name); netif_stop_queue(ndev); return prism54_bring_down(priv);}static intprism54_bring_down(islpci_private *priv){ 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); /* 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 */ synchronize_irq(priv->pdev->irq); reg = readl(device_base + ISL38XX_CTRL_STAT_REG); reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT); writel(reg, device_base + ISL38XX_CTRL_STAT_REG); wmb(); udelay(ISL38XX_WRITEIO_DELAY); 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 reset */ schedule_timeout_uninterruptible(msecs_to_jiffies(50)); return 0;}static intislpci_upload_fw(islpci_private *priv){ islpci_state_t old_state; u32 rc; old_state = islpci_set_state(priv, PRV_STATE_BOOT); printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name); rc = isl_upload_firmware(priv); if (rc) { /* error uploading the firmware */ printk(KERN_ERR "%s: could not upload firmware ('%s')\n", priv->ndev->name, priv->firmware); islpci_set_state(priv, old_state); return rc; } printk(KERN_DEBUG "%s: firmware upload complete\n", priv->ndev->name); islpci_set_state(priv, PRV_STATE_POSTBOOT); return 0;}static intislpci_reset_if(islpci_private *priv){ long remaining; int result = -ETIME; int count;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -