lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Tue, 8 Feb 2011 20:18:05 +0530 From: Balaji Venkatachalam <balaji.v@...takaa.com> To: netdev@...r.kernel.org Cc: mohan@...takaa.com, blue.cube@...nam.cz, lanconelli.claudio@...ar.com, sriram@...takaa.com, vbalaji.acs@...il.com Subject: Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus From: Balaji Venkatachalam <balaji.v@...takaa.com> Updated patch [1.30] for Microchip enc424j600 ethernet chip controlled via SPI. I tested it on my custom board with ARM9 (Freescale i.MX233) with Kernel 2.6.31.14. I tested the corrected get_regs implementation with ethtool using the command: "ethtool -d eth0". It gives output in "Offset Value" format. Changes done since V1.29 to V1.30 1. Fixed Compiler Warning due to wrong get_regs implementation 2. added enc424j600_get_regs, get_regs functionality for use with ethtool. 3. removed unnecessary & unformatted comments 4. replaced enc424j600_set_bits and enc424j600_clear_bits with enc424j600_write_bits. 5. removed unnecessary empty lines. Changes done since V1.28 to V1.29 1. enc424j600_spi_trans function implementation optimized 2. Unnecessary comments and Improper Comments removed 3. Added an empty line after variable declaration in all functions 4. mapped enc424j600_dump_regs to eth_ops get_regs 5. removed the hardcoding of return type in enc424j600_probe function Changes done since V1.27 to V1.28 1. did some code formatting Changes done since V1.24 to V1.27 1. Timeout Mechanism implemented for enc424j600_soft_reset function 2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function 3. Window Naming changed to enum 4. Removed WRITEVERIFY functionality Todo List: 1. Low Power Mode Functionality implementation 2. Provide Support for On-Chip DMA 3. Remove mutex_lock wherever not required Any comments are welcome. Signed-off-by: Balaji Venkatachalam <balaji.v@...takaa.com> --- diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600.c b/drivers/net/enc424j600.c --- a/drivers/net/enc424j600.c 1970-01-01 05:30:00.000000000 +0530 +++ b/drivers/net/enc424j600.c 2011-02-08 20:10:26.000000000 +0530 @@ -0,0 +1,1748 @@ +/* + * Microchip ENC424J600 ethernet driver (MAC + PHY) on SPI bus + * + * Copyright (C) 2011 Thotaka Technologies Pvt Ltd + * Author: Balaji Venkatachalam <balaji.v@...takaa.com> + * based on enc424j600.c written by Kuba Marek + * based on enc28j60.c written by Claudio Lanconelli + * + * Changes done since V1.29 to V1.30 + * 1. Fixed Compiler Warning due to wrong get_regs implementation + * 2. added enc424j600_get_regs, get_regs functionality + * for use with ethtool. + * 3. removed unnecessary & unformatted comments + * 4. replaced enc424j600_set_bits and enc424j600_clear_bits with + * enc424j600_write_bits. + * 5. removed unnecessary empty lines. + * + * Changes done since V1.28 to V1.29 + * 1. enc424j600_spi_trans function implementation optimized + * 2. Unnecessary comments and Improper Comments removed + * 3. Added an empty line after variable declaration in all functions + * 4. mapped enc424j600_dump_regs to eth_ops get_regs + * 5. removed the hardcoding of return type in enc424j600_probe function + * + * Changes done since V1.27 to V1.28 + * 1. did some code formatting + * + * Changes done since V1.24 to V1.27 + * 1. Timeout Mechanism implemented for enc424j600_soft_reset function + * 2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function + * 3. Window Naming changed to enum + * 4. Removed WRITEVERIFY functionality + * + * Todo List: + * 1. Low Power Mode Functionality implementation + * 2. Provide Support for On-Chip DMA + * 3. Remove mutex_lock wherever not required + * 4. Propogate the return status code of enc424j600_hw_init + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/tcp.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> + +#include "enc424j600_hw.h" + +#define DRV_NAME "enc424j600" +#define DRV_VERSION "1.30" + +#define ENC424J600_MSG_DEFAULT \ + (NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK) + +#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN) +#define TX_TIMEOUT (4 * HZ) + +/* Max TX retries in case of collision as suggested by errata datasheet */ +#define MAX_TX_RETRYCOUNT 16 + +#define SPI_OPLEN 1 + +#define SRAMSIZE 0x6000 +#define TXSTART 0x0000 +#define RXSTART 0x1600 + +/* Enable SPI DMA. Default: 0 (Off) */ +static int enc424j600_enable_dma; + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; +enum { + RXWINDOW, + USERWINDOW, + GPWINDOW +}; + +/* Driver local data */ +struct enc424j600_net { + struct net_device *netdev; + struct spi_device *spi; + struct mutex lock; + struct sk_buff *tx_skb; + struct work_struct tx_work; + struct work_struct irq_work; + struct work_struct setrx_work; + struct work_struct restart_work; + u8 bank; /* current register bank selected */ + u16 next_pk_ptr; /* next packet pointer within FIFO */ + u16 max_pk_counter; /* statistics: max packet counter */ + u16 tx_retry_count; + bool hw_enable; + bool full_duplex; + bool autoneg; + bool speed100; + int rxfilter; + u32 msg_enable; + + u8 *spi_rx_buf; + u8 *spi_tx_buf; + dma_addr_t spi_tx_dma; + dma_addr_t spi_rx_dma; +}; + +/* use ethtool to change the level for any given device */ +static struct { + u32 msg_enable; +} debug = { -1 }; + +static int enc424j600_spi_trans(struct enc424j600_net *priv, int len) +{ + /*modified to suit half duplexed spi */ + struct spi_transfer tt = { + .tx_buf = priv->spi_tx_buf, + .len = SPI_OPLEN, + }; + struct spi_transfer tr = { + .rx_buf = priv->spi_rx_buf, + .len = len, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + + spi_message_add_tail(&tt, &m); + spi_message_add_tail(&tr, &m); + + ret = spi_sync(priv->spi, &m); + + if (ret) { + dev_err(&priv->spi->dev, + "spi transfer failed: ret = %d\n", ret); + goto out; + } + + memcpy(priv->spi_rx_buf, tr.rx_buf, len); + +out: + return ret; +} + +/*Read data from chip SRAM.*/ +static int enc424j600_read_sram(struct enc424j600_net *priv, + u8 *dst, int len, u16 srcaddr, int window) +{ + int ret; + + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0) + return -EINVAL; + + /* First set the write pointer as per selected window */ + if (window == RXWINDOW) + priv->spi_tx_buf[0] = WRXRDPT; + else if (window == USERWINDOW) + priv->spi_tx_buf[0] = WUDARDPT; + else if (window == GPWINDOW) + priv->spi_tx_buf[0] = WGPRDPT; + + priv->spi_tx_buf[1] = srcaddr & 0xFF; + priv->spi_tx_buf[2] = srcaddr >> 8; + ret = spi_write(priv->spi, priv->spi_tx_buf, 3); + + /* Transfer the data */ + if (window == RXWINDOW) + priv->spi_tx_buf[0] = RRXDATA; + else if (window == USERWINDOW) + priv->spi_tx_buf[0] = RUDADATA; + else if (window == GPWINDOW) + priv->spi_tx_buf[0] = RGPDATA; + + ret = enc424j600_spi_trans(priv, len + 1); + + /* Copy the data from the rx buffer */ + memcpy(dst, &priv->spi_rx_buf[0], len); + + return ret; +} + +/* Write data to chip SRAM.*/ +static int enc424j600_write_sram(struct enc424j600_net *priv, + const u8 *src, int len, u16 dstaddr, + int window) +{ + int ret; + + if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0) + return -EINVAL; + + /* First set the general purpose write pointer */ + if (window == RXWINDOW) + priv->spi_tx_buf[0] = WRXWRPT; + else if (window == USERWINDOW) + priv->spi_tx_buf[0] = WUDAWRPT; + else if (window == GPWINDOW) + priv->spi_tx_buf[0] = WGPWRPT; + + priv->spi_tx_buf[1] = dstaddr & 0xFF; + priv->spi_tx_buf[2] = dstaddr >> 8; + ret = spi_write(priv->spi, priv->spi_tx_buf, 3); + + /* Copy the data to the tx buffer */ + memcpy(&priv->spi_tx_buf[1], src, len); + + /* Transfer the data */ + if (window == RXWINDOW) + priv->spi_tx_buf[0] = WRXDATA; + else if (window == USERWINDOW) + priv->spi_tx_buf[0] = WUDADATA; + else if (window == GPWINDOW) + priv->spi_tx_buf[0] = WGPDATA; + + ret = spi_write(priv->spi, priv->spi_tx_buf, len + 1); + + return ret; +} + +/* Select the current register bank if necessary to be able to read @addr.*/ +static void enc424j600_set_bank(struct enc424j600_net *priv, u8 addr) +{ + u8 b = (addr & BANK_MASK) >> BANK_SHIFT; + + /* These registers are present in all banks, no need to switch bank */ + if (addr >= EUDASTL && addr <= ECON1H) + return; + if (priv->bank == b) + return; + + priv->spi_tx_buf[0] = BXSEL(b); + + enc424j600_spi_trans(priv, 1); + priv->bank = b; +} + +/* sets and clears SFR registers */ +static void enc424j600_write_bits(struct enc424j600_net *priv, u8 addr, + u8 bits, u8 mask) +{ + enc424j600_set_bank(priv, addr); + priv->spi_tx_buf[0] = bits; + priv->spi_tx_buf[1] = mask; + spi_write(priv->spi, priv->spi_tx_buf, 2); +} + +/* Write a 8bit special function register. + The @sfr parameters takes address of the register.*/ +static int enc424j600_write_8b_sfr(struct enc424j600_net *priv, u8 sfr, u8 data) +{ + int ret; + + enc424j600_set_bank(priv, sfr); + priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK); + priv->spi_tx_buf[1] = data & 0xFF; + ret = spi_write(priv->spi, priv->spi_tx_buf, 2); + + return ret; +} + +/* Read a 8bit special function register. + The @sfr parameters takes address of the register.*/ +static int enc424j600_read_8b_sfr(struct enc424j600_net *priv, + u8 sfr, u8 *data) +{ + int ret; + + enc424j600_set_bank(priv, sfr); + priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK); + ret = enc424j600_spi_trans(priv, 2); + *data = priv->spi_rx_buf[0]; + + return ret; +} + +/* Write a 16bit special function register. + The @sfr parameters takes address of the low byte of the register. + Takes care of the endiannes & buffers.*/ +static int enc424j600_write_16b_sfr(struct enc424j600_net *priv, + u8 sfr, u16 data) +{ + int ret; + + enc424j600_set_bank(priv, sfr); + priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK); + priv->spi_tx_buf[1] = data & 0xFF; + priv->spi_tx_buf[2] = data >> 8; + ret = spi_write(priv->spi, priv->spi_tx_buf, 3); + if (ret && netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n", + __func__, ret); + + return ret; +} + +/*Read a 16bit special function register. + The @sfr parameters takes address of the low byte of the register. + Takes care of the endiannes & buffers. */ +static int enc424j600_read_16b_sfr(struct enc424j600_net *priv, + u8 sfr, u16 *data) +{ + int ret; + + enc424j600_set_bank(priv, sfr); + priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK); + priv->spi_tx_buf[1] = 0; + priv->spi_tx_buf[2] = 0; + priv->spi_tx_buf[3] = 0; + ret = enc424j600_spi_trans(priv, 3); + *data = priv->spi_rx_buf[0] | priv->spi_rx_buf[1] << (u16) 8; + + return ret; +} + +static unsigned long msec20_to_jiffies; + +/*Wait for bits in register to become equal to @readyMask, but at most 20ms.*/ +static int checktimeout_16bit(struct enc424j600_net *priv, + u8 reg, u16 mask, u16 readyMask) +{ + unsigned long timeout = jiffies + msec20_to_jiffies; + u16 value; + + /* 20 msec timeout read */ + enc424j600_read_16b_sfr(priv, reg, &value); + while ((value & mask) != readyMask) { + if (time_after(jiffies, timeout)) { + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, + "reg %02x ready timeout!\n", reg); + return -ETIMEDOUT; + } + cpu_relax(); + enc424j600_read_16b_sfr(priv, reg, &value); + } + + return 0; +} + +/* wait 20 ms for (value&mask) to become readyMask*/ +static int checktimeout_8bit(struct enc424j600_net *priv, + u8 reg, u8 mask, u8 readyMask) +{ + unsigned long timeout = jiffies + msec20_to_jiffies; + u8 value; + + /* 20 msec timeout read */ + enc424j600_read_8b_sfr(priv, reg, &value); + while ((value & mask) != readyMask) { + if (time_after(jiffies, timeout)) { + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, + "reg %02x ready timeout!\n", reg); + return -ETIMEDOUT; + } + cpu_relax(); + enc424j600_read_8b_sfr(priv, reg, &value); + } + + return 0; +} + +/* Reset the enc424j600.*/ +static int enc424j600_soft_reset(struct enc424j600_net *priv) +{ + int ret; + u16 eudast; + + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + enc424j600_write_16b_sfr(priv, EUDASTL, EUDAST_TEST_VAL); + ret = checktimeout_16bit(priv, EUDASTL, 0xFFFF, EUDAST_TEST_VAL); + if (ret != 0) + return ret; + ret = checktimeout_16bit(priv, ESTATH, CLKRDY, CLKRDY); + if (ret != 0) + return ret; + + priv->spi_tx_buf[0] = SETETHRST; + enc424j600_spi_trans(priv, 1); + /*inline with the datasheet */ + udelay(25); + + enc424j600_read_16b_sfr(priv, EUDASTL, &eudast); + if (netif_msg_hw(priv) && eudast != 0) + printk(KERN_DEBUG DRV_NAME + ": %s() EUDASTL is not zero!\n", __func__); + /*inline with the datasheet */ + /*datasheet says to wait for 256 usec atleast */ + udelay(300); + return 0; +} + +/* + * PHY register read + * PHY registers are not accessed directly, but through the MII + */ +static int enc424j600_phy_read(struct enc424j600_net *priv, + u16 address, u16 *data) +{ + int ret; + + enc424j600_write_16b_sfr(priv, MIREGADRL, + address | (MIREGADRH_VAL << 8)); + enc424j600_write_16b_sfr(priv, MICMDL, MIIRD); + udelay(26); + ret = !checktimeout_8bit(priv, MISTATL, BUSY, 0); + enc424j600_write_16b_sfr(priv, MICMDL, 0); + enc424j600_read_16b_sfr(priv, MIRDL, data); + return ret; +} + +static int enc424j600_phy_write(struct enc424j600_net *priv, u16 address, + u16 data) +{ + enc424j600_write_16b_sfr(priv, MIREGADRL, + address | (MIREGADRH_VAL << 8)); + enc424j600_write_16b_sfr(priv, MIWRL, data); + udelay(26); + return !checktimeout_8bit(priv, MISTATL, BUSY, 0); +} + +/* Read the hardware MAC address to dev->dev_addr. */ +static int enc424j600_get_hw_macaddr(struct net_device *ndev) +{ + struct enc424j600_net *priv = netdev_priv(ndev); + u16 maadr1, maadr2, maadr3; + + mutex_lock(&priv->lock); + + if (netif_msg_drv(priv)) + printk(KERN_INFO DRV_NAME + ": %s: Setting MAC address to %pM\n", + ndev->name, ndev->dev_addr); + + enc424j600_read_16b_sfr(priv, MAADR3L, &maadr3); + ndev->dev_addr[5] = maadr3 >> 8; + ndev->dev_addr[4] = maadr3 & 0xff; + enc424j600_read_16b_sfr(priv, MAADR2L, &maadr2); + ndev->dev_addr[3] = maadr2 >> 8; + ndev->dev_addr[2] = maadr2 & 0xff; + enc424j600_read_16b_sfr(priv, MAADR1L, &maadr1); + ndev->dev_addr[1] = maadr1 >> 8; + ndev->dev_addr[0] = maadr1 & 0xff; + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Program the hardware MAC address from dev->dev_addr.*/ +static int enc424j600_set_hw_macaddr(struct net_device *ndev) +{ + struct enc424j600_net *priv = netdev_priv(ndev); + + mutex_lock(&priv->lock); + + if (priv->hw_enable) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME + ": %s() Hardware must be disabled to set " + "Mac address\n", __func__); + mutex_unlock(&priv->lock); + return -EBUSY; + } + + if (netif_msg_drv(priv)) + printk(KERN_INFO DRV_NAME + ": %s: Setting MAC address to %pM\n", + ndev->name, ndev->dev_addr); + + enc424j600_write_16b_sfr(priv, MAADR3L, + ndev->dev_addr[4] | ndev->dev_addr[5] << 8); + enc424j600_write_16b_sfr(priv, MAADR2L, + ndev->dev_addr[2] | ndev->dev_addr[3] << 8); + enc424j600_write_16b_sfr(priv, MAADR1L, + ndev->dev_addr[0] | ndev->dev_addr[1] << 8); + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ +static int enc424j600_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return enc424j600_set_hw_macaddr(dev); +} + +u8 nolock_regb_read(struct enc424j600_net *priv, u8 address) +{ + u8 data; + + enc424j600_read_8b_sfr(priv, address, &data); + return data; +} + +u16 nolock_regw_read(struct enc424j600_net *priv, u8 address) +{ + u16 data; + + enc424j600_read_16b_sfr(priv, address, &data); + return data; +} + +/*Debug routine to dump useful register contents*/ +static void enc424j600_dump_regs(struct enc424j600_net *priv, const char *msg) +{ + mutex_lock(&priv->lock); + printk(KERN_DEBUG DRV_NAME " %s\n" + "Cntrl: ECON1H ECON1L ECON2H ECON2L ESTATH ESTATL EIRH " + "EIRL EIEH EIEL\n" + " 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x 0x%02x 0x%02x\n" + "MAC : MACON1 MACON2\n" + " 0x%04x 0x%04x\n" + "Rx : ERXST ERXTAIL ERXHEAD ERXWRPT ERXRDPT ERXFCON MAMXFL\n" + " 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n" + "Tx : ETXST ETXLEN MACLCON1 \n" + " 0x%04x 0x%04x 0x%02x\n", + msg, + nolock_regb_read(priv, ECON1H), nolock_regb_read(priv, ECON1L), + nolock_regb_read(priv, ECON2H), nolock_regb_read(priv, ECON2L), + nolock_regb_read(priv, ESTATH), nolock_regb_read(priv, ESTATL), + nolock_regb_read(priv, EIRH), nolock_regb_read(priv, EIRL), + nolock_regb_read(priv, EIEH), nolock_regb_read(priv, EIEL), + nolock_regw_read(priv, MACON1L), nolock_regw_read(priv, MACON2L), + nolock_regw_read(priv, ERXSTL), nolock_regw_read(priv, ERXTAILL), + nolock_regw_read(priv, ERXHEADL), + nolock_regw_read(priv, ERXWRPTL), nolock_regw_read(priv, + ERXRDPTL), + nolock_regw_read(priv, ERXFCONL), nolock_regw_read(priv, + MAMXFLL), + nolock_regw_read(priv, ETXSTL), nolock_regw_read(priv, ETXLENL), + nolock_regw_read(priv, MACLCONL)); + mutex_unlock(&priv->lock); +} + +/* + * TODO: Check the functionality + * Low power mode shrinks power consumption about 100x, so we'd like + * the chip to be in that mode whenever it's inactive. (However, we + * can't stay in lowpower mode during suspend with WOL active.) + */ +static void enc424j600_lowpower(struct enc424j600_net *priv, bool is_low) +{ + + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, "%s power...\n", + is_low ? "low" : "high"); + +#if 0 + mutex_lock(&priv->lock); + if (is_low) { + nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); + checktimeout_8bit(priv, ESTAT, ESTAT_RXBUSY, 0); + checktimeout_8bit(priv, ECON1, ECON1_TXRTS, 0); + /* ECON2_VRPS was set during initialization */ + nolock_reg_bfset(priv, ECON2, ECON2_PWRSV); + } else { + nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV); + checktimeout_8bit(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY); + /* caller sets ECON1_RXEN */ + } + mutex_unlock(&priv->lock); +#endif +} + +static unsigned long msec2000_to_jiffies; +/* Waits for autonegotiation to complete. */ +static int enc424j600_wait_for_autoneg(struct enc424j600_net *priv) +{ + unsigned long timeout = jiffies + msec2000_to_jiffies; + u16 value; + + /* 20 msec timeout read */ + enc424j600_phy_read(priv, PHSTAT1, &value); + while ((value & ANDONE) == 0) { + if (time_after(jiffies, timeout)) { + if (netif_msg_drv(priv)) + dev_dbg(&priv->spi->dev, + "reg %02x ready timeout!\n", PHSTAT1); + return -ETIMEDOUT; + } + cpu_relax(); + enc424j600_phy_read(priv, PHSTAT1, &value); + } + return 0; +} + +/* + * Reset and initialize the chip, but don't enable interrupts and don't + * start receiving yet. + */ +static int enc424j600_hw_init(struct enc424j600_net *priv) +{ + u8 eidledl; + u16 phcon1; + u16 macon2; + u16 econ1l; + + /*priv->autoneg = AUTONEG_ENABLE; */ + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() - %s\n", __func__, + priv->autoneg ? "Autoneg" : (priv-> + full_duplex ? "FullDuplex" : "HalfDuplex")); + + mutex_lock(&priv->lock); + + priv->bank = 0; + priv->hw_enable = false; + priv->tx_retry_count = 0; + priv->max_pk_counter = 0; + priv->rxfilter = RXFILTER_NORMAL; + + if (enc424j600_soft_reset(priv) != 0) + return 0; + + /* Check the device id and silicon revision id. */ + enc424j600_read_8b_sfr(priv, EIDLEDL, &eidledl); + + if ((eidledl & DEVID_MASK) >> DEVID_SHIFT != ENC424J600_DEV_ID) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME + ": %s() Invalid device ID: %d\n", __func__, + (eidledl & DEVID_MASK) >> DEVID_SHIFT); + return 0; + } + + if (netif_msg_drv(priv)) + printk(KERN_INFO DRV_NAME ": Silicon revision ID: 0x%02x\n", + (eidledl & REVID_MASK) >> REVID_SHIFT); + + enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART); + enc424j600_write_16b_sfr(priv, ERXSTL, RXSTART); + + priv->next_pk_ptr = RXSTART; + enc424j600_write_16b_sfr(priv, ERXTAILL, SRAMSIZE - 2); + enc424j600_write_16b_sfr(priv, ERXFCONL, UCEN | BCEN | CRCEN | RUNTEN); + + enc424j600_phy_write(priv, PHANA, PHANA_DEFAULT); + + /* PHCON1 */ + phcon1 = 0; + if (priv->autoneg) { + /* Enable autonegotiation and renegotiate */ + phcon1 |= ANEN | RENEG; + } else { + if (priv->speed100) + phcon1 |= SPD100; + if (priv->full_duplex) + phcon1 |= PFULDPX; + } + enc424j600_phy_write(priv, PHCON1, phcon1); + + /* MACON2 + * defer transmission if collision occurs (only for half duplex) + * pad to 60 or 64 bytes and append CRC + * enable receiving huge frames (instead of limiting packet size) */ + macon2 = MACON2_DEFER | PADCFG2 | PADCFG0 | TXCRCEN | HFRMEN; + + /* If autonegotiation is enabled, we have to wait untill it finishes + * and set the PHYDPX bit in MACON2 correctly */ + if (priv->autoneg) { + u8 estath; + if (!enc424j600_wait_for_autoneg(priv)) { + /* read the PHYDPX bit in ESTAT and set FULDPX in + MACON2 accordingly */ + enc424j600_read_8b_sfr(priv, ESTATH, &estath); + if (estath & PHYDPX) + macon2 |= FULDPX; + } else /*if timedout, just disable autoneg */ + priv->autoneg = AUTONEG_DISABLE; + } else if (priv->full_duplex) + macon2 |= FULDPX; + + enc424j600_write_16b_sfr(priv, MACON2L, macon2); + + /* MAIPGL + * Recomended values for inter packet gaps */ + if (!priv->autoneg) { + enc424j600_write_16b_sfr(priv, MAIPGL, + MAIPGL_VAL | (MAIPGH_VAL << 8)); + } + + /* + * Select enabled interrupts, but don't set the global + * interrupt enable flag. + */ + + enc424j600_write_16b_sfr(priv, EIEL, + LINKIE << 8 | PKTIE | DMAIE | TXIE | TXABTIE | + RXABTIE); + + enc424j600_read_16b_sfr(priv, ECON1L, &econ1l); + econ1l |= (RXEN); + enc424j600_write_16b_sfr(priv, ECON1L, econ1l); + + mutex_unlock(&priv->lock); + + if (netif_msg_hw(priv)) + enc424j600_dump_regs(priv, "Hw initialized."); + + return 1; +} + +static void enc424j600_hw_enable(struct enc424j600_net *priv) +{ + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n", + __func__); + + mutex_lock(&priv->lock); + + /* Clear any pending interrupts */ + enc424j600_write_16b_sfr(priv, EIRL, 0); + + /* Enable global interrupt flag */ + enc424j600_write_bits(priv, EIEH, BFS(EIEH), INTIE); + + /* enable receive logic */ + enc424j600_write_bits(priv, ECON1L, BFS(ECON1L), RXEN); + priv->hw_enable = true; + mutex_unlock(&priv->lock); +} + +static void enc424j600_hw_disable(struct enc424j600_net *priv) +{ + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() disabling interrupts.\n", + __func__); + + mutex_lock(&priv->lock); + + /* disable receive logic */ + enc424j600_write_bits(priv, ECON1L, BFC(ECON1L), RXEN); + + /* Disable global interrupt flag */ + enc424j600_write_bits(priv, EIEH, BFC(EIEH), INTIE); + + priv->hw_enable = false; + + mutex_unlock(&priv->lock); +} + +static int +enc424j600_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex) +{ + struct enc424j600_net *priv = netdev_priv(ndev); + int ret = 0; + + if (!priv->hw_enable) { + /* link is in low power mode now; duplex setting + * will take effect on next enc424j600_hw_init(). + */ + if (speed == SPEED_10 || speed == SPEED_100) { + priv->autoneg = (autoneg == AUTONEG_ENABLE); + priv->full_duplex = (duplex == DUPLEX_FULL); + priv->speed100 = (speed == SPEED_100); + } else { + if (netif_msg_link(priv)) + dev_warn(&ndev->dev, + "unsupported link setting\n"); + /*speeds other than SPEED_10 and SPEED_100 */ + /*are not supported by chip */ + ret = -EOPNOTSUPP; + } + } else { + if (netif_msg_link(priv)) + dev_warn(&ndev->dev, "Warning: hw must be disabled " + "to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +/* + * Receive Status vector + */ +static void enc424j600_dump_rsv(struct enc424j600_net *priv, const char *msg, + u16 pk_ptr, int len, u16 sts) +{ + printk(KERN_DEBUG DRV_NAME ": %s - NextPk: 0x%04x - RSV:\n", + msg, pk_ptr); + printk(KERN_DEBUG DRV_NAME ": ByteCount: %d, DribbleNibble: %d\n", len, + RSV_GETBIT(sts, RSV_DRIBBLENIBBLE)); + printk(KERN_DEBUG DRV_NAME ": RxOK: %d, CRCErr:%d, LenChkErr: %d," + " LenOutOfRange: %d\n", RSV_GETBIT(sts, RSV_RXOK), + RSV_GETBIT(sts, RSV_CRCERROR), + RSV_GETBIT(sts, RSV_LENCHECKERR), + RSV_GETBIT(sts, RSV_LENOUTOFRANGE)); + printk(KERN_DEBUG DRV_NAME ": Multicast: %d, Broadcast: %d, " + "LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(sts, RSV_RXMULTICAST), + RSV_GETBIT(sts, RSV_RXBROADCAST), + RSV_GETBIT(sts, RSV_RXLONGEVDROPEV), + RSV_GETBIT(sts, RSV_CARRIEREV)); + printk(KERN_DEBUG DRV_NAME ": ControlFrame: %d, PauseFrame: %d," + " UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(sts, RSV_RXCONTROLFRAME), + RSV_GETBIT(sts, RSV_RXPAUSEFRAME), + RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(sts, RSV_RXTYPEVLAN)); +} + +static void dump_packet(const char *msg, int len, const char *data) +{ + + printk(KERN_ALERT ": %s - packet len:%d\n", msg, len); + print_hex_dump(KERN_ALERT, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1, + data, len, true); +} + +/* + * Calculate wrap around when reading beyond the end of the RX buffer + */ +static u16 rx_packet_start(u16 ptr) +{ + if (ptr + RSV_SIZE > RXEND_INIT) + return (ptr + RSV_SIZE) - (RXEND_INIT - RXSTART + 1); + else + return ptr + RSV_SIZE; +} + +/* + * ERXRDPT need to be set always at odd addresses, refer to errata datasheet + */ +static u16 erxrdpt_workaround(u16 next_packet_ptr, u16 start, u16 end) +{ + u16 erxrdpt; + + if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end)) + erxrdpt = end; + else + erxrdpt = next_packet_ptr - 1; + + return erxrdpt; +} + +static void nolock_rxfifo_init(struct enc424j600_net *priv, u16 start, u16 end) +{ + u16 erxrdpt; + + if (start > 0x5FFF || end > 0x5FFF || start > end) { + if (netif_msg_drv(priv)) + printk(KERN_ERR DRV_NAME ": %s(%d, %d) RXFIFO " + "bad parameters!\n", __func__, start, end); + return; + } + /* set receive buffer start + end */ + priv->next_pk_ptr = start; + enc424j600_write_16b_sfr(priv, ERXSTL, start); + erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end); + enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt); + enc424j600_write_16b_sfr(priv, ERXTAILL, end); +} + +/* + * Hardware receive function. + * Read the buffer memory, update the FIFO pointer to free the buffer, + * check the status vector and decrement the packet counter. + */ +static void enc424j600_hw_rx(struct net_device *ndev) +{ + struct enc424j600_net *priv = netdev_priv(ndev); + struct sk_buff *skb = NULL; + u16 erxrdpt, next_packet, rxstat; + u8 pkcnt; + u16 head, tail; + u8 rsv[RSV_SIZE]; + u16 newrxtail; + int len; + + if (netif_msg_rx_status(priv)) + printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n", + priv->next_pk_ptr); + if (unlikely(priv->next_pk_ptr > RXEND_INIT)) { + if (netif_msg_rx_err(priv)) + dev_err(&ndev->dev, + "%s() Invalid packet address!! 0x%04x\n", + __func__, priv->next_pk_ptr); + mutex_lock(&priv->lock); + enc424j600_write_bits(priv, ECON1L, BFC(ECON1L), RXEN); + enc424j600_write_bits(priv, ECON2L, BFS(ECON2L), RXRST); + enc424j600_write_bits(priv, ECON2L, BFC(ECON2L), RXRST); + nolock_rxfifo_init(priv, RXSTART, RXEND_INIT); + enc424j600_write_bits(priv, EIRL, BFC(EIRL), RXABTIF); + enc424j600_write_bits(priv, ECON1L, BFS(ECON1L), RXEN); + mutex_unlock(&priv->lock); + ndev->stats.rx_errors++; + return; + } + + /* Read next packet pointer and rx status vector */ + enc424j600_read_sram(priv, rsv, sizeof(rsv), priv->next_pk_ptr, + RXWINDOW); + + next_packet = rsv[1]; + next_packet <<= 8; + next_packet |= rsv[0]; + + len = rsv[3]; + len <<= 8; + len |= rsv[2]; + + rxstat = rsv[5]; + rxstat <<= 8; + rxstat |= rsv[4]; + + if (netif_msg_rx_status(priv)) + enc424j600_dump_rsv(priv, __func__, next_packet, len, rxstat); + + if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) { + if (netif_msg_rx_err(priv)) + dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat); + ndev->stats.rx_errors++; + if (RSV_GETBIT(rxstat, RSV_CRCERROR)) + ndev->stats.rx_crc_errors++; + if (RSV_GETBIT(rxstat, RSV_LENCHECKERR)) + ndev->stats.rx_frame_errors++; + if (len > MAX_FRAMELEN) + ndev->stats.rx_over_errors++; + } else { + skb = dev_alloc_skb(len + NET_IP_ALIGN); + if (!skb) { + if (netif_msg_rx_err(priv)) + dev_err(&ndev->dev, + "out of memory for Rx'd frame\n"); + ndev->stats.rx_dropped++; + } else { + skb->dev = ndev; + skb_reserve(skb, NET_IP_ALIGN); + + /* copy the packet from the receive buffer */ + enc424j600_read_sram(priv, skb_put(skb, len), len, + rx_packet_start(priv->next_pk_ptr), + RXWINDOW); + + if (netif_msg_pktdata(priv)) + dump_packet(__func__, skb->len, skb->data); + skb->protocol = eth_type_trans(skb, ndev); + /* update statistics */ + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + netif_rx_ni(skb); + } + } + newrxtail = next_packet - 2; + if (next_packet == RXSTART) + newrxtail = SRAMSIZE - 2; + + enc424j600_write_16b_sfr(priv, ERXTAILL, newrxtail); + /* + * Move the RX read pointer to the start of the next + * received packet. + * This frees the memory we just read out + */ + erxrdpt = erxrdpt_workaround(next_packet, RXSTART, RXEND_INIT); + if (netif_msg_hw(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n", __func__, + erxrdpt); + + /*TODO: remove mutex_lock wherever not required */ + mutex_lock(&priv->lock); + enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt); + + priv->next_pk_ptr = next_packet; + enc424j600_read_8b_sfr(priv, ESTATL, &pkcnt); + enc424j600_read_16b_sfr(priv, ERXHEADL, &head); + enc424j600_read_16b_sfr(priv, ERXTAILL, &tail); + /* we are done with this packet, decrement the packet counter */ + enc424j600_write_bits(priv, ECON1H, BFS(ECON1H), PKTDEC); + + mutex_unlock(&priv->lock); +} + +/* + * Access the PHY to determine link status + */ +static void enc424j600_check_link_status(struct enc424j600_net *priv) +{ + u8 estath; + u16 macon2; + + enc424j600_read_8b_sfr(priv, ESTATH, &estath); + if (estath & PHYLNK) { + if (priv->autoneg) { + if (!enc424j600_wait_for_autoneg(priv)) { + if (estath & PHYDPX) { + printk(KERN_ALERT "Full Duplex"); + enc424j600_read_16b_sfr(priv, MACON2L, + &macon2); + macon2 |= FULDPX; + enc424j600_write_16b_sfr(priv, MACON2L, + macon2); + } + } else /*if timed out, disable autoneg and continue */ + priv->autoneg = AUTONEG_DISABLE; + } + netif_carrier_on(priv->netdev); + if (netif_msg_ifup(priv)) + dev_info(&(priv->netdev->dev), "link up\n"); + } else { + if (netif_msg_ifdown(priv)) + dev_info(&(priv->netdev->dev), "link down\n"); + netif_carrier_off(priv->netdev); + } +} + +static void enc424j600_tx_clear(struct enc424j600_net *priv, bool err) +{ + struct net_device *ndev = priv->netdev; + + if (err) + ndev->stats.tx_errors++; + else + ndev->stats.tx_packets++; + + if (priv->tx_skb) { + if (!err) + ndev->stats.tx_bytes += priv->tx_skb->len; + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + } + + netif_wake_queue(ndev); +} + +static int enc424j600_int_rx_abbort_handler(struct enc424j600_net *priv, + int loop) +{ + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": intRXAbt(%d)\n", loop); + mutex_lock(&priv->lock); + priv->netdev->stats.rx_dropped++; + enc424j600_write_bits(priv, EIRL, BFC(EIRL), RXABTIF); + mutex_unlock(&priv->lock); + + return loop; +} + +static int enc424j600_int_link_handler(struct enc424j600_net *priv, int loop) +{ + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": intLINK(%d)\n", loop); + + /* we check more than is necessary here -- + * only PHYLNK would be needed. */ + enc424j600_check_link_status(priv); + + return loop; +} + +static int enc424j600_int_tx_handler(struct enc424j600_net *priv, int loop) +{ + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": intTX(%d)\n", loop); + + mutex_lock(&priv->lock); + enc424j600_tx_clear(priv, false); + enc424j600_write_bits(priv, EIRL, BFC(EIRL), TXIF); + mutex_unlock(&priv->lock); + + return loop; +} + +static int enc424j600_int_tx_err_handler(struct enc424j600_net *priv, int loop) +{ + u8 etxstat; + + loop++; + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": intTXErr(%d)\n", loop); + + mutex_lock(&priv->lock); + + enc424j600_read_8b_sfr(priv, ETXSTATH, &etxstat); + + if (etxstat & LATECOL) { + if (netif_msg_tx_err(priv)) + printk(KERN_DEBUG DRV_NAME + ": Late collision TXErr (%d)\n", + priv->tx_retry_count); + if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT) + enc424j600_write_bits(priv, ECON1L, BFS(ECON1L), TXRTS); + else + enc424j600_tx_clear(priv, true); + } else if (etxstat & MAXCOL) { + if (netif_msg_tx_err(priv)) + printk(KERN_DEBUG DRV_NAME ": Max collisions TXErr\n"); + enc424j600_tx_clear(priv, true); + } else { + enc424j600_tx_clear(priv, true); + } + + mutex_unlock(&priv->lock); + + return loop; +} + +static int enc424j600_int_received_packet_handler(struct enc424j600_net *priv) +{ + uint8_t pk_counter; + int ret; + + enc424j600_read_8b_sfr(priv, ESTATL, &pk_counter); + if (pk_counter && netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter); + if (pk_counter > priv->max_pk_counter) { + /* update statistics */ + priv->max_pk_counter = pk_counter; + if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1) + printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n", + priv->max_pk_counter); + } + ret = pk_counter; + while (pk_counter-- > 0) + enc424j600_hw_rx(priv->netdev); + return ret; +} + +static void enc424j600_irq_work_handler(struct work_struct *work) +{ + + struct enc424j600_net *priv = + container_of(work, struct enc424j600_net, irq_work); + int loop; + + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + /* disable further interrupts */ + enc424j600_write_bits(priv, EIEH, BFC(EIEH), INTIE); + + do { + u16 intflags; + enc424j600_read_16b_sfr(priv, EIRL, &intflags); + loop = 0; + + /* LINK changed handler */ + if ((intflags & LINKIF) != 0) + loop = enc424j600_int_link_handler(priv, loop); + + /* TX complete handler */ + if ((intflags & TXIF) != 0) + loop = enc424j600_int_tx_handler(priv, loop); + + /* TX Error handler */ + if ((intflags & TXABTIF) != 0) { + printk(KERN_ALERT "ABORTING TRANSMITTING PACKET"); + loop = enc424j600_int_tx_err_handler(priv, loop); + } + /* RX Error handler */ + if ((intflags & RXABTIF) != 0) { + printk(KERN_ALERT "ABORTING RECEIVED PACKET"); + loop = enc424j600_int_rx_abbort_handler(priv, loop); + } + /* RX handler */ + if ((intflags & PKTIF) != 0) + loop = enc424j600_int_received_packet_handler(priv); + enc424j600_write_bits(priv, EIRL, BFC(EIRL), intflags && 0xff); + enc424j600_write_bits(priv, EIRH, BFC(EIRH), intflags >> 8); + } while (loop); + /* re-enable interrupts */ + enc424j600_write_bits(priv, EIEH, BFS(EIEH), INTIE); + + if (netif_msg_intr(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__); +} + +void locked_reg_bfset(struct enc424j600_net *priv, u8 addr, u8 mask) +{ + mutex_lock(&priv->lock); + enc424j600_write_bits(priv, addr, BFS(addr), mask); + mutex_unlock(&priv->lock); +} + +/* + * Hardware transmit function. + * Fill the buffer memory and send the contents of the transmit buffer + * onto the network + */ +static void enc424j600_hw_tx(struct enc424j600_net *priv) +{ + if (!priv->tx_skb) { + enc424j600_tx_clear(priv, false); + return; + } + + if (netif_msg_tx_queued(priv)) + printk(KERN_DEBUG DRV_NAME ": Tx Packet Len:%d\n", + priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet(__func__, priv->tx_skb->len, priv->tx_skb->data); + + enc424j600_write_sram(priv, priv->tx_skb->data, priv->tx_skb->len, + TXSTART, GPWINDOW); + + /* Set the tx pointer to start of general purpose SRAM area */ + enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART); + + /* Write the transfer length */ + enc424j600_write_16b_sfr(priv, ETXLENL, priv->tx_skb->len); + + /* set TX request flag */ + locked_reg_bfset(priv, ECON1L, TXRTS); +} + +static int enc424j600_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + if (netif_msg_tx_queued(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + /* If some error occurs while trying to transmit this + * packet, you should return '1' from this function. + * In such a case you _may not_ do anything to the + * SKB, it is still owned by the network queueing + * layer when an error is returned. This means you + * may not modify any SKB fields, you may not free + * the SKB, etc. + */ + netif_stop_queue(dev); + + /* save the timestamp */ + priv->netdev->trans_start = jiffies; + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + schedule_work(&priv->tx_work); + + return NETDEV_TX_OK; +} + +static void enc424j600_tx_work_handler(struct work_struct *work) +{ + struct enc424j600_net *priv = + container_of(work, struct enc424j600_net, tx_work); + + /* actual delivery of data */ + enc424j600_hw_tx(priv); +} + +static irqreturn_t enc424j600_irq(int irq, void *dev_id) +{ + struct enc424j600_net *priv = dev_id; + + /* + * Can't do anything in interrupt context because we need to + * block (spi_sync() is blocking) so fire of the interrupt + * handling workqueue. + * Remember that we access enc424j600 registers through SPI bus + * via spi_sync() call. + */ + schedule_work(&priv->irq_work); + + return IRQ_HANDLED; +} + +static void enc424j600_tx_timeout(struct net_device *ndev) +{ + struct enc424j600_net *priv = netdev_priv(ndev); + + if (netif_msg_timer(priv)) + dev_err(&ndev->dev, DRV_NAME " tx timeout\n"); + + ndev->stats.tx_errors++; + /* can't restart safely under softirq */ + schedule_work(&priv->restart_work); +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int enc424j600_net_open(struct net_device *dev) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + if (!is_valid_ether_addr(dev->dev_addr)) { + if (netif_msg_ifup(priv)) + dev_err(&dev->dev, "invalid MAC address %pM\n", + dev->dev_addr); + return -EADDRNOTAVAIL; + } + /* Reset the hardware here (and take it out of low power mode) */ + enc424j600_lowpower(priv, false); + enc424j600_hw_disable(priv); + if (!enc424j600_hw_init(priv)) { + if (netif_msg_ifup(priv)) + dev_err(&dev->dev, "hw_reset() failed\n"); + return -EINVAL; + } + /* Update the MAC address (in case user has changed it) */ + enc424j600_set_hw_macaddr(dev); + /* Enable interrupts */ + enc424j600_hw_enable(priv); + /* check link status */ + enc424j600_check_link_status(priv); + /* We are now ready to accept transmit requests from + * the queueing layer of the networking. + */ + netif_start_queue(dev); + + return 0; +} + +/* The inverse routine to net_open(). */ +static int enc424j600_net_close(struct net_device *dev) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__); + + enc424j600_hw_disable(priv); + enc424j600_lowpower(priv, true); + netif_stop_queue(dev); + + return 0; +} + +/* + * Set or clear the multicast filter for this adapter + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, filter out multicast packets + * num_addrs > 0 Multicast mode, receive normal and MC packets + */ +static void enc424j600_set_multicast_list(struct net_device *dev) +{ + struct enc424j600_net *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + if (netif_msg_link(priv)) + dev_info(&dev->dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count) { + if (netif_msg_link(priv)) + dev_info(&dev->dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + if (netif_msg_link(priv)) + dev_info(&dev->dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + schedule_work(&priv->setrx_work); +} + +void locked_regb_write(struct enc424j600_net *priv, u8 address, u8 data) +{ + mutex_lock(&priv->lock); + enc424j600_write_8b_sfr(priv, address, data); + mutex_unlock(&priv->lock); +} + +static void enc424j600_setrx_work_handler(struct work_struct *work) +{ + u16 macon1; + struct enc424j600_net *priv = + container_of(work, struct enc424j600_net, setrx_work); + + if (priv->rxfilter == RXFILTER_PROMISC) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": promiscuous mode\n"); + enc424j600_read_16b_sfr(priv, MACON1L, &macon1); + macon1 = macon1 | PASSALL; + enc424j600_write_16b_sfr(priv, MACON1L, macon1); + locked_regb_write(priv, ERXFCONL, UCEN | MCEN | NOTMEEN); + } else if (priv->rxfilter == RXFILTER_MULTI) { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": multicast mode\n"); + locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN | MCEN); + } else { + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": normal mode\n"); + locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN); + } +} + +static void enc424j600_restart_work_handler(struct work_struct *work) +{ + struct enc424j600_net *priv = + container_of(work, struct enc424j600_net, restart_work); + struct net_device *ndev = priv->netdev; + int ret; + + rtnl_lock(); + if (netif_running(ndev)) { + enc424j600_net_close(ndev); + ret = enc424j600_net_open(ndev); + if (unlikely(ret)) { + dev_info(&ndev->dev, " could not restart %d\n", ret); + dev_close(ndev); + } + } + rtnl_unlock(); +} + +/* ......................... ETHTOOL SUPPORT ........................... */ +static int enc424j600_get_regs_len(struct net_device *dev) +{ + return SFR_REG_COUNT; +} + +static void +enc424j600_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, + dev_name(dev->dev.parent), sizeof(info->bus_info)); +} + +static int +enc424j600_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + cmd->transceiver = XCVR_INTERNAL; + cmd->supported = SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_TP; + + cmd->speed = priv->speed100 ? SPEED_100 : SPEED_10; + cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_TP; + cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int +enc424j600_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + return enc424j600_setlink(dev, cmd->autoneg, cmd->speed, cmd->duplex); +} + +static u32 enc424j600_get_msglevel(struct net_device *dev) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + return priv->msg_enable; +} + +static void enc424j600_set_msglevel(struct net_device *dev, u32 val) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + priv->msg_enable = val; +} + +static void enc424j600_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *p) +{ + struct enc424j600_net *priv = netdev_priv(dev); + u8 *buff = p; + u8 addroffset; + + regs->version = 1; + mutex_lock(&priv->lock); + for (addroffset = 0; addroffset < SFR_REG_COUNT; addroffset++) { + switch (addroffset) { + /*skip reading reserved regs */ + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x81: + case 0x83: + case 0x85: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + buff[addroffset] = 0; + break; + default: + buff[addroffset] = nolock_regb_read(priv, addroffset); + break; + } + } + mutex_unlock(&priv->lock); +} + +static const struct ethtool_ops enc424j600_ethtool_ops = { + .get_settings = enc424j600_get_settings, + .set_settings = enc424j600_set_settings, + .get_drvinfo = enc424j600_get_drvinfo, + .get_msglevel = enc424j600_get_msglevel, + .set_msglevel = enc424j600_set_msglevel, + .get_regs_len = enc424j600_get_regs_len, + .get_regs = enc424j600_get_regs, +}; + +static int enc424j600_chipset_init(struct net_device *dev) +{ + struct enc424j600_net *priv = netdev_priv(dev); + + enc424j600_get_hw_macaddr(dev); + return enc424j600_hw_init(priv); +} + +static const struct net_device_ops enc424j600_netdev_ops = { + .ndo_open = enc424j600_net_open, + .ndo_stop = enc424j600_net_close, + .ndo_start_xmit = enc424j600_send_packet, + .ndo_set_multicast_list = enc424j600_set_multicast_list, + .ndo_set_mac_address = enc424j600_set_mac_address, + .ndo_tx_timeout = enc424j600_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __devinit enc424j600_probe(struct spi_device *spi) +{ + struct net_device *dev; + struct enc424j600_net *priv; + int ret = 0; + + if (netif_msg_drv(&debug)) + dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n", + DRV_VERSION); + + dev = alloc_etherdev(sizeof(struct enc424j600_net)); + if (!dev) { + if (netif_msg_drv(&debug)) + dev_err(&spi->dev, DRV_NAME + ": unable to alloc new ethernet\n"); + ret = -ENOMEM; + goto error_alloc; + } + priv = netdev_priv(dev); + + priv->netdev = dev; + + priv->spi = spi; + priv->msg_enable = netif_msg_init(debug.msg_enable, + ENC424J600_MSG_DEFAULT); + mutex_init(&priv->lock); + INIT_WORK(&priv->tx_work, enc424j600_tx_work_handler); + INIT_WORK(&priv->setrx_work, enc424j600_setrx_work_handler); + INIT_WORK(&priv->irq_work, enc424j600_irq_work_handler); + INIT_WORK(&priv->restart_work, enc424j600_restart_work_handler); + dev_set_drvdata(&spi->dev, priv); + SET_NETDEV_DEV(dev, &spi->dev); + /*TODO: chip DMA features to be utilized */ + /* If requested, allocate DMA buffers */ + if (enc424j600_enable_dma) { + spi->dev.coherent_dma_mask = ~0; + + /* + * Minimum coherent DMA allocation is PAGE_SIZE, so allocate + * that much and share it between Tx and Rx DMA buffers. + */ +#if SPI_TRANSFER_BUF_LEN > PAGE_SIZE / 2 +#error "A problem in DMA buffer allocation" +#endif + priv->spi_tx_buf = dma_alloc_coherent(&spi->dev, + PAGE_SIZE, + &priv->spi_tx_dma, + GFP_DMA); + + if (priv->spi_tx_buf) { + priv->spi_rx_buf = (u8 *) (priv->spi_tx_buf + + (PAGE_SIZE / 2)); + priv->spi_rx_dma = (dma_addr_t) (priv->spi_tx_dma + + (PAGE_SIZE / 2)); + } else { + /* Fall back to non-DMA */ + enc424j600_enable_dma = 0; + } + } + + /* Allocate non-DMA buffers */ + if (!enc424j600_enable_dma) { + priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_tx_buf; + } + priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL); + if (!priv->spi_rx_buf) { + ret = -ENOMEM; + goto error_rx_buf; + } + } + + if (!enc424j600_chipset_init(dev)) { + if (netif_msg_probe(priv)) + dev_info(&spi->dev, DRV_NAME " chip not found\n"); + ret = -EIO; + goto error_irq; + } + + /* Board setup must set the relevant edge trigger type; + * level triggers won't currently work. + */ + ret = request_irq(spi->irq, enc424j600_irq, 0, DRV_NAME, priv); + if (ret < 0) { + if (netif_msg_probe(priv)) + dev_err(&spi->dev, DRV_NAME ": request irq %d failed " + "(ret = %d)\n", spi->irq, ret); + goto error_irq; + } + + dev->if_port = IF_PORT_10BASET; + dev->irq = spi->irq; + dev->netdev_ops = &enc424j600_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + SET_ETHTOOL_OPS(dev, &enc424j600_ethtool_ops); + + enc424j600_lowpower(priv, true); + + ret = register_netdev(dev); + if (ret) { + if (netif_msg_probe(priv)) + dev_err(&spi->dev, "register netdev " DRV_NAME + " failed (ret = %d)\n", ret); + goto error_register; + } + dev_info(&dev->dev, DRV_NAME " driver registered\n"); + + return ret; + +error_register: + free_irq(spi->irq, priv); +error_irq: + free_netdev(dev); + if (!enc424j600_enable_dma) + kfree(priv->spi_rx_buf); +error_rx_buf: + if (!enc424j600_enable_dma) + kfree(priv->spi_tx_buf); +error_tx_buf: + if (enc424j600_enable_dma) { + dma_free_coherent(&spi->dev, PAGE_SIZE, + priv->spi_tx_buf, priv->spi_tx_dma); + } +error_alloc: + return ret; +} + +static int __devexit enc424j600_remove(struct spi_device *spi) +{ + struct enc424j600_net *priv = dev_get_drvdata(&spi->dev); + + if (netif_msg_drv(priv)) + printk(KERN_DEBUG DRV_NAME ": remove\n"); + + unregister_netdev(priv->netdev); + free_irq(spi->irq, priv); + free_netdev(priv->netdev); + + return 0; +} + +static struct spi_driver enc424j600_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = enc424j600_probe, + .remove = __devexit_p(enc424j600_remove), +}; + +static int __init enc424j600_init(void) +{ + msec20_to_jiffies = msecs_to_jiffies(20); + /*autoneg works from 1600ms */ + msec2000_to_jiffies = msecs_to_jiffies(2000); + + return spi_register_driver(&enc424j600_driver); +} + +module_init(enc424j600_init); + +static void __exit enc424j600_exit(void) +{ + spi_unregister_driver(&enc424j600_driver); +} + +module_exit(enc424j600_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Balaji Venkatachalam <balaji.v@...takaa.com>"); +MODULE_LICENSE("GPL"); +module_param_named(debug, debug.msg_enable, int, 0); +MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)"); +module_param(enc424j600_enable_dma, int, S_IRUGO); +MODULE_PARM_DESC(enc424j600_enable_dma, "Enable SPI DMA. Default: 0 (Off)"); +MODULE_ALIAS("spi:" DRV_NAME); + diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600_hw.h b/drivers/net/enc424j600_hw.h --- a/drivers/net/enc424j600_hw.h 1970-01-01 05:30:00.000000000 +0530 +++ b/drivers/net/enc424j600_hw.h 2011-02-08 18:55:19.000000000 +0530 @@ -0,0 +1,460 @@ +/* +* enc424j600_hw.h: Register definitions +* +*/ + +#ifndef _ENC424J600_HW_H +#define _ENC424J600_HW_H +#define SFR_REG_COUNT 0xA0 +/* +* ENC424J600 Control Registers +* Control register definitions are a combination of address +* and bank number +* - Register address (bits 0-4) +* - Bank number (bits 5-6) +*/ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define BANK_SHIFT 5 + +/* All-bank registers */ +#define EUDASTL 0x16 +#define EUDASTH 0x17 +#define EUDANDL 0x18 +#define EUDANDH 0x19 +#define ESTATL 0x1A +#define ESTATH 0x1B +#define EIRL 0x1C +#define EIRH 0x1D +#define ECON1L 0x1E +#define ECON1H 0x1F + +/* Bank 0 registers */ +#define ETXSTL (0x00 | 0x00) +#define ETXSTH (0x01 | 0x00) +#define ETXLENL (0x02 | 0x00) +#define ETXLENH (0x03 | 0x00) +#define ERXSTL (0x04 | 0x00) +#define ERXSTH (0x05 | 0x00) +#define ERXTAILL (0x06 | 0x00) +#define ERXTAILH (0x07 | 0x00) +#define ERXHEADL (0x08 | 0x00) +#define ERXHEADH (0x09 | 0x00) +#define EDMASTL (0x0A | 0x00) +#define EDMASTH (0x0B | 0x00) +#define EDMALENL (0x0C | 0x00) +#define EDMALENH (0x0D | 0x00) +#define EDMADSTL (0x0E | 0x00) +#define EDMADSTH (0x0F | 0x00) +#define EDMACSL (0x10 | 0x00) +#define EDMACSH (0x11 | 0x00) +#define ETXSTATL (0x12 | 0x00) +#define ETXSTATH (0x13 | 0x00) +#define ETXWIREL (0x14 | 0x00) +#define ETXWIREH (0x15 | 0x00) + +/* Bank 1 registers */ +#define EHT1L (0x00 | 0x20) +#define EHT1H (0x01 | 0x20) +#define EHT2L (0x02 | 0x20) +#define EHT2H (0x03 | 0x20) +#define EHT3L (0x04 | 0x20) +#define EHT3H (0x05 | 0x20) +#define EHT4L (0x06 | 0x20) +#define EHT4H (0x07 | 0x20) +#define EPMM1L (0x08 | 0x20) +#define EPMM1H (0x09 | 0x20) +#define EPMM2L (0x0A | 0x20) +#define EPMM2H (0x0B | 0x20) +#define EPMM3L (0x0C | 0x20) +#define EPMM3H (0x0D | 0x20) +#define EPMM4L (0x0E | 0x20) +#define EPMM4H (0x0F | 0x20) +#define EPMCSL (0x10 | 0x20) +#define EPMCSH (0x11 | 0x20) +#define EPMOL (0x12 | 0x20) +#define EPMOH (0x13 | 0x20) +#define ERXFCONL (0x14 | 0x20) +#define ERXFCONH (0x15 | 0x20) + +/* Bank 2 registers */ +#define MACON1L (0x00 | 0x40) +#define MACON1H (0x01 | 0x40) +#define MACON2L (0x02 | 0x40) +#define MACON2H (0x03 | 0x40) +#define MABBIPGL (0x04 | 0x40) +#define MABBIPGH (0x05 | 0x40) +#define MAIPGL (0x06 | 0x40) +#define MAIPGH (0x07 | 0x40) +#define MACLCONL (0x08 | 0x40) +#define MACLCONH (0x09 | 0x40) +#define MAMXFLL (0x0A | 0x40) +#define MAMXFLH (0x0B | 0x40) +#define MICMDL (0x12 | 0x40) +#define MICMDH (0x13 | 0x40) +#define MIREGADRL (0x14 | 0x40) +#define MIREGADRH (0x15 | 0x40) + +/* Bank 3 registers */ +#define MAADR3L (0x00 | 0x60) +#define MAADR3H (0x01 | 0x60) +#define MAADR2L (0x02 | 0x60) +#define MAADR2H (0x03 | 0x60) +#define MAADR1L (0x04 | 0x60) +#define MAADR1H (0x05 | 0x60) +#define MIWRL (0x06 | 0x60) +#define MIWRH (0x07 | 0x60) +#define MIRDL (0x08 | 0x60) +#define MIRDH (0x09 | 0x60) +#define MISTATL (0x0A | 0x60) +#define MISTATH (0x0B | 0x60) +#define EPAUSL (0x0C | 0x60) +#define EPAUSH (0x0D | 0x60) +#define ECON2L (0x0E | 0x60) +#define ECON2H (0x0F | 0x60) +#define ERXWML (0x10 | 0x60) +#define ERXWMH (0x11 | 0x60) +#define EIEL (0x12 | 0x60) +#define EIEH (0x13 | 0x60) +#define EIDLEDL (0x14 | 0x60) +#define EIDLEDH (0x15 | 0x60) + +/* Unbanked registers */ +#define EGPDATA (0x00 | 0x80) +#define ERXDATA (0x02 | 0x80) +#define EUDADATA (0x04 | 0x80) +#define EGPRDPTL (0x06 | 0x80) +#define EGPRDPTH (0x07 | 0x80) +#define EGPWRPTL (0x08 | 0x80) +#define EGPWRPTH (0x09 | 0x80) +#define ERXRDPTL (0x0A | 0x80) +#define ERXRDPTH (0x0B | 0x80) +#define ERXWRPTL (0x0C | 0x80) +#define ERXWRPTH (0x0D | 0x80) +#define EUDARDPTL (0x0E | 0x80) +#define EUDARDPTH (0x0F | 0x80) +#define EUDAWRPTL (0x10 | 0x80) +#define EUDAWRPTH (0x11 | 0x80) + +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHANA 0x04 +#define PHANLPA 0x05 +#define PHANE 0x06 +#define PHCON2 0x11 +#define PHSTAT2 0x1B +#define PHSTAT3 0x1F + +/* Single-byte instructions */ +#define BXSEL(bank) (0xC0 + (bank & (BANK_MASK >> BANK_SHIFT)) * 2) +/* Bank X Select */ +#define B0SEL 0xC0 /* Bank 0 Select */ +#define B1SEL 0xC2 /* Bank 1 Select */ +#define B2SEL 0xC4 /* Bank 2 Select */ +#define B3SEL 0xC6 /* Bank 3 Select */ +#define SETETHRST 0xCA /* System Reset */ +#define FCDISABLE 0xE0 /* Flow Control Disable */ +#define FCSINGLE 0xE2 /* Flow Control Single */ +#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ +#define FCCLEAR 0xE6 /* Flow Control Clear */ +#define SETPKTDEC 0xCC /* Decrement Packet Counter */ +#define DMASTOP 0xD2 /* DMA Stop */ +#define DMACKSUM 0xD8 /* DMA Start Checksum */ +#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ +#define DMACOPY 0xDC /* DMA Start Copy */ +#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ +#define SETTXRTS 0xD4 /* Request Packet Transmission */ +#define ENABLERX 0xE8 /* Enable RX */ +#define DISABLERX 0xEA /* Disable RX */ +#define SETEIE 0xEC /* Enable Interrupts */ +#define CLREIE 0xEE /* Disable Interrupts */ + +/* Two byte instructions */ +#define RBSEL 0xC8 /* Read Bank Select */ + +/* Three byte instructions */ +#define WGPRDPT 0x60 /* Write EGPRDPT */ +#define RGPRDPT 0x62 /* Read EGPRDPT */ +#define WRXRDPT 0x64 /* Write ERXRDPT */ +#define RRXRDPT 0x66 /* Read ERXRDPT */ +#define WUDARDPT 0x68 /* Write EUDARDPT */ +#define RUDARDPT 0x6A /* Read EUDARDPT */ +#define WGPWRPT 0x6C /* Write EGPWRPT */ +#define RGPWRPT 0x6E /* Read EGPWRPT */ +#define WRXWRPT 0x70 /* Write ERXWRPT */ +#define RRXWRPT 0x72 /* Read ERXWRPT */ +#define WUDAWRPT 0x74 /* Write EUDAWRPT */ +#define RUDAWRPT 0x76 /* Read EUDAWRPT */ + +/* n byte instructions */ +#define RCR(addr) (0x00 | (addr & ADDR_MASK)) /* Read Control Register */ +#define WCR(addr) (0x40 | (addr & ADDR_MASK)) /* Write Control Register */ +#define RCRU 0x20 /* Read Control Register Unbanked */ +#define WCRU 0x22 /* Write Control Register Unbanked */ +#define BFS(addr) (0x80 | (addr & ADDR_MASK)) /* Bit Field Set */ +#define BFC(addr) (0xA0 | (addr & ADDR_MASK)) /* Bit Field Clear */ +#define BFSU 0x24 /* Bit Field Set Unbanked */ +#define BFCU 0x26 /* Bit Field Clear Unbanked */ +#define RGPDATA 0x28 /* Read EGPDATA */ +#define WGPDATA 0x2A /* Write EGPDATA */ +#define RRXDATA 0x2C /* Read ERXDATA */ +#define WRXDATA 0x2E /* Write ERXDATA */ +#define RUDADATA 0x30 /* Read EUDADATA */ +#define WUDADATA 0x32 /* Write EUDADATA */ + +/* Register bit definitions */ +/* ESTATH */ +#define INT (1 << 7) +#define FCIDLE (1 << 6) +#define RXBUSY (1 << 5) +#define CLKRDY (1 << 4) +#define PHYDPX (1 << 2) +#define PHYLNK (1 << 0) + +/* EIRH */ +/*for ease of use lets access it as a word*/ +#define CRYPTEN (1 << 15) +#define MODEXIF (1 << 14) +#define HASHIF (1 << 13) +#define AESIF (1 << 12) +#define LINKIF (1 << 11) + +/* EIRL */ +#define PKTIF (1 << 6) +#define DMAIF (1 << 5) +#define TXIF (1 << 3) +#define TXABTIF (1 << 2) +#define RXABTIF (1 << 1) +#define PCFULIF (1 << 0) + +/* ECON1H */ +#define MODEXST (1 << 7) +#define HASHEN (1 << 6) +#define HASHOP (1 << 5) +#define HASHLST (1 << 4) +#define AESST (1 << 3) +#define AESOP1 (1 << 2) +#define AESOP0 (1 << 1) +#define PKTDEC (1 << 0) + +/* ECON1L */ +#define FCOP1 (1 << 7) +#define FCOP0 (1 << 6) +#define DMAST (1 << 5) +#define DMACPY (1 << 4) +#define DMACSSD (1 << 3) +#define DMANOCS (1 << 2) +#define TXRTS (1 << 1) +#define RXEN (1 << 0) + +/* ETXSTATH */ +#define LATECOL (1 << 2) +#define MAXCOL (1 << 1) +#define EXDEFER (1 << 0) + +/* ETXSTATL */ +#define ETXSTATL_DEFER (1 << 7) +#define CRCBAD (1 << 4) +#define COLCNT_MASK 0xF + +/* ERXFCONH */ +#define HTEN (1 << 7) +#define MPEN (1 << 6) +#define NOTPM (1 << 4) +#define PMEN3 (1 << 3) +#define PMEN2 (1 << 2) +#define PMEN1 (1 << 1) +#define PMEN0 (1 << 0) + +/* ERXFCONL */ +#define CRCEEN (1 << 7) +#define CRCEN (1 << 6) +#define RUNTEEN (1 << 5) +#define RUNTEN (1 << 4) +#define UCEN (1 << 3) +#define NOTMEEN (1 << 2) +#define MCEN (1 << 1) +#define BCEN (1 << 0) +/*no bytewise access*/ +/* MACON1L */ +#define LOOPBK (1 << 4) +#define RXPAUS (1 << 2) +#define PASSALL (1 << 1) + +/* MACON2 */ +#define MACON2_DEFER (1 << 14) +#define BPEN (1 << 13) +#define NOBKOFF (1 << 12) +#define PADCFG2 (1 << 7) +#define PADCFG1 (1 << 6) +#define PADCFG0 (1 << 5) +#define TXCRCEN (1 << 4) +#define PHDREN (1 << 3) +#define HFRMEN (1 << 2) +#define FULDPX (1 << 0) + +/* MAIPG */ +/* value of the high byte is given by the reserved bits, +* value of the low byte is recomended setting of the +* IPG parameter. +*/ +#define MAIPGH_VAL 0x0C +#define MAIPGL_VAL 0x12 + +/* MIREGADRH */ +#define MIREGADRH_VAL 0x01 + +/* MIREGADRL */ +#define PHREG_MASK 0x1F + +/* MICMDL */ +#define MIISCAN (1 << 1) +#define MIIRD (1 << 0) + +/* MISTATL */ +#define NVALID (1 << 2) +#define SCAN (1 << 1) +#define BUSY (1 << 0) + +/* ECON2H */ +#define ETHEN (1 << 7) +#define STRCH (1 << 6) +#define TXMAC (1 << 5) +#define SHA1MD5 (1 << 4) +#define COCON3 (1 << 3) +#define COCON2 (1 << 2) +#define COCON1 (1 << 1) +#define COCON0 (1 << 0) + +/* ECON2L */ +#define AUTOFC (1 << 7) +#define TXRST (1 << 6) +#define RXRST (1 << 5) +#define ETHRST (1 << 4) +#define MODLEN1 (1 << 3) +#define MODLEN0 (1 << 2) +#define AESLEN1 (1 << 1) +#define AESLEN0 (1 << 0) + +/* EIEH */ +#define INTIE (1 << 7) +#define MODEXIE (1 << 6) +#define HASHIE (1 << 5) +#define AESIE (1 << 4) +#define LINKIE (1 << 3) + +/* EIEL */ +#define PKTIE (1 << 6) +#define DMAIE (1 << 5) +#define TXIE (1 << 3) +#define TXABTIE (1 << 2) +#define RXABTIE (1 << 1) +#define PCFULIE (1 << 0) + +/* EIDLEDH */ +#define LACFG3 (1 << 7) +#define LACFG2 (1 << 6) +#define LACFG1 (1 << 5) +#define LACFG0 (1 << 4) +#define LBCFG3 (1 << 3) +#define LBCFG2 (1 << 2) +#define LBCFG1 (1 << 1) +#define LBCFG0 (1 << 0) + +/* EIDLEDL */ +#define DEVID_SHIFT 5 +#define DEVID_MASK (0x7 << DEVID_SHIFT) +#define REVID_SHIFT 0 +#define REVID_MASK (0x1F << REVID_SHIFT) + +/* PHANA */ +/* Default value for PHY initialization*/ +#define PHANA_DEFAULT 0x05E1 + +/* PHCON1 */ +#define PRST (1 << 15) +#define PLOOPBK (1 << 14) +#define SPD100 (1 << 13) +#define ANEN (1 << 12) +#define PSLEEP (1 << 11) +#define RENEG (1 << 9) +#define PFULDPX (1 << 8) + +/* PHSTAT */ +#define FULL100 (1 << 14) +#define HALF100 (1 << 13) +#define FULL10 (1 << 12) +#define HALF10 (1 << 11) +#define ANDONE (1 << 5) +#define LRFAULT (1 << 4) +#define ANABLE (1 << 3) +#define LLSTAT (1 << 2) +#define EXTREGS (1 << 0) + +#define EUDAST_TEST_VAL 0x1234 + +#define TSV_SIZE 7 + +#define ENC424J600_DEV_ID 0x1 + +/* Configuration */ + +/* Led is on when the link is present and driven low +* temporarily when packet is TX'd or RX'd */ +#define LED_A_SETTINGS 0xC + +/* Led is on if the link is in 100 Mbps mode */ +#define LED_B_SETTINGS 0x8 + +/* maximum ethernet frame length +* Currently not used as a limit anywhere +* (we're using the "huge frame enable" feature of +* enc424j600). */ +#define MAX_FRAMELEN 1518 + +/* Size in bytes of the receive buffer in enc424j600. +* Must be word aligned (even). +*/ +#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) + +/* Start of the general purpose area in sram */ +#define SRAM_GP_START 0x0 + +/* SRAM size */ +#define SRAM_SIZE 0x6000 + +/* Start of the receive buffer */ +#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) + +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_RUNTFILTERMATCH 31 +#define RSV_NOTMEFILTERMATCH 32 +#define RSV_HASHFILTERMATCH 33 +#define RSV_MAGICPKTFILTERMATCH 34 +#define RSV_PTRNMTCHFILTERMATCH 35 +#define RSV_UNICASTFILTERMATCH 36 + +#define RSV_SIZE 8 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ + +#define RXSTART_INIT ERXST_VAL +#define RXEND_INIT 0x5FFF + +#endif diff -uprN -X a/Documentation/dontdiff a/drivers/net/Kconfig b/drivers/net/Kconfig --- a/drivers/net/Kconfig 2010-07-05 22:41:43.000000000 +0530 +++ b/drivers/net/Kconfig 2011-01-16 15:26:16.000000000 +0530 @@ -973,6 +973,16 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. +config ENC424J600 + tristate "ENC424J600 support" + depends on EXPERIMENTAL && SPI && NET_ETHERNET + select CRC32 + ---help--- + Support for the Microchip EN424J600 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called enc424j600. + config ETHOC tristate "OpenCores 10/100 Mbps Ethernet MAC support" depends on NET_ETHERNET && HAS_IOMEM diff -uprN -X a/Documentation/dontdiff a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile 2010-07-05 22:41:43.000000000 +0530 +++ b/drivers/net/Makefile 2011-01-05 21:46:57.000000000 +0530 @@ -240,6 +240,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_d pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o obj-$(CONFIG_MLX4_CORE) += mlx4/ obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ENC424J600) += enc424j600.o obj-$(CONFIG_ETHOC) += ethoc.o obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists