Index: linux-2.6.22/drivers/net/enc28j60.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/net/enc28j60.c 2007-09-12 16:50:09.000000000 -0600 @@ -0,0 +1,542 @@ +/* enc28j60.c: EDTP FrameThrower style enc28j60 driver for Linux 2.4 + * + * (c) Copyright 2006 American Microsystems Limited + * Written by David Anders. + * + * Based on the Procyon AVRlib enc28j60.c written by Pascal Stang + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include +#include +#include +#include /* eth_type_trans */ +#include /* struct iphdr */ +#include /* struct tcphdr */ +#include +#include /* size_t */ + +#include +#include +#include +#include + +#include +#include + +#include "enc28j60.h" + +#define SPIPORT (S3C24XX_VA_SPI) + +#define SPITXDATA (SPIPORT + S3C2410_SPTDAT) +#define SPIRXDATA (SPIPORT + S3C2410_SPRDAT) +#define SPIPRESCALER (SPIPORT + S3C2410_SPPRE) +#define SPICONTROL (SPIPORT + S3C2410_SPCON) + +u8 Enc28j60Bank=-1; +u16 NextPacketPtr; + +void enc28j60ChipSelect(int cs_stat) +{ + s3c2410_gpio_setpin(S3C2410_GPH0,cs_stat); +} + + + +void enc28j60ReadBuffer(u16 len, u8* data) +{ + u8 tmpval; + // assert CS + enc28j60ChipSelect(0); + // issue read command + tmpval=ENC28J60_READ_BUF_MEM; + __raw_writeb(tmpval,SPITXDATA); + udelay(4); + while(len--) + { + // read data + tmpval=0x00; + __raw_writeb(tmpval,SPITXDATA); + udelay(4); + *data++ = __raw_readb(SPIRXDATA); + udelay(4); + } + // release CS + enc28j60ChipSelect(1); +} + +void enc28j60WriteBuffer(u16 len, u8* data) +{ + u8 tmpval; + // assert CS + enc28j60ChipSelect(0); + + // issue write command + tmpval = ENC28J60_WRITE_BUF_MEM; + __raw_writeb(tmpval,SPITXDATA); + udelay(1); + while(len--) + { + // write data + tmpval = *data++; + __raw_writeb(tmpval,SPITXDATA); + udelay(1); + } + // release CS + enc28j60ChipSelect(1); +} + + +u8 enc28j60ReadOp(u8 op, u8 address) +{ + u8 data; + u8 tmpval; + + // assert CS + enc28j60ChipSelect(0); + + // issue read command + tmpval = op | (address & ADDR_MASK); + __raw_writeb( tmpval, SPITXDATA); + data = __raw_readb(SPIRXDATA); + udelay(1); + tmpval = 0x00; + __raw_writeb( tmpval, SPITXDATA); + data = __raw_readb(SPIRXDATA); + udelay(1); + + // do dummy read if needed + if(address & 0x80) + { + tmpval = 0x00; + __raw_writeb( tmpval, SPITXDATA); + data = __raw_readb(SPIRXDATA); + udelay(1); + } + data = __raw_readb(SPIRXDATA); + + // release CS + enc28j60ChipSelect(1); + + return data; +} + +void enc28j60WriteOp(u8 op, u8 address, u8 data) +{ + u8 tmpval; + + // assert CS + enc28j60ChipSelect(0); + + // issue write command + tmpval = op | (address & ADDR_MASK); + __raw_writeb( tmpval, SPITXDATA); + udelay(1); + tmpval = data; + __raw_writeb( tmpval, SPITXDATA); + udelay(1); + + // release CS + enc28j60ChipSelect(1); +} + + +void enc28j60SetBank(u8 address) +{ + // set the bank (if needed) + if((address & BANK_MASK) != Enc28j60Bank) + { + // set the bank + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); + Enc28j60Bank = (address & BANK_MASK); + } +} + +u8 enc28j60Read(u8 address) +{ + // set the bank + enc28j60SetBank(address); + // do the read + return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +} + +void enc28j60Write(u8 address, u8 data) +{ + // set the bank + enc28j60SetBank(address); + // do the write + enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +u16 enc28j60PhyRead(u8 address) +{ + u16 data; + + // Set the right address and start the register read operation + enc28j60Write(MIREGADR, address); + enc28j60Write(MICMD, MICMD_MIIRD); + + // wait until the PHY read completes + while(enc28j60Read(MISTAT) & MISTAT_BUSY); + + // quit reading + enc28j60Write(MICMD, 0x00); + + // get data value + data = enc28j60Read(MIRDL); + data |= enc28j60Read(MIRDH); + // return the data + return data; +} + +void enc28j60PhyWrite(u8 address, u16 data) +{ + // set the PHY register address + enc28j60Write(MIREGADR, address); + + // write the PHY data + enc28j60Write(MIWRL, data); + enc28j60Write(MIWRH, data>>8); + +} + +void nicGetMacAddress(u8* macaddr) +{ + // read MAC address registers + // NOTE: MAC address in ENC28J60 is byte-backward + *macaddr++ = enc28j60Read(MAADR5); + *macaddr++ = enc28j60Read(MAADR4); + *macaddr++ = enc28j60Read(MAADR3); + *macaddr++ = enc28j60Read(MAADR2); + *macaddr++ = enc28j60Read(MAADR1); + *macaddr++ = enc28j60Read(MAADR0); +} + +void nicSetMacAddress(u8* macaddr) +{ + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + enc28j60Write(MAADR5, *macaddr++); + enc28j60Write(MAADR4, *macaddr++); + enc28j60Write(MAADR3, *macaddr++); + enc28j60Write(MAADR2, *macaddr++); + enc28j60Write(MAADR1, *macaddr++); + enc28j60Write(MAADR0, *macaddr++); +} + +void enc28j60_RegDump(void) +{ + + printk("RevID: 0x%02x\r\n", enc28j60Read(EREVID)); + + printk("Cntrl: ECON1 ECON2 ESTAT EIR EIE\r\n"); + printk(" 0x%02x ",enc28j60Read(ECON1)); + printk("0x%02x ",enc28j60Read(ECON2)); + printk("0x%02x ",enc28j60Read(ESTAT)); + printk("0x%02x ",enc28j60Read(EIR)); + printk("0x%02x\n\n",enc28j60Read(EIE)); + + printk("MAC : MACON1 MACON2 MACON3 MACON4 MAC-Address\r\n"); + printk(" 0x%02x ",enc28j60Read(MACON1)); + printk("0x%02x ",enc28j60Read(MACON2)); + printk("0x%02x ",enc28j60Read(MACON3)); + printk("0x%02x ",enc28j60Read(MACON4)); + printk("%02x:",enc28j60Read(MAADR5)); + printk("%02x:",enc28j60Read(MAADR4)); + printk("%02x:",enc28j60Read(MAADR3)); + printk("%02x:",enc28j60Read(MAADR2)); + printk("%02x:",enc28j60Read(MAADR1)); + printk("%02x\n\n",enc28j60Read(MAADR0)); + + + printk("Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\r\n"); + printk(" 0x%02x:",enc28j60Read(ERXSTH)); + printk("0x%02x ",enc28j60Read(ERXSTL)); + printk("0x%02x:",enc28j60Read(ERXNDH)); + printk("0x%02x ",enc28j60Read(ERXNDL)); + printk("0x%02x:",enc28j60Read(ERXWRPTH)); + printk("0x%02x ",enc28j60Read(ERXWRPTL)); + printk("0x%02x:",enc28j60Read(ERXRDPTH)); + printk("0x%02x ",enc28j60Read(ERXRDPTL)); + printk("0x%02x ",enc28j60Read(ERXFCON)); + printk("0x%02x ",enc28j60Read(EPKTCNT)); + printk("0x%02x:",enc28j60Read(MAMXFLH)); + printk("0x%02x\n",enc28j60Read(MAMXFLL)); + + printk("Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\r\n"); + printk("0x%02x:",enc28j60Read(ETXSTH)); + printk("0x%02x ",enc28j60Read(ETXSTL)); + printk("0x%02x:",enc28j60Read(ETXNDH)); + printk("0x%02x ",enc28j60Read(ETXNDL)); + printk("0x%02x ",enc28j60Read(MACLCON1)); + printk("0x%02x ",enc28j60Read(MACLCON2)); + printk("0x%02x\n",enc28j60Read(MAPHSUP)); + udelay(1000); +} + + + + +static struct net_device_stats * +get_stats(struct net_device *dev) +{ + return (struct net_device_stats *)(dev->priv); +} + +static irqreturn_t enc28j60_interrupt(int irq, void *dev_id) +{ + struct sk_buff *skb; + struct net_device *dev = dev_id; + u16 rxstat; + u16 len=0; + + while (enc28j60Read(EIR) & EIR_PKTIF) { + + // Set the read pointer to the start of the received packet + enc28j60Write(ERDPTL, (NextPacketPtr)); + enc28j60Write(ERDPTH, (NextPacketPtr)>>8); + // read the next packet pointer + NextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); + NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; + // read the packet length + len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); + len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; + // read the receive status + rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); + rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; + + skb = dev_alloc_skb(len); + skb->dev = dev; + skb_put(skb,len); + + // copy the packet from the receive buffer + enc28j60ReadBuffer(len, skb->data); + + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + enc28j60Write(ERXRDPTL, (NextPacketPtr)); + enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8); + +// decrement the packet counter indicate we are done with this packet + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + + skb->protocol= eth_type_trans(skb,dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); + + + } + + return IRQ_RETVAL(1); + +} + +int enc28j60_open (struct net_device *dev) +{ + int result; + + printk("enc28j60_open called\n"); + result = request_irq(IRQ_EINT10,enc28j60_interrupt , 0, "enc28j60" , dev); + printk("request_irq returned: %d\n",result); + netif_start_queue (dev); + return 0; +} + +int enc28j60_release (struct net_device *dev) +{ + printk ("enc28j60_release called\n"); + netif_stop_queue(dev); + free_irq(IRQ_EINT10, dev); + return 0; +} + +static int enc28j60_xmit (struct sk_buff *skb, + struct net_device *dev) +{ + // Set the write pointer to start of transmit buffer area + enc28j60Write(EWRPTL, TXSTART_INIT); + enc28j60Write(EWRPTH, TXSTART_INIT>>8); + // Set the TXND pointer to correspond to the packet size given + enc28j60Write(ETXNDL, (TXSTART_INIT+skb->len)); + enc28j60Write(ETXNDH, (TXSTART_INIT+skb->len)>>8); + // write per-packet control byte + enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); + + // copy the packet into the transmit buffer + enc28j60WriteBuffer(skb->len, skb->data); + // send the contents of the transmit buffer onto the network + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); + dev_kfree_skb(skb); +// need to fix this!!!!!!!!!!!!!!!!!! + udelay(2000); + + return 0; +} + + +int enc28j60_init (struct net_device *dev) +{ + static struct net_device_stats enc28j60_netstats; + dev->priv = &enc28j60_netstats ; + memset(dev->priv, 0, sizeof(struct net_device_stats)); + dev->get_stats = get_stats; + + dev->open = enc28j60_open; + dev->stop = enc28j60_release; + dev->hard_start_xmit = enc28j60_xmit; + + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x00; + dev->dev_addr[2] = 'F'; + dev->dev_addr[3] = 'I'; + dev->dev_addr[4] = 'C'; + dev->dev_addr[5] = 'E'; + + ether_setup(dev); + + printk("dev->hard_header_len: 0x%02x\n",dev->hard_header_len); + // do bank 0 stuff + // initialize receive buffer + // 16-bit transfers, must write low byte first + // set receive buffer start address + + + printk("starting enc28j60 init process!\n"); + return 0; +} + +struct net_device enc28j60 = {init: enc28j60_init}; + +int enc28j60_init_module (void) +{ + int result; + unsigned long tmpval; + + printk("starting module init!!\n"); + strcpy (enc28j60.name, "eth%d"); + if ((result = register_netdev (&enc28j60))) { + printk ("enc28j60: Error %d initializing enc28j60 based device",result); + return result; + } + + printk("configuring GPIO's!\n"); + s3c2410_gpio_cfgpin(S3C2410_GPH0, S3C2410_GPH0_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0); + s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0); + s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0); + s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_EINT10); + + s3c2410_gpio_pullup(S3C2410_GPH0, 1); + s3c2410_gpio_pullup(S3C2410_GPE11, 1); + s3c2410_gpio_pullup(S3C2410_GPE12, 1); + s3c2410_gpio_pullup(S3C2410_GPE13, 1); + s3c2410_gpio_pullup(S3C2410_GPG2, 1); + + + printk("configuring SPI prescaler!\n"); + //configure the SPI prescaler for 10mhz baud rate + tmpval = 0x00000001 ; + writel(tmpval, SPIPRESCALER); + + printk("configuring SPI for master and turning it on!\n"); + //configure the SPI for master and turn it on + tmpval = 0x00000018 ; + writel(tmpval, SPICONTROL); + + printk("issue soft reset command!\n"); + + enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + udelay(1000); + + printk("turn on PHY!\n"); + enc28j60PhyWrite(PHLCON, 0x3412); +// enc28j60PhyWrite(PHLCON, 0x3882); //turn on both leds for testing + + enc28j60SetBank(ECON1); + + NextPacketPtr = RXSTART_INIT; + enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); + enc28j60Write(ERXSTH, RXSTART_INIT>>8); + // set receive pointer address + enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); + enc28j60Write(ERXRDPTH, RXSTART_INIT>>8); + // set receive buffer end + // ERXND defaults to 0x1FFF (end of ram) + enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); + enc28j60Write(ERXNDH, RXSTOP_INIT>>8); + // set transmit buffer start + // ETXST defaults to 0x0000 (beginnging of ram) + enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); + enc28j60Write(ETXSTH, TXSTART_INIT>>8); + + printk("bank2 enc28j60 init process!\n"); + + // do bank 2 stuff + // enable MAC receive + enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); + // bring MAC out of reset + enc28j60Write(MACON2, 0x00); + // enable automatic padding and CRC operations + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + // set inter-frame gap (non-back-to-back) + enc28j60Write(MAIPGL, 0x12); + enc28j60Write(MAIPGH, 0x0C); + // set inter-frame gap (back-to-back) + enc28j60Write(MABBIPG, 0x12); + // Set the maximum packet size which the controller will accept + enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF); + enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8); + + printk("bank3 enc28j60 init process!\n"); + // do bank 3 stuff + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + enc28j60Write(MAADR5, ENC28J60_MAC0); + enc28j60Write(MAADR4, ENC28J60_MAC1); + enc28j60Write(MAADR3, ENC28J60_MAC2); + enc28j60Write(MAADR2, ENC28J60_MAC3); + enc28j60Write(MAADR1, ENC28J60_MAC4); + enc28j60Write(MAADR0, ENC28J60_MAC5); + + // no loopback of transmitted frames + printk("phy enc28j60 init process!\n"); + enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); + + + // enable interrutps + // enable packet reception + + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +// enc28j60Write(ERXFCON, 0x00); + printk("enabling interupts!\n"); + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); + + // Reenable receive logic + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + + printk ("enc28j60 device initialized\n"); +// enc28j60PhyWrite(PHLCON, 0x3412); + + +return 0; + +} + +void enc28j60_cleanup (void) +{ + printk ("Cleaning Up the Module\n"); + enc28j60PhyWrite(PHLCON, 0x3492); + unregister_netdev (&enc28j60); + return; +} + +module_init (enc28j60_init_module); +module_exit (enc28j60_cleanup); +MODULE_LICENSE("GPL");