patch-2.1.125 linux/drivers/net/rrunner.c
Next file: linux/drivers/net/rrunner.h
Previous file: linux/drivers/net/ppp.c
Back to the patch index
Back to the overall index
- Lines: 1209
- Date:
Wed Oct 7 11:13:49 1998
- Orig file:
v2.1.124/linux/drivers/net/rrunner.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.124/linux/drivers/net/rrunner.c linux/drivers/net/rrunner.c
@@ -0,0 +1,1208 @@
+/*
+ * rrunner.c: Linux driver for the Essential RoadRunner HIPPI board.
+ *
+ * Written 1998 by Jes Sorensen, <Jes.Sorensen@cern.ch>.
+ *
+ * Thanks to Essential Communication for providing us with hardware
+ * and very comprehensive documentation without which I would not have
+ * been able to write this driver. A special thank you to John Gibbon
+ * for sorting out the legal issues, with the NDA, allowing the code to
+ * be released under the GPL.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#define DEBUG 1
+#define RX_DMA_SKBUFF 1
+#define PKT_COPY_THRESHOLD 512
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/hippidevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/cache.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "rrunner.h"
+
+/*
+ * Implementation notes:
+ *
+ * The DMA engine only allows for DMA within physical 64KB chunks of
+ * memory. The current approach of the driver (and stack) is to use
+ * linear blocks of memory for the skbuffs. However, as the data block
+ * is always the first part of the skb and skbs are 2^n aligned so we
+ * are guarantted to get the whole block within one 64KB align 64KB
+ * chunk.
+ *
+ * On the long term, relying on being able to allocate 64KB linear
+ * chunks of memory is not feasible and the skb handling code and the
+ * stack will need to know about I/O vectors or something similar.
+ */
+
+static const char *version = "rrunner.c: v0.06 09/02/98 Jes Sorensen (Jes.Sorensen@cern.ch)\n";
+
+static unsigned int read_eeprom(struct rr_private *rrpriv,
+ unsigned long offset,
+ unsigned char *buf,
+ unsigned long length);
+static u32 read_eeprom_word(struct rr_private *rrpriv,
+ void * offset);
+static int rr_load_firmware(struct device *dev);
+
+
+__initfunc(int rr_hippi_probe (struct device *dev))
+{
+ static int i = 0;
+ int boards_found = 0;
+ int version_disp; /* was version info already displayed? */
+ u8 pci_bus; /* PCI bus number (0-255) */
+ u8 pci_dev_fun; /* PCI device and function numbers (0-255) */
+ u8 pci_latency;
+ u16 command; /* PCI Configuration space Command register */
+ unsigned int tmp;
+ u8 irq;
+ struct rr_private *rrpriv;
+
+ if (!pci_present()) /* is PCI BIOS even present? */
+ return -ENODEV;
+
+ version_disp = 0;
+
+ for (; i < 255; i++)
+ {
+ if (pcibios_find_device(PCI_VENDOR_ID_ESSENTIAL,
+ PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER,
+ i, &pci_bus, &pci_dev_fun) != 0)
+ break;
+
+ pcibios_read_config_word(pci_bus, pci_dev_fun,
+ PCI_COMMAND, &command);
+
+ /* Enable mastering */
+
+ command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_dev_fun,
+ PCI_COMMAND, command);
+
+ if (!(command & PCI_COMMAND_MEMORY)){
+ printk("shared mem not enabled - unable to configure RoadRunner\n");
+ break;
+ }
+
+ /*
+ * So we found our HIPPI ... time to tell the system.
+ */
+
+ dev = init_hippi_dev(dev, sizeof(struct rr_private));
+
+ if (dev == NULL)
+ break;
+
+ rrpriv = (struct rr_private *)dev->priv;
+
+ /* Read register base address from
+ PCI Configuration Space */
+
+ pcibios_read_config_dword(pci_bus, pci_dev_fun,
+ PCI_BASE_ADDRESS_0, &tmp);
+
+ pcibios_read_config_byte(pci_bus, pci_dev_fun,
+ PCI_INTERRUPT_LINE, &irq);
+
+ dev->irq = irq;
+ rrpriv->pci_bus = pci_bus;
+ rrpriv->pci_dev_fun = pci_dev_fun;
+ sprintf(rrpriv->name, "RoadRunner serial HIPPI");
+#ifdef __SMP__
+ spin_lock_init(&rrpriv->lock);
+#endif
+
+ dev->open = &rr_open;
+ dev->hard_start_xmit = &rr_start_xmit;
+ dev->stop = &rr_close;
+ dev->get_stats = &rr_get_stats;
+ dev->do_ioctl = &rr_ioctl;
+
+ /*
+ * Dummy value.
+ */
+ dev->base_addr = 42;
+
+ /* display version info if adapter is found */
+ if (!version_disp)
+ {
+ /* set display flag to TRUE so that */
+ /* we only display this string ONCE */
+ version_disp = 1;
+ printk(version);
+ }
+
+ printk(KERN_INFO "%s: Essential RoadRunner serial HIPPI at 0x%08x, irq %i\n",
+ dev->name, tmp, dev->irq);
+
+ pcibios_read_config_byte(pci_bus, pci_dev_fun,
+ PCI_LATENCY_TIMER, &pci_latency);
+#if 0
+ if (pci_latency <= 48){
+ printk(" PCI latency counter too low (%i), setting to 48 clocks\n", pci_latency);
+ pcibios_write_config_byte(pci_bus, pci_dev_fun,
+ PCI_LATENCY_TIMER, 48);
+ }
+#else
+ if (pci_latency <= 0x58)
+ pcibios_write_config_byte(pci_bus, pci_dev_fun,
+ PCI_LATENCY_TIMER, 0x58);
+#endif
+ /*
+ * Remap the regs into kernel space.
+ */
+
+ rrpriv->regs = (struct rr_regs *)ioremap(tmp, 0x1000);
+ if (!rrpriv->regs){
+ printk(KERN_ERR "%s: Unable to map I/O register, RoadRunner %i will be disabled.\n", dev->name, i);
+ break;
+ }
+
+ /*
+ * Don't access any registes before this point!
+ */
+#ifdef __BIG_ENDIAN
+ regs->HostCtrl |= NO_SWAP;
+#endif
+ /*
+ * Need to add a case for little-endian 64-bit hosts here.
+ */
+
+ rr_init(dev);
+
+ boards_found++;
+
+ /*
+ * This is bollocks, but we need to tell the net-init
+ * code that it shall go for the next device.
+ */
+ dev->base_addr = 0;
+ }
+
+ /*
+ * If we're at this point we're going through rr_hippi_probe()
+ * for the first time. Return success (0) if we've initialized
+ * 1 or more boards. Otherwise, return failure (-ENODEV).
+ */
+
+ if (boards_found > 0)
+ return 0;
+ else
+ return -ENODEV;
+}
+
+
+/*
+ * Commands are considered to be slow, thus there is no reason to
+ * inline this.
+ */
+static void rr_issue_cmd(struct rr_private *rrpriv, struct cmd *cmd)
+{
+ struct rr_regs *regs;
+ u32 idx;
+
+ regs = rrpriv->regs;
+ /*
+ * This is temporary - it will go away in the final version.
+ * We probably also want to make this function inline.
+ */
+ if (regs->HostCtrl & NIC_HALTED){
+ printk("issuing command for halted NIC, code 0x%x, HostCtrl %08x\n", cmd->code, regs->HostCtrl);
+ if (regs->Mode & FATAL_ERR)
+ printk("error code %02x\n", regs->Fail1);
+ }
+
+ idx = rrpriv->info->cmd_ctrl.pi;
+
+ regs->CmdRing[idx] = *(u32*)(cmd);
+
+ idx = (idx - 1) % CMD_RING_ENTRIES;
+ rrpriv->info->cmd_ctrl.pi = idx;
+
+ if (regs->Mode & FATAL_ERR)
+ printk("error code %02x\n", regs->Fail1);
+}
+
+
+/*
+ * Reset the board in a sensible manner. The NIC is already halted
+ * when we get here and a spin-lock is held.
+ */
+static int rr_reset(struct device *dev)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ struct eeprom *hw = NULL;
+ u32 start_pc;
+ int i;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ rr_load_firmware(dev);
+
+ regs->TX_state = 0x01000000;
+ regs->RX_state = 0xff800000;
+ regs->AssistState = 0;
+ regs->LocalCtrl = CLEAR_INTA;
+ regs->BrkPt = 0x01;
+ regs->Timer = 0;
+ regs->TimerRef = 0;
+ regs->DmaReadState = RESET_DMA;
+ regs->DmaWriteState = RESET_DMA;
+ regs->DmaWriteHostHi = 0;
+ regs->DmaWriteHostLo = 0;
+ regs->DmaReadHostHi = 0;
+ regs->DmaReadHostLo = 0;
+ regs->DmaReadLen = 0;
+ regs->DmaWriteLen = 0;
+ regs->DmaWriteLcl = 0;
+ regs->DmaWriteIPchecksum = 0;
+ regs->DmaReadLcl = 0;
+ regs->DmaReadIPchecksum = 0;
+ regs->PciState = 0; /* 0x90 for GE? */
+ regs->Mode = SWAP_DATA;
+
+#if 0
+ /*
+ * Don't worry, this is just black magic.
+ */
+ regs->RxBase = 0xdf000;
+ regs->RxPrd = 0xdf000;
+ regs->RxCon = 0xdf000;
+ regs->TxBase = 0xce000;
+ regs->TxPrd = 0xce000;
+ regs->TxCon = 0xce000;
+ regs->RxIndPro = 0;
+ regs->RxIndCon = 0;
+ regs->RxIndRef = 0;
+ regs->TxIndPro = 0;
+ regs->TxIndCon = 0;
+ regs->TxIndRef = 0;
+ regs->pad10[0] = 0xcc000;
+ regs->DrCmndPro = 0;
+ regs->DrCmndCon = 0;
+ regs->DwCmndPro = 0;
+ regs->DwCmndCon = 0;
+ regs->DwCmndRef = 0;
+ regs->DrDataPro = 0;
+ regs->DrDataCon = 0;
+ regs->DrDataRef = 0;
+ regs->DwDataPro = 0;
+ regs->DwDataCon = 0;
+ regs->DwDataRef = 0;
+#endif
+
+ regs->MbEvent = 0xffffffff;
+ regs->Event = 0;
+
+ regs->TxPi = 0;
+ regs->IpRxPi = 0;
+
+ regs->EvtCon = 0;
+ regs->EvtPrd = 0;
+
+ rrpriv->info->evt_ctrl.pi = 0;
+
+ for (i = 0; i < CMD_RING_ENTRIES; i++)
+ regs->CmdRing[i] = 0;
+
+ regs->PciState = 0;
+
+ start_pc = read_eeprom_word(rrpriv, &hw->rncd_info.FwStart);
+
+#if (DEBUG > 1)
+ printk("%s: Executing firmware at address 0x%06x\n",
+ dev->name, start_pc);
+#endif
+
+ regs->Pc = start_pc + 0x800;
+ udelay(5);
+
+ regs->Pc = start_pc;
+
+ return 0;
+}
+
+/*
+ * Read a string from the EEPROM.
+ */
+static unsigned int read_eeprom(struct rr_private *rrpriv,
+ unsigned long offset,
+ unsigned char *buf,
+ unsigned long length)
+{
+ struct rr_regs *regs = rrpriv->regs;
+ u32 misc, io, i;
+
+ io = regs->ExtIo;
+ regs->ExtIo = 0;
+ misc = regs->LocalCtrl;
+ regs->LocalCtrl = 0;
+
+ for (i = 0; i < length; i++){
+ regs->WinBase = (EEPROM_BASE + ((offset+i) << 3));
+ buf[i] = (regs->WinData >> 24) & 0xff;
+ }
+
+ regs->LocalCtrl = misc;
+ regs->ExtIo = io;
+
+ return i;
+}
+
+
+/*
+ * Shortcut to read one word (4 bytes) out of the EEPROM and convert
+ * it to our CPU byte-order.
+ */
+static u32 read_eeprom_word(struct rr_private *rrpriv,
+ void * offset)
+{
+ u32 word;
+
+ if ((read_eeprom(rrpriv, (unsigned long)offset,
+ (char *)&word, 4) == 4))
+ return be32_to_cpu(word);
+ return 0;
+}
+
+
+__initfunc(static int rr_init(struct device *dev))
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ u32 sram_size, rev;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ rev = regs->FwRev;
+ if (rev > 0x00020024)
+ printk(" Firmware revision: %i.%i.%i\n", (rev >> 16),
+ ((rev >> 8) & 0xff), (rev & 0xff));
+ else{
+ printk(" Firmware revision too old: %i.%i.%i, please upgrade to 2.0.37 or later.\n",
+ (rev >> 16), ((rev >> 8) & 0xff), (rev & 0xff));
+ return -EFAULT;
+
+ }
+
+ printk(" Maximum receive rings %i\n", regs->MaxRxRng);
+
+ sram_size = read_eeprom_word(rrpriv, (void *)8);
+ printk(" SRAM size 0x%06x\n", sram_size);
+
+ return 0;
+}
+
+
+static int rr_init1(struct device *dev)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ u32 hostctrl;
+ unsigned long myjif, flags, tmp_ptr;
+ struct cmd cmd;
+ short i;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ spin_lock_irqsave(&rrpriv->lock, flags);
+
+ hostctrl = regs->HostCtrl;
+ regs->HostCtrl |= HALT_NIC;
+
+ if (hostctrl & PARITY_ERR){
+ printk("%s: Parity error halting NIC - this is serious!\n",
+ dev->name);
+ spin_unlock_irqrestore(&rrpriv->lock, flags);
+ return -EFAULT;
+ }
+
+
+ memset(rrpriv->rx_ctrl, 0, 256 * sizeof(struct ring_ctrl));
+ memset(rrpriv->info, 0, sizeof(struct rr_info));
+
+ tmp_ptr = virt_to_bus((void *)rrpriv->rx_ctrl);
+#if (BITS_PER_LONG == 64)
+ regs->RxRingHi = (tmp_ptr >> 32);
+#else
+ regs->RxRingHi = 0;
+#endif
+ regs->RxRingLo = ((tmp_ptr) & 0xffffffff);
+
+ tmp_ptr = virt_to_bus((void *)rrpriv->info);
+#if (BITS_PER_LONG == 64)
+ regs->InfoPtrHi = (tmp_ptr >> 32);
+#else
+ regs->InfoPtrHi = 0;
+#endif
+ regs->InfoPtrLo = ((tmp_ptr) & 0xffffffff);
+
+ rrpriv->info->evt_ctrl.entry_size = sizeof(struct event);
+ rrpriv->info->evt_ctrl.entries = EVT_RING_ENTRIES;
+ rrpriv->info->evt_ctrl.mode = 0;
+ rrpriv->info->evt_ctrl.pi = 0;
+ rrpriv->info->evt_ctrl.rngptr = virt_to_bus(rrpriv->evt_ring);
+
+ rrpriv->info->cmd_ctrl.entry_size = sizeof(struct cmd);
+ rrpriv->info->cmd_ctrl.entries = CMD_RING_ENTRIES;
+ rrpriv->info->cmd_ctrl.mode = 0;
+ rrpriv->info->cmd_ctrl.pi = 15;
+
+ for (i = 0; i < CMD_RING_ENTRIES; i++) {
+ regs->CmdRing[i] = 0;
+ }
+
+ for (i = 0; i < TX_RING_ENTRIES; i++) {
+ rrpriv->tx_ring[i].size = 0;
+ rrpriv->tx_ring[i].addr = 0;
+ rrpriv->tx_skbuff[i] = 0;
+ }
+
+ rrpriv->info->tx_ctrl.entry_size = sizeof(struct tx_desc);
+ rrpriv->info->tx_ctrl.entries = TX_RING_ENTRIES;
+ rrpriv->info->tx_ctrl.mode = 0;
+ rrpriv->info->tx_ctrl.pi = 0;
+ rrpriv->info->tx_ctrl.rngptr = virt_to_bus(rrpriv->tx_ring);
+
+ /*
+ * Set dirty_tx before we start receiving interrupts, otherwise
+ * the interrupt handler might think it is supposed to process
+ * tx ints before we are up and running, which may cause a null
+ * pointer access in the int handler.
+ */
+ rrpriv->tx_full = 0;
+ rrpriv->cur_rx = 0;
+ rrpriv->dirty_rx = rrpriv->dirty_tx = 0;
+
+ rr_reset(dev);
+
+ regs->IntrTmr = 0x60;
+ regs->WriteDmaThresh = 0x80 | 0x1f;
+ regs->ReadDmaThresh = 0x80 | 0x1f;
+
+ rrpriv->fw_running = 0;
+
+ hostctrl &= ~(HALT_NIC | INVALID_INST_B | PARITY_ERR);
+ regs->HostCtrl = hostctrl;
+
+ spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+ udelay(1000);
+
+ /*
+ * Now start the FirmWare.
+ */
+ cmd.code = C_START_FW;
+ cmd.ring = 0;
+ cmd.index = 0;
+
+ rr_issue_cmd(rrpriv, &cmd);
+
+ /*
+ * Give the FirmWare time to chew on the `get running' command.
+ */
+ myjif = jiffies + 5 * HZ;
+ while ((jiffies < myjif) && !rrpriv->fw_running);
+
+ for (i = 0; i < RX_RING_ENTRIES; i++) {
+ struct sk_buff *skb;
+
+ rrpriv->rx_ring[i].mode = 0;
+ skb = alloc_skb(dev->mtu + HIPPI_HLEN, GFP_ATOMIC);
+ rrpriv->rx_skbuff[i] = skb;
+ /*
+ * Sanity test to see if we conflict with the DMA
+ * limitations of the Roadrunner.
+ */
+ if ((((unsigned long)skb->data) & 0xfff) > ~65320)
+ printk("skb alloc error\n");
+
+#if (BITS_PER_LONG == 32)
+ rrpriv->rx_ring[i].zero = 0;
+#endif
+ rrpriv->rx_ring[i].addr = virt_to_bus(skb->data);
+ rrpriv->rx_ring[i].size = dev->mtu + HIPPI_HLEN;
+ }
+
+ rrpriv->rx_ctrl[4].entry_size = sizeof(struct rx_desc);
+ rrpriv->rx_ctrl[4].entries = RX_RING_ENTRIES;
+ rrpriv->rx_ctrl[4].mode = 8;
+ rrpriv->rx_ctrl[4].pi = 0;
+ rrpriv->rx_ctrl[4].rngptr = virt_to_bus(rrpriv->rx_ring);
+
+ cmd.code = C_NEW_RNG;
+ cmd.ring = 4;
+ cmd.index = 0;
+ rr_issue_cmd(rrpriv, &cmd);
+
+#if 0
+{
+ u32 tmp;
+ tmp = regs->ExtIo;
+ regs->ExtIo = 0x80;
+
+ i = jiffies + 1 * HZ;
+ while (jiffies < i);
+ regs->ExtIo = tmp;
+}
+#endif
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+}
+
+
+/*
+ * All events are considered to be slow (RX/TX ints do not generate
+ * events) and are handled here, outside the main interrupt handler,
+ * to reduce the size of the handler.
+ */
+static u32 rr_handle_event(struct device *dev, u32 prodidx)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ u32 tmp, eidx;
+#if 0
+ short i;
+#endif
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+ eidx = rrpriv->info->evt_ctrl.pi;
+
+ while (prodidx != eidx){
+ switch (rrpriv->evt_ring[eidx].code){
+ case E_NIC_UP:
+ tmp = regs->FwRev;
+ printk("%s: Firmware revision %i.%i.%i up and running\n",
+ dev->name, (tmp >> 16), ((tmp >> 8) & 0xff),
+ (tmp & 0xff));
+ rrpriv->fw_running = 1;
+ break;
+ case E_LINK_ON:
+ printk("%s: Optical link ON\n", dev->name);
+ break;
+ case E_LINK_OFF:
+ printk("%s: Optical link OFF\n", dev->name);
+ break;
+ case E_RX_IDLE:
+ printk("%s: RX data not moving\n", dev->name);
+ break;
+ case E_WATCHDOG:
+ printk("%s: The watchdog is here to see us\n",
+ dev->name);
+ break;
+ /*
+ * TX events.
+ */
+ case E_CON_REJ:
+ printk("%s: Connection rejected\n", dev->name);
+ rrpriv->stats.tx_aborted_errors++;
+ break;
+ case E_CON_TMOUT:
+ printk("%s: Connection timeout\n", dev->name);
+ break;
+ case E_DISC_ERR:
+ printk("%s: HIPPI disconnect error\n", dev->name);
+ rrpriv->stats.tx_aborted_errors++;
+ break;
+ case E_TX_IDLE:
+ printk("%s: Transmitter idle\n", dev->name);
+ break;
+ case E_TX_LINK_DROP:
+ printk("%s: Link lost during transmit\n", dev->name);
+ rrpriv->stats.tx_aborted_errors++;
+ break;
+ /*
+ * RX events.
+ */
+ case E_VAL_RNG: /* Should be ignored */
+#if (DEBUG > 2)
+ printk("%s: RX ring valid event\n", dev->name);
+#endif
+ regs->IpRxPi = RX_RING_ENTRIES - 1;
+ break;
+ case E_INV_RNG:
+ printk("%s: RX ring invalid event\n", dev->name);
+ break;
+ case E_RX_RNG_OUT:
+ printk("%s: Receive ring full\n", dev->name);
+ break;
+
+ case E_RX_PAR_ERR:
+ printk("%s: Receive parity error.\n", dev->name);
+ break;
+ case E_RX_LLRC_ERR:
+ printk("%s: Receive LLRC error.\n", dev->name);
+ break;
+ case E_PKT_LN_ERR:
+ printk("%s: Receive packet length error.\n",
+ dev->name);
+ break;
+ default:
+ printk("%s: Unhandled event 0x%02x\n",
+ dev->name, rrpriv->evt_ring[eidx].code);
+ }
+ eidx = (eidx + 1) % EVT_RING_ENTRIES;
+ }
+
+ rrpriv->info->evt_ctrl.pi = eidx;
+ return eidx;
+}
+
+
+static int rx_int(struct device *dev, u32 rxlimit)
+{
+ struct rr_private *rrpriv = (struct rr_private *)dev->priv;
+ u32 index, pkt_len;
+ struct rr_regs *regs = rrpriv->regs;
+
+ index = rrpriv->cur_rx;
+
+ while(index != rxlimit){
+ pkt_len = rrpriv->rx_ring[index].size;
+#if (DEBUG > 2)
+ printk("index %i, rxlimit %i\n", index, rxlimit);
+ printk("len %x, mode %x\n", pkt_len,
+ rrpriv->rx_ring[index].mode);
+#endif
+#if 0
+/*
+ * I have never seen this occur
+ */
+ if(!(rrpriv->rx_skbuff[index])){
+ printk("Trying to receive in empty skbuff\n");
+ goto out;
+ }
+#endif
+
+ if (pkt_len > 0){
+ struct sk_buff *skb;
+
+ if (pkt_len < PKT_COPY_THRESHOLD) {
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
+ if (skb == NULL){
+ printk("%s: Out of memory deferring packet\n", dev->name);
+ rrpriv->stats.rx_dropped++;
+ goto defer;
+ }else
+ memcpy(skb_put(skb, pkt_len),
+ rrpriv->rx_skbuff[index]->data,
+ pkt_len);
+ }else{
+ struct sk_buff *newskb;
+
+ newskb = alloc_skb(dev->mtu + HIPPI_HLEN, GFP_ATOMIC);
+ if (newskb){
+ skb = rrpriv->rx_skbuff[index];
+ skb_put(skb, pkt_len);
+ rrpriv->rx_skbuff[index] = newskb;
+ rrpriv->rx_ring[index].addr = virt_to_bus(newskb->data);
+ }else{
+ printk("%s: Out of memory, deferring packet\n", dev->name);
+ rrpriv->stats.rx_dropped++;
+ goto defer;
+ }
+ }
+ skb->dev = dev;
+ skb->protocol = hippi_type_trans(skb, dev);
+
+ netif_rx(skb); /* send it up */
+
+ rrpriv->stats.rx_packets++;
+ rrpriv->stats.rx_bytes += skb->len;
+ }
+ defer:
+ rrpriv->rx_ring[index].mode = 0;
+ rrpriv->rx_ring[index].size = dev->mtu + HIPPI_HLEN;
+
+ if ((index & 7) == 7)
+ regs->IpRxPi = index;
+
+ index = (index + 1) % RX_RING_ENTRIES;
+ }
+
+ rrpriv->cur_rx = index;
+ return index;
+}
+
+
+static void rr_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ struct device *dev = (struct device *)dev_id;
+ u32 prodidx, eidx, txcsmr, rxlimit, txcon;
+ unsigned long flags;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ if (!(regs->HostCtrl & RR_INT)){
+#if 0
+ /* These are harmless */
+ printk("%s: spurious interrupt detected\n", dev->name);
+#endif
+ return;
+ }
+
+ if (test_and_set_bit(0, (void*)&dev->interrupt) != 0) {
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ return;
+ }
+
+ spin_lock_irqsave(&rrpriv->lock, flags);
+
+ prodidx = regs->EvtPrd;
+ txcsmr = (prodidx >> 8) & 0xff;
+ rxlimit = (prodidx >> 16) & 0xff;
+ prodidx &= 0xff;
+
+#if (DEBUG > 2)
+ printk("%s: interrupt, prodidx = %i, eidx = %i\n", dev->name,
+ prodidx, rrpriv->info->evt_ctrl.pi);
+#endif
+
+ txcon = rrpriv->dirty_tx;
+ if (txcsmr != txcon) {
+ do {
+ rrpriv->stats.tx_packets++;
+ rrpriv->stats.tx_bytes +=rrpriv->tx_skbuff[txcon]->len;
+ dev_kfree_skb(rrpriv->tx_skbuff[txcon]);
+
+ rrpriv->tx_skbuff[txcon] = NULL;
+ rrpriv->tx_ring[txcon].size = 0;
+ rrpriv->tx_ring[txcon].addr = 0;
+ rrpriv->tx_ring[txcon].mode = 0;
+
+ txcon = (txcon + 1) % TX_RING_ENTRIES;
+ } while (txcsmr != txcon);
+
+ rrpriv->dirty_tx = txcon;
+ if (rrpriv->tx_full && dev->tbusy &&
+ (((rrpriv->info->tx_ctrl.pi + 1) % TX_RING_ENTRIES)
+ != rrpriv->dirty_tx)){
+ rrpriv->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ }
+
+ rx_int(dev, rxlimit);
+
+ eidx = rrpriv->info->evt_ctrl.pi;
+
+ if (prodidx != eidx)
+ eidx = rr_handle_event(dev, prodidx);
+
+ eidx |= ((txcsmr << 8) | (rxlimit << 16));
+ regs->EvtCon = eidx;
+
+ spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+ dev->interrupt = 0;
+}
+
+
+static int rr_open(struct device *dev)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+#if 0
+ regs->HostCtrl |= (HALT_NIC | RR_CLEAR_INT);
+#endif
+
+ if (request_irq(dev->irq, rr_interrupt, 0, rrpriv->name, dev))
+ {
+ printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
+ dev->name, dev->irq);
+ return -EAGAIN;
+ }
+
+ rrpriv->rx_ctrl = kmalloc(256*sizeof(struct ring_ctrl),
+ GFP_KERNEL | GFP_DMA);
+ rrpriv->info = kmalloc(sizeof(struct rr_info), GFP_KERNEL | GFP_DMA);
+
+ rr_init1(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static void rr_dump(struct device *dev)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ u32 index, cons;
+ short i;
+ int len;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ printk("%s: dumping NIC TX rings\n", dev->name);
+
+ printk("RxPrd %08x, TxPrd %02x, EvtPrd %08x, TxPi %02x, TxCtrlPi %02x\n",
+ regs->RxPrd, regs->TxPrd, regs->EvtPrd, regs->TxPi,
+ rrpriv->info->tx_ctrl.pi);
+
+ printk("Error code 0x%x\n", regs->Fail1);
+
+ index = (((regs->EvtPrd >> 8) & 0xff ) - 1) % EVT_RING_ENTRIES;
+ cons = rrpriv->dirty_tx;
+ printk("TX ring index %i, TX consumer %i\n",
+ index, cons);
+
+ if (rrpriv->tx_skbuff[index]){
+ len = min(0x80, rrpriv->tx_skbuff[index]->len);
+ printk("skbuff for index %i is valid - dumping data (0x%x bytes - DMA len 0x%x)\n", index, len, rrpriv->tx_ring[index].size);
+ for (i = 0; i < len; i++){
+ if (!(i & 7))
+ printk("\n");
+ printk("%02x ", (unsigned char) rrpriv->tx_skbuff[index]->data[i]);
+ }
+ printk("\n");
+ }
+
+ if (rrpriv->tx_skbuff[cons]){
+ len = min(0x80, rrpriv->tx_skbuff[cons]->len);
+ printk("skbuff for cons %i is valid - dumping data (0x%x bytes - skbuff len 0x%x)\n", cons, len, rrpriv->tx_skbuff[cons]->len);
+ printk("mode 0x%x, size 0x%x,\n phys %08x (virt %08x), skbuff-addr %08x, truesize 0x%x\n",
+ rrpriv->tx_ring[cons].mode,
+ rrpriv->tx_ring[cons].size,
+ rrpriv->tx_ring[cons].addr,
+ (unsigned int)bus_to_virt(rrpriv->tx_ring[cons].addr),
+ (unsigned int)rrpriv->tx_skbuff[cons]->data,
+ (unsigned int)rrpriv->tx_skbuff[cons]->truesize);
+ for (i = 0; i < len; i++){
+ if (!(i & 7))
+ printk("\n");
+ printk("%02x ", (unsigned char)rrpriv->tx_ring[cons].size);
+ }
+ printk("\n");
+ }
+
+ printk("dumping TX ring info:\n");
+ for (i = 0; i < TX_RING_ENTRIES; i++)
+ printk("mode 0x%x, size 0x%x, phys-addr %08x\n",
+ rrpriv->tx_ring[i].mode,
+ rrpriv->tx_ring[i].size,
+ rrpriv->tx_ring[i].addr);
+
+}
+
+
+static int rr_close(struct device *dev)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+ u32 tmp;
+ short i;
+
+ dev->start = 0;
+ set_bit(0, (void*)&dev->tbusy);
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ tmp = regs->HostCtrl;
+ if (tmp & NIC_HALTED){
+ printk("%s: NIC already halted\n", dev->name);
+ rr_dump(dev);
+ }else
+ tmp |= HALT_NIC;
+ regs->HostCtrl = tmp;
+
+ /*
+ * Lock to make sure we are not cleaning up while another CPU
+ * handling interrupts.
+ */
+ spin_lock(&rrpriv->lock);
+
+ regs->TxPi = 0;
+ regs->IpRxPi = 0;
+
+ regs->EvtCon = 0;
+ regs->EvtPrd = 0;
+
+ for (i = 0; i < CMD_RING_ENTRIES; i++)
+ regs->CmdRing[i] = 0;
+
+ rrpriv->info->tx_ctrl.entries = 0;
+ rrpriv->info->cmd_ctrl.pi = 0;
+ rrpriv->info->evt_ctrl.pi = 0;
+ rrpriv->rx_ctrl[4].entries = 0;
+
+ for (i = 0; i < TX_RING_ENTRIES; i++) {
+ if (rrpriv->tx_skbuff[i]) {
+ rrpriv->tx_ring[i].size = 0;
+ rrpriv->tx_ring[i].addr = 0;
+ dev_kfree_skb(rrpriv->tx_skbuff[i]);
+ }
+ }
+
+ for (i = 0; i < RX_RING_ENTRIES; i++) {
+ if (rrpriv->rx_skbuff[i]) {
+ rrpriv->rx_ring[i].size = 0;
+ rrpriv->rx_ring[i].addr = 0;
+ dev_kfree_skb(rrpriv->rx_skbuff[i]);
+ }
+ }
+
+ kfree(rrpriv->rx_ctrl);
+ kfree(rrpriv->info);
+
+ free_irq(dev->irq, dev);
+ spin_unlock(&rrpriv->lock);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+static int rr_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct rr_private *rrpriv = (struct rr_private *)dev->priv;
+ struct rr_regs *regs = rrpriv->regs;
+ struct ring_ctrl *txctrl;
+ unsigned long flags;
+ u32 index, len = skb->len;
+ u32 *ifield;
+ struct sk_buff *new_skb;
+
+ /*
+ * We probably need to deal with tbusy here to prevent overruns.
+ */
+
+ if (skb_headroom(skb) < 8){
+ printk("incoming skb too small - reallocating\n");
+ if (!(new_skb = dev_alloc_skb(len + 8))) {
+ dev_kfree_skb(skb);
+ dev->tbusy = 0;
+ return -EBUSY;
+ }
+ skb_reserve(new_skb, 8);
+ skb_put(new_skb, len);
+ memcpy(new_skb->data, skb->data, len);
+ dev_kfree_skb(skb);
+ skb = new_skb;
+ }
+
+ ifield = (u32 *)skb_push(skb, 8);
+
+ ifield[0] = 0;
+ ifield[1] = skb->private.ifield;
+
+ /*
+ * We don't need the lock before we are actually going to start
+ * fiddling with the control blocks.
+ */
+ spin_lock_irqsave(&rrpriv->lock, flags);
+
+ txctrl = &rrpriv->info->tx_ctrl;
+
+ index = txctrl->pi;
+
+ rrpriv->tx_skbuff[index] = skb;
+ rrpriv->tx_ring[index].addr = virt_to_bus(skb->data);
+ rrpriv->tx_ring[index].size = len + 8; /* include IFIELD */
+ rrpriv->tx_ring[index].mode = PACKET_START | PACKET_END;
+ txctrl->pi = (index + 1) % TX_RING_ENTRIES;
+ regs->TxPi = txctrl->pi;
+
+ if (txctrl->pi == rrpriv->dirty_tx){
+ rrpriv->tx_full = 1;
+ set_bit(0, (void*)&dev->tbusy);
+ }
+
+ spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+ dev->trans_start = jiffies;
+ return 0;
+}
+
+
+static struct net_device_stats *rr_get_stats(struct device *dev)
+{
+ struct rr_private *rrpriv;
+
+ rrpriv = (struct rr_private *)dev->priv;
+
+ return(&rrpriv->stats);
+}
+
+
+/*
+ * Read the firmware out of the EEPROM and put it into the SRAM
+ * (or from user space - later)
+ *
+ * This operation requires the NIC to be halted and is performed with
+ * interrupts disabled and with the spinlock hold.
+ */
+static int rr_load_firmware(struct device *dev)
+{
+ struct rr_private *rrpriv;
+ struct rr_regs *regs;
+#if 0
+ unsigned long flags;
+#endif
+ int i, j;
+ u32 localctrl, eptr, sptr, segptr, len, tmp;
+ u32 p2len, p2size, nr_seg, revision, io, sram_size;
+ struct eeprom *hw = NULL;
+
+ rrpriv = (struct rr_private *)dev->priv;
+ regs = rrpriv->regs;
+
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (!(regs->HostCtrl & NIC_HALTED)){
+ printk("%s: Trying to load firmware to a running NIC.\n",
+ dev->name);
+ return -EBUSY;
+ }
+
+ localctrl = regs->LocalCtrl;
+ regs->LocalCtrl = 0;
+
+ regs->EvtPrd = 0;
+ regs->RxPrd = 0;
+ regs->TxPrd = 0;
+
+ /*
+ * First wipe the entire SRAM, otherwise we might run into all
+ * kinds of trouble ... sigh, this took almost all afternoon
+ * to track down ;-(
+ */
+ io = regs->ExtIo;
+ regs->ExtIo = 0;
+ sram_size = read_eeprom_word(rrpriv, (void *)8);
+
+ for (i = 200; i < sram_size / 4; i++){
+ regs->WinBase = i * 4;
+ regs->WinData = 0;
+ }
+ regs->ExtIo = io;
+
+ eptr = read_eeprom_word(rrpriv, &hw->rncd_info.AddrRunCodeSegs);
+ eptr = ((eptr & 0x1fffff) >> 3);
+
+ p2len = read_eeprom_word(rrpriv, (void *)(0x83*4));
+ p2len = (p2len << 2);
+ p2size = read_eeprom_word(rrpriv, (void *)(0x84*4));
+ p2size = ((p2size & 0x1fffff) >> 3);
+
+ if ((eptr < p2size) || (eptr > (p2size + p2len))){
+ printk("%s: eptr is invalid\n", dev->name);
+ goto out;
+ }
+
+ revision = read_eeprom_word(rrpriv, &hw->manf.HeaderFmt);
+
+ if (revision != 1){
+ printk("%s: invalid firmware format (%i)\n",
+ dev->name, revision);
+ goto out;
+ }
+
+ nr_seg = read_eeprom_word(rrpriv, (void *)eptr);
+ eptr +=4;
+#if (DEBUG > 1)
+ printk("%s: nr_seg %i\n", dev->name, nr_seg);
+#endif
+
+ for (i = 0; i < nr_seg; i++){
+ sptr = read_eeprom_word(rrpriv, (void *)eptr);
+ eptr += 4;
+ len = read_eeprom_word(rrpriv, (void *)eptr);
+ eptr += 4;
+ segptr = read_eeprom_word(rrpriv, (void *)eptr);
+ segptr = ((segptr & 0x1fffff) >> 3);
+ eptr += 4;
+#if (DEBUG > 1)
+ printk("%s: segment %i, sram address %06x, length %04x, segptr %06x\n",
+ dev->name, i, sptr, len, segptr);
+#endif
+ for (j = 0; j < len; j++){
+ tmp = read_eeprom_word(rrpriv, (void *)segptr);
+ regs->WinBase = sptr;
+ regs->WinData = tmp;
+ segptr += 4;
+ sptr += 4;
+ }
+ }
+
+out:
+ regs->LocalCtrl = localctrl;
+ return 0;
+}
+
+
+static int rr_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct rr_private *rrpriv;
+
+ rrpriv = (struct rr_private *)dev->priv;
+
+ switch(cmd){
+ case SIOCRRPFW:
+ if (!suser())
+ return -EPERM;
+
+ if (rrpriv->fw_running){
+ printk("%s: firmware already running\n", dev->name);
+ return -EPERM;
+ }
+ printk("%s: updating firmware", dev->name);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 -c rrunner.c"
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov