📄 smc9194.c
字号:
insw(port, data, len>>1); if (len & 1) data[len-1] = readw(port);}static inline void smc_outb(u8 val, u_int base, u_int reg){ u_int port = base + (reg & ~1) * 4; u_int res = readw(port); if (reg & 1) { res &= 0xff; res |= val << 8; } else { res &= 0xff00; res |= val; } writew(res, port);}static inline void smc_outw(u16 val, u_int base, u_int reg){ u_int port = base + reg * 4; writew(val, port);}static inline void smc_outl(u32 val, u_int base, u_int reg){ u_int port = base + reg * 4; writew(val, port); writew(val >> 16, port + 8);}static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len){ u_int port = base + reg * 4; outsw(port, data, len>>1);}/*------------------------------------------------------------------------- . I define some macros to make it easier to do somewhat common . or slightly complicated, repeated tasks. --------------------------------------------------------------------------*//* select a register bank, 0 to 3 */#define SMC_SELECT_BANK(x) \ { \ smc_outw(x, ioaddr, BANK_SELECT); \ } /* define a small delay for the reset */#define SMC_DELAY() \ { \ smc_inw(ioaddr, RCR); \ smc_inw(ioaddr, RCR); \ smc_inw(ioaddr, RCR); \ }/* this enables an interrupt in the interrupt mask register */#define SMC_ENABLE_INT(x) \ { \ word mask; \ mask = smc_inw(ioaddr, INTERRUPT); \ mask |= (x) << 8; \ smc_outw(mask, ioaddr, INT_MASK); \ }/* this sets the absolutel interrupt mask */#define SMC_SET_INT(x) \ { \ smc_outw((x) << 8, ioaddr, INTERRUPT); \ }#endif/* . A rather simple routine to print out a packet for debugging purposes.*/#if SMC_DEBUG > 2static void print_packet(byte * buf, int length){ int i; int remainder; int lines; printk("Packet of length %d \n", length); lines = length / 16; remainder = length % 16; for (i = 0; i < lines ; i ++) { int cur; for (cur = 0; cur < 8; cur ++) { byte a, b; a = *(buf ++); b = *(buf ++); printk("%02x%02x ", a, b); } printk("\n"); } for (i = 0; i < remainder/2 ; i++) { byte a, b; a = *(buf ++); b = *(buf ++); printk("%02x%02x ", a, b); } if (remainder & 1) { byte a; a = *buf++; printk("%02x", a); } printk("\n");}#else#define print_packet(buf,len) do { } while (0)#endif/* . Function: smc_reset(struct net_device *dev) . Purpose: . This sets the SMC91xx chip to its normal state, hopefully from whatever . mess that any other DOS driver has put it in. . . Maybe I should reset more registers to defaults in here? SOFTRESET should . do that for me. . . Method: . 1. send a SOFT RESET . 2. wait for it to finish . 3. enable autorelease mode . 4. reset the memory management unit . 5. clear all interrupts .*/static void smc_reset(struct net_device *dev){ u_int ioaddr = dev->base_addr; /* This resets the registers mostly to defaults, but doesn't affect EEPROM. That seems unnecessary */ SMC_SELECT_BANK(0); smc_outw(RCR_SOFTRESET, ioaddr, RCR); /* this should pause enough for the chip to be happy */ SMC_DELAY(); /* Set the transmit and receive configuration registers to default values */ smc_outw(RCR_CLEAR, ioaddr, RCR); smc_outw(TCR_CLEAR, ioaddr, TCR); /* set the control register to automatically release successfully transmitted packets, to make the best use out of our limited memory */ SMC_SELECT_BANK(1); smc_outw(smc_inw(ioaddr, CONTROL) | CTL_AUTO_RELEASE, ioaddr, CONTROL); /* Reset the MMU */ SMC_SELECT_BANK(2); smc_outw(MC_RESET, ioaddr, MMU_CMD); /* Note: It doesn't seem that waiting for the MMU busy is needed here, but this is a place where future chipsets _COULD_ break. Be wary of issuing another MMU command right after this */ SMC_SET_INT(0);}/* . Function: smc_enable . Purpose: let the chip talk to the outside work . Method: . 1. Enable the transmitter . 2. Enable the receiver . 3. Enable interrupts*/static void smc_enable(struct net_device *dev){ u_int ioaddr = dev->base_addr; SMC_SELECT_BANK(0); /* see the header file for options in TCR/RCR NORMAL*/ smc_outw(TCR_NORMAL, ioaddr, TCR); smc_outw(RCR_NORMAL, ioaddr, RCR); /* now, enable interrupts */ SMC_SELECT_BANK(2); SMC_SET_INT(SMC_INTERRUPT_MASK);}/* . Function: smc_shutdown(struct net_device *dev) . Purpose: closes down the SMC91xxx chip. . Method: . 1. zero the interrupt mask . 2. clear the enable receive flag . 3. clear the enable xmit flags . . TODO: . (1) maybe utilize power down mode. . Why not yet? Because while the chip will go into power down mode, . the manual says that it will wake up in response to any I/O requests . in the register space. Empirical results do not show this working.*/static void smc_shutdown(struct net_device *dev){ u_int ioaddr = dev->base_addr; /* no more interrupts for me */ SMC_SELECT_BANK(2); SMC_SET_INT(0); /* and tell the card to stay away from that nasty outside world */ SMC_SELECT_BANK(0); smc_outb(RCR_CLEAR, ioaddr, RCR); smc_outb(TCR_CLEAR, ioaddr, TCR);#if 0 /* finally, shut the chip down */ SMC_SELECT_BANK(1); smc_outw(smc_inw(ioaddr, CONTROL), CTL_POWERDOWN, ioaddr, CONTROL);#endif}/* . Function: smc_setmulticast(int ioaddr, int count, dev_mc_list * adds) . Purpose: . This sets the internal hardware table to filter out unwanted multicast . packets before they take up memory. . . The SMC chip uses a hash table where the high 6 bits of the CRC of . address are the offset into the table. If that bit is 1, then the . multicast packet is accepted. Otherwise, it's dropped silently. . . To use the 6 bits as an offset into the table, the high 3 bits are the . number of the 8 bit register, while the low 3 bits are the bit within . that register. . . This routine is based very heavily on the one provided by Peter Cammaert.*/static void smc_setmulticast(struct net_device *dev, int count, struct dev_mc_list * addrs){ u_int ioaddr = dev->base_addr; int i; unsigned char multicast_table[8]; struct dev_mc_list * cur_addr; /* table for flipping the order of 3 bits */ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* start with a table of all zeros: reject all */ memset(multicast_table, 0, sizeof(multicast_table)); cur_addr = addrs; for (i = 0; i < count ; i ++, cur_addr = cur_addr->next) { int position; /* do we have a pointer here? */ if (!cur_addr) break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ if (!(*cur_addr->dmi_addr & 1)) continue; /* only use the low order bits */ position = crc32(cur_addr->dmi_addr, 6) & 0x3f; /* do some messy swapping to put the bit in the right spot */ multicast_table[invert3[position&7]] |= (1<<invert3[(position>>3)&7]); } /* now, the table can be loaded into the chipset */ SMC_SELECT_BANK(3); for (i = 0; i < 8 ; i++) { smc_outb(multicast_table[i], ioaddr, MULTICAST1 + i); }}/* Finds the CRC32 of a set of bytes. Again, from Peter Cammaert's code.*/static int crc32(char * s, int length){ /* indices */ int perByte; int perBit; /* crc polynomial for Ethernet */ const unsigned long poly = 0xedb88320; /* crc value - preinitialized to all 1's */ unsigned long crc_value = 0xffffffff; for (perByte = 0; perByte < length; perByte ++) { unsigned char c; c = *(s++); for (perBit = 0; perBit < 8; perBit++) { crc_value = (crc_value>>1)^ (((crc_value^c)&0x01)?poly:0); c >>= 1; } } return crc_value;}/* . Function: smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *) . Purpose: . Attempt to allocate memory for a packet, if chip-memory is not . available, then tell the card to generate an interrupt when it . is available. . . Algorithm: . . o if the saved_skb is not currently null, then drop this packet . on the floor. This should never happen, because of TBUSY. . o if the saved_skb is null, then replace it with the current packet, . o See if I can sending it now. . o (NO): Enable interrupts and let the interrupt handler deal with it. . o (YES):Send it now.*/static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device * dev){ struct smc_local *lp = (struct smc_local *)dev->priv; u_int ioaddr = dev->base_addr; word length; unsigned short numPages; word time_out; netif_stop_queue(dev); /* Well, I want to send the packet.. but I don't know if I can send it right now... */ if (lp->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ lp->stats.tx_aborted_errors++; printk("%s: Bad Craziness - sent packet while busy.\n", dev->name); return 1; } lp->saved_skb = skb; length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* ** The MMU wants the number of pages to be the number of 256 bytes ** 'pages', minus 1 (since a packet can't ever have 0 pages :)) ** ** Pkt size for allocating is data length +6 (for additional status words, ** length and ctl!) If odd size last byte is included in this header. */ numPages = ((length & 0xfffe) + 6) / 256; if (numPages > 7) { printk("%s: Far too big packet error.\n", dev->name); /* freeing the packet is a good thing here... but should . any packets of this size get down here? */ dev_kfree_skb (skb); lp->saved_skb = NULL; /* this IS an error, but, i don't want the skb saved */ netif_wake_queue(dev); return 0; } /* either way, a packet is waiting now */ lp->packets_waiting++; /* now, try to allocate the memory */ SMC_SELECT_BANK(2); smc_outw(MC_ALLOC | numPages, ioaddr, MMU_CMD); /* . Performance Hack . . wait a short amount of time.. if I can send a packet now, I send . it now. Otherwise, I enable an interrupt and wait for one to be . available. . . I could have handled this a slightly different way, by checking to . see if any memory was available in the FREE MEMORY register. However, . either way, I need to generate an allocation, and the allocation works . no matter what, so I saw no point in checking free memory. */ time_out = MEMORY_WAIT_TIME; do { word status; status = smc_inb(ioaddr, INTERRUPT); if (status & IM_ALLOC_INT) { /* acknowledge the interrupt */ smc_outb(IM_ALLOC_INT, ioaddr, INTERRUPT); break; } } while (-- time_out); if (!time_out) { /* oh well, wait until the chip finds memory later */ SMC_ENABLE_INT(IM_ALLOC_INT); PRINTK2(("%s: memory allocation deferred.\n", dev->name)); /* it's deferred, but I'll handle it later */ return 0; } /* or YES! I can send the packet now.. */ smc_hardware_send_packet(dev); netif_wake_queue(dev); return 0;}/* . Function: smc_hardware_send_packet(struct net_device *) . Purpose: . This sends the actual packet to the SMC9xxx chip. . . Algorithm: . First, see if a saved_skb is available. . (this should NOT be called if there is no 'saved_skb' . Now, find the packet number that the chip allocated . Point the data pointers at it in memory . Set the length word in the chip's memory . Dump the packet to chip memory . Check if a last byte is needed (odd length packet) . if so, set the control flag right . Tell the card to send it . Enable the transmit interrupt, so I know if it failed . Free the kernel data if I actually sent it.*/static void smc_hardware_send_packet(struct net_device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; struct sk_buff *skb = lp->saved_skb; word length, lastword; u_int ioaddr = dev->base_addr; byte packet_no; byte *buf; if (!skb) { PRINTK(("%s: In XMIT with no packet to send\n", dev->name)); return; } length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; buf = skb->data; /* If I get here, I _know_ there is a packet slot waiting for me */ packet_no = smc_inb(ioaddr, PNR_ARR + 1); if (packet_no & 0x80) { /* or isn't there? BAD CHIP! */ printk(KERN_DEBUG "%s: Memory allocation failed.\n", dev->name); dev_kfree_skb_any(skb); lp->saved_skb = NULL; netif_wake_queue(dev); return; } /* we have a packet address, so tell the card to use it */ smc_outb(packet_no, ioaddr, PNR_ARR); /* point to the beginning of the packet */ smc_outw(PTR_AUTOINC, ioaddr, POINTER); PRINTK3(("%s: Trying to xmit packet of length %x\n", dev->name, length)); print_packet(buf, length); /* send the packet length (+6 for status, length and ctl byte) and the status word (set to zeros) */ smc_outl((length + 6) << 16, ioaddr, DATA_1); /* send the actual data . I _think_ it's faster to send the longs first, and then . mop up by sending the last word. It depends heavily . on alignment, at least on the 486. Maybe it would be . a good idea to check which is optimal? But that could take . almost as much time as is saved? */ smc_outs(ioaddr, DATA_1, buf, length); /* Send the last byte, if there is one. */ if ((length & 1) == 0) lastword = 0; else lastword = 0x2000 | buf[length - 1]; smc_outw(lastword, ioaddr, DATA_1); /* enable the interrupts */ SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT); /* and let the chipset deal with it */ smc_outw(MC_ENQUEUE, ioaddr, MMU_CMD); PRINTK2(("%s: Sent packet of length %d\n", dev->name, length)); lp->saved_skb = NULL; dev_kfree_skb_any (skb); dev->trans_start = jiffies; /* we can send another packet */ netif_wake_queue(dev); return;}/*------------------------------------------------------------------------- | | smc_init(struct net_device * dev) | Input parameters: | dev->base_addr == 0, try to find all possible locations | dev->base_addr == 1, return failure code | dev->base_addr == 2, always allocate space, and return success | dev->base_addr == <anything else> this is the address to check | | Output: | 0 --> there is a device | anything else, error | ---------------------------------------------------------------------------*/int __init smc_init(struct net_device *dev){ int ret = -ENODEV;#if defined(CONFIG_ASSABET_NEPONSET) if (machine_is_assabet() && machine_has_neponset()) { unsigned int *addr; unsigned char ecor; unsigned long flags; NCR_0 |= NCR_ENET_OSC_EN; dev->irq = IRQ_NEPONSET_SMC9196; /* * Map the attribute space. This is overkill, but clean. */ addr = ioremap(0x18000000 + (1 << 25), 64 * 1024 * 4); if (!addr) return -ENOMEM; /* * Reset the device. We must disable IRQs around this. */ local_irq_save(flags); ecor = readl(addr + ECOR) & ~ECOR_RESET; writel(ecor | ECOR_RESET, addr + ECOR); udelay(100); /* * The device will ignore all writes to the enable bit while * reset is asserted, even if the reset bit is cleared in the * same write. Must clear reset first, then enable the device. */ writel(ecor, addr + ECOR); writel(ecor | ECOR_ENABLE, addr + ECOR); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -