📄 smc9194.c
字号:
/* and tell the card to stay away from that nasty outside world */ SMC_SELECT_BANK(0); smc_outw(RCR_CLEAR, ioaddr, RCR); smc_outw(TCR_CLEAR, ioaddr, TCR); outb( RCR_CLEAR, ioaddr + RCR ); 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); outw( 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( int ioaddr, int count, struct dev_mc_list * addrs ) { 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 = ether_crc_le(6, cur_addr->dmi_addr) & 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++ ) { outb( multicast_table[i], ioaddr + MULTICAST1 + i ); }}/* . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) . 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 = netdev_priv(dev); unsigned 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(CARDNAME": Bad Craziness - sent packet while busy.\n" ); 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); printk(CARDNAME": Far too big packet error. \n"); /* 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 */ 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((CARDNAME": memory allocation deferred. \n")); /* 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 = netdev_priv(dev); byte packet_no; struct sk_buff * skb = lp->saved_skb; word length; unsigned int ioaddr; byte * buf; ioaddr = dev->base_addr; PRINTK((CARDNAME": In XMIT with no packet to send \n")); 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 CARDNAME": Memory allocation failed. \n"); 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((CARDNAME": Trying to xmit packet of length %x\n", length ));#if SMC_DEBUG > 2 print_packet( buf, length );#endif /* 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); outw( 0, ioaddr + DATA_1 ); outb( (length+6) & 0xFF,ioaddr + DATA_1 ); outb( (length+6) >> 8 , 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);#if !defined(__H8300H__) && !defined(__H8300S__)#else ctrl_outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);#endif /* Send the last byte, if there is one. */ if ((length & 1) == 0) if ( (length & 1) == 0 ) { outw( 0, ioaddr + DATA_1 ); } else { outb( buf[length -1 ], ioaddr + DATA_1 ); outb( 0x20, 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((CARDNAME": Sent packet of length %d \n",length)); lp->saved_skb = NULL; dev_kfree_skb_any (skb); dev->trans_start = jiffies; /* we can send another packet */ netif_wake_queue(dev); return;}#ifdef CONFIG_OMAP_INNOVATOR_CEE /* MVL-CEE */#include <linux/device.h>static int smc9194_suspend(struct device * dev, u32 state, u32 level);static int smc9194_resume(struct device * dev, u32 level);static struct device_driver smc9194_driver_ldm = { name: "smc9194", devclass: NULL, probe: NULL, suspend: smc9194_suspend, resume: smc9194_resume, remove: NULL,};static struct device smc9194_device_ldm = { name: "SMC9194 ethernet", bus_id: "eth", driver: NULL, power_state: DPM_POWER_ON,};static void smc9194_ldm_register(struct net_device *netdev){ extern void dsp_public_driver_register(struct device_driver *driver); extern void dsp_public_device_register(struct device *device); smc9194_device_ldm.platform_data = (void *) netdev; dsp_public_driver_register(&smc9194_driver_ldm); dsp_public_device_register(&smc9194_device_ldm);}static int smc9194_suspend(struct device * dev, u32 state, u32 level){ struct net_device *netdev = (struct net_device *) dev->platform_data; u_int ioaddr = netdev->base_addr; switch (level) { case SUSPEND_POWER_DOWN: SMC_SELECT_BANK(1); smc_outw(smc_inw(ioaddr, CONTROL) | CTL_POWERDOWN, ioaddr, CONTROL); break; } return 0;}static int smc9194_resume(struct device * dev, u32 level){ struct net_device *netdev = (struct net_device *) dev->platform_data; u_int ioaddr = netdev->base_addr; switch (level) { case RESUME_POWER_ON: SMC_SELECT_BANK(1); smc_outw((smc_inw(ioaddr, CONTROL) & ~CTL_POWERDOWN) | CTL_AUTO_RELEASE, ioaddr, CONTROL); break; } return 0;}#endif /* MVL-CEE *//*------------------------------------------------------------------------- | | 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 | ---------------------------------------------------------------------------*/static int io;static int irq;static int ifport; static struct devlist *smcdev = smc_devlist;#ifndef NO_AUTOPROBE smcdev = smc_devlist;#endif SET_MODULE_OWNER(dev); dev->irq = INT_ETHER_H4; if (smc_probe(dev, base_addr) == 0) { return 0; }#else#error unknown OMAP type#endif#else /* Assume default is CONFIG_ISA */ int i; int base_addr = dev->base_addr; SET_MODULE_OWNER(dev); /* try a specific location */ if (base_addr > 0x1ff) return smc_probe(dev, base_addr); else if (base_addr != 0) return -ENXIO; /* check every ethernet address */ for (i = 0; smc_portlist[i]; i++) if (smc_probe(dev, smc_portlist[i]) == 0) for (;smcdev->port; smcdev++) { if (smc_probe(dev, smcdev->port) == 0) if (!smcdev->port) return 0; /* couldn't find anything */#endif /* Assume default is CONFIG_ISA */ return ret;}/*---------------------------------------------------------------------- . smc_findirq . . This routine has a simple purpose -- make the SMC chip generate an . interrupt, so an auto-detect routine can detect it, and find the IRQ, ------------------------------------------------------------------------*/int __init smc_findirq( int ioaddr ){#ifndef NO_AUTOPROBE int timeout = 20; unsigned long cookie; /* I have to do a STI() here, because this is called from a routine that does an CLI during this process, making it rather difficult to get interrupts for auto detection */ sti(); cookie = probe_irq_on(); /* * What I try to do here is trigger an ALLOC_INT. This is done * by allocating a small chunk of memory, which will give an interrupt
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -