ambassador.c
来自「linux 内核源代码」· C语言 代码 · 共 2,334 行 · 第 1/5 页
C
2,334 行
PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify"); if (count > MAX_TRANSFER_DATA) return -EINVAL; tb->address = cpu_to_be32 (address); tb->count = cpu_to_be32 (count); res = do_loader_command (lb, dev, read_adapter_memory); if (!res) for (i = 0; i < count; ++i) if (tb->data[i] != cpu_to_be32 (data[i])) { res = -EINVAL; break; } return res;}/* loader: start microcode */static int __devinit loader_start (loader_block * lb, const amb_dev * dev, u32 address) { PRINTD (DBG_FLOW|DBG_LOAD, "loader_start"); lb->payload.start = cpu_to_be32 (address); return do_loader_command (lb, dev, adapter_start);}/********** reset card **********/static inline void sf (const char * msg){ PRINTK (KERN_ERR, "self-test failed: %s", msg);}static int amb_reset (amb_dev * dev, int diags) { u32 word; PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset"); word = rd_plain (dev, offsetof(amb_mem, reset_control)); // put card into reset state wr_plain (dev, offsetof(amb_mem, reset_control), word | AMB_RESET_BITS); // wait a short while udelay (10);#if 1 // put card into known good state wr_plain (dev, offsetof(amb_mem, interrupt_control), AMB_DOORBELL_BITS); // clear all interrupts just in case wr_plain (dev, offsetof(amb_mem, interrupt), -1);#endif // clear self-test done flag wr_plain (dev, offsetof(amb_mem, mb.loader.ready), 0); // take card out of reset state wr_plain (dev, offsetof(amb_mem, reset_control), word &~ AMB_RESET_BITS); if (diags) { unsigned long timeout; // 4.2 second wait msleep(4200); // half second time-out timeout = 500; while (!rd_plain (dev, offsetof(amb_mem, mb.loader.ready))) if (timeout) { timeout = msleep_interruptible(timeout); } else { PRINTD (DBG_LOAD|DBG_ERR, "reset timed out"); return -ETIMEDOUT; } // get results of self-test // XXX double check byte-order word = rd_mem (dev, offsetof(amb_mem, mb.loader.result)); if (word & SELF_TEST_FAILURE) { if (word & GPINT_TST_FAILURE) sf ("interrupt"); if (word & SUNI_DATA_PATTERN_FAILURE) sf ("SUNI data pattern"); if (word & SUNI_DATA_BITS_FAILURE) sf ("SUNI data bits"); if (word & SUNI_UTOPIA_FAILURE) sf ("SUNI UTOPIA interface"); if (word & SUNI_FIFO_FAILURE) sf ("SUNI cell buffer FIFO"); if (word & SRAM_FAILURE) sf ("bad SRAM"); // better return value? return -EIO; } } return 0;}/********** transfer and start the microcode **********/static int __devinit ucode_init (loader_block * lb, amb_dev * dev) { unsigned int i = 0; unsigned int total = 0; const u32 * pointer = ucode_data; u32 address; unsigned int count; int res; PRINTD (DBG_FLOW|DBG_LOAD, "ucode_init"); while (address = ucode_regions[i].start, count = ucode_regions[i].count) { PRINTD (DBG_LOAD, "starting region (%x, %u)", address, count); while (count) { unsigned int words; if (count <= MAX_TRANSFER_DATA) words = count; else words = MAX_TRANSFER_DATA; total += words; res = loader_write (lb, dev, pointer, address, words); if (res) return res; res = loader_verify (lb, dev, pointer, address, words); if (res) return res; count -= words; address += sizeof(u32) * words; pointer += words; } i += 1; } if (*pointer == ATM_POISON) { return loader_start (lb, dev, ucode_start); } else { // cast needed as there is no %? for pointer differnces PRINTD (DBG_LOAD|DBG_ERR, "offset=%li, *pointer=%x, address=%x, total=%u", (long) (pointer - ucode_data), *pointer, address, total); PRINTK (KERN_ERR, "incorrect microcode data"); return -ENOMEM; }}/********** give adapter parameters **********/ static inline __be32 bus_addr(void * addr) { return cpu_to_be32 (virt_to_bus (addr));}static int __devinit amb_talk (amb_dev * dev) { adap_talk_block a; unsigned char pool; unsigned long timeout; PRINTD (DBG_FLOW, "amb_talk %p", dev); a.command_start = bus_addr (dev->cq.ptrs.start); a.command_end = bus_addr (dev->cq.ptrs.limit); a.tx_start = bus_addr (dev->txq.in.start); a.tx_end = bus_addr (dev->txq.in.limit); a.txcom_start = bus_addr (dev->txq.out.start); a.txcom_end = bus_addr (dev->txq.out.limit); for (pool = 0; pool < NUM_RX_POOLS; ++pool) { // the other "a" items are set up by the adapter a.rec_struct[pool].buffer_start = bus_addr (dev->rxq[pool].in.start); a.rec_struct[pool].buffer_end = bus_addr (dev->rxq[pool].in.limit); a.rec_struct[pool].rx_start = bus_addr (dev->rxq[pool].out.start); a.rec_struct[pool].rx_end = bus_addr (dev->rxq[pool].out.limit); a.rec_struct[pool].buffer_size = cpu_to_be32 (dev->rxq[pool].buffer_size); } #ifdef AMB_NEW_MICROCODE // disable fast PLX prefetching a.init_flags = 0;#endif // pass the structure wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (&a)); // 2.2 second wait (must not touch doorbell during 2 second DMA test) msleep(2200); // give the adapter another half second? timeout = 500; while (rd_plain (dev, offsetof(amb_mem, doorbell))) if (timeout) { timeout = msleep_interruptible(timeout); } else { PRINTD (DBG_INIT|DBG_ERR, "adapter init timed out"); return -ETIMEDOUT; } return 0;}// get microcode versionstatic void __devinit amb_ucode_version (amb_dev * dev) { u32 major; u32 minor; command cmd; cmd.request = cpu_to_be32 (SRB_GET_VERSION); while (command_do (dev, &cmd)) { set_current_state(TASK_UNINTERRUPTIBLE); schedule(); } major = be32_to_cpu (cmd.args.version.major); minor = be32_to_cpu (cmd.args.version.minor); PRINTK (KERN_INFO, "microcode version is %u.%u", major, minor);} // get end station addressstatic void __devinit amb_esi (amb_dev * dev, u8 * esi) { u32 lower4; u16 upper2; command cmd; cmd.request = cpu_to_be32 (SRB_GET_BIA); while (command_do (dev, &cmd)) { set_current_state(TASK_UNINTERRUPTIBLE); schedule(); } lower4 = be32_to_cpu (cmd.args.bia.lower4); upper2 = be32_to_cpu (cmd.args.bia.upper2); PRINTD (DBG_LOAD, "BIA: lower4: %08x, upper2 %04x", lower4, upper2); if (esi) { unsigned int i; PRINTDB (DBG_INIT, "ESI:"); for (i = 0; i < ESI_LEN; ++i) { if (i < 4) esi[i] = bitrev8(lower4>>(8*i)); else esi[i] = bitrev8(upper2>>(8*(i-4))); PRINTDM (DBG_INIT, " %02x", esi[i]); } PRINTDE (DBG_INIT, ""); } return;} static void fixup_plx_window (amb_dev *dev, loader_block *lb){ // fix up the PLX-mapped window base address to match the block unsigned long blb; u32 mapreg; blb = virt_to_bus(lb); // the kernel stack had better not ever cross a 1Gb boundary! mapreg = rd_plain (dev, offsetof(amb_mem, stuff[10])); mapreg &= ~onegigmask; mapreg |= blb & onegigmask; wr_plain (dev, offsetof(amb_mem, stuff[10]), mapreg); return;}static int __devinit amb_init (amb_dev * dev){ loader_block lb; u32 version; if (amb_reset (dev, 1)) { PRINTK (KERN_ERR, "card reset failed!"); } else { fixup_plx_window (dev, &lb); if (get_loader_version (&lb, dev, &version)) { PRINTK (KERN_INFO, "failed to get loader version"); } else { PRINTK (KERN_INFO, "loader version is %08x", version); if (ucode_init (&lb, dev)) { PRINTK (KERN_ERR, "microcode failure"); } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) { PRINTK (KERN_ERR, "failed to get memory for queues"); } else { if (amb_talk (dev)) { PRINTK (KERN_ERR, "adapter did not accept queues"); } else { amb_ucode_version (dev); return 0; } /* amb_talk */ destroy_queues (dev); } /* create_queues, ucode_init */ amb_reset (dev, 0); } /* get_loader_version */ } /* amb_reset */ return -EINVAL;}static void setup_dev(amb_dev *dev, struct pci_dev *pci_dev) { unsigned char pool; memset (dev, 0, sizeof(amb_dev)); // set up known dev items straight away dev->pci_dev = pci_dev; pci_set_drvdata(pci_dev, dev); dev->iobase = pci_resource_start (pci_dev, 1); dev->irq = pci_dev->irq; dev->membase = bus_to_virt(pci_resource_start(pci_dev, 0)); // flags (currently only dead) dev->flags = 0; // Allocate cell rates (fibre) // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53 // to be really pedantic, this should be ATM_OC3c_PCR dev->tx_avail = ATM_OC3_PCR; dev->rx_avail = ATM_OC3_PCR; #ifdef FILL_RX_POOLS_IN_BH // initialise bottom half INIT_WORK(&dev->bh, (void (*)(void *)) fill_rx_pools, dev);#endif // semaphore for txer/rxer modifications - we cannot use a // spinlock as the critical region needs to switch processes init_MUTEX (&dev->vcc_sf); // queue manipulation spinlocks; we want atomic reads and // writes to the queue descriptors (handles IRQ and SMP) // consider replacing "int pending" -> "atomic_t available" // => problem related to who gets to move queue pointers spin_lock_init (&dev->cq.lock); spin_lock_init (&dev->txq.lock); for (pool = 0; pool < NUM_RX_POOLS; ++pool) spin_lock_init (&dev->rxq[pool].lock);}static void setup_pci_dev(struct pci_dev *pci_dev){ unsigned char lat; // enable bus master accesses pci_set_master(pci_dev); // frobnicate latency (upwards, usually) pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat); if (!pci_lat) pci_lat = (lat < MIN_PCI_LATENCY) ? MIN_PCI_LATENCY : lat; if (lat != pci_lat) { PRINTK (KERN_INFO, "Changing PCI latency timer from %hu to %hu", lat, pci_lat); pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat); }}static int __devinit amb_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent){ amb_dev * dev; int err; unsigned int irq; err = pci_enable_device(pci_dev); if (err < 0) { PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card"); goto out; } // read resources from PCI configuration space irq = pci_dev->irq; if (pci_dev->device == PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD) { PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card"); err = -EINVAL; goto out_disable; } PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at" " IO %llx, IRQ %u, MEM %p", (unsigned long long)pci_resource_start(pci_dev, 1), irq, bus_to_virt(pci_resource_start(pci_dev, 0))); // check IO region err = pci_request_region(pci_dev, 1, DEV_LABEL); if (err < 0) { PRINTK (KERN_ERR, "IO range already in use!"); goto out_disable; } dev = kmalloc (sizeof(amb_dev), GFP_KERNEL); if (!dev) { PRINTK (KERN_ERR, "out of memory!"); err = -ENOMEM; goto out_release; } setup_dev(dev, pci_dev); err = amb_init(dev); if (err < 0) { PRINTK (KERN_ERR, "adapter initialisation failure"); goto out_free; } setup_pci_dev(pci_dev); // grab (but share) IRQ and install handler err = request_irq(irq, interrupt_handler, IRQF_SHARED, DEV_LABEL, dev); if (err < 0) { PRINTK (KERN_ERR, "request IRQ failed!"); goto out_reset; } dev->atm_dev = atm_dev_register (DEV_LABEL, &amb_ops, -1, NULL); if (!dev->atm_dev) { PRINTD (DBG_ERR, "failed to register Madge ATM adapter"); err = -EINVAL; goto out_free_irq; } PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p", dev->atm_dev->number, dev, dev->atm_dev); dev->atm_dev->dev_data = (void *) dev; // register our address amb_esi (dev, dev->atm_dev->esi); // 0 bits for vpi, 10 bits for vci dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS; dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS; init_timer(&dev->housekeeping); dev->housekeeping.function = do_housekeeping; dev->housekeeping.data = (unsigned long) dev; mod_timer(&dev->housekeeping, jiffies); // enable host interrupts interrupts_on (dev);out: return err;out_free_irq: free_irq(irq, dev);out_reset: amb_reset(dev, 0);out_free: kfree(dev);out_release: pci_release_region(pci_dev, 1);out_disable: pci_disable_device(pci_dev); goto out;}static void __devexit amb_remove_one(struct pci_dev *pci_dev){ struct amb_dev *dev; dev = pci_get_drvdata(pci_dev); PRINTD(DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev); del_timer_sync(&dev->housekeeping); // the drain should not be necessary drain_rx_pools(dev); interrupts_off(dev); amb
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?