📄 farsync.c
字号:
* physical offsets so we have to convert. The only saving grace is that * this should all collapse back to a simple indirection eventually. */#define WIN_OFFSET(X) ((long)&(((struct fst_shared *)SMC_BASE)->X))#define FST_RDB(C,E) readb ((C)->mem + WIN_OFFSET(E))#define FST_RDW(C,E) readw ((C)->mem + WIN_OFFSET(E))#define FST_RDL(C,E) readl ((C)->mem + WIN_OFFSET(E))#define FST_WRB(C,E,B) writeb ((B), (C)->mem + WIN_OFFSET(E))#define FST_WRW(C,E,W) writew ((W), (C)->mem + WIN_OFFSET(E))#define FST_WRL(C,E,L) writel ((L), (C)->mem + WIN_OFFSET(E))/* * Debug support */#if FST_DEBUGstatic int fst_debug_mask = { FST_DEBUG };/* Most common debug activity is to print something if the corresponding bit * is set in the debug mask. Note: this uses a non-ANSI extension in GCC to * support variable numbers of macro parameters. The inverted if prevents us * eating someone else's else clause. */#define dbg(F,fmt,A...) if ( ! ( fst_debug_mask & (F))) \ ; \ else \ printk ( KERN_DEBUG FST_NAME ": " fmt, ## A )#else#define dbg(X...) /* NOP */#endif/* Printing short cuts */#define printk_err(fmt,A...) printk ( KERN_ERR FST_NAME ": " fmt, ## A )#define printk_warn(fmt,A...) printk ( KERN_WARNING FST_NAME ": " fmt, ## A )#define printk_info(fmt,A...) printk ( KERN_INFO FST_NAME ": " fmt, ## A )/* * PCI ID lookup table */static struct pci_device_id fst_pci_dev_id[] __devinitdata = { {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2P, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T2P}, {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4P, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T4P}, {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T1U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T1U}, {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T2U}, {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T4U}, {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, {0,} /* End */};MODULE_DEVICE_TABLE(pci, fst_pci_dev_id);/* * Device Driver Work Queues * * So that we don't spend too much time processing events in the * Interrupt Service routine, we will declare a work queue per Card * and make the ISR schedule a task in the queue for later execution. * In the 2.4 Kernel we used to use the immediate queue for BH's * Now that they are gone, tasklets seem to be much better than work * queues. */static void do_bottom_half_tx(struct fst_card_info *card);static void do_bottom_half_rx(struct fst_card_info *card);static void fst_process_tx_work_q(unsigned long work_q);static void fst_process_int_work_q(unsigned long work_q);DECLARE_TASKLET(fst_tx_task, fst_process_tx_work_q, 0);DECLARE_TASKLET(fst_int_task, fst_process_int_work_q, 0);struct fst_card_info *fst_card_array[FST_MAX_CARDS];spinlock_t fst_work_q_lock;u64 fst_work_txq;u64 fst_work_intq;static voidfst_q_work_item(u64 * queue, int card_index){ unsigned long flags; u64 mask; /* * Grab the queue exclusively */ spin_lock_irqsave(&fst_work_q_lock, flags); /* * Making an entry in the queue is simply a matter of setting * a bit for the card indicating that there is work to do in the * bottom half for the card. Note the limitation of 64 cards. * That ought to be enough */ mask = 1 << card_index; *queue |= mask; spin_unlock_irqrestore(&fst_work_q_lock, flags);}static voidfst_process_tx_work_q(unsigned long /*void **/work_q){ unsigned long flags; u64 work_txq; int i; /* * Grab the queue exclusively */ dbg(DBG_TX, "fst_process_tx_work_q\n"); spin_lock_irqsave(&fst_work_q_lock, flags); work_txq = fst_work_txq; fst_work_txq = 0; spin_unlock_irqrestore(&fst_work_q_lock, flags); /* * Call the bottom half for each card with work waiting */ for (i = 0; i < FST_MAX_CARDS; i++) { if (work_txq & 0x01) { if (fst_card_array[i] != NULL) { dbg(DBG_TX, "Calling tx bh for card %d\n", i); do_bottom_half_tx(fst_card_array[i]); } } work_txq = work_txq >> 1; }}static voidfst_process_int_work_q(unsigned long /*void **/work_q){ unsigned long flags; u64 work_intq; int i; /* * Grab the queue exclusively */ dbg(DBG_INTR, "fst_process_int_work_q\n"); spin_lock_irqsave(&fst_work_q_lock, flags); work_intq = fst_work_intq; fst_work_intq = 0; spin_unlock_irqrestore(&fst_work_q_lock, flags); /* * Call the bottom half for each card with work waiting */ for (i = 0; i < FST_MAX_CARDS; i++) { if (work_intq & 0x01) { if (fst_card_array[i] != NULL) { dbg(DBG_INTR, "Calling rx & tx bh for card %d\n", i); do_bottom_half_rx(fst_card_array[i]); do_bottom_half_tx(fst_card_array[i]); } } work_intq = work_intq >> 1; }}/* Card control functions * ====================== *//* Place the processor in reset state * * Used to be a simple write to card control space but a glitch in the latest * AMD Am186CH processor means that we now have to do it by asserting and de- * asserting the PLX chip PCI Adapter Software Reset. Bit 30 in CNTRL register * at offset 9052_CNTRL. Note the updates for the TXU. */static inline voidfst_cpureset(struct fst_card_info *card){ unsigned char interrupt_line_register; unsigned long j = jiffies + 1; unsigned int regval; if (card->family == FST_FAMILY_TXU) { if (pci_read_config_byte (card->device, PCI_INTERRUPT_LINE, &interrupt_line_register)) { dbg(DBG_ASS, "Error in reading interrupt line register\n"); } /* * Assert PLX software reset and Am186 hardware reset * and then deassert the PLX software reset but 186 still in reset */ outw(0x440f, card->pci_conf + CNTRL_9054 + 2); outw(0x040f, card->pci_conf + CNTRL_9054 + 2); /* * We are delaying here to allow the 9054 to reset itself */ j = jiffies + 1; while (jiffies < j) /* Do nothing */ ; outw(0x240f, card->pci_conf + CNTRL_9054 + 2); /* * We are delaying here to allow the 9054 to reload its eeprom */ j = jiffies + 1; while (jiffies < j) /* Do nothing */ ; outw(0x040f, card->pci_conf + CNTRL_9054 + 2); if (pci_write_config_byte (card->device, PCI_INTERRUPT_LINE, interrupt_line_register)) { dbg(DBG_ASS, "Error in writing interrupt line register\n"); } } else { regval = inl(card->pci_conf + CNTRL_9052); outl(regval | 0x40000000, card->pci_conf + CNTRL_9052); outl(regval & ~0x40000000, card->pci_conf + CNTRL_9052); }}/* Release the processor from reset */static inline voidfst_cpurelease(struct fst_card_info *card){ if (card->family == FST_FAMILY_TXU) { /* * Force posted writes to complete */ (void) readb(card->mem); /* * Release LRESET DO = 1 * Then release Local Hold, DO = 1 */ outw(0x040e, card->pci_conf + CNTRL_9054 + 2); outw(0x040f, card->pci_conf + CNTRL_9054 + 2); } else { (void) readb(card->ctlmem); }}/* Clear the cards interrupt flag */static inline voidfst_clear_intr(struct fst_card_info *card){ if (card->family == FST_FAMILY_TXU) { (void) readb(card->ctlmem); } else { /* Poke the appropriate PLX chip register (same as enabling interrupts) */ outw(0x0543, card->pci_conf + INTCSR_9052); }}/* Enable card interrupts */static inline voidfst_enable_intr(struct fst_card_info *card){ if (card->family == FST_FAMILY_TXU) { outl(0x0f0c0900, card->pci_conf + INTCSR_9054); } else { outw(0x0543, card->pci_conf + INTCSR_9052); }}/* Disable card interrupts */static inline voidfst_disable_intr(struct fst_card_info *card){ if (card->family == FST_FAMILY_TXU) { outl(0x00000000, card->pci_conf + INTCSR_9054); } else { outw(0x0000, card->pci_conf + INTCSR_9052); }}/* Process the result of trying to pass a recieved frame up the stack */static voidfst_process_rx_status(int rx_status, char *name){ switch (rx_status) { case NET_RX_SUCCESS: { /* * Nothing to do here */ break; } case NET_RX_CN_LOW: { dbg(DBG_ASS, "%s: Receive Low Congestion\n", name); break; } case NET_RX_CN_MOD: { dbg(DBG_ASS, "%s: Receive Moderate Congestion\n", name); break; } case NET_RX_CN_HIGH: { dbg(DBG_ASS, "%s: Receive High Congestion\n", name); break; } case NET_RX_DROP: { dbg(DBG_ASS, "%s: Received packet dropped\n", name); break; } }}/* Initilaise DMA for PLX 9054 */static inline voidfst_init_dma(struct fst_card_info *card){ /* * This is only required for the PLX 9054 */ if (card->family == FST_FAMILY_TXU) { pci_set_master(card->device); outl(0x00020441, card->pci_conf + DMAMODE0); outl(0x00020441, card->pci_conf + DMAMODE1); outl(0x0, card->pci_conf + DMATHR); }}/* Tx dma complete interrupt */static voidfst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, int len, int txpos){ struct net_device *dev = port_to_dev(port); struct net_device_stats *stats = hdlc_stats(dev); /* * Everything is now set, just tell the card to go */ dbg(DBG_TX, "fst_tx_dma_complete\n"); FST_WRB(card, txDescrRing[port->index][txpos].bits, DMA_OWN | TX_STP | TX_ENP); stats->tx_packets++; stats->tx_bytes += len; dev->trans_start = jiffies;}/* Rx dma complete interrupt */static voidfst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, int len, struct sk_buff *skb, int rxp){ struct net_device *dev = port_to_dev(port); struct net_device_stats *stats = hdlc_stats(dev); int pi; int rx_status; dbg(DBG_TX, "fst_rx_dma_complete\n"); pi = port->index; memcpy(skb_put(skb, len), card->rx_dma_handle_host, len); /* Reset buffer descriptor */ FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); /* Update stats */ stats->rx_packets++; stats->rx_bytes += len; /* Push upstream */ dbg(DBG_RX, "Pushing the frame up the stack\n"); skb->mac.raw = skb->data; skb->dev = dev; if (port->mode == FST_RAW) { /* * Mark it for our own raw sockets interface */ skb->protocol = htons(ETH_P_CUST); skb->pkt_type = PACKET_HOST; } else { skb->protocol = hdlc_type_trans(skb, skb->dev); } rx_status = netif_rx(skb); fst_process_rx_status(rx_status, port_to_dev(port)->name); if (rx_status == NET_RX_DROP) stats->rx_dropped++; dev->last_rx = jiffies;}/* * Receive a frame through the DMA */static inline voidfst_rx_dma(struct fst_card_info *card, unsigned char *skb, unsigned char *mem, int len){ /* * This routine will setup the DMA and start it */ dbg(DBG_RX, "In fst_rx_dma %p %p %d\n", skb, mem, len); if (card->dmarx_in_progress) { dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n"); } outl((unsigned long) skb, card->pci_conf + DMAPADR0); /* Copy to here */ outl((unsigned long) mem, card->pci_conf + DMALADR0); /* from here */ outl(len, card->pci_conf + DMASIZ0); /* for this length */ outl(0x00000000c, card->pci_conf + DMADPR0); /* In this direction */ /* * We use the dmarx_in_progress flag to flag the channel as busy */ card->dmarx_in_progress = 1; outb(0x03, card->pci_conf + DMACSR0); /* Start the transfer */}/* * Send a frame through the DMA */static inline voidfst_tx_dma(struct fst_card_info *card, unsigned char *skb, unsigned char *mem, int len){ /* * This routine will setup the DMA and start it. */ dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len); if (card->dmatx_in_progress) { dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n"); } outl((unsigned long) skb, card->pci_conf + DMAPADR1); /* Copy from here */ outl((unsigned long) mem, card->pci_conf + DMALADR1); /* to here */ outl(len, card->pci_conf + DMASIZ1); /* for this length */ outl(0x000000004, card->pci_conf + DMADPR1); /* In this direction */ /* * We use the dmatx_in_progress to flag the channel as busy */ card->dmatx_in_progress = 1; outb(0x03, card->pci_conf + DMACSR1); /* Start the transfer */}/* Issue a Mailbox command for a port. * Note we issue them on a fire and forget basis, not expecting to see an * error and not waiting for completion. */static voidfst_issue_cmd(struct fst_port_info *port, unsigned short cmd){ struct fst_card_info *card; unsigned short mbval; unsigned long flags; int safety; card = port->card; spin_lock_irqsave(&card->card_lock, flags); mbval = FST_RDW(card, portMailbox[port->index][0]); safety = 0; /* Wait for any previous command to complete */ while (mbval > NAK) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -