diff options
Diffstat (limited to 'target/linux/amazon-2.6/files/drivers/net/amazon_sw.c')
-rw-r--r-- | target/linux/amazon-2.6/files/drivers/net/amazon_sw.c | 876 |
1 files changed, 0 insertions, 876 deletions
diff --git a/target/linux/amazon-2.6/files/drivers/net/amazon_sw.c b/target/linux/amazon-2.6/files/drivers/net/amazon_sw.c deleted file mode 100644 index d19db6e36d..0000000000 --- a/target/linux/amazon-2.6/files/drivers/net/amazon_sw.c +++ /dev/null @@ -1,876 +0,0 @@ -/* - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - */ -//----------------------------------------------------------------------- -/* - * Description: - * Driver for Infineon Amazon 3 port switch - */ -//----------------------------------------------------------------------- -/* Author: Wu Qi Ming[Qi-Ming.Wu@infineon.com] - * Created: 7-April-2004 - */ -//----------------------------------------------------------------------- -/* History - * Changed on: Jun 28, 2004 - * Changed by: peng.liu@infineon.com - * Reason: add hardware flow control (HFC) (CONFIG_NET_HW_FLOWCONTROL) - * - * Changed on: Apr 6, 2005 - * Changed by: mars.lin@infineon.com - * Reason : supoort port identification - */ - - -// copyright 2004-2005 infineon.com - -// copyright 2007 john crispin <blogic@openwrt.org> -// copyright 2007 felix fietkau <nbd@openwrt.org> - - -// TODO -// port vlan code from bcrm target... the tawainese code was scrapped due to crappyness -// check all the mmi reg settings and possibly document them better -// verify the ethtool code -// remove the while(1) stuff -// further clean up and rework ... but it works for now -// check the mode[]=bridge stuff -// verify that the ethaddr can be set from u-boot - - -#ifndef __KERNEL__ -#define __KERNEL__ -#endif - - -#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) -#define MODVERSIONS -#endif - -#if defined(MODVERSIONS) && !defined(__GENKSYMS__) -#include <linux/modversions.h> -#endif - -#include <linux/module.h> -#include <linux/string.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/mii.h> -#include <asm/uaccess.h> -#include <linux/in.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ip.h> -#include <linux/tcp.h> -#include <linux/skbuff.h> -#include <linux/in6.h> -#include <linux/proc_fs.h> -#include <linux/mm.h> -#include <linux/ethtool.h> -#include <asm/checksum.h> -#include <linux/init.h> - -#include <asm/amazon/amazon.h> -#include <asm/amazon/amazon_dma.h> -#include <asm/amazon/amazon_sw.h> - -// how many mii ports are there ? -#define AMAZON_SW_INT_NO 2 - -#define ETHERNET_PACKET_DMA_BUFFER_SIZE 1536 - -/***************************************** Module Parameters *************************************/ -char mode[] = "bridge"; -module_param_array(mode, charp, NULL, 0); - -static int timeout = 1 * HZ; -module_param(timeout, int, 0); - -int switch_init(struct net_device *dev); -void switch_tx_timeout(struct net_device *dev); - -struct net_device switch_devs[2] = { - {init:switch_init,}, - {init:switch_init,} -}; - -int add_mac_table_entry(u64 entry_value) -{ - int i; - u32 data1, data2; - - AMAZON_SW_REG32(AMAZON_SW_ARL_CTL) = ~7; - - for (i = 0; i < 32; i++) { - AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0x80000000 | 0x20 | i; - while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {}; - data1 = AMAZON_SW_REG32(AMAZON_SW_DATA1); - data2 = AMAZON_SW_REG32(AMAZON_SW_DATA2); - if ((data1 & (0x00700000)) != 0x00700000) - continue; - AMAZON_SW_REG32(AMAZON_SW_DATA1) = (u32) (entry_value >> 32); - AMAZON_SW_REG32(AMAZON_SW_DATA2) = (u32) entry_value & 0xffffffff; - AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0xc0000020 | i; - while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {}; - break; - } - AMAZON_SW_REG32(AMAZON_SW_ARL_CTL) |= 7; - if (i >= 32) - return -1; - return OK; -} - -u64 read_mac_table_entry(int index) -{ - u32 data1, data2; - u64 value; - AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0x80000000 | 0x20 | index; - while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {}; - data1 = AMAZON_SW_REG32(AMAZON_SW_DATA1) & 0xffffff; - data2 = AMAZON_SW_REG32(AMAZON_SW_DATA2); - value = (u64) data1 << 32 | (u64) data2; - return value; -} - -int write_mac_table_entry(int index, u64 value) -{ - u32 data1, data2; - data1 = (u32) (value >> 32); - data2 = (u32) value & 0xffffffff; - AMAZON_SW_REG32(AMAZON_SW_DATA1) = data1; - AMAZON_SW_REG32(AMAZON_SW_DATA2) = data2; - AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) = 0xc0000020 | index; - while (AMAZON_SW_REG32(AMAZON_SW_CPU_ACTL) & (0x80000000)) {}; - return OK; -} - -u32 get_mdio_reg(int phy_addr, int reg_num) -{ - u32 value; - AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) = (3 << 30) | ((phy_addr & 0x1f) << 21) | ((reg_num & 0x1f) << 16); - while (AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) & (1 << 31)) {}; - value = AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) & 0xffff; - return value; -} - -int set_mdio_reg(int phy_addr, int reg_num, u32 value) -{ - AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) = (2 << 30) | ((phy_addr & 0x1f) << 21) | ((reg_num & 0x1f) << 16) | (value & 0xffff); - while (AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) & (1 << 31)) {}; - return OK; -} - -int auto_negotiate(int phy_addr) -{ - u32 value = 0; - value = get_mdio_reg(phy_addr, MDIO_BASE_CONTROL_REG); - set_mdio_reg(phy_addr, MDIO_BASE_CONTROL_REG, (value | RESTART_AUTO_NEGOTIATION | AUTO_NEGOTIATION_ENABLE | PHY_RESET)); - return OK; -} - -/* - In this version of switch driver, we split the dma channels for the switch. - 2 for port0 and 2 for port1. So that we can do internal bridging if necessary. - In switch mode, packets coming in from port0 or port1 is able to do Destination - address lookup. Packets coming from port0 with destination address of port1 should - not go to pmac again. The switch hardware should be able to do the switch in the hard - ware level. Packets coming from the pmac should not do the DA look up in that the - desination is already known for the kernel. It only needs to go to the correct NIC to - find its way out. - */ -int amazon_sw_chip_init(void) -{ - u32 tmp1; - int i = 0; - - /* Aging tick select: 5mins */ - tmp1 = 0xa0; - if (strcmp(mode, "bridge") == 0) { - // bridge mode, set militarised mode to 1, no learning! - tmp1 |= 0xC00; - } else { - // enable learning for P0 and P1, - tmp1 |= 3; - } - - /* unknown broadcast/multicast/unicast to all ports */ - AMAZON_SW_REG32(AMAZON_SW_UN_DEST) = 0x1ff; - - AMAZON_SW_REG32(AMAZON_SW_ARL_CTL) = tmp1; - - /* OCS:1 set OCS bit, split the two NIC in rx direction EDL:1 (enable DA lookup) */ -#if defined(CONFIG_IFX_NFEXT_AMAZON_SWITCH_PHYPORT) || defined(CONFIG_IFX_NFEXT_AMAZON_SWITCH_PHYPORT_MODULE) - AMAZON_SW_REG32(AMAZON_SW_P2_PCTL) = 0x700; -#else - AMAZON_SW_REG32(AMAZON_SW_P2_PCTL) = 0x401; -#endif - - /* EPC: 1 split the two NIC in tx direction CRC is generated */ - AMAZON_SW_REG32(AMAZON_SW_P2_CTL) = 0x6; - - // for bi-directional - AMAZON_SW_REG32(AMAZON_SW_P0_WM) = 0x14141412; - AMAZON_SW_REG32(AMAZON_SW_P1_WM) = 0x14141412; - AMAZON_SW_REG32(AMAZON_SW_P2_WM) = 0x28282826; - AMAZON_SW_REG32(AMAZON_SW_GBL_WM) = 0x0; - - AMAZON_SW_REG32(AMAZON_CGU_PLL0SR) = (AMAZON_SW_REG32(AMAZON_CGU_PLL0SR)) | 0x58000000; - // clock for PHY - AMAZON_SW_REG32(AMAZON_CGU_IFCCR) = (AMAZON_SW_REG32(AMAZON_CGU_IFCCR)) | 0x80000004; - // enable power for PHY - AMAZON_SW_REG32(AMAZON_PMU_PWDCR) = (AMAZON_SW_REG32(AMAZON_PMU_PWDCR)) | AMAZON_PMU_PWDCR_EPHY; - // set reverse MII, enable MDIO statemachine - AMAZON_SW_REG32(AMAZON_SW_MDIO_CFG) = 0x800027bf; - while (1) - if (((AMAZON_SW_REG32(AMAZON_SW_MDIO_CFG)) & 0x80000000) == 0) - break; - AMAZON_SW_REG32(AMAZON_SW_EPHY) = 0xff; - - // auto negotiation - AMAZON_SW_REG32(AMAZON_SW_MDIO_ACC) = 0x83e08000; - auto_negotiate(0x1f); - - /* enable all ports */ - AMAZON_SW_REG32(AMAZON_SW_PS_CTL) = 0x7; - for (i = 0; i < 32; i++) - write_mac_table_entry(i, 1 << 50); - return 0; -} - -static unsigned char my_ethaddr[MAX_ADDR_LEN]; -/* need to get the ether addr from u-boot */ -static int __init ethaddr_setup(char *line) -{ - char *ep; - int i; - - memset(my_ethaddr, 0, MAX_ADDR_LEN); - for (i = 0; i < 6; i++) { - my_ethaddr[i] = line ? simple_strtoul(line, &ep, 16) : 0; - if (line) - line = (*ep) ? ep + 1 : ep; - } - printk("mac address %2x-%2x-%2x-%2x-%2x-%2x \n", my_ethaddr[0], my_ethaddr[1], my_ethaddr[2], my_ethaddr[3], my_ethaddr[4], my_ethaddr[5]); - return 0; -} - -__setup("ethaddr=", ethaddr_setup); - -static void open_rx_dma(struct net_device *dev) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - struct dma_device_info *dma_dev = priv->dma_device; - int i; - - for (i = 0; i < dma_dev->num_rx_chan; i++) - dma_dev->rx_chan[i].control = 1; - dma_device_update_rx(dma_dev); -} - -#ifdef CONFIG_NET_HW_FLOWCONTROL -static void close_rx_dma(struct net_device *dev) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - struct dma_device_info *dma_dev = priv->dma_device; - int i; - - for (i = 0; i < dma_dev->num_rx_chan; i++) - dma_dev->rx_chan[i].control = 0; - dma_device_update_rx(dma_dev); -} - -void amazon_xon(struct net_device *dev) -{ - unsigned long flag; - local_irq_save(flag); - open_rx_dma(dev); - local_irq_restore(flag); -} -#endif - -int switch_open(struct net_device *dev) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - if (!strcmp(dev->name, "eth1")) { - priv->mdio_phy_addr = PHY0_ADDR; - } - open_rx_dma(dev); - -#ifdef CONFIG_NET_HW_FLOWCONTROL - if ((priv->fc_bit = netdev_register_fc(dev, amazon_xon)) == 0) { - printk("Hardware Flow Control register fails\n"); - } -#endif - - netif_start_queue(dev); - return OK; -} - -int switch_release(struct net_device *dev) -{ - int i; - struct switch_priv *priv = (struct switch_priv *) dev->priv; - struct dma_device_info *dma_dev = priv->dma_device; - - for (i = 0; i < dma_dev->num_tx_chan; i++) - dma_dev->tx_chan[i].control = 0; - for (i = 0; i < dma_dev->num_rx_chan; i++) - dma_dev->rx_chan[i].control = 0; - - dma_device_update(dma_dev); - -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (priv->fc_bit) { - netdev_unregister_fc(priv->fc_bit); - } -#endif - netif_stop_queue(dev); - - return OK; -} - - -void switch_rx(struct net_device *dev, int len, struct sk_buff *skb) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; -#ifdef CONFIG_NET_HW_FLOWCONTROL - int mit_sel = 0; -#endif - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - -#ifdef CONFIG_NET_HW_FLOWCONTROL - mit_sel = netif_rx(skb); - switch (mit_sel) { - case NET_RX_SUCCESS: - case NET_RX_CN_LOW: - case NET_RX_CN_MOD: - break; - case NET_RX_CN_HIGH: - break; - case NET_RX_DROP: - if ((priv->fc_bit) - && (!test_and_set_bit(priv->fc_bit, &netdev_fc_xoff))) { - close_rx_dma(dev); - } - break; - } -#else - netif_rx(skb); -#endif - priv->stats.rx_packets++; - priv->stats.rx_bytes += len; - return; -} - -int asmlinkage switch_hw_tx(char *buf, int len, struct net_device *dev) -{ - struct switch_priv *priv = dev->priv; - struct dma_device_info *dma_dev = priv->dma_device; - - dma_dev->current_tx_chan = 0; - return dma_device_write(dma_dev, buf, len, priv->skb); -} - -int asmlinkage switch_tx(struct sk_buff *skb, struct net_device *dev) -{ - int len; - char *data; - struct switch_priv *priv = (struct switch_priv *) dev->priv; - - len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; - data = skb->data; - priv->skb = skb; - dev->trans_start = jiffies; - - if (switch_hw_tx(data, len, dev) != len) { - dev_kfree_skb_any(skb); - return OK; - } - - priv->stats.tx_packets++; - priv->stats.tx_bytes += len; - return OK; -} - -void switch_tx_timeout(struct net_device *dev) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - priv->stats.tx_errors++; - netif_wake_queue(dev); - return; -} - -void negotiate(struct net_device *dev) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - unsigned short data = get_mdio_reg(priv->mdio_phy_addr, MDIO_ADVERTISMENT_REG); - - data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD); - - switch (priv->current_speed_selection) { - case 10: - if (priv->current_duplex == full) - data |= MDIO_ADVERT_10_FD; - else if (priv->current_duplex == half) - data |= MDIO_ADVERT_10_HD; - else - data |= MDIO_ADVERT_10_HD | MDIO_ADVERT_10_FD; - break; - - case 100: - if (priv->current_duplex == full) - data |= MDIO_ADVERT_100_FD; - else if (priv->current_duplex == half) - data |= MDIO_ADVERT_100_HD; - else - data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD; - break; - - case 0: /* Auto */ - if (priv->current_duplex == full) - data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD; - else if (priv->current_duplex == half) - data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD; - else - data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; - break; - - default: /* assume autoneg speed and duplex */ - data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; - } - - set_mdio_reg(priv->mdio_phy_addr, MDIO_ADVERTISMENT_REG, data); - - /* Renegotiate with link partner */ - - data = get_mdio_reg(priv->mdio_phy_addr, MDIO_BASE_CONTROL_REG); - data |= MDIO_BC_NEGOTIATE; - - set_mdio_reg(priv->mdio_phy_addr, MDIO_BASE_CONTROL_REG, data); - -} - - -void set_duplex(struct net_device *dev, enum duplex new_duplex) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - if (new_duplex != priv->current_duplex) { - priv->current_duplex = new_duplex; - negotiate(dev); - } -} - -void set_speed(struct net_device *dev, unsigned long speed) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - priv->current_speed_selection = speed; - negotiate(dev); -} - -static int switch_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - struct ethtool_cmd ecmd; - - if (copy_from_user(&ecmd, ifr->ifr_data, sizeof(ecmd))) - return -EFAULT; - - switch (ecmd.cmd) { - case ETHTOOL_GSET: - memset((void *) &ecmd, 0, sizeof(ecmd)); - ecmd.supported = SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; - ecmd.port = PORT_TP; - ecmd.transceiver = XCVR_EXTERNAL; - ecmd.phy_address = priv->mdio_phy_addr; - - ecmd.speed = priv->current_speed; - - ecmd.duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - - ecmd.advertising = ADVERTISED_TP; - if (priv->current_duplex == autoneg && priv->current_speed_selection == 0) - ecmd.advertising |= ADVERTISED_Autoneg; - else { - ecmd.advertising |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; - if (priv->current_speed_selection == 10) - ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full); - else if (priv->current_speed_selection == 100) - ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full); - if (priv->current_duplex == half) - ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full); - else if (priv->current_duplex == full) - ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half); - } - ecmd.autoneg = AUTONEG_ENABLE; - if (copy_to_user(ifr->ifr_data, &ecmd, sizeof(ecmd))) - return -EFAULT; - break; - - case ETHTOOL_SSET: - if (!capable(CAP_NET_ADMIN)) { - return -EPERM; - } - if (ecmd.autoneg == AUTONEG_ENABLE) { - set_duplex(dev, autoneg); - set_speed(dev, 0); - } else { - set_duplex(dev, ecmd.duplex == DUPLEX_HALF ? half : full); - set_speed(dev, ecmd.speed == SPEED_10 ? 10 : 100); - } - break; - - case ETHTOOL_GDRVINFO: - { - struct ethtool_drvinfo info; - memset((void *) &info, 0, sizeof(info)); - strncpy(info.driver, "AMAZONE", sizeof(info.driver) - 1); - strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1); - strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1); - info.regdump_len = 0; - info.eedump_len = 0; - info.testinfo_len = 0; - if (copy_to_user(ifr->ifr_data, &info, sizeof(info))) - return -EFAULT; - } - break; - case ETHTOOL_NWAY_RST: - if (priv->current_duplex == autoneg && priv->current_speed_selection == 0) - negotiate(dev); - break; - default: - return -EOPNOTSUPP; - break; - } - return 0; -} - - - -int mac_table_tools_ioctl(struct net_device *dev, struct mac_table_req *req) -{ - int cmd; - int i; - cmd = req->cmd; - switch (cmd) { - case RESET_MAC_TABLE: - for (i = 0; i < 32; i++) { - write_mac_table_entry(i, 0); - } - break; - case READ_MAC_ENTRY: - req->entry_value = read_mac_table_entry(req->index); - break; - case WRITE_MAC_ENTRY: - write_mac_table_entry(req->index, req->entry_value); - break; - case ADD_MAC_ENTRY: - add_mac_table_entry(req->entry_value); - break; - default: - return -EINVAL; - } - - return 0; -} - - -/* - the ioctl for the switch driver is developed in the conventional way - the control type falls into some basic categories, among them, the - SIOCETHTOOL is the traditional eth interface. VLAN_TOOLS and - MAC_TABLE_TOOLS are designed specifically for amazon chip. User - should be aware of the data structures used in these interfaces. -*/ -int switch_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct data_req *switch_data_req = (struct data_req *) ifr->ifr_data; - struct mac_table_req *switch_mac_table_req; - switch (cmd) { - case SIOCETHTOOL: - switch_ethtool_ioctl(dev, ifr); - break; - case SIOCGMIIPHY: /* Get PHY address */ - break; - case SIOCGMIIREG: /* Read MII register */ - break; - case SIOCSMIIREG: /* Write MII register */ - break; - case SET_ETH_SPEED_10: /* 10 Mbps */ - break; - case SET_ETH_SPEED_100: /* 100 Mbps */ - break; - case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */ - break; - case SET_ETH_DUPLEX_HALF: /* Half duplex. */ - break; - case SET_ETH_DUPLEX_FULL: /* Full duplex. */ - break; - case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex */ - break; - case SET_ETH_REG: - AMAZON_SW_REG32(switch_data_req->index) = switch_data_req->value; - break; - case MAC_TABLE_TOOLS: - switch_mac_table_req = (struct mac_table_req *) ifr->ifr_data; - mac_table_tools_ioctl(dev, switch_mac_table_req); - break; - default: - return -EINVAL; - } - - return 0; -} - -struct net_device_stats *switch_stats(struct net_device *dev) -{ - struct switch_priv *priv = (struct switch_priv *) dev->priv; - return &priv->stats; -} - -int switch_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu >= 1516) - new_mtu = 1516; - dev->mtu = new_mtu; - return 0; -} - -int switch_hw_receive(struct net_device *dev, struct dma_device_info *dma_dev) -{ - u8 *buf = NULL; - int len = 0; - struct sk_buff *skb = NULL; - - len = dma_device_read(dma_dev, &buf, (void **) &skb); - - if (len >= 0x600) { - printk("packet too large %d\n", len); - goto switch_hw_receive_err_exit; - } - - /* remove CRC */ - len -= 4; - if (skb == NULL) { - printk("cannot restore pointer\n"); - goto switch_hw_receive_err_exit; - } - if (len > (skb->end - skb->tail)) { - printk("BUG, len:%d end:%p tail:%p\n", (len + 4), skb->end, skb->tail); - goto switch_hw_receive_err_exit; - } - skb_put(skb, len); - skb->dev = dev; - switch_rx(dev, len, skb); - return OK; - - switch_hw_receive_err_exit: - if (skb) - dev_kfree_skb_any(skb); - return -EIO; -} - -int dma_intr_handler(struct dma_device_info *dma_dev, int status) -{ - struct net_device *dev; - - dev = switch_devs + (u32) dma_dev->priv; - switch (status) { - case RCV_INT: - switch_hw_receive(dev, dma_dev); - break; - case TX_BUF_FULL_INT: - netif_stop_queue(dev); - break; - case TRANSMIT_CPT_INT: - netif_wake_queue(dev); - break; - } - return OK; -} - -/* reserve 2 bytes in front of data pointer*/ -u8 *dma_buffer_alloc(int len, int *byte_offset, void **opt) -{ - u8 *buffer = NULL; - struct sk_buff *skb = NULL; - skb = dev_alloc_skb(ETHERNET_PACKET_DMA_BUFFER_SIZE); - if (skb == NULL) { - return NULL; - } - buffer = (u8 *) (skb->data); - skb_reserve(skb, 2); - *(int *) opt = (int) skb; - *byte_offset = 2; - return buffer; -} - -int dma_buffer_free(u8 * dataptr, void *opt) -{ - struct sk_buff *skb = NULL; - if (opt == NULL) { - kfree(dataptr); - } else { - skb = (struct sk_buff *) opt; - dev_kfree_skb_any(skb); - } - return OK; -} - -int init_dma_device(_dma_device_info * dma_dev) -{ - int i; - int num_tx_chan, num_rx_chan; - if (strcmp(dma_dev->device_name, "switch1") == 0) { - num_tx_chan = 1; - num_rx_chan = 2; - dma_dev->priv = (void *) 0; - } else { - num_tx_chan = 1; - num_rx_chan = 2; - dma_dev->priv = (void *) 1; - } - - dma_dev->weight = 1; - dma_dev->num_tx_chan = num_tx_chan; - dma_dev->num_rx_chan = num_rx_chan; - dma_dev->ack = 1; - dma_dev->tx_burst_len = 4; - dma_dev->rx_burst_len = 4; - for (i = 0; i < dma_dev->num_tx_chan; i++) { - dma_dev->tx_chan[i].weight = QOS_DEFAULT_WGT; - dma_dev->tx_chan[i].desc_num = 10; - dma_dev->tx_chan[i].packet_size = 0; - dma_dev->tx_chan[i].control = 0; - } - for (i = 0; i < num_rx_chan; i++) { - dma_dev->rx_chan[i].weight = QOS_DEFAULT_WGT; - dma_dev->rx_chan[i].desc_num = 10; - dma_dev->rx_chan[i].packet_size = ETHERNET_PACKET_DMA_BUFFER_SIZE; - dma_dev->rx_chan[i].control = 0; - } - dma_dev->intr_handler = dma_intr_handler; - dma_dev->buffer_alloc = dma_buffer_alloc; - dma_dev->buffer_free = dma_buffer_free; - return 0; -} - -int switch_set_mac_address(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - return OK; -} - - -int switch_init(struct net_device *dev) -{ - u64 retval = 0; - int i; - int result; - struct switch_priv *priv; - ether_setup(dev); /* assign some of the fields */ - printk("%s up using ", dev->name); - dev->open = switch_open; - dev->stop = switch_release; - dev->hard_start_xmit = switch_tx; - dev->do_ioctl = switch_ioctl; - dev->get_stats = switch_stats; - dev->change_mtu = switch_change_mtu; - dev->set_mac_address = switch_set_mac_address; - dev->tx_timeout = switch_tx_timeout; - dev->watchdog_timeo = timeout; - - SET_MODULE_OWNER(dev); - - dev->priv = kmalloc(sizeof(struct switch_priv), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct switch_priv)); - priv = dev->priv; - priv->dma_device = (struct dma_device_info *) kmalloc(sizeof(struct dma_device_info), GFP_KERNEL); - if ((dev - switch_devs) == 0) { - sprintf(priv->dma_device->device_name, "switch1"); - } else if ((dev - switch_devs) == 1) { - sprintf(priv->dma_device->device_name, "switch2"); - } - printk("\"%s\"\n", priv->dma_device->device_name); - init_dma_device(priv->dma_device); - result = dma_device_register(priv->dma_device); - - /* read the mac address from the mac table and put them into the mac table. */ - for (i = 0; i < 6; i++) { - retval += my_ethaddr[i]; - } - /* ethaddr not set in u-boot ? */ - if (retval == 0) { - dev->dev_addr[0] = 0x00; - dev->dev_addr[1] = 0x20; - dev->dev_addr[2] = 0xda; - dev->dev_addr[3] = 0x86; - dev->dev_addr[4] = 0x23; - dev->dev_addr[5] = 0x74 + (unsigned char) (dev - switch_devs); - } else { - for (i = 0; i < 6; i++) { - dev->dev_addr[i] = my_ethaddr[i]; - } - dev->dev_addr[5] += +(unsigned char) (dev - switch_devs); - } - return OK; -} - -int switch_init_module(void) -{ - int i = 0, result, device_present = 0; - - for (i = 0; i < AMAZON_SW_INT_NO; i++) { - sprintf(switch_devs[i].name, "eth%d", i); - - if ((result = register_netdev(switch_devs + i))) - printk("error %i registering device \"%s\"\n", result, switch_devs[i].name); - else - device_present++; - } - amazon_sw_chip_init(); - return device_present ? 0 : -ENODEV; -} - -void switch_cleanup(void) -{ - int i; - struct switch_priv *priv; - for (i = 0; i < AMAZON_SW_INT_NO; i++) { - priv = switch_devs[i].priv; - if (priv->dma_device) { - dma_device_unregister(priv->dma_device); - kfree(priv->dma_device); - } - kfree(switch_devs[i].priv); - unregister_netdev(switch_devs + i); - } - return; -} - -module_init(switch_init_module); -module_exit(switch_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Wu Qi Ming"); |