diff options
author | kaloz <kaloz@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-05-01 07:00:17 +0000 |
---|---|---|
committer | kaloz <kaloz@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-05-01 07:00:17 +0000 |
commit | 7e7649baa9de9d592a76e150970e3079d7610138 (patch) | |
tree | 7e3c3b52269e3c7564c6a88d67dc4ea6219b041c /target/linux/coldfire/patches/021-Add-ethernet-switch-driver-for-MCF54418.patch | |
parent | 7877ca154f130fe0c83a5f151320c3ce902195d1 (diff) |
[coldfire]: switch to 2.6.38
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@31546 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/coldfire/patches/021-Add-ethernet-switch-driver-for-MCF54418.patch')
-rw-r--r-- | target/linux/coldfire/patches/021-Add-ethernet-switch-driver-for-MCF54418.patch | 6152 |
1 files changed, 6152 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/021-Add-ethernet-switch-driver-for-MCF54418.patch b/target/linux/coldfire/patches/021-Add-ethernet-switch-driver-for-MCF54418.patch new file mode 100644 index 0000000000..e5bbc69738 --- /dev/null +++ b/target/linux/coldfire/patches/021-Add-ethernet-switch-driver-for-MCF54418.patch @@ -0,0 +1,6152 @@ +From 51e66f289f280a33bb17047717d2e6539a2917e1 Mon Sep 17 00:00:00 2001 +From: Alison Wang <b18965@freescale.com> +Date: Thu, 4 Aug 2011 09:59:44 +0800 +Subject: [PATCH 21/52] Add ethernet switch driver for MCF54418 + +Add ethernet switch driver support for MCF54418. + +Signed-off-by: Alison Wang <b18965@freescale.com> +--- + arch/m68k/coldfire/m5441x/l2switch.c | 284 +++ + arch/m68k/include/asm/mcfswitch.h | 324 +++ + drivers/net/Kconfig | 8 + + drivers/net/Makefile | 1 + + drivers/net/modelo_switch.c | 4293 ++++++++++++++++++++++++++++++++++ + drivers/net/modelo_switch.h | 1141 +++++++++ + include/linux/fsl_devices.h | 17 + + net/core/dev.c | 8 + + 8 files changed, 6076 insertions(+), 0 deletions(-) + create mode 100644 arch/m68k/coldfire/m5441x/l2switch.c + create mode 100644 arch/m68k/include/asm/mcfswitch.h + create mode 100644 drivers/net/modelo_switch.c + create mode 100644 drivers/net/modelo_switch.h + +--- /dev/null ++++ b/arch/m68k/coldfire/m5441x/l2switch.c +@@ -0,0 +1,284 @@ ++/* ++ * l2switch.c ++ * ++ * Sub-architcture dependant initialization code for the Freescale ++ * 5441X L2 Switch module. ++ * ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ShrekWu B16972@freescale.com ++ * ++ * ++ * 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 ++ */ ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/param.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++ ++#include <asm/traps.h> ++#include <asm/machdep.h> ++#include <asm/coldfire.h> ++#include <asm/mcfswitch.h> ++#include <asm/mcfsim.h> ++ ++static unsigned char switch_mac_default[] = { ++ 0x00, 0x04, 0x9F, 0x00, 0xB3, 0x49, ++}; ++ ++static unsigned char switch_mac_addr[6]; ++ ++static void switch_request_intrs(struct net_device *dev, ++ irqreturn_t switch_net_irq_handler(int irq, void *private), ++ void *irq_privatedata) ++{ ++ struct switch_enet_private *fep; ++ int b; ++ static const struct idesc { ++ char *name; ++ unsigned short irq; ++ } *idp, id[] = { ++ /*{ "esw_isr(EBERR)", 38 },*/ ++ { "esw_isr(RxBuffer)", 39 }, ++ { "esw_isr(RxFrame)", 40 }, ++ { "esw_isr(TxBuffer)", 41 }, ++ { "esw_isr(TxFrame)", 42 }, ++ { "esw_isr(QM)", 43 }, ++ { "esw_isr(P0OutputDiscard)", 44 }, ++ { "esw_isr(P1OutputDiscard)", 45 }, ++ { "esw_isr(P2OutputDiscard)", 46 }, ++ { "esw_isr(LearningRecord)", 47 }, ++ { NULL }, ++ }; ++ ++ fep = netdev_priv(dev); ++ /*intrruption L2 ethernet SWITCH */ ++ b = 64 + 64 + 64; ++ ++ /* Setup interrupt handlers. */ ++ for (idp = id; idp->name; idp++) { ++ if (request_irq(b+idp->irq, ++ switch_net_irq_handler, IRQF_DISABLED, ++ idp->name, irq_privatedata) != 0) ++ printk(KERN_ERR "FEC: Could not alloc %s IRQ(%d)!\n", ++ idp->name, b+idp->irq); ++ } ++ ++ /* Configure RMII */ ++ MCF_GPIO_PAR_FEC = (MCF_GPIO_PAR_FEC & ++ MCF_GPIO_PAR_FEC_FEC_MASK) | ++ MCF_GPIO_PAR_FEC_FEC_RMII0FUL_1FUL; ++ ++ MCF_GPIO_PAR_FEC = ++ (MCF_GPIO_PAR_FEC & ++ MCF_GPIO_PAR_FEC_FEC_MASK) | ++ MCF_GPIO_PAR_FEC_FEC_RMII0FUL_1FUL; ++ ++ MCF_GPIO_SRCR_FEC = 0x0F; ++ ++ MCF_GPIO_PAR_SIMP0H = ++ (MCF_GPIO_PAR_SIMP0H & ++ MCF_GPIO_PAR_SIMP0H_DAT_MASK) | ++ MCF_GPIO_PAR_SIMP0H_DAT_GPIO; ++ ++ MCF_GPIO_PDDR_G = ++ (MCF_GPIO_PDDR_G & ++ MCF_GPIO_PDDR_G4_MASK) | ++ MCF_GPIO_PDDR_G4_OUTPUT; ++ ++ MCF_GPIO_PODR_G = ++ (MCF_GPIO_PODR_G & ++ MCF_GPIO_PODR_G4_MASK); ++} ++ ++static void switch_set_mii(struct net_device *dev) ++{ ++ struct switch_enet_private *fep = netdev_priv(dev); ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ MCF_FEC_RCR0 = (MCF_FEC_RCR_PROM | MCF_FEC_RCR_RMII_MODE | ++ MCF_FEC_RCR_MAX_FL(1522) | MCF_FEC_RCR_CRC_FWD); ++ MCF_FEC_RCR1 = (MCF_FEC_RCR_PROM | MCF_FEC_RCR_RMII_MODE | ++ MCF_FEC_RCR_MAX_FL(1522) | MCF_FEC_RCR_CRC_FWD); ++ /* TCR */ ++ MCF_FEC_TCR0 = MCF_FEC_TCR_FDEN; ++ MCF_FEC_TCR1 = MCF_FEC_TCR_FDEN; ++ /* ECR */ ++#ifdef MODELO_ENHANCE_BUFFER ++ MCF_FEC_ECR0 = MCF_FEC_ECR_ETHER_EN | MCF_FEC_ECR_ENA_1588; ++ MCF_FEC_ECR1 = MCF_FEC_ECR_ETHER_EN | MCF_FEC_ECR_ENA_1588; ++#else /*legac buffer*/ ++ MCF_FEC_ECR0 = MCF_FEC_ECR_ETHER_EN; ++ MCF_FEC_ECR1 = MCF_FEC_ECR_ETHER_EN; ++#endif ++ /* ++ * Set MII speed to 2.5 MHz ++ */ ++ MCF_FEC_MSCR0 = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; ++ MCF_FEC_MSCR1 = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; ++ ++} ++ ++static void switch_get_mac(struct net_device *dev) ++{ ++ struct switch_enet_private *fep = netdev_priv(dev); ++ volatile switch_t *fecp; ++ unsigned char *iap; ++ ++ fecp = fep->hwp; ++ ++ if (FEC_FLASHMAC) { ++ /* ++ * Get MAC address from FLASH. ++ * If it is all 1's or 0's, use the default. ++ */ ++ iap = FEC_FLASHMAC; ++ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && ++ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) ++ iap = switch_mac_default; ++ if ((iap[0] == 0xff) && (iap[1] == 0xff) && ++ (iap[2] == 0xff) && (iap[3] == 0xff) && ++ (iap[4] == 0xff) && (iap[5] == 0xff)) ++ iap = switch_mac_default; ++ ++ } else { ++ iap = &switch_mac_addr[0]; ++ ++ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) && ++ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0)) ++ iap = switch_mac_default; ++ if ((iap[0] == 0xff) && (iap[1] == 0xff) && ++ (iap[2] == 0xff) && (iap[3] == 0xff) && ++ (iap[4] == 0xff) && (iap[5] == 0xff)) ++ iap = switch_mac_default; ++ } ++ ++ memcpy(dev->dev_addr, iap, ETH_ALEN); ++ /* Adjust MAC if using default MAC address */ ++ if (iap == switch_mac_default) ++ dev->dev_addr[ETH_ALEN-1] = switch_mac_default[ETH_ALEN-1] + ++ fep->index; ++} ++ ++static void switch_enable_phy_intr(void) ++{ ++} ++ ++static void switch_disable_phy_intr(void) ++{ ++} ++ ++static void switch_phy_ack_intr(void) ++{ ++} ++ ++static void switch_localhw_setup(void) ++{ ++} ++ ++static void switch_uncache(unsigned long addr) ++{ ++} ++ ++static void switch_platform_flush_cache(void) ++{ ++} ++ ++/* ++ * Define the fixed address of the FEC hardware. ++ */ ++static unsigned int switch_platform_hw[] = { ++ (0xfc0dc000), ++ (0xfc0e000), ++}; ++ ++static struct coldfire_switch_platform_data mcf5441x_switch_data = { ++ .hash_table = 0, ++ .switch_hw = switch_platform_hw, ++ .request_intrs = switch_request_intrs, ++ .set_mii = switch_set_mii, ++ .get_mac = switch_get_mac, ++ .enable_phy_intr = switch_enable_phy_intr, ++ .disable_phy_intr = switch_disable_phy_intr, ++ .phy_ack_intr = switch_phy_ack_intr, ++ .localhw_setup = switch_localhw_setup, ++ .uncache = switch_uncache, ++ .platform_flush_cache = switch_platform_flush_cache, ++}; ++ ++static struct resource l2switch_coldfire_resources[] = { ++ [0] = { ++ .start = 0xFC0DC000, ++ .end = 0xFC0DC508, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = (64 + 64 + 64 + 38), ++ .end = (64 + 64 + 64 + 48), ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ .start = 0xFC0E0000, ++ .end = 0xFC0E3FFC, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device l2switch_coldfire_device = { ++ .name = "coldfire-switch", ++ .id = 0, ++ .resource = l2switch_coldfire_resources, ++ .num_resources = ARRAY_SIZE(l2switch_coldfire_resources), ++ .dev = { ++ .platform_data = &mcf5441x_switch_data, ++ .coherent_dma_mask = ~0, /* $$$ REVISIT */ ++ } ++}; ++ ++ ++static int __init mcf5441x_switch_dev_init(void) ++{ ++ int retval = 0; ++ ++ retval = platform_device_register(&l2switch_coldfire_device); ++ ++ if (retval < 0) { ++ printk(KERN_ERR "MCF5441x L2Switch: platform_device_register" ++ " failed with code=%d\n", retval); ++ } ++ ++ return retval; ++} ++ ++static int __init param_switch_addr_setup(char *str) ++{ ++ char *end; ++ int i; ++ ++ for (i = 0; i < 6; i++) { ++ switch_mac_addr[i] = str ? simple_strtoul(str, &end, 16) : 0; ++ if (str) ++ str = (*end) ? end + 1 : end; ++ } ++ return 0; ++} ++__setup("switchaddr=", param_switch_addr_setup); ++ ++arch_initcall(mcf5441x_switch_dev_init); +--- /dev/null ++++ b/arch/m68k/include/asm/mcfswitch.h +@@ -0,0 +1,324 @@ ++/****************************************************************************/ ++ ++/* ++ * mcfswitch -- L2 SWITCH Controller for Motorola ColdFire SoC ++ * processors. ++ * ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * 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 ++ */ ++ ++/****************************************************************************/ ++#ifndef SWITCH_H ++#define SWITCH_H ++/****************************************************************************/ ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/skbuff.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/platform_device.h> ++#include <asm/pgtable.h> ++ ++#define FEC_FLASHMAC 0 ++#define SWITCH_EPORT_NUMBER 2 ++ ++#ifdef CONFIG_SWITCH_DMA_USE_SRAM ++#define TX_RING_SIZE 8 /* Must be power of two */ ++#define TX_RING_MOD_MASK 7 /* for this to work */ ++#else ++#define TX_RING_SIZE 16 /* Must be power of two */ ++#define TX_RING_MOD_MASK 15 /* for this to work */ ++#endif ++ ++typedef struct l2switch_port_statistics_status { ++ /*outgoing frames discarded due to transmit queue congestion*/ ++ unsigned long MCF_ESW_POQC; ++ /*incoming frames discarded due to VLAN domain mismatch*/ ++ unsigned long MCF_ESW_PMVID; ++ /*incoming frames discarded due to untagged discard*/ ++ unsigned long MCF_ESW_PMVTAG; ++ /*incoming frames discarded due port is in blocking state*/ ++ unsigned long MCF_ESW_PBL; ++} esw_port_statistics_status; ++ ++typedef struct l2switch { ++ unsigned long ESW_REVISION; ++ unsigned long ESW_SCRATCH; ++ unsigned long ESW_PER; ++ unsigned long reserved0[1]; ++ unsigned long ESW_VLANV; ++ unsigned long ESW_DBCR; ++ unsigned long ESW_DMCR; ++ unsigned long ESW_BKLR; ++ unsigned long ESW_BMPC; ++ unsigned long ESW_MODE; ++ unsigned long ESW_VIMSEL; ++ unsigned long ESW_VOMSEL; ++ unsigned long ESW_VIMEN; ++ unsigned long ESW_VID;/*0x34*/ ++ /*from 0x38 0x3C*/ ++ unsigned long esw_reserved0[2]; ++ unsigned long ESW_MCR;/*0x40*/ ++ unsigned long ESW_EGMAP; ++ unsigned long ESW_INGMAP; ++ unsigned long ESW_INGSAL; ++ unsigned long ESW_INGSAH; ++ unsigned long ESW_INGDAL; ++ unsigned long ESW_INGDAH; ++ unsigned long ESW_ENGSAL; ++ unsigned long ESW_ENGSAH; ++ unsigned long ESW_ENGDAL; ++ unsigned long ESW_ENGDAH; ++ unsigned long ESW_MCVAL;/*0x6C*/ ++ /*from 0x70--0x7C*/ ++ unsigned long esw_reserved1[4]; ++ unsigned long ESW_MMSR;/*0x80*/ ++ unsigned long ESW_LMT; ++ unsigned long ESW_LFC; ++ unsigned long ESW_PCSR; ++ unsigned long ESW_IOSR; ++ unsigned long ESW_QWT;/*0x94*/ ++ unsigned long esw_reserved2[1];/*0x98*/ ++ unsigned long ESW_P0BCT;/*0x9C*/ ++ /*from 0xA0-0xB8*/ ++ unsigned long esw_reserved3[7]; ++ unsigned long ESW_P0FFEN;/*0xBC*/ ++ unsigned long ESW_PSNP[8]; ++ unsigned long ESW_IPSNP[8]; ++ unsigned long ESW_PVRES[3]; ++ /*from 0x10C-0x13C*/ ++ unsigned long esw_reserved4[13]; ++ unsigned long ESW_IPRES;/*0x140*/ ++ /*from 0x144-0x17C*/ ++ unsigned long esw_reserved5[15]; ++ ++ /*port0-port2 Priority Configuration 0xFC0D_C180-C188*/ ++ unsigned long ESW_PRES[3]; ++ /*from 0x18C-0x1FC*/ ++ unsigned long esw_reserved6[29]; ++ ++ /*port0-port2 VLAN ID 0xFC0D_C200-C208*/ ++ unsigned long ESW_PID[3]; ++ /*from 0x20C-0x27C*/ ++ unsigned long esw_reserved7[29]; ++ ++ /*port0-port2 VLAN domain resolution entry 0xFC0D_C280-C2FC*/ ++ unsigned long ESW_VRES[32]; ++ ++ unsigned long ESW_DISCN;/*0x300*/ ++ unsigned long ESW_DISCB; ++ unsigned long ESW_NDISCN; ++ unsigned long ESW_NDISCB;/*0xFC0DC30C*/ ++ /*per port statistics 0xFC0DC310_C33C*/ ++ esw_port_statistics_status port_statistics_status[3]; ++ /*from 0x340-0x400*/ ++ unsigned long esw_reserved8[48]; ++ ++ /*0xFC0DC400---0xFC0DC418*/ ++ /*unsigned long MCF_ESW_ISR;*/ ++ unsigned long switch_ievent; /* Interrupt event reg */ ++ /*unsigned long MCF_ESW_IMR;*/ ++ unsigned long switch_imask; /* Interrupt mask reg */ ++ /*unsigned long MCF_ESW_RDSR;*/ ++ unsigned long fec_r_des_start; /* Receive descriptor ring */ ++ /*unsigned long MCF_ESW_TDSR;*/ ++ unsigned long fec_x_des_start; /* Transmit descriptor ring */ ++ /*unsigned long MCF_ESW_MRBR;*/ ++ unsigned long fec_r_buff_size; /* Maximum receive buff size */ ++ /*unsigned long MCF_ESW_RDAR;*/ ++ unsigned long fec_r_des_active; /* Receive descriptor reg */ ++ /*unsigned long MCF_ESW_TDAR;*/ ++ unsigned long fec_x_des_active; /* Transmit descriptor reg */ ++ /*from 0x420-0x4FC*/ ++ unsigned long esw_reserved9[57]; ++ ++ /*0xFC0DC500---0xFC0DC508*/ ++ unsigned long ESW_LREC0; ++ unsigned long ESW_LREC1; ++ unsigned long ESW_LSR; ++} switch_t; ++ ++typedef struct _64bTableEntry { ++ unsigned int lo; /* lower 32 bits */ ++ unsigned int hi; /* upper 32 bits */ ++} AddrTable64bEntry; ++ ++typedef struct l2switchaddrtable { ++ AddrTable64bEntry eswTable64bEntry[2048]; ++} eswAddrTable_t; ++ ++#define MCF_FEC_MSCR0 (*(volatile unsigned long *)(0xFC0D4044)) ++#define MCF_FEC_MSCR1 (*(volatile unsigned long *)(0xFC0D8044)) ++#define MCF_FEC_RCR0 (*(volatile unsigned long *)(0xFC0D4084)) ++#define MCF_FEC_RCR1 (*(volatile unsigned long *)(0xFC0D8084)) ++#define MCF_FEC_TCR0 (*(volatile unsigned long *)(0xFC0D40C4)) ++#define MCF_FEC_TCR1 (*(volatile unsigned long *)(0xFC0D80C4)) ++#define MCF_FEC_ECR0 (*(volatile unsigned long *)(0xFC0D4024)) ++#define MCF_FEC_ECR1 (*(volatile unsigned long *)(0xFC0D8024)) ++ ++#define MCF_FEC_RCR_PROM (0x00000008) ++#define MCF_FEC_RCR_RMII_MODE (0x00000100) ++#define MCF_FEC_RCR_MAX_FL(x) (((x)&0x00003FFF)<<16) ++#define MCF_FEC_RCR_CRC_FWD (0x00004000) ++ ++#define MCF_FEC_TCR_FDEN (0x00000004) ++ ++#define MCF_FEC_ECR_ETHER_EN (0x00000002) ++#define MCF_FEC_ECR_ENA_1588 (0x00000010) ++ ++ ++typedef struct bufdesc { ++ unsigned short cbd_sc; /* Control and status info */ ++ unsigned short cbd_datlen; /* Data length */ ++ unsigned long cbd_bufaddr; /* Buffer address */ ++#ifdef MODELO_BUFFER ++ unsigned long ebd_status; ++ unsigned short length_proto_type; ++ unsigned short payload_checksum; ++ unsigned long bdu; ++ unsigned long timestamp; ++ unsigned long reserverd_word1; ++ unsigned long reserverd_word2; ++#endif ++} cbd_t; ++ ++/* Forward declarations of some structures to support different PHYs ++ */ ++typedef struct { ++ uint mii_data; ++ void (*funct)(uint mii_reg, struct net_device *dev); ++} phy_cmd_t; ++ ++typedef struct { ++ uint id; ++ char *name; ++ ++ const phy_cmd_t *config; ++ const phy_cmd_t *startup; ++ const phy_cmd_t *ack_int; ++ const phy_cmd_t *shutdown; ++} phy_info_t; ++ ++/* The switch buffer descriptors track the ring buffers. The rx_bd_base and ++ * tx_bd_base always point to the base of the buffer descriptors. The ++ * cur_rx and cur_tx point to the currently available buffer. ++ * The dirty_tx tracks the current buffer that is being sent by the ++ * controller. The cur_tx and dirty_tx are equal under both completely ++ * empty and completely full conditions. The empty/ready indicator in ++ * the buffer descriptor determines the actual condition. ++ */ ++struct switch_enet_private { ++ /* Hardware registers of the switch device */ ++ volatile switch_t *hwp; ++ volatile eswAddrTable_t *hwentry; ++ ++ struct net_device *netdev; ++ struct platform_device *pdev; ++ /* The saved address of a sent-in-place packet/buffer, for skfree(). */ ++ unsigned char *tx_bounce[TX_RING_SIZE]; ++ struct sk_buff *tx_skbuff[TX_RING_SIZE]; ++ ushort skb_cur; ++ ushort skb_dirty; ++ ++ /* CPM dual port RAM relative addresses. ++ */ ++ cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ ++ cbd_t *tx_bd_base; ++ cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ ++ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ ++ uint tx_full; ++ /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ ++ spinlock_t hw_lock; ++ ++ /* hold while accessing the mii_list_t() elements */ ++ spinlock_t mii_lock; ++ struct mii_bus *mdio_bus; ++ struct phy_device *phydev[SWITCH_EPORT_NUMBER]; ++ ++ uint phy_id; ++ uint phy_id_done; ++ uint phy_status; ++ uint phy_speed; ++ phy_info_t const *phy; ++ struct work_struct phy_task; ++ volatile switch_t *phy_hwp; ++ ++ uint sequence_done; ++ uint mii_phy_task_queued; ++ ++ uint phy_addr; ++ ++ int index; ++ int opened; ++ int full_duplex; ++ int msg_enable; ++ int phy1_link; ++ int phy1_old_link; ++ int phy1_duplex; ++ int phy1_speed; ++ ++ int phy2_link; ++ int phy2_old_link; ++ int phy2_duplex; ++ int phy2_speed; ++ /* --------------Statistics--------------------------- */ ++ /* when a new element deleted a element with in ++ * a block due to lack of space */ ++ int atBlockOverflows; ++ /* Peak number of valid entries in the address table */ ++ int atMaxEntries; ++ /* current number of valid entries in the address table */ ++ int atCurrEntries; ++ /* maximum entries within a block found ++ * (updated within ageing)*/ ++ int atMaxEntriesPerBlock; ++ ++ /* -------------------ageing function------------------ */ ++ /* maximum age allowed for an entry */ ++ int ageMax; ++ /* last LUT entry to block that was ++ * inspected by the Ageing task*/ ++ int ageLutIdx; ++ /* last element within block inspected by the Ageing task */ ++ int ageBlockElemIdx; ++ /* complete table has been processed by ageing process */ ++ int ageCompleted; ++ /* delay setting */ ++ int ageDelay; ++ /* current delay Counter */ ++ int ageDelayCnt; ++ ++ /* ----------------timer related---------------------------- */ ++ /* current time (for timestamping) */ ++ int currTime; ++ /* flag set by timer when currTime changed ++ * and cleared by serving function*/ ++ int timeChanged; ++ ++ /* Timer for Aging */ ++ struct timer_list timer_aging; ++ int learning_irqhandle_enable; ++}; ++ ++struct switch_platform_private { ++ struct platform_device *pdev; ++ ++ unsigned long quirks; ++ int num_slots; /* Slots on controller */ ++ struct switch_enet_private *fep_host[0]; /* Pointers to hosts */ ++}; ++#endif +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -1950,6 +1950,14 @@ config FEC + Say Y here if you want to use the built-in 10/100 Fast ethernet + controller on some Motorola ColdFire and Freescale i.MX processors. + ++config MODELO_SWITCH ++ bool "ethernet switch controller (of ColdFire CPUs)" ++ depends on !FEC && M5441X ++ help ++ Say Y here if you want to use the built-in ethernet switch ++ controller on some ColdFire processors. ++ The Integrated Ethernet switch engine is compatible with ++ 10/100 MAC-NET core. + + config FEC2 + bool "Second FEC ethernet controller (on some ColdFire CPUs)" +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -127,6 +127,7 @@ ifeq ($(CONFIG_FEC_1588), y) + obj-$(CONFIG_FEC) += fec_1588.o + endif + obj-$(CONFIG_FEC_548x) += fec_m547x.o ++obj-$(CONFIG_MODELO_SWITCH) += modelo_switch.o + obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o + ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) + obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o +--- /dev/null ++++ b/drivers/net/modelo_switch.c +@@ -0,0 +1,4293 @@ ++/* ++ * L2 switch Controller (Etheren switch) driver for MCF5441x. ++ * ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Shrek Wu (B16972@freescale.com) ++ * Alison Wang (b18965@freescale.com) ++ * Jason Jin (Jason.jin@freescale.com) ++ * ++ * 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/string.h> ++#include <linux/ptrace.h> ++#include <linux/errno.h> ++#include <linux/ioport.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/pci.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/skbuff.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/bitops.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <linux/phy.h> ++#include <linux/kthread.h> ++#include <linux/syscalls.h> ++#include <linux/uaccess.h> ++#include <linux/io.h> ++#include <linux/signal.h> ++ ++#include <asm/irq.h> ++#include <asm/pgtable.h> ++#include <asm/cacheflush.h> ++#include <asm/coldfire.h> ++#include <asm/mcfsim.h> ++#include "modelo_switch.h" ++ ++#define SWITCH_MAX_PORTS 1 ++#define CONFIG_FEC_SHARED_PHY ++ ++/* Interrupt events/masks. ++*/ ++#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ ++#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ ++#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ ++#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ ++#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ ++#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ ++#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ ++#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ ++#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ ++#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ ++ ++static int switch_enet_open(struct net_device *dev); ++static int switch_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); ++static irqreturn_t switch_enet_interrupt(int irq, void *dev_id); ++static void switch_enet_tx(struct net_device *dev); ++static void switch_enet_rx(struct net_device *dev); ++static int switch_enet_close(struct net_device *dev); ++static void set_multicast_list(struct net_device *dev); ++static void switch_restart(struct net_device *dev, int duplex); ++static void switch_stop(struct net_device *dev); ++static void switch_set_mac_address(struct net_device *dev); ++ ++#define NMII 20 ++ ++/* Make MII read/write commands for the FEC. ++*/ ++#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) ++#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ ++ (VAL & 0xffff)) ++ ++/* Transmitter timeout. ++*/ ++#define TX_TIMEOUT (2*HZ) ++ ++/*last read entry from learning interface*/ ++eswPortInfo g_info; ++/* switch ports status */ ++struct port_status ports_link_status; ++ ++/* the user space pid, used to send the link change to user space */ ++long user_pid = 1; ++ ++/* ----------------------------------------------------------------*/ ++/* ++ * Calculate Galois Field Arithmetic CRC for Polynom x^8+x^2+x+1. ++ * It omits the final shift in of 8 zeroes a "normal" CRC would do ++ * (getting the remainder). ++ * ++ * Examples (hexadecimal values):<br> ++ * 10-11-12-13-14-15 => CRC=0xc2 ++ * 10-11-cc-dd-ee-00 => CRC=0xe6 ++ * ++ * param: pmacaddress ++ * A 6-byte array with the MAC address. ++ * The first byte is the first byte transmitted ++ * return The 8-bit CRC in bits 7:0 ++ */ ++int crc8_calc(unsigned char *pmacaddress) ++{ ++ /* byte index */ ++ int byt; ++ /* bit index */ ++ int bit; ++ int inval; ++ int crc; ++ /* preset */ ++ crc = 0x12; ++ for (byt = 0; byt < 6; byt++) { ++ inval = (((int)pmacaddress[byt]) & 0xff); ++ /* ++ * shift bit 0 to bit 8 so all our bits ++ * travel through bit 8 ++ * (simplifies below calc) ++ */ ++ inval <<= 8; ++ ++ for (bit = 0; bit < 8; bit++) { ++ /* next input bit comes into d7 after shift */ ++ crc |= inval & 0x100; ++ if (crc & 0x01) ++ /* before shift */ ++ crc ^= 0x1c0; ++ ++ crc >>= 1; ++ inval >>= 1; ++ } ++ ++ } ++ /* upper bits are clean as we shifted in zeroes! */ ++ return crc; ++} ++ ++void read_atable(struct switch_enet_private *fep, ++ int index, unsigned long *read_lo, unsigned long *read_hi) ++{ ++ unsigned long atable_base = 0xFC0E0000; ++ ++ *read_lo = *((volatile unsigned long *)(atable_base + (index<<3))); ++ *read_hi = *((volatile unsigned long *)(atable_base + (index<<3) + 4)); ++} ++ ++void write_atable(struct switch_enet_private *fep, ++ int index, unsigned long write_lo, unsigned long write_hi) ++{ ++ unsigned long atable_base = 0xFC0E0000; ++ ++ *((volatile unsigned long *)(atable_base + (index<<3))) = write_lo; ++ *((volatile unsigned long *)(atable_base + (index<<3) + 4)) = write_hi; ++} ++ ++/* Check if the Port Info FIFO has data available ++ * for reading. 1 valid, 0 invalid*/ ++int esw_portinfofifo_status(struct switch_enet_private *fep) ++{ ++ volatile switch_t *fecp; ++ fecp = fep->hwp; ++ return fecp->ESW_LSR; ++} ++ ++/* Initialize the Port Info FIFO. */ ++void esw_portinfofifo_initialize(struct switch_enet_private *fep) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp; ++ fecp = fep->hwp; ++ ++ /*disable all learn*/ ++ fecp->switch_imask &= (~MCF_ESW_IMR_LRN); ++ /* remove all entries from FIFO */ ++ while (esw_portinfofifo_status(fep)) { ++ /* read one data word */ ++ tmp = fecp->ESW_LREC0; ++ tmp = fecp->ESW_LREC1; ++ } ++ ++} ++ ++/* Read one element from the HW receive FIFO (Queue) ++ * if available and return it. ++ * return ms_HwPortInfo or null if no data is available ++ */ ++eswPortInfo *esw_portinfofifo_read(struct switch_enet_private *fep) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp; ++ ++ fecp = fep->hwp; ++ /* check learning record valid */ ++ if (fecp->ESW_LSR == 0) ++ return NULL; ++ ++ /*read word from FIFO*/ ++ g_info.maclo = fecp->ESW_LREC0; ++ ++ /*but verify that we actually did so ++ * (0=no data available)*/ ++ if (g_info.maclo == 0) ++ return NULL; ++ ++ /* read 2nd word from FIFO */ ++ tmp = fecp->ESW_LREC1; ++ g_info.machi = tmp & 0xffff; ++ g_info.hash = (tmp >> 16) & 0xff; ++ g_info.port = (tmp >> 24) & 0xf; ++ ++ return &g_info; ++} ++ ++/* ++ * Clear complete MAC Look Up Table ++ */ ++void esw_clear_atable(struct switch_enet_private *fep) ++{ ++ int index; ++ for (index = 0; index < 2048; index++) ++ write_atable(fep, index, 0, 0); ++} ++ ++void esw_dump_atable(struct switch_enet_private *fep) ++{ ++ int index; ++ unsigned long read_lo, read_hi; ++ for (index = 0; index < 2048; index++) ++ read_atable(fep, index, &read_lo, &read_hi); ++} ++ ++/* ++ * pdates MAC address lookup table with a static entry ++ * Searches if the MAC address is already there in the block and replaces ++ * the older entry with new one. If MAC address is not there then puts a ++ * new entry in the first empty slot available in the block ++ * ++ * mac_addr Pointer to the array containing MAC address to ++ * be put as static entry ++ * port Port bitmask numbers to be added in static entry, ++ * valid values are 1-7 ++ * priority Priority for the static entry in table ++ * ++ * return 0 for a successful update else -1 when no slot available ++ */ ++int esw_update_atable_static(unsigned char *mac_addr, ++ unsigned int port, unsigned int priority, ++ struct switch_enet_private *fep) ++{ ++ unsigned long block_index, entry, index_end; ++ unsigned long read_lo, read_hi; ++ unsigned long write_lo, write_hi; ++ ++ write_lo = (unsigned long)((mac_addr[3] << 24) | ++ (mac_addr[2] << 16) | ++ (mac_addr[1] << 8) | ++ mac_addr[0]); ++ write_hi = (unsigned long)(0 | ++ (port << AT_SENTRY_PORTMASK_shift) | ++ (priority << AT_SENTRY_PRIO_shift) | ++ (AT_ENTRY_TYPE_STATIC << AT_ENTRY_TYPE_shift) | ++ (AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift) | ++ (mac_addr[5] << 8) | (mac_addr[4])); ++ ++ block_index = GET_BLOCK_PTR(crc8_calc(mac_addr)); ++ index_end = block_index + ATABLE_ENTRY_PER_SLOT; ++ /* Now search all the entries in the selected block */ ++ for (entry = block_index; entry < index_end; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ /* ++ * MAC address matched, so update the ++ * existing entry ++ * even if its a dynamic one ++ */ ++ if ((read_lo == write_lo) && ((read_hi & 0x0000ffff) == ++ (write_hi & 0x0000ffff))) { ++ write_atable(fep, entry, write_lo, write_hi); ++ return 0; ++ } else if (!(read_hi & (1 << 16))) { ++ /* ++ * Fill this empty slot (valid bit zero), ++ * assuming no holes in the block ++ */ ++ write_atable(fep, entry, write_lo, write_hi); ++ fep->atCurrEntries++; ++ return 0; ++ } ++ } ++ ++ /* No space available for this static entry */ ++ return -1; ++} ++ ++/* lookup entry in given Address Table slot and ++ * insert (learn) it if it is not found. ++ * return 0 if entry was found and updated. ++ * 1 if entry was not found and has been inserted (learned). ++ */ ++int esw_update_atable_dynamic(unsigned char *mac_addr, unsigned int port, ++ unsigned int currTime, struct switch_enet_private *fep) ++{ ++ unsigned long block_index, entry, index_end; ++ unsigned long read_lo, read_hi; ++ unsigned long write_lo, write_hi; ++ unsigned long tmp; ++ int time, timeold, indexold; ++ ++ /* prepare update port and timestamp */ ++ write_hi = (mac_addr[5] << 8) | (mac_addr[4]); ++ write_lo = (unsigned long)((mac_addr[3] << 24) | ++ (mac_addr[2] << 16) | ++ (mac_addr[1] << 8) | ++ mac_addr[0]); ++ tmp = AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift; ++ tmp |= AT_ENTRY_TYPE_DYNAMIC << AT_ENTRY_TYPE_shift; ++ tmp |= currTime << AT_DENTRY_TIME_shift; ++ tmp |= port << AT_DENTRY_PORT_shift; ++ tmp |= write_hi; ++ ++ /* ++ * linear search through all slot ++ * entries and update if found ++ */ ++ block_index = GET_BLOCK_PTR(crc8_calc(mac_addr)); ++ index_end = block_index + ATABLE_ENTRY_PER_SLOT; ++ /* Now search all the entries in the selected block */ ++ for (entry = block_index; entry < index_end; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ ++ if ((read_lo == write_lo) && ++ ((read_hi & 0x0000ffff) == ++ (write_hi & 0x0000ffff))) { ++ /* found correct address, ++ * update timestamp. */ ++ write_atable(fep, entry, write_lo, tmp); ++ return 0; ++ } else if (!(read_hi & (1 << 16))) { ++ /* slot is empty, then use it ++ * for new entry ++ * Note: There are no holes, ++ * therefore cannot be any ++ * more that need to be compared. ++ */ ++ write_atable(fep, entry, write_lo, tmp); ++ /* statistics (we do it between writing ++ * .hi an .lo due to ++ * hardware limitation... ++ */ ++ fep->atCurrEntries++; ++ /* newly inserted */ ++ return 1; ++ } ++ } ++ ++ /* ++ * no more entry available in blockk ... ++ * overwrite oldest ++ */ ++ timeold = 0; ++ indexold = 0; ++ for (entry = block_index; entry < index_end; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ time = AT_EXTRACT_TIMESTAMP(read_hi); ++ time = TIMEDELTA(currTime, time); ++ if (time > timeold) { ++ /* is it older ?*/ ++ timeold = time; ++ indexold = entry; ++ } ++ } ++ ++ write_atable(fep, indexold, write_lo, tmp); ++ /* Statistics (do it inbetween ++ * writing to .lo and .hi*/ ++ fep->atBlockOverflows++; ++ /* newly inserted */ ++ return 1; ++} ++ ++int esw_update_atable_dynamic1(unsigned long write_lo, unsigned long write_hi, ++ int block_index, unsigned int port, unsigned int currTime, ++ struct switch_enet_private *fep) ++{ ++ unsigned long entry, index_end; ++ unsigned long read_lo, read_hi; ++ unsigned long tmp; ++ int time, timeold, indexold; ++ ++ /* prepare update port and timestamp */ ++ tmp = AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift; ++ tmp |= AT_ENTRY_TYPE_DYNAMIC << AT_ENTRY_TYPE_shift; ++ tmp |= currTime << AT_DENTRY_TIME_shift; ++ tmp |= port << AT_DENTRY_PORT_shift; ++ tmp |= write_hi; ++ ++ /* ++ * linear search through all slot ++ * entries and update if found ++ */ ++ index_end = block_index + ATABLE_ENTRY_PER_SLOT; ++ /* Now search all the entries in the selected block */ ++ for (entry = block_index; entry < index_end; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ if ((read_lo == write_lo) && ++ ((read_hi & 0x0000ffff) == ++ (write_hi & 0x0000ffff))) { ++ /* found correct address, ++ * update timestamp. */ ++ write_atable(fep, entry, write_lo, tmp); ++ return 0; ++ } else if (!(read_hi & (1 << 16))) { ++ /* slot is empty, then use it ++ * for new entry ++ * Note: There are no holes, ++ * therefore cannot be any ++ * more that need to be compared. ++ */ ++ write_atable(fep, entry, write_lo, tmp); ++ /* statistics (we do it between writing ++ * .hi an .lo due to ++ * hardware limitation... ++ */ ++ fep->atCurrEntries++; ++ /* newly inserted */ ++ return 1; ++ } ++ } ++ ++ /* ++ * no more entry available in block ... ++ * overwrite oldest ++ */ ++ timeold = 0; ++ indexold = 0; ++ for (entry = block_index; entry < index_end; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ time = AT_EXTRACT_TIMESTAMP(read_hi); ++ time = TIMEDELTA(currTime, time); ++ if (time > timeold) { ++ /* is it older ?*/ ++ timeold = time; ++ indexold = entry; ++ } ++ } ++ ++ write_atable(fep, indexold, write_lo, tmp); ++ /* Statistics (do it inbetween ++ * writing to .lo and .hi*/ ++ fep->atBlockOverflows++; ++ /* newly inserted */ ++ return 1; ++} ++ ++/* ++ * Delete one dynamic entry within the given block ++ * of 64-bit entries. ++ * return number of valid entries in the block after deletion. ++ */ ++int esw_del_atable_dynamic(struct switch_enet_private *fep, ++ int blockidx, int entryidx) ++{ ++ unsigned long index_start, index_end; ++ int i; ++ unsigned long read_lo, read_hi; ++ ++ /* the entry to delete */ ++ index_start = blockidx + entryidx; ++ /* one after last */ ++ index_end = blockidx + ATABLE_ENTRY_PER_SLOT; ++ /* Statistics */ ++ fep->atCurrEntries--; ++ ++ if (entryidx == (ATABLE_ENTRY_PER_SLOT - 1)) { ++ /* if it is the very last entry, ++ * just delete it without further efford*/ ++ write_atable(fep, index_start, 0, 0); ++ /*number of entries left*/ ++ i = ATABLE_ENTRY_PER_SLOT - 1; ++ return i; ++ } else { ++ /*not the last in the block, then ++ * shift all that follow the one ++ * that is deleted to avoid "holes". ++ */ ++ for (i = index_start; i < (index_end - 1); i++) { ++ read_atable(fep, i + 1, &read_lo, &read_hi); ++ /* move it down */ ++ write_atable(fep, i, read_lo, read_hi); ++ if (!(read_hi & (1 << 16))) { ++ /* stop if we just copied the last */ ++ return i - blockidx; ++ } ++ } ++ ++ /*moved all entries up to the last. ++ * then set invalid flag in the last*/ ++ write_atable(fep, index_end - 1, 0, 0); ++ /* number of valid entries left */ ++ return i - blockidx; ++ } ++} ++ ++void esw_atable_dynamicms_del_entries_for_port( ++ struct switch_enet_private *fep, int port_index) ++{ ++ unsigned long read_lo, read_hi; ++ unsigned int port_idx; ++ int i; ++ ++ for (i = 0; i < ESW_ATABLE_MEM_NUM_ENTRIES; i++) { ++ read_atable(fep, i, &read_lo, &read_hi); ++ if (read_hi & (1 << 16)) { ++ port_idx = AT_EXTRACT_PORT(read_hi); ++ ++ if (port_idx == port_index) ++ write_atable(fep, i, 0, 0); ++ } ++ } ++} ++ ++void esw_atable_dynamicms_del_entries_for_other_port( ++ struct switch_enet_private *fep, ++ int port_index) ++{ ++ unsigned long read_lo, read_hi; ++ unsigned int port_idx; ++ int i; ++ ++ for (i = 0; i < ESW_ATABLE_MEM_NUM_ENTRIES; i++) { ++ read_atable(fep, i, &read_lo, &read_hi); ++ if (read_hi & (1 << 16)) { ++ port_idx = AT_EXTRACT_PORT(read_hi); ++ ++ if (port_idx != port_index) ++ write_atable(fep, i, 0, 0); ++ } ++ } ++} ++ ++/* ++ * Scan one complete block (Slot) for outdated entries and delete them. ++ * blockidx index of block of entries that should be analyzed. ++ * return number of deleted entries, 0 if nothing was modified. ++ */ ++int esw_atable_dynamicms_check_block_age( ++ struct switch_enet_private *fep, int blockidx) { ++ ++ int i, tm, tdelta; ++ int deleted = 0, entries = 0; ++ unsigned long read_lo, read_hi; ++ /* Scan all entries from last down to ++ * have faster deletion speed if necessary*/ ++ for (i = (blockidx + ATABLE_ENTRY_PER_SLOT - 1); ++ i >= blockidx; i--) { ++ read_atable(fep, i, &read_lo, &read_hi); ++ ++ if (read_hi & (1 << 16)) { ++ /* the entry is valide*/ ++ tm = AT_EXTRACT_TIMESTAMP(read_hi); ++ tdelta = TIMEDELTA(fep->currTime, tm); ++ if (tdelta > fep->ageMax) { ++ esw_del_atable_dynamic(fep, ++ blockidx, i-blockidx); ++ deleted++; ++ } else { ++ /* statistics */ ++ entries++; ++ } ++ } ++ } ++ ++ /*update statistics*/ ++ if (fep->atMaxEntriesPerBlock < entries) ++ fep->atMaxEntriesPerBlock = entries; ++ ++ return deleted; ++} ++ ++/* scan the complete address table and find the most current entry. ++ * The time of the most current entry then is used as current time ++ * for the context structure. ++ * In addition the atCurrEntries value is updated as well. ++ * return time that has been set in the context. ++ */ ++int esw_atable_dynamicms_find_set_latesttime( ++ struct switch_enet_private *fep) { ++ ++ int tm_min, tm_max, tm; ++ int delta, current, i; ++ unsigned long read_lo, read_hi; ++ ++ tm_min = (1 << AT_DENTRY_TIMESTAMP_WIDTH) - 1; ++ tm_max = 0; ++ current = 0; ++ ++ for (i = 0; i < ESW_ATABLE_MEM_NUM_ENTRIES; i++) { ++ read_atable(fep, i, &read_lo, &read_hi); ++ if (read_hi & (1 << 16)) { ++ /*the entry is valid*/ ++ tm = AT_EXTRACT_TIMESTAMP(read_hi); ++ if (tm > tm_max) ++ tm_max = tm; ++ if (tm < tm_min) ++ tm_min = tm; ++ current++; ++ } ++ } ++ ++ delta = TIMEDELTA(tm_max, tm_min); ++ if (delta < fep->ageMax) { ++ /*Difference must be in range*/ ++ fep->currTime = tm_max; ++ } else { ++ fep->currTime = tm_min; ++ } ++ ++ fep->atCurrEntries = current; ++ return fep->currTime; ++} ++ ++int esw_atable_dynamicms_get_port( ++ struct switch_enet_private *fep, ++ unsigned long write_lo, ++ unsigned long write_hi, ++ int block_index) ++{ ++ int i, index_end; ++ unsigned long read_lo, read_hi, port; ++ ++ index_end = block_index + ATABLE_ENTRY_PER_SLOT; ++ /* Now search all the entries in the selected block */ ++ for (i = block_index; i < index_end; i++) { ++ read_atable(fep, i, &read_lo, &read_hi); ++ ++ if ((read_lo == write_lo) && ++ ((read_hi & 0x0000ffff) == ++ (write_hi & 0x0000ffff))) { ++ /* found correct address,*/ ++ if (read_hi & (1 << 16)) { ++ /*extract the port index from the valid entry*/ ++ port = AT_EXTRACT_PORT(read_hi); ++ return port; ++ } ++ } ++ } ++ ++ return -1; ++} ++ ++/* Get the port index from the source MAC address ++ * of the received frame ++ * @return port index ++ */ ++int esw_atable_dynamicms_get_portindex_from_mac( ++ struct switch_enet_private *fep, ++ unsigned char *mac_addr, ++ unsigned long write_lo, ++ unsigned long write_hi) ++{ ++ int blockIdx; ++ int rc; ++ /*compute the block index*/ ++ blockIdx = GET_BLOCK_PTR(crc8_calc(mac_addr)); ++ /* Get the ingress port index of the received BPDU */ ++ rc = esw_atable_dynamicms_get_port(fep, ++ write_lo, write_hi, blockIdx); ++ ++ return rc; ++} ++ ++/* dynamicms MAC address table learn and migration*/ ++int esw_atable_dynamicms_learn_migration( ++ struct switch_enet_private *fep, ++ int currTime) ++{ ++ eswPortInfo *pESWPortInfo; ++ int index; ++ int inserted = 0; ++ ++ pESWPortInfo = esw_portinfofifo_read(fep); ++ /* Anything to learn */ ++ if (pESWPortInfo != 0) { ++ /*get block index from lookup table*/ ++ index = GET_BLOCK_PTR(pESWPortInfo->hash); ++ inserted = esw_update_atable_dynamic1( ++ pESWPortInfo->maclo, ++ pESWPortInfo->machi, index, ++ pESWPortInfo->port, currTime, fep); ++ } ++ ++ return 0; ++} ++/* -----------------------------------------------------------------*/ ++/* ++ * esw_forced_forward ++ * The frame is forwared to the forced destination ports. ++ * It only replace the MAC lookup function, ++ * all other filtering(eg.VLAN verification) act as normal ++ */ ++int esw_forced_forward(struct switch_enet_private *fep, ++ int port1, int port2, int enable) ++{ ++ unsigned long tmp = 0; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ /* Enable Forced forwarding for port num */ ++ if ((port1 == 1) && (port2 == 1)) ++ tmp |= MCF_ESW_P0FFEN_FD(3); ++ else if (port1 == 1) ++ /*Enable Forced forwarding for port 1 only*/ ++ tmp |= MCF_ESW_P0FFEN_FD(1); ++ else if (port2 == 1) ++ /*Enable Forced forwarding for port 2 only*/ ++ tmp |= MCF_ESW_P0FFEN_FD(2); ++ else { ++ printk(KERN_ERR "%s:do not support " ++ "the forced forward mode" ++ "port1 %x port2 %x\n", ++ __func__, port1, port2); ++ return -1; ++ } ++ ++ if (enable == 1) ++ tmp |= MCF_ESW_P0FFEN_FEN; ++ else if (enable == 0) ++ tmp &= ~MCF_ESW_P0FFEN_FEN; ++ else { ++ printk(KERN_ERR "%s: the enable %x is error\n", ++ __func__, enable); ++ return -2; ++ } ++ ++ fecp->ESW_P0FFEN = tmp; ++ return 0; ++} ++ ++void esw_get_forced_forward( ++ struct switch_enet_private *fep, ++ unsigned long *ulForceForward) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulForceForward = fecp->ESW_P0FFEN; ++} ++ ++void esw_get_port_enable( ++ struct switch_enet_private *fep, ++ unsigned long *ulPortEnable) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulPortEnable = fecp->ESW_PER; ++} ++/* ++ * enable or disable port n tx or rx ++ * tx_en 0 disable port n tx ++ * tx_en 1 enable port n tx ++ * rx_en 0 disbale port n rx ++ * rx_en 1 enable port n rx ++ */ ++int esw_port_enable_config(struct switch_enet_private *fep, ++ int port, int tx_en, int rx_en) ++{ ++ unsigned long tmp = 0; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ tmp = fecp->ESW_PER; ++ if (tx_en == 1) { ++ if (port == 0) ++ tmp |= MCF_ESW_PER_TE0; ++ else if (port == 1) ++ tmp |= MCF_ESW_PER_TE1; ++ else if (port == 2) ++ tmp |= MCF_ESW_PER_TE2; ++ else { ++ printk(KERN_ERR "%s:do not support the" ++ " port %x tx enable\n", ++ __func__, port); ++ return -1; ++ } ++ } else if (tx_en == 0) { ++ if (port == 0) ++ tmp &= (~MCF_ESW_PER_TE0); ++ else if (port == 1) ++ tmp &= (~MCF_ESW_PER_TE1); ++ else if (port == 2) ++ tmp &= (~MCF_ESW_PER_TE2); ++ else { ++ printk(KERN_ERR "%s:do not support " ++ "the port %x tx disable\n", ++ __func__, port); ++ return -2; ++ } ++ } else { ++ printk(KERN_ERR "%s:do not support the port %x" ++ " tx op value %x\n", ++ __func__, port, tx_en); ++ return -3; ++ } ++ ++ if (rx_en == 1) { ++ if (port == 0) ++ tmp |= MCF_ESW_PER_RE0; ++ else if (port == 1) ++ tmp |= MCF_ESW_PER_RE1; ++ else if (port == 2) ++ tmp |= MCF_ESW_PER_RE2; ++ else { ++ printk(KERN_ERR "%s:do not support the " ++ "port %x rx enable\n", ++ __func__, port); ++ return -4; ++ } ++ } else if (rx_en == 0) { ++ if (port == 0) ++ tmp &= (~MCF_ESW_PER_RE0); ++ else if (port == 1) ++ tmp &= (~MCF_ESW_PER_RE1); ++ else if (port == 2) ++ tmp &= (~MCF_ESW_PER_RE2); ++ else { ++ printk(KERN_ERR "%s:do not support the " ++ "port %x rx disable\n", ++ __func__, port); ++ return -5; ++ } ++ } else { ++ printk(KERN_ERR "%s:do not support the port %x" ++ " rx op value %x\n", ++ __func__, port, tx_en); ++ return -6; ++ } ++ ++ fecp->ESW_PER = tmp; ++ return 0; ++} ++ ++ ++void esw_get_port_broadcast(struct switch_enet_private *fep, ++ unsigned long *ulPortBroadcast) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulPortBroadcast = fecp->ESW_DBCR; ++} ++ ++int esw_port_broadcast_config(struct switch_enet_private *fep, ++ int port, int enable) ++{ ++ unsigned long tmp = 0; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port > 2) || (port < 0)) { ++ printk(KERN_ERR "%s:do not support the port %x" ++ " default broadcast\n", ++ __func__, port); ++ return -1; ++ } ++ ++ tmp = fecp->ESW_DBCR; ++ if (enable == 1) { ++ if (port == 0) ++ tmp |= MCF_ESW_DBCR_P0; ++ else if (port == 1) ++ tmp |= MCF_ESW_DBCR_P1; ++ else if (port == 2) ++ tmp |= MCF_ESW_DBCR_P2; ++ } else if (enable == 0) { ++ if (port == 0) ++ tmp &= ~MCF_ESW_DBCR_P0; ++ else if (port == 1) ++ tmp &= ~MCF_ESW_DBCR_P1; ++ else if (port == 2) ++ tmp &= ~MCF_ESW_DBCR_P2; ++ } ++ ++ fecp->ESW_DBCR = tmp; ++ return 0; ++} ++ ++ ++void esw_get_port_multicast(struct switch_enet_private *fep, ++ unsigned long *ulPortMulticast) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulPortMulticast = fecp->ESW_DMCR; ++} ++ ++int esw_port_multicast_config(struct switch_enet_private *fep, ++ int port, int enable) ++{ ++ unsigned long tmp = 0; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port > 2) || (port < 0)) { ++ printk(KERN_ERR "%s:do not support the port %x" ++ " default broadcast\n", ++ __func__, port); ++ return -1; ++ } ++ ++ tmp = fecp->ESW_DMCR; ++ if (enable == 1) { ++ if (port == 0) ++ tmp |= MCF_ESW_DMCR_P0; ++ else if (port == 1) ++ tmp |= MCF_ESW_DMCR_P1; ++ else if (port == 2) ++ tmp |= MCF_ESW_DMCR_P2; ++ } else if (enable == 0) { ++ if (port == 0) ++ tmp &= ~MCF_ESW_DMCR_P0; ++ else if (port == 1) ++ tmp &= ~MCF_ESW_DMCR_P1; ++ else if (port == 2) ++ tmp &= ~MCF_ESW_DMCR_P2; ++ } ++ ++ fecp->ESW_DMCR = tmp; ++ return 0; ++} ++ ++ ++void esw_get_port_blocking(struct switch_enet_private *fep, ++ unsigned long *ulPortBlocking) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulPortBlocking = (fecp->ESW_BKLR & 0x0000000f); ++} ++ ++int esw_port_blocking_config(struct switch_enet_private *fep, ++ int port, int enable) ++{ ++ unsigned long tmp = 0; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port > 2) || (port < 0)) { ++ printk(KERN_ERR "%s:do not support the port %x" ++ " default broadcast\n", ++ __func__, port); ++ return -1; ++ } ++ ++ tmp = fecp->ESW_BKLR; ++ if (enable == 1) { ++ if (port == 0) ++ tmp |= MCF_ESW_BKLR_BE0; ++ else if (port == 1) ++ tmp |= MCF_ESW_BKLR_BE1; ++ else if (port == 2) ++ tmp |= MCF_ESW_BKLR_BE2; ++ } else if (enable == 0) { ++ if (port == 0) ++ tmp &= ~MCF_ESW_BKLR_BE0; ++ else if (port == 1) ++ tmp &= ~MCF_ESW_BKLR_BE1; ++ else if (port == 2) ++ tmp &= ~MCF_ESW_BKLR_BE2; ++ } ++ ++ fecp->ESW_BKLR = tmp; ++ return 0; ++} ++ ++ ++void esw_get_port_learning(struct switch_enet_private *fep, ++ unsigned long *ulPortLearning) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulPortLearning = (fecp->ESW_BKLR & 0x000f0000) >> 16; ++} ++ ++int esw_port_learning_config(struct switch_enet_private *fep, ++ int port, int disable) ++{ ++ unsigned long tmp = 0; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port > 2) || (port < 0)) { ++ printk(KERN_ERR "%s:do not support the port %x" ++ " default broadcast\n", ++ __func__, port); ++ return -1; ++ } ++ ++ tmp = fecp->ESW_BKLR; ++ if (disable == 0) { ++ fep->learning_irqhandle_enable = 0; ++ if (port == 0) ++ tmp |= MCF_ESW_BKLR_LD0; ++ else if (port == 1) ++ tmp |= MCF_ESW_BKLR_LD1; ++ else if (port == 2) ++ tmp |= MCF_ESW_BKLR_LD2; ++ } else if (disable == 1) { ++ if (port == 0) ++ tmp &= ~MCF_ESW_BKLR_LD0; ++ else if (port == 1) ++ tmp &= ~MCF_ESW_BKLR_LD1; ++ else if (port == 2) ++ tmp &= ~MCF_ESW_BKLR_LD2; ++ } ++ ++ fecp->ESW_BKLR = tmp; ++ return 0; ++} ++/*********************************************************************/ ++void esw_mac_lookup_table_range(struct switch_enet_private *fep) ++{ ++ int index; ++ unsigned long read_lo, read_hi; ++ /* Pointer to switch address look up memory*/ ++ for (index = 0; index < 2048; index++) ++ write_atable(fep, index, index, (~index)); ++ ++ /* Pointer to switch address look up memory*/ ++ for (index = 0; index < 2048; index++) { ++ read_atable(fep, index, &read_lo, &read_hi); ++ if (read_lo != index) { ++ printk(KERN_ERR "%s:Mismatch at low %d\n", ++ __func__, index); ++ return; ++ } ++ ++ if (read_hi != (~index)) { ++ printk(KERN_ERR "%s:Mismatch at high %d\n", ++ __func__, index); ++ return; ++ } ++ } ++} ++ ++/* ++ * Checks IP Snoop options of handling the snooped frame. ++ * mode 0 : The snooped frame is forward only to management port ++ * mode 1 : The snooped frame is copy to management port and ++ * normal forwarding is checked. ++ * mode 2 : The snooped frame is discarded. ++ * mode 3 : Disable the ip snoop function ++ * ip_header_protocol : the IP header protocol field ++ */ ++int esw_ip_snoop_config(struct switch_enet_private *fep, ++ int mode, unsigned long ip_header_protocol) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp = 0, protocol_type = 0; ++ int num = 0; ++ ++ fecp = fep->hwp; ++ /* Config IP Snooping */ ++ if (mode == 0) { ++ /* Enable IP Snooping */ ++ tmp = MCF_ESW_IPSNP_EN; ++ tmp |= MCF_ESW_IPSNP_MODE(0);/*For Forward*/ ++ } else if (mode == 1) { ++ /* Enable IP Snooping */ ++ tmp = MCF_ESW_IPSNP_EN; ++ /*For Forward and copy_to_mangmnt_port*/ ++ tmp |= MCF_ESW_IPSNP_MODE(1); ++ } else if (mode == 2) { ++ /* Enable IP Snooping */ ++ tmp = MCF_ESW_IPSNP_EN; ++ tmp |= MCF_ESW_IPSNP_MODE(2);/*discard*/ ++ } else if (mode == 3) { ++ /* disable IP Snooping */ ++ tmp = MCF_ESW_IPSNP_EN; ++ tmp &= ~MCF_ESW_IPSNP_EN; ++ } else { ++ printk(KERN_ERR "%s: the mode %x " ++ "we do not support\n", __func__, mode); ++ return -1; ++ } ++ ++ protocol_type = ip_header_protocol; ++ for (num = 0; num < 8; num++) { ++ if (protocol_type == ++ AT_EXTRACT_IP_PROTOCOL(fecp->ESW_IPSNP[num])) { ++ fecp->ESW_IPSNP[num] = ++ tmp | MCF_ESW_IPSNP_PROTOCOL(protocol_type); ++ break; ++ } else if (!(fecp->ESW_IPSNP[num])) { ++ fecp->ESW_IPSNP[num] = ++ tmp | MCF_ESW_IPSNP_PROTOCOL(protocol_type); ++ break; ++ } ++ } ++ if (num == 8) { ++ printk(KERN_INFO "IP snooping table is full\n"); ++ return 0; ++ } ++ ++ return 0; ++} ++ ++void esw_get_ip_snoop_config(struct switch_enet_private *fep, ++ unsigned long *ulpESW_IPSNP) ++{ ++ int i; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ for (i = 0; i < 8; i++) ++ *(ulpESW_IPSNP + i) = fecp->ESW_IPSNP[i]; ++} ++/* ++ * Checks TCP/UDP Port Snoop options of handling the snooped frame. ++ * mode 0 : The snooped frame is forward only to management port ++ * mode 1 : The snooped frame is copy to management port and ++ * normal forwarding is checked. ++ * mode 2 : The snooped frame is discarded. ++ * mode 3 : Disable the TCP/UDP port snoop function ++ * compare_port : port number in the TCP/UDP header ++ * compare_num 1: TCP/UDP source port number is compared ++ * compare_num 2: TCP/UDP destination port number is compared ++ * compare_num 3: TCP/UDP source and destination port number is compared ++ */ ++int esw_tcpudp_port_snoop_config(struct switch_enet_private *fep, ++ int mode, int compare_port, int compare_num) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp; ++ int num; ++ ++ fecp = fep->hwp; ++ ++ /* Enable TCP/UDP port Snooping */ ++ tmp = MCF_ESW_PSNP_EN; ++ if (mode == 0) ++ tmp |= MCF_ESW_PSNP_MODE(0);/*For Forward*/ ++ else if (mode == 1)/*For Forward and copy_to_mangmnt_port*/ ++ tmp |= MCF_ESW_PSNP_MODE(1); ++ else if (mode == 2) ++ tmp |= MCF_ESW_PSNP_MODE(2);/*discard*/ ++ else if (mode == 3) /*disable the port function*/ ++ tmp &= (~MCF_ESW_PSNP_EN); ++ else { ++ printk(KERN_ERR "%s: the mode %x we do not support\n", ++ __func__, mode); ++ return -1; ++ } ++ ++ if (compare_num == 1) ++ tmp |= MCF_ESW_PSNP_CS; ++ else if (compare_num == 2) ++ tmp |= MCF_ESW_PSNP_CD; ++ else if (compare_num == 3) ++ tmp |= MCF_ESW_PSNP_CD | MCF_ESW_PSNP_CS; ++ else { ++ printk(KERN_ERR "%s: the compare port address %x" ++ " we do not support\n", ++ __func__, compare_num); ++ return -1; ++ } ++ ++ for (num = 0; num < 8; num++) { ++ if (compare_port == ++ AT_EXTRACT_TCP_UDP_PORT(fecp->ESW_PSNP[num])) { ++ fecp->ESW_PSNP[num] = ++ tmp | MCF_ESW_PSNP_PORT_COMPARE(compare_port); ++ break; ++ } else if (!(fecp->ESW_PSNP[num])) { ++ fecp->ESW_PSNP[num] = ++ tmp | MCF_ESW_PSNP_PORT_COMPARE(compare_port); ++ break; ++ } ++ } ++ if (num == 8) { ++ printk(KERN_INFO "TCP/UDP port snooping table is full\n"); ++ return 0; ++ } ++ ++ return 0; ++} ++ ++void esw_get_tcpudp_port_snoop_config( ++ struct switch_enet_private *fep, ++ unsigned long *ulpESW_PSNP) ++{ ++ int i; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ for (i = 0; i < 8; i++) ++ *(ulpESW_PSNP + i) = fecp->ESW_PSNP[i]; ++} ++/*-----------------mirror----------------------------------------*/ ++void esw_get_port_mirroring(struct switch_enet_private *fep) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ printk(KERN_INFO "Mirror Port: %1ld Egress Port Match:%s " ++ "Ingress Port Match:%s\n", fecp->ESW_MCR & 0xf, ++ (fecp->ESW_MCR >> 6) & 1 ? "Y" : "N", ++ (fecp->ESW_MCR >> 5) & 1 ? "Y" : "N"); ++ ++ if ((fecp->ESW_MCR >> 6) & 1) ++ printk(KERN_INFO "Egress Port to be mirrored: Port %ld\n", ++ fecp->ESW_EGMAP >> 1); ++ if ((fecp->ESW_MCR >> 5) & 1) ++ printk(KERN_INFO "Ingress Port to be mirrored: Port %ld\n", ++ fecp->ESW_INGMAP >> 1); ++ ++ printk(KERN_INFO "Egress Des Address Match:%s " ++ "Egress Src Address Match:%s\n", ++ (fecp->ESW_MCR >> 10) & 1 ? "Y" : "N", ++ (fecp->ESW_MCR >> 9) & 1 ? "Y" : "N"); ++ printk(KERN_INFO "Ingress Des Address Match:%s " ++ "Ingress Src Address Match:%s\n", ++ (fecp->ESW_MCR >> 8) & 1 ? "Y" : "N", ++ (fecp->ESW_MCR >> 7) & 1 ? "Y" : "N"); ++ ++ if ((fecp->ESW_MCR >> 10) & 1) ++ printk(KERN_INFO "Egress Des Address to be mirrored: " ++ "%02lx-%02lx-%02lx-%02lx-%02lx-%02lx\n", ++ fecp->ESW_ENGDAL & 0xff, (fecp->ESW_ENGDAL >> 8) & 0xff, ++ (fecp->ESW_ENGDAL >> 16) & 0xff, ++ (fecp->ESW_ENGDAL >> 24) & 0xff, ++ fecp->ESW_ENGDAH & 0xff, ++ (fecp->ESW_ENGDAH >> 8) & 0xff); ++ if ((fecp->ESW_MCR >> 9) & 1) ++ printk("Egress Src Address to be mirrored: " ++ "%02lx-%02lx-%02lx-%02lx-%02lx-%02lx\n", ++ fecp->ESW_ENGSAL & 0xff, (fecp->ESW_ENGSAL >> 8) & 0xff, ++ (fecp->ESW_ENGSAL >> 16) & 0xff, ++ (fecp->ESW_ENGSAL >> 24) & 0xff, ++ fecp->ESW_ENGSAH & 0xff, ++ (fecp->ESW_ENGSAH >> 8) & 0xff); ++ if ((fecp->ESW_MCR >> 8) & 1) ++ printk("Ingress Des Address to be mirrored: " ++ "%02lx-%02lx-%02lx-%02lx-%02lx-%02lx\n", ++ fecp->ESW_INGDAL & 0xff, (fecp->ESW_INGDAL >> 8) & 0xff, ++ (fecp->ESW_INGDAL >> 16) & 0xff, ++ (fecp->ESW_INGDAL >> 24) & 0xff, ++ fecp->ESW_INGDAH & 0xff, ++ (fecp->ESW_INGDAH >> 8) & 0xff); ++ if ((fecp->ESW_MCR >> 7) & 1) ++ printk("Ingress Src Address to be mirrored: " ++ "%02lx-%02lx-%02lx-%02lx-%02lx-%02lx\n", ++ fecp->ESW_INGSAL & 0xff, (fecp->ESW_INGSAL >> 8) & 0xff, ++ (fecp->ESW_INGSAL >> 16) & 0xff, ++ (fecp->ESW_INGSAL >> 24) & 0xff, ++ fecp->ESW_INGSAH & 0xff, ++ (fecp->ESW_INGSAH >> 8) & 0xff); ++} ++ ++int esw_port_mirroring_config_port_match(struct switch_enet_private *fep, ++ int mirror_port, int port_match_en, int port) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp = 0; ++ ++ fecp = fep->hwp; ++ ++ tmp = fecp->ESW_MCR; ++ if (mirror_port != (tmp & 0xf)) ++ tmp = 0; ++ ++ switch (port_match_en) { ++ case MIRROR_EGRESS_PORT_MATCH: ++ tmp |= MCF_ESW_MCR_EGMAP; ++ if (port == 0) ++ fecp->ESW_EGMAP = MCF_ESW_EGMAP_EG0; ++ else if (port == 1) ++ fecp->ESW_EGMAP = MCF_ESW_EGMAP_EG1; ++ else if (port == 2) ++ fecp->ESW_EGMAP = MCF_ESW_EGMAP_EG2; ++ break; ++ case MIRROR_INGRESS_PORT_MATCH: ++ tmp |= MCF_ESW_MCR_INGMAP; ++ if (port == 0) ++ fecp->ESW_INGMAP = MCF_ESW_INGMAP_ING0; ++ else if (port == 1) ++ fecp->ESW_INGMAP = MCF_ESW_INGMAP_ING1; ++ else if (port == 2) ++ fecp->ESW_INGMAP = MCF_ESW_INGMAP_ING2; ++ break; ++ default: ++ tmp = 0; ++ break; ++ } ++ ++ tmp = tmp & 0x07e0; ++ if (port_match_en) ++ tmp |= MCF_ESW_MCR_MEN | MCF_ESW_MCR_PORT(mirror_port); ++ ++ fecp->ESW_MCR = tmp; ++ return 0; ++} ++ ++int esw_port_mirroring_config(struct switch_enet_private *fep, ++ int mirror_port, int port, int mirror_enable, ++ unsigned char *src_mac, unsigned char *des_mac, ++ int egress_en, int ingress_en, ++ int egress_mac_src_en, int egress_mac_des_en, ++ int ingress_mac_src_en, int ingress_mac_des_en) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp; ++ ++ fecp = fep->hwp; ++ ++ /*mirroring config*/ ++ tmp = 0; ++ if (egress_en == 1) { ++ tmp |= MCF_ESW_MCR_EGMAP; ++ if (port == 0) ++ fecp->ESW_EGMAP = MCF_ESW_EGMAP_EG0; ++ else if (port == 1) ++ fecp->ESW_EGMAP = MCF_ESW_EGMAP_EG1; ++ else if (port == 2) ++ fecp->ESW_EGMAP = MCF_ESW_EGMAP_EG2; ++ else { ++ printk(KERN_ERR "%s: the port %x we do not support\n", ++ __func__, port); ++ return -1; ++ } ++ } else if (egress_en == 0) { ++ tmp &= (~MCF_ESW_MCR_EGMAP); ++ } else { ++ printk(KERN_ERR "%s: egress_en %x we do not support\n", ++ __func__, egress_en); ++ return -1; ++ } ++ ++ if (ingress_en == 1) { ++ tmp |= MCF_ESW_MCR_INGMAP; ++ if (port == 0) ++ fecp->ESW_INGMAP = MCF_ESW_INGMAP_ING0; ++ else if (port == 1) ++ fecp->ESW_INGMAP = MCF_ESW_INGMAP_ING1; ++ else if (port == 2) ++ fecp->ESW_INGMAP = MCF_ESW_INGMAP_ING2; ++ else { ++ printk(KERN_ERR "%s: the port %x we do not support\n", ++ __func__, port); ++ return -1; ++ } ++ } else if (ingress_en == 0) { ++ tmp &= ~MCF_ESW_MCR_INGMAP; ++ } else{ ++ printk(KERN_ERR "%s: ingress_en %x we do not support\n", ++ __func__, ingress_en); ++ return -1; ++ } ++ ++ if (egress_mac_src_en == 1) { ++ tmp |= MCF_ESW_MCR_EGSA; ++ fecp->ESW_ENGSAH = (src_mac[5] << 8) | (src_mac[4]); ++ fecp->ESW_ENGSAL = (unsigned long)((src_mac[3] << 24) | ++ (src_mac[2] << 16) | ++ (src_mac[1] << 8) | ++ src_mac[0]); ++ } else if (egress_mac_src_en == 0) { ++ tmp &= ~MCF_ESW_MCR_EGSA; ++ } else { ++ printk(KERN_ERR "%s: egress_mac_src_en %x we do not support\n", ++ __func__, egress_mac_src_en); ++ return -1; ++ } ++ ++ if (egress_mac_des_en == 1) { ++ tmp |= MCF_ESW_MCR_EGDA; ++ fecp->ESW_ENGDAH = (des_mac[5] << 8) | (des_mac[4]); ++ fecp->ESW_ENGDAL = (unsigned long)((des_mac[3] << 24) | ++ (des_mac[2] << 16) | ++ (des_mac[1] << 8) | ++ des_mac[0]); ++ } else if (egress_mac_des_en == 0) { ++ tmp &= ~MCF_ESW_MCR_EGDA; ++ } else { ++ printk(KERN_ERR "%s: egress_mac_des_en %x we do not support\n", ++ __func__, egress_mac_des_en); ++ return -1; ++ } ++ ++ if (ingress_mac_src_en == 1) { ++ tmp |= MCF_ESW_MCR_INGSA; ++ fecp->ESW_INGSAH = (src_mac[5] << 8) | (src_mac[4]); ++ fecp->ESW_INGSAL = (unsigned long)((src_mac[3] << 24) | ++ (src_mac[2] << 16) | ++ (src_mac[1] << 8) | ++ src_mac[0]); ++ } else if (ingress_mac_src_en == 0) { ++ tmp &= ~MCF_ESW_MCR_INGSA; ++ } else { ++ printk(KERN_ERR "%s: ingress_mac_src_en %x we do not support\n", ++ __func__, ingress_mac_src_en); ++ return -1; ++ } ++ ++ if (ingress_mac_des_en == 1) { ++ tmp |= MCF_ESW_MCR_INGDA; ++ fecp->ESW_INGDAH = (des_mac[5] << 8) | (des_mac[4]); ++ fecp->ESW_INGDAL = (unsigned long)((des_mac[3] << 24) | ++ (des_mac[2] << 16) | ++ (des_mac[1] << 8) | ++ des_mac[0]); ++ } else if (ingress_mac_des_en == 0) { ++ tmp &= ~MCF_ESW_MCR_INGDA; ++ } else { ++ printk(KERN_ERR "%s: ingress_mac_des_en %x we do not support\n", ++ __func__, ingress_mac_des_en); ++ return -1; ++ } ++ ++ if (mirror_enable == 1) ++ tmp |= MCF_ESW_MCR_MEN | MCF_ESW_MCR_PORT(mirror_port); ++ else if (mirror_enable == 0) ++ tmp &= ~MCF_ESW_MCR_MEN; ++ else ++ printk(KERN_ERR "%s: the mirror enable %x is error\n", ++ __func__, mirror_enable); ++ ++ ++ fecp->ESW_MCR = tmp; ++ return 0; ++} ++ ++int esw_port_mirroring_config_addr_match(struct switch_enet_private *fep, ++ int mirror_port, int addr_match_enable, unsigned char *mac_addr) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp = 0; ++ ++ fecp = fep->hwp; ++ ++ tmp = fecp->ESW_MCR; ++ if (mirror_port != (tmp & 0xf)) ++ tmp = 0; ++ ++ switch (addr_match_enable) { ++ case MIRROR_EGRESS_SOURCE_MATCH: ++ tmp |= MCF_ESW_MCR_EGSA; ++ fecp->ESW_ENGSAH = (mac_addr[5] << 8) | (mac_addr[4]); ++ fecp->ESW_ENGSAL = (unsigned long)((mac_addr[3] << 24) | ++ (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0]); ++ break; ++ case MIRROR_INGRESS_SOURCE_MATCH: ++ tmp |= MCF_ESW_MCR_INGSA; ++ fecp->ESW_INGSAH = (mac_addr[5] << 8) | (mac_addr[4]); ++ fecp->ESW_INGSAL = (unsigned long)((mac_addr[3] << 24) | ++ (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0]); ++ break; ++ case MIRROR_EGRESS_DESTINATION_MATCH: ++ tmp |= MCF_ESW_MCR_EGDA; ++ fecp->ESW_ENGDAH = (mac_addr[5] << 8) | (mac_addr[4]); ++ fecp->ESW_ENGDAL = (unsigned long)((mac_addr[3] << 24) | ++ (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0]); ++ break; ++ case MIRROR_INGRESS_DESTINATION_MATCH: ++ tmp |= MCF_ESW_MCR_INGDA; ++ fecp->ESW_INGDAH = (mac_addr[5] << 8) | (mac_addr[4]); ++ fecp->ESW_INGDAL = (unsigned long)((mac_addr[3] << 24) | ++ (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0]); ++ break; ++ default: ++ tmp = 0; ++ break; ++ } ++ ++ tmp = tmp & 0x07e0; ++ if (addr_match_enable) ++ tmp |= MCF_ESW_MCR_MEN | MCF_ESW_MCR_PORT(mirror_port); ++ ++ fecp->ESW_MCR = tmp; ++ return 0; ++} ++ ++void esw_get_vlan_verification(struct switch_enet_private *fep, ++ unsigned long *ulValue) ++{ ++ volatile switch_t *fecp; ++ fecp = fep->hwp; ++ *ulValue = fecp->ESW_VLANV; ++} ++ ++int esw_set_vlan_verification(struct switch_enet_private *fep, int port, ++ int vlan_domain_verify_en, int vlan_discard_unknown_en) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ if ((port < 0) || (port > 2)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ ++ if (vlan_domain_verify_en == 1) { ++ if (port == 0) ++ fecp->ESW_VLANV |= MCF_ESW_VLANV_VV0; ++ else if (port == 1) ++ fecp->ESW_VLANV |= MCF_ESW_VLANV_VV1; ++ else if (port == 2) ++ fecp->ESW_VLANV |= MCF_ESW_VLANV_VV2; ++ } else if (vlan_domain_verify_en == 0) { ++ if (port == 0) ++ fecp->ESW_VLANV &= ~MCF_ESW_VLANV_VV0; ++ else if (port == 1) ++ fecp->ESW_VLANV &= ~MCF_ESW_VLANV_VV1; ++ else if (port == 2) ++ fecp->ESW_VLANV &= ~MCF_ESW_VLANV_VV2; ++ } else { ++ printk(KERN_INFO "%s: donot support " ++ "vlan_domain_verify %x\n", ++ __func__, vlan_domain_verify_en); ++ return -2; ++ } ++ ++ if (vlan_discard_unknown_en == 1) { ++ if (port == 0) ++ fecp->ESW_VLANV |= MCF_ESW_VLANV_DU0; ++ else if (port == 1) ++ fecp->ESW_VLANV |= MCF_ESW_VLANV_DU1; ++ else if (port == 2) ++ fecp->ESW_VLANV |= MCF_ESW_VLANV_DU2; ++ } else if (vlan_discard_unknown_en == 0) { ++ if (port == 0) ++ fecp->ESW_VLANV &= ~MCF_ESW_VLANV_DU0; ++ else if (port == 1) ++ fecp->ESW_VLANV &= ~MCF_ESW_VLANV_DU1; ++ else if (port == 2) ++ fecp->ESW_VLANV &= ~MCF_ESW_VLANV_DU2; ++ } else { ++ printk(KERN_INFO "%s: donot support " ++ "vlan_discard_unknown %x\n", ++ __func__, vlan_discard_unknown_en); ++ return -3; ++ } ++ ++ return 0; ++} ++ ++void esw_get_vlan_resolution_table(struct switch_enet_private *fep, ++ struct eswVlanTableItem *tableaddr) ++{ ++ volatile switch_t *fecp; ++ int vnum = 0; ++ int i; ++ ++ fecp = fep->hwp; ++ for (i = 0; i < 32; i++) { ++ if (fecp->ESW_VRES[i]) { ++ tableaddr->table[i].port_vlanid = ++ fecp->ESW_VRES[i] >> 3; ++ tableaddr->table[i].vlan_domain_port = ++ fecp->ESW_VRES[i] & 7; ++ vnum++; ++ } ++ } ++ tableaddr->valid_num = vnum; ++} ++ ++int esw_set_vlan_id(struct switch_enet_private *fep, unsigned long configData) ++{ ++ volatile switch_t *fecp; ++ int i; ++ ++ fecp = fep->hwp; ++ ++ for (i = 0; i < 32; i++) { ++ if (fecp->ESW_VRES[i] == 0) { ++ fecp->ESW_VRES[i] = MCF_ESW_VRES_VLANID(configData); ++ return 0; ++ } else if (((fecp->ESW_VRES[i] >> 3) & 0xfff) == configData) { ++ printk(KERN_INFO "The VLAN already exists\n"); ++ return 0; ++ } ++ } ++ ++ printk(KERN_INFO "The VLAN can't create, because VLAN table is full\n"); ++ return 0; ++} ++ ++int esw_set_vlan_id_cleared(struct switch_enet_private *fep, ++ unsigned long configData) ++{ ++ volatile switch_t *fecp; ++ int i; ++ ++ fecp = fep->hwp; ++ ++ for (i = 0; i < 32; i++) { ++ if (((fecp->ESW_VRES[i] >> 3) & 0xfff) == configData) { ++ fecp->ESW_VRES[i] = 0; ++ break; ++ } ++ } ++ return 0; ++} ++ ++int esw_set_port_in_vlan_id(struct switch_enet_private *fep, ++ eswIoctlVlanResoultionTable configData) ++{ ++ volatile switch_t *fecp; ++ int i; ++ int lastnum = 0; ++ ++ fecp = fep->hwp; ++ ++ for (i = 0; i < 32; i++) { ++ if (fecp->ESW_VRES[i] == 0) { ++ lastnum = i; ++ break; ++ } else if (((fecp->ESW_VRES[i] >> 3) & 0xfff) == ++ configData.port_vlanid) { ++ /* update the port members of this vlan */ ++ fecp->ESW_VRES[i] |= 1 << configData.vlan_domain_port; ++ return 0; ++ } ++ } ++ /* creat a new vlan in vlan table */ ++ fecp->ESW_VRES[lastnum] = MCF_ESW_VRES_VLANID(configData.port_vlanid) | ++ (1 << configData.vlan_domain_port); ++ return 0; ++} ++ ++int esw_set_vlan_resolution_table(struct switch_enet_private *fep, ++ unsigned short port_vlanid, int vlan_domain_num, ++ int vlan_domain_port) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ if ((vlan_domain_num < 0) ++ || (vlan_domain_num > 31)) { ++ printk(KERN_ERR "%s: do not support the " ++ "vlan_domain_num %d\n", ++ __func__, vlan_domain_num); ++ return -1; ++ } ++ ++ if ((vlan_domain_port < 0) ++ || (vlan_domain_port > 7)) { ++ printk(KERN_ERR "%s: do not support the " ++ "vlan_domain_port %d\n", ++ __func__, vlan_domain_port); ++ return -2; ++ } ++ ++ fecp->ESW_VRES[vlan_domain_num] = ++ MCF_ESW_VRES_VLANID(port_vlanid) ++ | vlan_domain_port; ++ ++ return 0; ++} ++ ++void esw_get_vlan_input_config(struct switch_enet_private *fep, ++ eswIoctlVlanInputStatus *pVlanInputConfig) ++{ ++ volatile switch_t *fecp; ++ int i; ++ ++ fecp = fep->hwp; ++ for (i = 0; i < 3; i++) ++ pVlanInputConfig->ESW_PID[i] = fecp->ESW_PID[i]; ++ ++ pVlanInputConfig->ESW_VLANV = fecp->ESW_VLANV; ++ pVlanInputConfig->ESW_VIMSEL = fecp->ESW_VIMSEL; ++ pVlanInputConfig->ESW_VIMEN = fecp->ESW_VIMEN; ++ ++ for (i = 0; i < 32; i++) ++ pVlanInputConfig->ESW_VRES[i] = fecp->ESW_VRES[i]; ++} ++ ++ ++int esw_vlan_input_process(struct switch_enet_private *fep, ++ int port, int mode, unsigned short port_vlanid) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((mode < 0) || (mode > 5)) { ++ printk(KERN_ERR "%s: do not support the" ++ " VLAN input processing mode %d\n", ++ __func__, mode); ++ return -1; ++ } ++ ++ if ((port < 0) || (port > 3)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, mode); ++ return -2; ++ } ++ ++ fecp->ESW_PID[port] = MCF_ESW_PID_VLANID(port_vlanid); ++ if (port == 0) { ++ if (mode == 4) ++ fecp->ESW_VIMEN &= ~MCF_ESW_VIMEN_EN0; ++ else ++ fecp->ESW_VIMEN |= MCF_ESW_VIMEN_EN0; ++ ++ fecp->ESW_VIMSEL &= ~MCF_ESW_VIMSEL_IM0(3); ++ fecp->ESW_VIMSEL |= MCF_ESW_VIMSEL_IM0(mode); ++ } else if (port == 1) { ++ if (mode == 4) ++ fecp->ESW_VIMEN &= ~MCF_ESW_VIMEN_EN1; ++ else ++ fecp->ESW_VIMEN |= MCF_ESW_VIMEN_EN1; ++ ++ fecp->ESW_VIMSEL &= ~MCF_ESW_VIMSEL_IM1(3); ++ fecp->ESW_VIMSEL |= MCF_ESW_VIMSEL_IM1(mode); ++ } else if (port == 2) { ++ if (mode == 4) ++ fecp->ESW_VIMEN &= ~MCF_ESW_VIMEN_EN2; ++ else ++ fecp->ESW_VIMEN |= MCF_ESW_VIMEN_EN2; ++ ++ fecp->ESW_VIMSEL &= ~MCF_ESW_VIMSEL_IM2(3); ++ fecp->ESW_VIMSEL |= MCF_ESW_VIMSEL_IM2(mode); ++ } else { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -2; ++ } ++ ++ return 0; ++} ++ ++void esw_get_vlan_output_config(struct switch_enet_private *fep, ++ unsigned long *ulVlanOutputConfig) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulVlanOutputConfig = fecp->ESW_VOMSEL; ++} ++ ++int esw_vlan_output_process(struct switch_enet_private *fep, ++ int port, int mode) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port < 0) || (port > 2)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, mode); ++ return -1; ++ } ++ ++ if (port == 0) { ++ fecp->ESW_VOMSEL &= ~MCF_ESW_VOMSEL_OM0(3); ++ fecp->ESW_VOMSEL |= MCF_ESW_VOMSEL_OM0(mode); ++ } else if (port == 1) { ++ fecp->ESW_VOMSEL &= ~MCF_ESW_VOMSEL_OM1(3); ++ fecp->ESW_VOMSEL |= MCF_ESW_VOMSEL_OM1(mode); ++ } else if (port == 2) { ++ fecp->ESW_VOMSEL &= ~MCF_ESW_VOMSEL_OM2(3); ++ fecp->ESW_VOMSEL |= MCF_ESW_VOMSEL_OM2(mode); ++ } else { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/*------------frame calssify and priority resolution------------*/ ++/*vlan priority lookup*/ ++int esw_framecalssify_vlan_priority_lookup(struct switch_enet_private *fep, ++ int port, int func_enable, int vlan_pri_table_num, ++ int vlan_pri_table_value) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port < 0) || (port > 3)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ ++ if (func_enable == 0) { ++ fecp->ESW_PRES[port] &= ~MCF_ESW_PRES_VLAN; ++ printk(KERN_ERR "%s: disable port %d VLAN priority " ++ "lookup function\n", __func__, port); ++ return 0; ++ } ++ ++ if ((vlan_pri_table_num < 0) || (vlan_pri_table_num > 7)) { ++ printk(KERN_ERR "%s: do not support the priority %d\n", ++ __func__, vlan_pri_table_num); ++ return -1; ++ } ++ ++ fecp->ESW_PVRES[port] |= ((vlan_pri_table_value & 0x3) ++ << (vlan_pri_table_num*3)); ++ /* enable port VLAN priority lookup function*/ ++ fecp->ESW_PRES[port] |= MCF_ESW_PRES_VLAN; ++ return 0; ++} ++ ++int esw_framecalssify_ip_priority_lookup(struct switch_enet_private *fep, ++ int port, int func_enable, int ipv4_en, int ip_priority_num, ++ int ip_priority_value) ++{ ++ volatile switch_t *fecp; ++ unsigned long tmp = 0, tmp_prio = 0; ++ ++ fecp = fep->hwp; ++ ++ if ((port < 0) || (port > 3)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ ++ if (func_enable == 0) { ++ fecp->ESW_PRES[port] &= ~MCF_ESW_PRES_IP; ++ printk(KERN_ERR "%s: disable port %d ip priority " ++ "lookup function\n", __func__, port); ++ return 0; ++ } ++ ++ /* IPV4 priority 64 entry table lookup*/ ++ /* IPv4 head 6 bit TOS field*/ ++ if (ipv4_en == 1) { ++ if ((ip_priority_num < 0) || (ip_priority_num > 63)) { ++ printk(KERN_ERR "%s: do not support the table entry %d\n", ++ __func__, ip_priority_num); ++ return -2; ++ } ++ } else { /* IPV6 priority 256 entry table lookup*/ ++ /* IPv6 head 8 bit COS field*/ ++ if ((ip_priority_num < 0) || (ip_priority_num > 255)) { ++ printk(KERN_ERR "%s: do not support the table entry %d\n", ++ __func__, ip_priority_num); ++ return -3; ++ } ++ } ++ ++ /* IP priority table lookup : address*/ ++ tmp = MCF_ESW_IPRES_ADDRESS(ip_priority_num); ++ /* IP priority table lookup : ipv4sel*/ ++ if (ipv4_en == 1) ++ tmp = tmp | MCF_ESW_IPRES_IPV4SEL; ++ /* IP priority table lookup : priority*/ ++ if (port == 0) ++ tmp |= MCF_ESW_IPRES_PRI0(ip_priority_value); ++ else if (port == 1) ++ tmp |= MCF_ESW_IPRES_PRI1(ip_priority_value); ++ else if (port == 2) ++ tmp |= MCF_ESW_IPRES_PRI2(ip_priority_value); ++ ++ /* configure*/ ++ fecp->ESW_IPRES = MCF_ESW_IPRES_READ | ++ MCF_ESW_IPRES_ADDRESS(ip_priority_num); ++ tmp_prio = fecp->ESW_IPRES; ++ ++ fecp->ESW_IPRES = tmp | tmp_prio; ++ ++ fecp->ESW_IPRES = MCF_ESW_IPRES_READ | ++ MCF_ESW_IPRES_ADDRESS(ip_priority_num); ++ tmp_prio = fecp->ESW_IPRES; ++ ++ /* enable port IP priority lookup function*/ ++ fecp->ESW_PRES[port] |= MCF_ESW_PRES_IP; ++ return 0; ++} ++ ++int esw_framecalssify_mac_priority_lookup( ++ struct switch_enet_private *fep, int port) ++{ ++ volatile switch_t *fecp; ++ ++ if ((port < 0) || (port > 3)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ ++ fecp = fep->hwp; ++ fecp->ESW_PRES[port] |= MCF_ESW_PRES_MAC; ++ ++ return 0; ++} ++ ++int esw_frame_calssify_priority_init(struct switch_enet_private *fep, ++ int port, unsigned char priority_value) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if ((port < 0) || (port > 3)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ /*disable all priority lookup function*/ ++ fecp->ESW_PRES[port] = 0; ++ fecp->ESW_PRES[port] = MCF_ESW_PRES_DFLT_PRI(priority_value & 0x7); ++ ++ return 0; ++} ++ ++/*---------------------------------------------------------------------------*/ ++int esw_get_statistics_status(struct switch_enet_private *fep, ++ esw_statistics_status *pStatistics) ++{ ++ volatile switch_t *fecp; ++ fecp = fep->hwp; ++ ++ pStatistics->ESW_DISCN = fecp->ESW_DISCN; ++ pStatistics->ESW_DISCB = fecp->ESW_DISCB; ++ pStatistics->ESW_NDISCN = fecp->ESW_NDISCN; ++ pStatistics->ESW_NDISCB = fecp->ESW_NDISCB; ++ return 0; ++} ++ ++int esw_get_port_statistics_status(struct switch_enet_private *fep, ++ int port, esw_port_statistics_status *pPortStatistics) ++{ ++ volatile switch_t *fecp; ++ ++ if ((port < 0) || (port > 3)) { ++ printk(KERN_ERR "%s: do not support the port %d\n", ++ __func__, port); ++ return -1; ++ } ++ ++ fecp = fep->hwp; ++ ++ pPortStatistics->MCF_ESW_POQC = ++ fecp->port_statistics_status[port].MCF_ESW_POQC; ++ pPortStatistics->MCF_ESW_PMVID = ++ fecp->port_statistics_status[port].MCF_ESW_PMVID; ++ pPortStatistics->MCF_ESW_PMVTAG = ++ fecp->port_statistics_status[port].MCF_ESW_PMVTAG; ++ pPortStatistics->MCF_ESW_PBL = ++ fecp->port_statistics_status[port].MCF_ESW_PBL; ++ return 0; ++} ++/*----------------------------------------------------------------------*/ ++int esw_get_output_queue_status(struct switch_enet_private *fep, ++ esw_output_queue_status *pOutputQueue) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ pOutputQueue->ESW_MMSR = fecp->ESW_MMSR; ++ pOutputQueue->ESW_LMT = fecp->ESW_LMT; ++ pOutputQueue->ESW_LFC = fecp->ESW_LFC; ++ pOutputQueue->ESW_IOSR = fecp->ESW_IOSR; ++ pOutputQueue->ESW_PCSR = fecp->ESW_PCSR; ++ pOutputQueue->ESW_QWT = fecp->ESW_QWT; ++ pOutputQueue->ESW_P0BCT = fecp->ESW_P0BCT; ++ return 0; ++} ++ ++/* set output queue memory status and configure*/ ++int esw_set_output_queue_memory(struct switch_enet_private *fep, ++ int fun_num, esw_output_queue_status *pOutputQueue) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if (fun_num == 1) { ++ /* memory manager status*/ ++ fecp->ESW_MMSR = pOutputQueue->ESW_MMSR; ++ } else if (fun_num == 2) { ++ /*low memory threshold*/ ++ fecp->ESW_LMT = pOutputQueue->ESW_LMT; ++ } else if (fun_num == 3) { ++ /*lowest number of free cells*/ ++ fecp->ESW_LFC = pOutputQueue->ESW_LFC; ++ } else if (fun_num == 4) { ++ /*queue weights*/ ++ fecp->ESW_QWT = pOutputQueue->ESW_QWT; ++ } else if (fun_num == 5) { ++ /*port 0 backpressure congenstion thresled*/ ++ fecp->ESW_P0BCT = pOutputQueue->ESW_P0BCT; ++ } else { ++ printk(KERN_ERR "%s: do not support the cmd %x\n", ++ __func__, fun_num); ++ return -1; ++ } ++ return 0; ++} ++/*--------------------------------------------------------------------*/ ++int esw_get_irq_status(struct switch_enet_private *fep, ++ eswIoctlIrqStatus *pIrqStatus) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ pIrqStatus->isr = fecp->switch_ievent; ++ pIrqStatus->imr = fecp->switch_imask; ++ pIrqStatus->rx_buf_pointer = fecp->fec_r_des_start; ++ pIrqStatus->tx_buf_pointer = fecp->fec_x_des_start; ++ pIrqStatus->rx_max_size = fecp->fec_r_buff_size; ++ pIrqStatus->rx_buf_active = fecp->fec_r_des_active; ++ pIrqStatus->tx_buf_active = fecp->fec_x_des_active; ++ return 0; ++} ++ ++int esw_set_irq_mask(struct switch_enet_private *fep, ++ unsigned long mask, int enable) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ ++ if (enable == 1) ++ fecp->switch_imask |= mask; ++ else if (enable == 1) ++ fecp->switch_imask &= (~mask); ++ else { ++ printk(KERN_INFO "%s: enable %lx is error value\n", ++ __func__, mask); ++ return -1; ++ } ++ return 0; ++} ++ ++void esw_clear_irq_event(struct switch_enet_private *fep, ++ unsigned long mask) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ fecp->switch_ievent |= mask; ++} ++ ++void esw_get_switch_mode(struct switch_enet_private *fep, ++ unsigned long *ulModeConfig) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulModeConfig = fecp->ESW_MODE; ++} ++ ++void esw_switch_mode_configure(struct switch_enet_private *fep, ++ unsigned long configure) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ fecp->ESW_MODE |= configure; ++} ++ ++void esw_get_bridge_port(struct switch_enet_private *fep, ++ unsigned long *ulBMPConfig) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ *ulBMPConfig = fecp->ESW_BMPC; ++} ++ ++void esw_bridge_port_configure(struct switch_enet_private *fep, ++ unsigned long configure) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ fecp->ESW_BMPC = configure; ++} ++ ++int esw_get_port_all_status(struct switch_enet_private *fep, ++ unsigned char portnum, struct port_all_status *port_alstatus) ++{ ++ volatile switch_t *fecp; ++ unsigned long PortBlocking; ++ unsigned long PortLearning; ++ unsigned long VlanVerify; ++ unsigned long DiscardUnknown; ++ unsigned long MultiReso; ++ unsigned long BroadReso; ++ unsigned long FTransmit; ++ unsigned long FReceive; ++ ++ fecp = fep->hwp; ++ PortBlocking = fecp->ESW_BKLR & 0x0000000f; ++ PortLearning = (fecp->ESW_BKLR & 0x000f0000) >> 16; ++ VlanVerify = fecp->ESW_VLANV & 0x0000000f; ++ DiscardUnknown = (fecp->ESW_VLANV & 0x000f0000) >> 16; ++ MultiReso = fecp->ESW_DMCR & 0x0000000f; ++ BroadReso = fecp->ESW_DBCR & 0x0000000f; ++ FTransmit = fecp->ESW_PER & 0x0000000f; ++ FReceive = (fecp->ESW_PER & 0x000f0000) >> 16; ++ ++ switch (portnum) { ++ case 0: ++ port_alstatus->link_status = 1; ++ port_alstatus->block_status = PortBlocking & 1; ++ port_alstatus->learn_status = PortLearning & 1; ++ port_alstatus->vlan_verify = VlanVerify & 1; ++ port_alstatus->discard_unknown = DiscardUnknown & 1; ++ port_alstatus->multi_reso = MultiReso & 1; ++ port_alstatus->broad_reso = BroadReso & 1; ++ port_alstatus->ftransmit = FTransmit & 1; ++ port_alstatus->freceive = FReceive & 1; ++ break; ++ case 1: ++ port_alstatus->link_status = ++ ports_link_status.port1_link_status; ++ port_alstatus->block_status = (PortBlocking >> 1) & 1; ++ port_alstatus->learn_status = (PortLearning >> 1) & 1; ++ port_alstatus->vlan_verify = (VlanVerify >> 1) & 1; ++ port_alstatus->discard_unknown = (DiscardUnknown >> 1) & 1; ++ port_alstatus->multi_reso = (MultiReso >> 1) & 1; ++ port_alstatus->broad_reso = (BroadReso >> 1) & 1; ++ port_alstatus->ftransmit = (FTransmit >> 1) & 1; ++ port_alstatus->freceive = (FReceive >> 1) & 1; ++ break; ++ case 2: ++ port_alstatus->link_status = ++ ports_link_status.port2_link_status; ++ port_alstatus->block_status = (PortBlocking >> 2) & 1; ++ port_alstatus->learn_status = (PortLearning >> 2) & 1; ++ port_alstatus->vlan_verify = (VlanVerify >> 2) & 1; ++ port_alstatus->discard_unknown = (DiscardUnknown >> 2) & 1; ++ port_alstatus->multi_reso = (MultiReso >> 2) & 1; ++ port_alstatus->broad_reso = (BroadReso >> 2) & 1; ++ port_alstatus->ftransmit = (FTransmit >> 2) & 1; ++ port_alstatus->freceive = (FReceive >> 2) & 1; ++ break; ++ default: ++ printk(KERN_ERR "%s:do not support the port %d", ++ __func__, portnum); ++ break; ++ } ++ return 0; ++} ++ ++int esw_atable_get_entry_port_number(struct switch_enet_private *fep, ++ unsigned char *mac_addr, unsigned char *port) ++{ ++ int block_index, block_index_end, entry; ++ unsigned long read_lo, read_hi; ++ unsigned long mac_addr_lo, mac_addr_hi; ++ ++ mac_addr_lo = (unsigned long)((mac_addr[3]<<24) | (mac_addr[2]<<16) | ++ (mac_addr[1]<<8) | mac_addr[0]); ++ mac_addr_hi = (unsigned long)((mac_addr[5]<<8) | (mac_addr[4])); ++ ++ block_index = GET_BLOCK_PTR(crc8_calc(mac_addr)); ++ block_index_end = block_index + ATABLE_ENTRY_PER_SLOT; ++ ++ /* now search all the entries in the selected block */ ++ for (entry = block_index; entry < block_index_end; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ if ((read_lo == mac_addr_lo) && ++ ((read_hi & 0x0000ffff) == ++ (mac_addr_hi & 0x0000ffff))) { ++ /* found the correct address */ ++ if ((read_hi & (1 << 16)) && (!(read_hi & (1 << 17)))) ++ *port = AT_EXTRACT_PORT(read_hi); ++ break; ++ } else ++ *port = -1; ++ } ++ ++ return 0; ++} ++ ++int esw_get_mac_address_lookup_table(struct switch_enet_private *fep, ++ unsigned long *tableaddr, unsigned long *dnum, unsigned long *snum) ++{ ++ unsigned long read_lo, read_hi; ++ unsigned long entry; ++ unsigned long dennum = 0; ++ unsigned long sennum = 0; ++ ++ for (entry = 0; entry < ESW_ATABLE_MEM_NUM_ENTRIES; entry++) { ++ read_atable(fep, entry, &read_lo, &read_hi); ++ if ((read_hi & (1 << 17)) && (read_hi & (1 << 16))) { ++ /* static entry */ ++ *(tableaddr + (2047 - sennum) * 11) = entry; ++ *(tableaddr + (2047 - sennum) * 11 + 2) = ++ read_lo & 0x000000ff; ++ *(tableaddr + (2047 - sennum) * 11 + 3) = ++ (read_lo & 0x0000ff00) >> 8; ++ *(tableaddr + (2047 - sennum) * 11 + 4) = ++ (read_lo & 0x00ff0000) >> 16; ++ *(tableaddr + (2047 - sennum) * 11 + 5) = ++ (read_lo & 0xff000000) >> 24; ++ *(tableaddr + (2047 - sennum) * 11 + 6) = ++ read_hi & 0x000000ff; ++ *(tableaddr + (2047 - sennum) * 11 + 7) = ++ (read_hi & 0x0000ff00) >> 8; ++ *(tableaddr + (2047 - sennum) * 11 + 8) = ++ AT_EXTRACT_PORTMASK(read_hi); ++ *(tableaddr + (2047 - sennum) * 11 + 9) = ++ AT_EXTRACT_PRIO(read_hi); ++ sennum++; ++ } else if ((read_hi & (1 << 16)) && (!(read_hi & (1 << 17)))) { ++ /* dynamic entry */ ++ *(tableaddr + dennum * 11) = entry; ++ *(tableaddr + dennum * 11 + 2) = read_lo & 0xff; ++ *(tableaddr + dennum * 11 + 3) = ++ (read_lo & 0x0000ff00) >> 8; ++ *(tableaddr + dennum * 11 + 4) = ++ (read_lo & 0x00ff0000) >> 16; ++ *(tableaddr + dennum * 11 + 5) = ++ (read_lo & 0xff000000) >> 24; ++ *(tableaddr + dennum * 11 + 6) = read_hi & 0xff; ++ *(tableaddr + dennum * 11 + 7) = ++ (read_hi & 0x0000ff00) >> 8; ++ *(tableaddr + dennum * 11 + 8) = ++ AT_EXTRACT_PORT(read_hi); ++ *(tableaddr + dennum * 11 + 9) = ++ AT_EXTRACT_TIMESTAMP(read_hi); ++ dennum++; ++ } ++ } ++ ++ *dnum = dennum; ++ *snum = sennum; ++ return 0; ++} ++ ++/*----------------------------------------------------------------------------*/ ++/* The timer should create an interrupt every 4 seconds*/ ++static void l2switch_aging_timer(unsigned long data) ++{ ++ struct switch_enet_private *fep; ++ ++ fep = (struct switch_enet_private *)data; ++ ++ if (fep) { ++ TIMEINCREMENT(fep->currTime); ++ fep->timeChanged++; ++ } ++ ++ mod_timer(&fep->timer_aging, jiffies + LEARNING_AGING_TIMER); ++} ++ ++/* ----------------------------------------------------------------------- */ ++void esw_check_rxb_txb_interrupt(struct switch_enet_private *fep) ++{ ++ volatile switch_t *fecp; ++ fecp = fep->hwp; ++ ++ /*Enable Forced forwarding for port 1*/ ++ fecp->ESW_P0FFEN = MCF_ESW_P0FFEN_FEN | ++ MCF_ESW_P0FFEN_FD(1); ++ /*Disable learning for all ports*/ ++ MCF_ESW_IMR = MCF_ESW_IMR_TXB | MCF_ESW_IMR_TXF | ++ MCF_ESW_IMR_RXB | MCF_ESW_IMR_RXF; ++} ++ ++/*----------------------------------------------------------------*/ ++static int switch_enet_learning(void *arg) ++{ ++ struct switch_enet_private *fep = arg; ++ volatile switch_t *fecp; ++ ++ fecp = fep->hwp; ++ while (!kthread_should_stop()) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ /* check learning record valid */ ++ if (fecp->ESW_LSR) ++ esw_atable_dynamicms_learn_migration(fep, ++ fep->currTime); ++ else ++ schedule_timeout(HZ/100); ++ } ++ ++ return 0; ++} ++ ++static int switch_enet_ioctl(struct net_device *dev, ++ struct ifreq *ifr, int cmd) ++{ ++ struct switch_enet_private *fep = netdev_priv(dev); ++ volatile switch_t *fecp; ++ int ret = 0; ++ ++ fecp = (volatile switch_t *)dev->base_addr; ++ ++ switch (cmd) { ++ /*------------------------------------------------------------*/ ++ case ESW_SET_PORTENABLE_CONF: ++ { ++ eswIoctlPortEnableConfig configData; ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, ++ sizeof(eswIoctlPortEnableConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_enable_config(fep, ++ configData.port, ++ configData.tx_enable, ++ configData.rx_enable); ++ } ++ break; ++ case ESW_SET_BROADCAST_CONF: ++ { ++ eswIoctlPortConfig configData; ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPortConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_broadcast_config(fep, ++ configData.port, configData.enable); ++ } ++ break; ++ ++ case ESW_SET_MULTICAST_CONF: ++ { ++ eswIoctlPortConfig configData; ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPortConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_multicast_config(fep, ++ configData.port, configData.enable); ++ } ++ break; ++ ++ case ESW_SET_BLOCKING_CONF: ++ { ++ eswIoctlPortConfig configData; ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPortConfig)); ++ ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_blocking_config(fep, ++ configData.port, configData.enable); ++ } ++ break; ++ ++ case ESW_SET_LEARNING_CONF: ++ { ++ eswIoctlPortConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPortConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_learning_config(fep, ++ configData.port, configData.enable); ++ } ++ break; ++ ++ case ESW_SET_PORT_ENTRY_EMPTY: ++ { ++ unsigned long portnum; ++ ++ ret = copy_from_user(&portnum, ++ ifr->ifr_data, sizeof(portnum)); ++ if (ret) ++ return -EFAULT; ++ esw_atable_dynamicms_del_entries_for_port(fep, portnum); ++ } ++ break; ++ ++ case ESW_SET_OTHER_PORT_ENTRY_EMPTY: ++ { ++ unsigned long portnum; ++ ++ ret = copy_from_user(&portnum, ++ ifr->ifr_data, sizeof(portnum)); ++ if (ret) ++ return -EFAULT; ++ ++ esw_atable_dynamicms_del_entries_for_other_port(fep, portnum); ++ } ++ break; ++ ++ case ESW_SET_IP_SNOOP_CONF: ++ { ++ eswIoctlIpsnoopConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlIpsnoopConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_ip_snoop_config(fep, configData.mode, ++ configData.ip_header_protocol); ++ } ++ break; ++ ++ case ESW_SET_PORT_SNOOP_CONF: ++ { ++ eswIoctlPortsnoopConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPortsnoopConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_tcpudp_port_snoop_config(fep, configData.mode, ++ configData.compare_port, ++ configData.compare_num); ++ } ++ break; ++ ++ case ESW_SET_PORT_MIRROR_CONF_PORT_MATCH: ++ { ++ struct eswIoctlMirrorCfgPortMatch configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(configData)); ++ if (ret) ++ return -EFAULT; ++ ret = esw_port_mirroring_config_port_match(fep, ++ configData.mirror_port, configData.port_match_en, ++ configData.port); ++ } ++ break; ++ ++ case ESW_SET_PORT_MIRROR_CONF: ++ { ++ eswIoctlPortMirrorConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPortMirrorConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_mirroring_config(fep, ++ configData.mirror_port, configData.port, ++ configData.mirror_enable, ++ configData.src_mac, configData.des_mac, ++ configData.egress_en, configData.ingress_en, ++ configData.egress_mac_src_en, ++ configData.egress_mac_des_en, ++ configData.ingress_mac_src_en, ++ configData.ingress_mac_des_en); ++ } ++ break; ++ ++ case ESW_SET_PORT_MIRROR_CONF_ADDR_MATCH: ++ { ++ struct eswIoctlMirrorCfgAddrMatch configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(configData)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_port_mirroring_config_addr_match(fep, ++ configData.mirror_port, configData.addr_match_en, ++ configData.mac_addr); ++ } ++ break; ++ ++ case ESW_SET_PIRORITY_VLAN: ++ { ++ eswIoctlPriorityVlanConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPriorityVlanConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_framecalssify_vlan_priority_lookup(fep, ++ configData.port, configData.func_enable, ++ configData.vlan_pri_table_num, ++ configData.vlan_pri_table_value); ++ } ++ break; ++ ++ case ESW_SET_PIRORITY_IP: ++ { ++ eswIoctlPriorityIPConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPriorityIPConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_framecalssify_ip_priority_lookup(fep, ++ configData.port, configData.func_enable, ++ configData.ipv4_en, configData.ip_priority_num, ++ configData.ip_priority_value); ++ } ++ break; ++ ++ case ESW_SET_PIRORITY_MAC: ++ { ++ eswIoctlPriorityMacConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPriorityMacConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_framecalssify_mac_priority_lookup(fep, ++ configData.port); ++ } ++ break; ++ ++ case ESW_SET_PIRORITY_DEFAULT: ++ { ++ eswIoctlPriorityDefaultConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlPriorityDefaultConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_frame_calssify_priority_init(fep, ++ configData.port, configData.priority_value); ++ } ++ break; ++ ++ case ESW_SET_P0_FORCED_FORWARD: ++ { ++ eswIoctlP0ForcedForwardConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlP0ForcedForwardConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_forced_forward(fep, configData.port1, ++ configData.port2, configData.enable); ++ } ++ break; ++ ++ case ESW_SET_BRIDGE_CONFIG: ++ { ++ unsigned long configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ ++ esw_bridge_port_configure(fep, configData); ++ } ++ break; ++ ++ case ESW_SET_SWITCH_MODE: ++ { ++ unsigned long configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ ++ esw_switch_mode_configure(fep, configData); ++ } ++ break; ++ ++ case ESW_SET_OUTPUT_QUEUE_MEMORY: ++ { ++ eswIoctlOutputQueue configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlOutputQueue)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_set_output_queue_memory(fep, ++ configData.fun_num, &configData.sOutputQueue); ++ } ++ break; ++ ++ case ESW_SET_VLAN_OUTPUT_PROCESS: ++ { ++ eswIoctlVlanOutputConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlVlanOutputConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_vlan_output_process(fep, ++ configData.port, configData.mode); ++ } ++ break; ++ ++ case ESW_SET_VLAN_INPUT_PROCESS: ++ { ++ eswIoctlVlanInputConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, ++ sizeof(eswIoctlVlanInputConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_vlan_input_process(fep, configData.port, ++ configData.mode, configData.port_vlanid); ++ } ++ break; ++ ++ case ESW_SET_VLAN_DOMAIN_VERIFICATION: ++ { ++ eswIoctlVlanVerificationConfig configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, ++ sizeof(eswIoctlVlanVerificationConfig)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_set_vlan_verification( ++ fep, configData.port, ++ configData.vlan_domain_verify_en, ++ configData.vlan_discard_unknown_en); ++ } ++ break; ++ ++ case ESW_SET_VLAN_RESOLUTION_TABLE: ++ { ++ eswIoctlVlanResoultionTable configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, ++ sizeof(eswIoctlVlanResoultionTable)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_set_vlan_resolution_table( ++ fep, configData.port_vlanid, ++ configData.vlan_domain_num, ++ configData.vlan_domain_port); ++ ++ } ++ break; ++ ++ case ESW_SET_VLAN_ID: ++ { ++ unsigned long configData; ++ ret = copy_from_user(&configData, ifr->ifr_data, ++ sizeof(configData)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_set_vlan_id(fep, configData); ++ } ++ break; ++ ++ case ESW_SET_VLAN_ID_CLEARED: ++ { ++ unsigned long configData; ++ ret = copy_from_user(&configData, ifr->ifr_data, ++ sizeof(configData)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_set_vlan_id_cleared(fep, configData); ++ } ++ break; ++ ++ case ESW_SET_PORT_IN_VLAN_ID: ++ { ++ eswIoctlVlanResoultionTable configData; ++ ++ ret = copy_from_user(&configData, ifr->ifr_data, ++ sizeof(configData)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_set_port_in_vlan_id(fep, configData); ++ } ++ break; ++ ++ /*--------------------------------------------------------------------*/ ++ case ESW_UPDATE_STATIC_MACTABLE: ++ { ++ eswIoctlUpdateStaticMACtable configData; ++ ++ ret = copy_from_user(&configData, ++ ifr->ifr_data, sizeof(eswIoctlUpdateStaticMACtable)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_update_atable_static(configData.mac_addr, ++ configData.port, configData.priority, fep); ++ } ++ break; ++ ++ case ESW_CLEAR_ALL_MACTABLE: ++ { ++ esw_clear_atable(fep); ++ } ++ break; ++ ++ /*-------------------get----------------------------------------------*/ ++ case ESW_GET_STATISTICS_STATUS: ++ { ++ esw_statistics_status Statistics; ++ esw_port_statistics_status PortSta; ++ int i; ++ ++ ret = esw_get_statistics_status(fep, &Statistics); ++ if (ret != 0) { ++ printk(KERN_ERR "%s: cmd %x fail\n", __func__, cmd); ++ return -1; ++ } ++ printk(KERN_INFO "DISCN : %10ld DISCB : %10ld\n", ++ Statistics.ESW_DISCN, Statistics.ESW_DISCB); ++ printk(KERN_INFO "NDISCN: %10ld NDISCB: %10ld\n", ++ Statistics.ESW_NDISCN, Statistics.ESW_NDISCB); ++ ++ for (i = 0; i < 3; i++) { ++ ret = esw_get_port_statistics_status(fep, i, ++ &PortSta); ++ if (ret != 0) { ++ printk(KERN_ERR "%s: cmd %x fail\n", ++ __func__, cmd); ++ return -1; ++ } ++ printk(KERN_INFO "port %d: POQC : %ld\n", ++ i, PortSta.MCF_ESW_POQC); ++ printk(KERN_INFO " PMVID : %ld\n", ++ PortSta.MCF_ESW_PMVID); ++ printk(KERN_INFO " PMVTAG: %ld\n", ++ PortSta.MCF_ESW_PMVTAG); ++ printk(KERN_INFO " PBL : %ld\n", ++ PortSta.MCF_ESW_PBL); ++ } ++ } ++ break; ++ ++ case ESW_GET_LEARNING_CONF: ++ { ++ unsigned long PortLearning; ++ ++ esw_get_port_learning(fep, &PortLearning); ++ ret = copy_to_user(ifr->ifr_data, &PortLearning, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_BLOCKING_CONF: ++ { ++ unsigned long PortBlocking; ++ ++ esw_get_port_blocking(fep, &PortBlocking); ++ ret = copy_to_user(ifr->ifr_data, &PortBlocking, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_MULTICAST_CONF: ++ { ++ unsigned long PortMulticast; ++ ++ esw_get_port_multicast(fep, &PortMulticast); ++ ret = copy_to_user(ifr->ifr_data, &PortMulticast, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_BROADCAST_CONF: ++ { ++ unsigned long PortBroadcast; ++ ++ esw_get_port_broadcast(fep, &PortBroadcast); ++ ret = copy_to_user(ifr->ifr_data, &PortBroadcast, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_PORTENABLE_CONF: ++ { ++ unsigned long PortEnable; ++ ++ esw_get_port_enable(fep, &PortEnable); ++ ret = copy_to_user(ifr->ifr_data, &PortEnable, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_IP_SNOOP_CONF: ++ { ++ unsigned long ESW_IPSNP[8]; ++ int i; ++ ++ esw_get_ip_snoop_config(fep, (unsigned long *)ESW_IPSNP); ++ printk(KERN_INFO "IP Protocol Mode Type\n"); ++ for (i = 0; i < 8; i++) { ++ if (ESW_IPSNP[i] != 0) ++ printk(KERN_INFO "%3ld " ++ "%1ld %s\n", ++ (ESW_IPSNP[i] >> 8) & 0xff, ++ (ESW_IPSNP[i] >> 1) & 3, ++ ESW_IPSNP[i] & 1 ? "Active" : ++ "Inactive"); ++ } ++ } ++ break; ++ ++ case ESW_GET_PORT_SNOOP_CONF: ++ { ++ unsigned long ESW_PSNP[8]; ++ int i; ++ ++ esw_get_tcpudp_port_snoop_config(fep, ++ (unsigned long *)ESW_PSNP); ++ printk(KERN_INFO "TCP/UDP Port SrcCompare DesCompare " ++ "Mode Type\n"); ++ for (i = 0; i < 8; i++) { ++ if (ESW_PSNP[i] != 0) ++ printk(KERN_INFO "%5ld %s " ++ "%s %1ld %s\n", ++ (ESW_PSNP[i] >> 16) & 0xffff, ++ (ESW_PSNP[i] >> 4) & 1 ? "Y" : "N", ++ (ESW_PSNP[i] >> 3) & 1 ? "Y" : "N", ++ (ESW_PSNP[i] >> 1) & 3, ++ ESW_PSNP[i] & 1 ? "Active" : ++ "Inactive"); ++ } ++ } ++ break; ++ ++ case ESW_GET_PORT_MIRROR_CONF: ++ esw_get_port_mirroring(fep); ++ break; ++ ++ case ESW_GET_P0_FORCED_FORWARD: ++ { ++ unsigned long ForceForward; ++ ++ esw_get_forced_forward(fep, &ForceForward); ++ ret = copy_to_user(ifr->ifr_data, &ForceForward, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_SWITCH_MODE: ++ { ++ unsigned long Config; ++ ++ esw_get_switch_mode(fep, &Config); ++ ret = copy_to_user(ifr->ifr_data, &Config, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_BRIDGE_CONFIG: ++ { ++ unsigned long Config; ++ ++ esw_get_bridge_port(fep, &Config); ++ ret = copy_to_user(ifr->ifr_data, &Config, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ case ESW_GET_OUTPUT_QUEUE_STATUS: ++ { ++ esw_output_queue_status Config; ++ esw_get_output_queue_status(fep, ++ &Config); ++ ret = copy_to_user(ifr->ifr_data, &Config, ++ sizeof(esw_output_queue_status)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_VLAN_OUTPUT_PROCESS: ++ { ++ unsigned long Config; ++ int tmp; ++ int i; ++ ++ esw_get_vlan_output_config(fep, &Config); ++ ++ for (i = 0; i < 3; i++) { ++ tmp = (Config >> (i << 1)) & 3; ++ ++ if (tmp != 0) ++ printk(KERN_INFO "port %d: vlan output " ++ "manipulation enable (mode %d)\n", ++ i, tmp); ++ else ++ printk(KERN_INFO "port %d: vlan output " ++ "manipulation disable\n", i); ++ } ++ } ++ break; ++ ++ case ESW_GET_VLAN_INPUT_PROCESS: ++ { ++ eswIoctlVlanInputStatus Config; ++ int i; ++ ++ esw_get_vlan_input_config(fep, &Config); ++ ++ for (i = 0; i < 3; i++) { ++ if (((Config.ESW_VIMEN >> i) & 1) == 0) ++ printk(KERN_INFO "port %d: vlan input " ++ "manipulation disable\n", i); ++ else ++ printk("port %d: vlan input manipulation enable" ++ " (mode %ld, vlan id %ld)\n", i, ++ (((Config.ESW_VIMSEL >> (i << 1)) & 3) ++ + 1), Config.ESW_PID[i]); ++ } ++ } ++ break; ++ ++ case ESW_GET_VLAN_RESOLUTION_TABLE: ++ { ++ struct eswVlanTableItem vtableitem; ++ unsigned char tmp0, tmp1, tmp2; ++ int i; ++ ++ esw_get_vlan_resolution_table(fep, &vtableitem); ++ ++ printk(KERN_INFO "VLAN Name VLAN Id Ports\n"); ++ for (i = 0; i < vtableitem.valid_num; i++) { ++ tmp0 = vtableitem.table[i].vlan_domain_port & 1; ++ tmp1 = (vtableitem.table[i].vlan_domain_port >> 1) & 1; ++ tmp2 = (vtableitem.table[i].vlan_domain_port >> 2) & 1; ++ printk(KERN_INFO "%2d %4d %s%s%s\n", ++ i, vtableitem.table[i].port_vlanid, ++ tmp0 ? "0 " : "", tmp1 ? "1 " : "", ++ tmp2 ? "2" : ""); ++ } ++ } ++ break; ++ ++ case ESW_GET_VLAN_DOMAIN_VERIFICATION: ++ { ++ unsigned long Config; ++ ++ esw_get_vlan_verification(fep, &Config); ++ ret = copy_to_user(ifr->ifr_data, &Config, ++ sizeof(unsigned long)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_ENTRY_PORT_NUMBER: ++ { ++ unsigned char mac_addr[6]; ++ unsigned char portnum; ++ ++ ret = copy_from_user(mac_addr, ++ ifr->ifr_data, sizeof(mac_addr)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = esw_atable_get_entry_port_number(fep, mac_addr, ++ &portnum); ++ ++ ret = copy_to_user(ifr->ifr_data, &portnum, ++ sizeof(unsigned char)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_LOOKUP_TABLE: ++ { ++ unsigned long *ConfigData; ++ unsigned long dennum, sennum; ++ int i; ++ int tmp; ++ ++ ConfigData = kmalloc(sizeof(struct eswAddrTableEntryExample) * ++ ESW_ATABLE_MEM_NUM_ENTRIES, GFP_KERNEL); ++ ret = esw_get_mac_address_lookup_table(fep, ConfigData, ++ &dennum, &sennum); ++ printk(KERN_INFO "Dynamic entries number: %ld\n", dennum); ++ printk(KERN_INFO "Static entries number: %ld\n", sennum); ++ printk(KERN_INFO "Type MAC address Port Timestamp\n"); ++ for (i = 0; i < dennum; i++) { ++ printk(KERN_INFO "dynamic " ++ "%02lx-%02lx-%02lx-%02lx-%02lx-%02lx " ++ "%01lx %4ld\n", *(ConfigData + i * 11 + 2), ++ *(ConfigData + i * 11 + 3), ++ *(ConfigData + i * 11 + 4), ++ *(ConfigData + i * 11 + 5), ++ *(ConfigData + i * 11 + 6), ++ *(ConfigData + i * 11 + 7), ++ *(ConfigData + i * 11 + 8), ++ *(ConfigData + i * 11 + 9)); ++ } ++ ++ if (sennum != 0) ++ printk(KERN_INFO "Type MAC address" ++ " Port Priority\n"); ++ ++ for (i = 0; i < sennum; i++) { ++ printk(KERN_INFO "static %02lx-%02lx-%02lx-%02lx" ++ "-%02lx-%02lx ", ++ *(ConfigData + (2047 - i) * 11 + 2), ++ *(ConfigData + (2047 - i) * 11 + 3), ++ *(ConfigData + (2047 - i) * 11 + 4), ++ *(ConfigData + (2047 - i) * 11 + 5), ++ *(ConfigData + (2047 - i) * 11 + 6), ++ *(ConfigData + (2047 - i) * 11 + 7)); ++ ++ tmp = *(ConfigData + (2047 - i) * 11 + 8); ++ if ((tmp == 0) || (tmp == 2) || (tmp == 4)) ++ printk("%01x ", tmp >> 1); ++ else if (tmp == 3) ++ printk("0,1 "); ++ else if (tmp == 5) ++ printk("0,2 "); ++ else if (tmp == 6) ++ printk("1,2 "); ++ ++ printk("%4ld\n", *(ConfigData + (2047 - i) * 11 + 9)); ++ } ++ kfree(ConfigData); ++ } ++ break; ++ ++ case ESW_GET_PORT_STATUS: ++ { ++ unsigned long PortBlocking; ++ ++ esw_get_port_blocking(fep, &PortBlocking); ++ ++ ports_link_status.port0_block_status = PortBlocking & 1; ++ ports_link_status.port1_block_status = (PortBlocking >> 1) & 1; ++ ports_link_status.port2_block_status = PortBlocking >> 2; ++ ++ ret = copy_to_user(ifr->ifr_data, &ports_link_status, ++ sizeof(ports_link_status)); ++ if (ret) ++ return -EFAULT; ++ } ++ break; ++ ++ case ESW_GET_PORT_ALL_STATUS: ++ { ++ unsigned char portnum; ++ struct port_all_status port_astatus; ++ ++ ret = copy_from_user(&portnum, ++ ifr->ifr_data, sizeof(portnum)); ++ if (ret) ++ return -EFAULT; ++ ++ esw_get_port_all_status(fep, portnum, &port_astatus); ++ printk(KERN_INFO "Port %d status:\n", portnum); ++ printk(KERN_INFO "Link:%-4s Blocking:%1s " ++ "Learning:%1s\n", ++ port_astatus.link_status ? "Up" : "Down", ++ port_astatus.block_status ? "Y" : "N", ++ port_astatus.learn_status ? "N" : "Y"); ++ printk(KERN_INFO "VLAN Verify:%1s Discard Unknown:%1s " ++ "Multicast Res:%1s\n", ++ port_astatus.vlan_verify ? "Y" : "N", ++ port_astatus.discard_unknown ? "Y" : "N", ++ port_astatus.multi_reso ? "Y" : "N"); ++ printk(KERN_INFO "Broadcast Res:%1s Transmit:%-7s " ++ "Receive:%7s\n", ++ port_astatus.broad_reso ? "Y" : "N", ++ port_astatus.ftransmit ? "Enable" : "Disable", ++ port_astatus.freceive ? "Enable" : "Disable"); ++ ++ } ++ break; ++ ++ case ESW_GET_USER_PID: ++ { ++ long get_pid = 0; ++ ret = copy_from_user(&get_pid, ++ ifr->ifr_data, sizeof(get_pid)); ++ ++ if (ret) ++ return -EFAULT; ++ user_pid = get_pid; ++ } ++ break; ++ /*------------------------------------------------------------------*/ ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return ret; ++} ++ ++static netdev_tx_t switch_enet_start_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct switch_enet_private *fep; ++ volatile switch_t *fecp; ++ cbd_t *bdp; ++ unsigned short status; ++ unsigned long flags; ++ ++ fep = netdev_priv(dev); ++ fecp = (switch_t *)fep->hwp; ++ ++ spin_lock_irqsave(&fep->hw_lock, flags); ++ /* Fill in a Tx ring entry */ ++ bdp = fep->cur_tx; ++ ++ status = bdp->cbd_sc; ++ ++ /* Clear all of the status flags. ++ */ ++ status &= ~BD_ENET_TX_STATS; ++ ++ /* Set buffer length and buffer pointer. ++ */ ++ bdp->cbd_bufaddr = __pa(skb->data); ++ bdp->cbd_datlen = skb->len; ++ ++ /* ++ * On some FEC implementations data must be aligned on ++ * 4-byte boundaries. Use bounce buffers to copy data ++ * and get it aligned. Ugh. ++ */ ++ if (bdp->cbd_bufaddr & 0x3) { ++ unsigned int index1; ++ index1 = bdp - fep->tx_bd_base; ++ ++ memcpy(fep->tx_bounce[index1], ++ (void *)skb->data, bdp->cbd_datlen); ++ bdp->cbd_bufaddr = __pa(fep->tx_bounce[index1]); ++ } ++ ++ /* Save skb pointer. */ ++ fep->tx_skbuff[fep->skb_cur] = skb; ++ ++ dev->stats.tx_bytes += skb->len; ++ fep->skb_cur = (fep->skb_cur+1) & TX_RING_MOD_MASK; ++ ++ /* Push the data cache so the CPM does not get stale memory ++ * data. ++ */ ++ flush_dcache_range((unsigned long)skb->data, ++ (unsigned long)skb->data + skb->len); ++ ++ /* Send it on its way. Tell FEC it's ready, interrupt when done, ++ * it's the last BD of the frame, and to put the CRC on the end. ++ */ ++ ++ status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR ++ | BD_ENET_TX_LAST | BD_ENET_TX_TC); ++ bdp->cbd_sc = status; ++ dev->trans_start = jiffies; ++ ++ /* Trigger transmission start */ ++ fecp->fec_x_des_active = MCF_ESW_TDAR_X_DES_ACTIVE; ++ ++ /* If this was the last BD in the ring, ++ * start at the beginning again.*/ ++ if (status & BD_ENET_TX_WRAP) ++ bdp = fep->tx_bd_base; ++ else ++ bdp++; ++ ++ if (bdp == fep->dirty_tx) { ++ fep->tx_full = 1; ++ netif_stop_queue(dev); ++ printk(KERN_ERR "%s: net stop\n", __func__); ++ } ++ ++ fep->cur_tx = (cbd_t *)bdp; ++ ++ spin_unlock_irqrestore(&fep->hw_lock, flags); ++ ++ return NETDEV_TX_OK; ++} ++ ++static void switch_timeout(struct net_device *dev) ++{ ++ struct switch_enet_private *fep = netdev_priv(dev); ++ ++ printk(KERN_ERR "%s: transmit timed out.\n", dev->name); ++ dev->stats.tx_errors++; ++ switch_restart(dev, fep->full_duplex); ++ netif_wake_queue(dev); ++} ++ ++/* The interrupt handler. ++ * This is called from the MPC core interrupt. ++ */ ++static irqreturn_t switch_enet_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *dev = dev_id; ++ volatile switch_t *fecp; ++ uint int_events; ++ irqreturn_t ret = IRQ_NONE; ++ ++ fecp = (switch_t *)dev->base_addr; ++ ++ /* Get the interrupt events that caused us to be here. ++ */ ++ do { ++ int_events = fecp->switch_ievent; ++ fecp->switch_ievent = int_events; ++ /* Handle receive event in its own function. */ ++ ++ /* Transmit OK, or non-fatal error. Update the buffer ++ descriptors. Switch handles all errors, we just discover ++ them as part of the transmit process. ++ */ ++ if (int_events & MCF_ESW_ISR_OD0) ++ ret = IRQ_HANDLED; ++ ++ if (int_events & MCF_ESW_ISR_OD1) ++ ret = IRQ_HANDLED; ++ ++ if (int_events & MCF_ESW_ISR_OD2) ++ ret = IRQ_HANDLED; ++ ++ if (int_events & MCF_ESW_ISR_RXB) ++ ret = IRQ_HANDLED; ++ ++ if (int_events & MCF_ESW_ISR_RXF) { ++ ret = IRQ_HANDLED; ++ switch_enet_rx(dev); ++ } ++ ++ if (int_events & MCF_ESW_ISR_TXB) ++ ret = IRQ_HANDLED; ++ ++ if (int_events & MCF_ESW_ISR_TXF) { ++ ret = IRQ_HANDLED; ++ switch_enet_tx(dev); ++ } ++ ++ } while (int_events); ++ ++ return ret; ++} ++ ++static void switch_enet_tx(struct net_device *dev) ++{ ++ struct switch_enet_private *fep; ++ cbd_t *bdp; ++ unsigned short status; ++ struct sk_buff *skb; ++ ++ fep = netdev_priv(dev); ++ spin_lock_irq(&fep->hw_lock); ++ bdp = fep->dirty_tx; ++ ++ while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { ++ if (bdp == fep->cur_tx && fep->tx_full == 0) ++ break; ++ ++ skb = fep->tx_skbuff[fep->skb_dirty]; ++ /* Check for errors. */ ++ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | ++ BD_ENET_TX_RL | BD_ENET_TX_UN | ++ BD_ENET_TX_CSL)) { ++ dev->stats.tx_errors++; ++ if (status & BD_ENET_TX_HB) /* No heartbeat */ ++ dev->stats.tx_heartbeat_errors++; ++ if (status & BD_ENET_TX_LC) /* Late collision */ ++ dev->stats.tx_window_errors++; ++ if (status & BD_ENET_TX_RL) /* Retrans limit */ ++ dev->stats.tx_aborted_errors++; ++ if (status & BD_ENET_TX_UN) /* Underrun */ ++ dev->stats.tx_fifo_errors++; ++ if (status & BD_ENET_TX_CSL) /* Carrier lost */ ++ dev->stats.tx_carrier_errors++; ++ } else { ++ dev->stats.tx_packets++; ++ } ++ ++ /* Deferred means some collisions occurred during transmit, ++ * but we eventually sent the packet OK. ++ */ ++ if (status & BD_ENET_TX_DEF) ++ dev->stats.collisions++; ++ ++ /* Free the sk buffer associated with this last transmit. ++ */ ++ dev_kfree_skb_any(skb); ++ fep->tx_skbuff[fep->skb_dirty] = NULL; ++ fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK; ++ ++ /* Update pointer to next buffer descriptor to be transmitted. ++ */ ++ if (status & BD_ENET_TX_WRAP) ++ bdp = fep->tx_bd_base; ++ else ++ bdp++; ++ ++ /* Since we have freed up a buffer, the ring is no longer ++ * full. ++ */ ++ if (fep->tx_full) { ++ fep->tx_full = 0; ++ printk(KERN_ERR "%s: tx full is zero\n", __func__); ++ if (netif_queue_stopped(dev)) ++ netif_wake_queue(dev); ++ } ++ } ++ fep->dirty_tx = (cbd_t *)bdp; ++ spin_unlock_irq(&fep->hw_lock); ++} ++ ++ ++/* During a receive, the cur_rx points to the current incoming buffer. ++ * When we update through the ring, if the next incoming buffer has ++ * not been given to the system, we just set the empty indicator, ++ * effectively tossing the packet. ++ */ ++static void switch_enet_rx(struct net_device *dev) ++{ ++ struct switch_enet_private *fep; ++ volatile switch_t *fecp; ++ cbd_t *bdp; ++ unsigned short status; ++ struct sk_buff *skb; ++ ushort pkt_len; ++ __u8 *data; ++ ++ fep = netdev_priv(dev); ++ /*fecp = (volatile switch_t *)dev->base_addr;*/ ++ fecp = (volatile switch_t *)fep->hwp; ++ ++ spin_lock_irq(&fep->hw_lock); ++ /* First, grab all of the stats for the incoming packet. ++ * These get messed up if we get called due to a busy condition. ++ */ ++ bdp = fep->cur_rx; ++ ++ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { ++ ++ /* Since we have allocated space to hold a complete frame, ++ * the last indicator should be set. ++ * */ ++ if ((status & BD_ENET_RX_LAST) == 0) ++ printk(KERN_ERR "SWITCH ENET: rcv is not +last\n"); ++ ++ if (!fep->opened) ++ goto rx_processing_done; ++ ++ /* Check for errors. */ ++ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | ++ BD_ENET_RX_CR | BD_ENET_RX_OV)) { ++ dev->stats.rx_errors++; ++ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { ++ /* Frame too long or too short. */ ++ dev->stats.rx_length_errors++; ++ } ++ if (status & BD_ENET_RX_NO) /* Frame alignment */ ++ dev->stats.rx_frame_errors++; ++ if (status & BD_ENET_RX_CR) /* CRC Error */ ++ dev->stats.rx_crc_errors++; ++ if (status & BD_ENET_RX_OV) /* FIFO overrun */ ++ dev->stats.rx_fifo_errors++; ++ } ++ /* Report late collisions as a frame error. ++ * On this error, the BD is closed, but we don't know what we ++ * have in the buffer. So, just drop this frame on the floor. ++ * */ ++ if (status & BD_ENET_RX_CL) { ++ dev->stats.rx_errors++; ++ dev->stats.rx_frame_errors++; ++ goto rx_processing_done; ++ } ++ /* Process the incoming frame */ ++ dev->stats.rx_packets++; ++ pkt_len = bdp->cbd_datlen; ++ dev->stats.rx_bytes += pkt_len; ++ data = (__u8 *)__va(bdp->cbd_bufaddr); ++ ++ /* This does 16 byte alignment, exactly what we need. ++ * The packet length includes FCS, but we don't want to ++ * include that when passing upstream as it messes up ++ * bridging applications. ++ * */ ++ skb = dev_alloc_skb(pkt_len); ++ ++ if (skb == NULL) ++ dev->stats.rx_dropped++; ++ else { ++ skb_put(skb, pkt_len); /* Make room */ ++ skb_copy_to_linear_data(skb, data, pkt_len); ++ skb->protocol = eth_type_trans(skb, dev); ++ netif_rx(skb); ++ } ++rx_processing_done: ++ ++ /* Clear the status flags for this buffer */ ++ status &= ~BD_ENET_RX_STATS; ++ ++ /* Mark the buffer empty */ ++ status |= BD_ENET_RX_EMPTY; ++ bdp->cbd_sc = status; ++ ++ /* Update BD pointer to next entry */ ++ if (status & BD_ENET_RX_WRAP) ++ bdp = fep->rx_bd_base; ++ else ++ bdp++; ++ ++ /* Doing this here will keep the FEC running while we process ++ * incoming frames. On a heavily loaded network, we should be ++ * able to keep up at the expense of system resources. ++ * */ ++ fecp->fec_r_des_active = MCF_ESW_RDAR_R_DES_ACTIVE; ++ } ++ fep->cur_rx = (cbd_t *)bdp; ++ ++ spin_unlock_irq(&fep->hw_lock); ++} ++ ++static int fec_mdio_transfer(struct mii_bus *bus, int phy_id, ++ int reg, int regval) ++{ ++ struct net_device *dev = bus->priv; ++ unsigned long flags; ++ struct switch_enet_private *fep; ++ int tries = 100; ++ int retval = 0; ++ ++ fep = netdev_priv(dev); ++ spin_lock_irqsave(&fep->mii_lock, flags); ++ ++ regval |= phy_id << 23; ++ MCF_FEC_MMFR0 = regval; ++ ++ /* wait for it to finish, this takes about 23 us on lite5200b */ ++ while (!(MCF_FEC_EIR0 & FEC_ENET_MII) && --tries) ++ udelay(5); ++ ++ if (!tries) { ++ printk(KERN_ERR "%s timeout\n", __func__); ++ return -ETIMEDOUT; ++ } ++ ++ MCF_FEC_EIR0 = FEC_ENET_MII; ++ retval = MCF_FEC_MMFR0; ++ spin_unlock_irqrestore(&fep->mii_lock, flags); ++ ++ return retval; ++} ++ ++ ++static int coldfire_fec_mdio_read(struct mii_bus *bus, ++ int phy_id, int reg) ++{ ++ int ret; ++ ret = fec_mdio_transfer(bus, phy_id, reg, ++ mk_mii_read(reg)); ++ return ret; ++} ++ ++static int coldfire_fec_mdio_write(struct mii_bus *bus, ++ int phy_id, int reg, u16 data) ++{ ++ return fec_mdio_transfer(bus, phy_id, reg, ++ mk_mii_write(reg, data)); ++} ++ ++static void switch_adjust_link1(struct net_device *dev) ++{ ++ struct switch_enet_private *priv = netdev_priv(dev); ++ struct phy_device *phydev1 = priv->phydev[0]; ++ int new_state = 0; ++ ++ if (phydev1->link != PHY_DOWN) { ++ if (phydev1->duplex != priv->phy1_duplex) { ++ new_state = 1; ++ priv->phy1_duplex = phydev1->duplex; ++ } ++ ++ if (phydev1->speed != priv->phy1_speed) { ++ new_state = 1; ++ priv->phy1_speed = phydev1->speed; ++ } ++ ++ if (priv->phy1_old_link == PHY_DOWN) { ++ new_state = 1; ++ priv->phy1_old_link = phydev1->link; ++ } ++ } else if (priv->phy1_old_link) { ++ new_state = 1; ++ priv->phy1_old_link = PHY_DOWN; ++ priv->phy1_speed = 0; ++ priv->phy1_duplex = -1; ++ } ++ ++ if (new_state) { ++ ports_link_status.port1_link_status = phydev1->link; ++ if (phydev1->link == PHY_DOWN) ++ esw_atable_dynamicms_del_entries_for_port(priv, 1); ++ ++ /*Send the new status to user space*/ ++ if (user_pid != 1) ++ sys_tkill(user_pid, SIGUSR1); ++ } ++} ++ ++static void switch_adjust_link2(struct net_device *dev) ++{ ++ struct switch_enet_private *priv = netdev_priv(dev); ++ struct phy_device *phydev2 = priv->phydev[1]; ++ int new_state = 0; ++ ++ if (phydev2->link != PHY_DOWN) { ++ if (phydev2->duplex != priv->phy2_duplex) { ++ new_state = 1; ++ priv->phy2_duplex = phydev2->duplex; ++ } ++ ++ if (phydev2->speed != priv->phy2_speed) { ++ new_state = 1; ++ priv->phy2_speed = phydev2->speed; ++ } ++ ++ if (priv->phy2_old_link == PHY_DOWN) { ++ new_state = 1; ++ priv->phy2_old_link = phydev2->link; ++ } ++ } else if (priv->phy2_old_link) { ++ new_state = 1; ++ priv->phy2_old_link = PHY_DOWN; ++ priv->phy2_speed = 0; ++ priv->phy2_duplex = -1; ++ } ++ ++ if (new_state) { ++ ports_link_status.port2_link_status = phydev2->link; ++ if (phydev2->link == PHY_DOWN) ++ esw_atable_dynamicms_del_entries_for_port(priv, 2); ++ ++ /*Send the new status to user space*/ ++ if (user_pid != 1) ++ sys_tkill(user_pid, SIGUSR1); ++ } ++} ++ ++static int coldfire_switch_init_phy(struct net_device *dev) ++{ ++ struct switch_enet_private *priv = netdev_priv(dev); ++ struct phy_device *phydev[SWITCH_EPORT_NUMBER] = {NULL, NULL}; ++ int i, startnode = 0; ++ ++ /* search for connect PHY device */ ++ for (i = 0; i < PHY_MAX_ADDR; i++) { ++ struct phy_device *const tmp_phydev = ++ priv->mdio_bus->phy_map[i]; ++ ++ if (!tmp_phydev) ++ continue; ++ ++#ifdef CONFIG_FEC_SHARED_PHY ++ if (priv->index == 0) ++ phydev[i] = tmp_phydev; ++ else if (priv->index == 1) { ++ if (startnode == 1) { ++ phydev[i] = tmp_phydev; ++ startnode = 0; ++ } else { ++ startnode++; ++ continue; ++ } ++ } else ++ printk(KERN_INFO "%s now we do not" ++ "support (%d) more than" ++ "2 phys shared " ++ "one mdio bus\n", ++ __func__, startnode); ++#else ++ phydev[i] = tmp_phydev; ++#endif ++ } ++ ++ /* now we are supposed to have a proper phydev, to attach to... */ ++ if ((!phydev[0]) && (!phydev[1])) { ++ printk(KERN_INFO "%s: Don't found any phy device at all\n", ++ dev->name); ++ return -ENODEV; ++ } ++ ++ priv->phy1_link = PHY_DOWN; ++ priv->phy1_old_link = PHY_DOWN; ++ priv->phy1_speed = 0; ++ priv->phy1_duplex = -1; ++ ++ priv->phy2_link = PHY_DOWN; ++ priv->phy2_old_link = PHY_DOWN; ++ priv->phy2_speed = 0; ++ priv->phy2_duplex = -1; ++ ++ phydev[0] = phy_connect(dev, dev_name(&phydev[0]->dev), ++ &switch_adjust_link1, 0, PHY_INTERFACE_MODE_MII); ++ if (IS_ERR(phydev[0])) { ++ printk(KERN_ERR " %s phy_connect failed\n", __func__); ++ return PTR_ERR(phydev[0]); ++ } ++ ++ phydev[1] = phy_connect(dev, dev_name(&phydev[1]->dev), ++ &switch_adjust_link2, 0, PHY_INTERFACE_MODE_MII); ++ if (IS_ERR(phydev[1])) { ++ printk(KERN_ERR " %s phy_connect failed\n", __func__); ++ return PTR_ERR(phydev[1]); ++ } ++ ++ for (i = 0; i < SWITCH_EPORT_NUMBER; i++) { ++ printk(KERN_INFO "attached phy %i to driver %s\n", ++ phydev[i]->addr, phydev[i]->drv->name); ++ priv->phydev[i] = phydev[i]; ++ } ++ ++ return 0; ++} ++/* -----------------------------------------------------------------------*/ ++static int switch_enet_open(struct net_device *dev) ++{ ++ struct switch_enet_private *fep = netdev_priv(dev); ++ volatile switch_t *fecp; ++ int i; ++ ++ fecp = (volatile switch_t *)fep->hwp; ++ /* I should reset the ring buffers here, but I don't yet know ++ * a simple way to do that. ++ */ ++ switch_set_mac_address(dev); ++ ++ fep->phy1_link = 0; ++ fep->phy2_link = 0; ++ ++ coldfire_switch_init_phy(dev); ++ for (i = 0; i < SWITCH_EPORT_NUMBER; i++) { ++ phy_write(fep->phydev[i], MII_BMCR, BMCR_RESET); ++ phy_start(fep->phydev[i]); ++ } ++ ++ fep->phy1_old_link = 0; ++ fep->phy2_old_link = 0; ++ fep->phy1_link = 1; ++ fep->phy2_link = 1; ++ ++ /* no phy, go full duplex, it's most likely a hub chip */ ++ switch_restart(dev, 1); ++ ++ /* if the fec is the fist open, we need to do nothing*/ ++ /* if the fec is not the fist open, we need to restart the FEC*/ ++ if (fep->sequence_done == 0) ++ switch_restart(dev, 1); ++ else ++ fep->sequence_done = 0; ++ ++ fep->currTime = 0; ++ fep->learning_irqhandle_enable = 0; ++ ++ MCF_ESW_PER = 0x70007; ++ fecp->ESW_DBCR = MCF_ESW_DBCR_P0 | MCF_ESW_DBCR_P1 | MCF_ESW_DBCR_P2; ++ fecp->ESW_DMCR = MCF_ESW_DMCR_P0 | MCF_ESW_DMCR_P1 | MCF_ESW_DMCR_P2; ++ ++ netif_start_queue(dev); ++ fep->opened = 1; ++ ++ return 0; ++} ++ ++static int switch_enet_close(struct net_device *dev) ++{ ++ struct switch_enet_private *fep = netdev_priv(dev); ++ int i; ++ ++ /* Don't know what to do yet.*/ ++ fep->opened = 0; ++ netif_stop_queue(dev); ++ switch_stop(dev); ++ ++ for (i = 0; i < SWITCH_EPORT_NUMBER; i++) { ++ phy_disconnect(fep->phydev[i]); ++ phy_stop(fep->phydev[i]); ++ phy_write(fep->phydev[i], MII_BMCR, BMCR_PDOWN); ++ } ++ ++ return 0; ++} ++ ++/* Set or clear the multicast filter for this adaptor. ++ * Skeleton taken from sunlance driver. ++ * The CPM Ethernet implementation allows Multicast as well as individual ++ * MAC address filtering. Some of the drivers check to make sure it is ++ * a group multicast address, and discard those that are not. I guess I ++ * will do the same for now, but just remove the test if you want ++ * individual filtering as well (do the upper net layers want or support ++ * this kind of feature?). ++ */ ++ ++#define HASH_BITS 6 /* #bits in hash */ ++#define CRC32_POLY 0xEDB88320 ++ ++static void set_multicast_list(struct net_device *dev) ++{ ++ struct switch_enet_private *fep; ++ volatile switch_t *ep; ++ unsigned int i, bit, data, crc; ++ struct netdev_hw_addr *ha; ++ ++ fep = netdev_priv(dev); ++ ep = fep->hwp; ++ ++ if (dev->flags & IFF_PROMISC) { ++ printk(KERN_INFO "%s IFF_PROMISC\n", __func__); ++ } else { ++ if (dev->flags & IFF_ALLMULTI) ++ /* Catch all multicast addresses, so set the ++ * filter to all 1's. ++ */ ++ printk(KERN_INFO "%s IFF_ALLMULTI\n", __func__); ++ else { ++ netdev_for_each_mc_addr(ha, dev) { ++ if (!(ha->addr[0] & 1)) ++ continue; ++ ++ /* calculate crc32 value of mac address ++ */ ++ crc = 0xffffffff; ++ ++ for (i = 0; i < dev->addr_len; i++) { ++ data = ha->addr[i]; ++ for (bit = 0; bit < 8; bit++, ++ data >>= 1) { ++ crc = (crc >> 1) ^ ++ (((crc ^ data) & 1) ? ++ CRC32_POLY : 0); ++ } ++ } ++ ++ } ++ } ++ } ++} ++ ++/* Set a MAC change in hardware.*/ ++static void switch_set_mac_address(struct net_device *dev) ++{ ++ volatile switch_t *fecp; ++ ++ fecp = ((struct switch_enet_private *)netdev_priv(dev))->hwp; ++} ++ ++static void switch_hw_init(void) ++{ ++ /* GPIO config - RMII mode for both MACs */ ++ MCF_GPIO_PAR_FEC = (MCF_GPIO_PAR_FEC & ++ MCF_GPIO_PAR_FEC_FEC_MASK) | ++ MCF_GPIO_PAR_FEC_FEC_RMII0FUL_1FUL; ++ ++ /* Initialize MAC 0/1 */ ++ /* RCR */ ++ MCF_FEC_RCR0 = (MCF_FEC_RCR_PROM | MCF_FEC_RCR_RMII_MODE | ++ MCF_FEC_RCR_MAX_FL(1522) | MCF_FEC_RCR_CRC_FWD); ++ MCF_FEC_RCR1 = (MCF_FEC_RCR_PROM | MCF_FEC_RCR_RMII_MODE | ++ MCF_FEC_RCR_MAX_FL(1522) | MCF_FEC_RCR_CRC_FWD); ++ /* TCR */ ++ MCF_FEC_TCR0 = MCF_FEC_TCR_FDEN; ++ MCF_FEC_TCR1 = MCF_FEC_TCR_FDEN; ++ /* ECR */ ++#ifdef MODELO_BUFFER ++ MCF_FEC_ECR0 = MCF_FEC_ECR_ETHER_EN | MCF_FEC_ECR_ENA_1588; ++ MCF_FEC_ECR1 = MCF_FEC_ECR_ETHER_EN | MCF_FEC_ECR_ENA_1588; ++#else ++ MCF_FEC_ECR0 = MCF_FEC_ECR_ETHER_EN; ++ MCF_FEC_ECR1 = MCF_FEC_ECR_ETHER_EN; ++#endif ++ MCF_FEC_MSCR0 = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; ++ MCF_FEC_MSCR1 = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; ++ ++ MCF_FEC_EIMR0 = FEC_ENET_TXF | FEC_ENET_RXF; ++ MCF_FEC_EIMR1 = FEC_ENET_TXF | FEC_ENET_RXF; ++ /*MCF_PPMHR0*/ ++ MCF_PPMCR0 = 0; ++} ++ ++static const struct net_device_ops switch_netdev_ops = { ++ .ndo_open = switch_enet_open, ++ .ndo_stop = switch_enet_close, ++ .ndo_start_xmit = switch_enet_start_xmit, ++ .ndo_set_multicast_list = set_multicast_list, ++ .ndo_do_ioctl = switch_enet_ioctl, ++ .ndo_tx_timeout = switch_timeout, ++}; ++ ++/* Initialize the FEC Ethernet. ++ */ ++ /* ++ * XXX: We need to clean up on failure exits here. ++ */ ++static int switch_enet_init(struct platform_device *pdev) ++{ ++ struct net_device *dev = platform_get_drvdata(pdev); ++ struct switch_enet_private *fep = netdev_priv(dev); ++ unsigned long mem_addr; ++ cbd_t *bdp; ++ cbd_t *cbd_base; ++ volatile switch_t *fecp; ++ int i, j; ++ struct coldfire_switch_platform_data *plat = ++ pdev->dev.platform_data; ++ ++ /* Allocate memory for buffer descriptors. ++ */ ++ mem_addr = __get_free_page(GFP_DMA); ++ if (mem_addr == 0) { ++ printk(KERN_ERR "Switch: allocate descriptor memory failed?\n"); ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&fep->hw_lock); ++ spin_lock_init(&fep->mii_lock); ++ ++ /* Create an Ethernet device instance. ++ */ ++ fecp = (volatile switch_t *)plat->switch_hw[0]; ++ fep->hwp = fecp; ++ fep->netdev = dev; ++ ++ /* ++ * SWITCH CONFIGURATION ++ */ ++ fecp->ESW_MODE = MCF_ESW_MODE_SW_RST; ++ udelay(10); ++ /* enable switch*/ ++ fecp->ESW_MODE = MCF_ESW_MODE_STATRST; ++ fecp->ESW_MODE = MCF_ESW_MODE_SW_EN; ++ ++ /* Enable transmit/receive on all ports */ ++ fecp->ESW_PER = 0xffffffff; ++ ++ /* Management port configuration, ++ * make port 0 as management port */ ++ fecp->ESW_BMPC = 0; ++ ++ /* clear all switch irq*/ ++ fecp->switch_ievent = 0xffffffff; ++ fecp->switch_imask = 0; ++ ++ udelay(10); ++ ++ /* Set the Ethernet address. If using multiple Enets on the 8xx, ++ * this needs some work to get unique addresses. ++ * ++ * This is our default MAC address unless the user changes ++ * it via eth_mac_addr (our dev->set_mac_addr handler). ++ */ ++ if (plat && plat->get_mac) ++ plat->get_mac(dev); ++ ++ cbd_base = (cbd_t *)mem_addr; ++ /* XXX: missing check for allocation failure */ ++ if (plat && plat->uncache) ++ plat->uncache(mem_addr); ++ ++ /* Set receive and transmit descriptor base. ++ */ ++ fep->rx_bd_base = cbd_base; ++ fep->tx_bd_base = cbd_base + RX_RING_SIZE; ++ ++ dev->base_addr = (unsigned long)fecp; ++ ++ /* The FEC Ethernet specific entries in the device structure. */ ++ dev->watchdog_timeo = TX_TIMEOUT; ++ dev->netdev_ops = &switch_netdev_ops; ++ ++ fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; ++ fep->cur_rx = fep->rx_bd_base; ++ ++ fep->skb_cur = fep->skb_dirty = 0; ++ ++ /* Initialize the receive buffer descriptors. */ ++ bdp = fep->rx_bd_base; ++ ++ for (i = 0; i < SWITCH_ENET_RX_PAGES; i++) { ++ ++ /* Allocate a page. ++ */ ++ mem_addr = __get_free_page(GFP_DMA); ++ /* XXX: missing check for allocation failure */ ++ if (plat && plat->uncache) ++ plat->uncache(mem_addr); ++ ++ /* Initialize the BD for every fragment in the page. ++ */ ++ for (j = 0; j < SWITCH_ENET_RX_FRPPG; j++) { ++ bdp->cbd_sc = BD_ENET_RX_EMPTY; ++ bdp->cbd_bufaddr = __pa(mem_addr); ++#ifdef MODELO_BUFFER ++ bdp->bdu = 0x00000000; ++ bdp->ebd_status = RX_BD_INT; ++#endif ++ mem_addr += SWITCH_ENET_RX_FRSIZE; ++ bdp++; ++ } ++ } ++ ++ /* Set the last buffer to wrap. ++ */ ++ bdp--; ++ bdp->cbd_sc |= BD_SC_WRAP; ++ ++ /* ...and the same for transmmit. ++ */ ++ bdp = fep->tx_bd_base; ++ for (i = 0, j = SWITCH_ENET_TX_FRPPG; i < TX_RING_SIZE; i++) { ++ if (j >= SWITCH_ENET_TX_FRPPG) { ++ mem_addr = __get_free_page(GFP_DMA); ++ j = 1; ++ } else { ++ mem_addr += SWITCH_ENET_TX_FRSIZE; ++ j++; ++ } ++ fep->tx_bounce[i] = (unsigned char *) mem_addr; ++ ++ /* Initialize the BD for every fragment in the page. ++ */ ++ bdp->cbd_sc = 0; ++ bdp->cbd_bufaddr = 0; ++ bdp++; ++ } ++ ++ /* Set the last buffer to wrap. ++ */ ++ bdp--; ++ bdp->cbd_sc |= BD_SC_WRAP; ++ ++ /* Set receive and transmit descriptor base. ++ */ ++ fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); ++ fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); ++ ++ /* Install our interrupt handlers. This varies depending on ++ * the architecture. ++ */ ++ if (plat && plat->request_intrs) ++ plat->request_intrs(dev, switch_enet_interrupt, dev); ++ ++ fecp->fec_r_buff_size = RX_BUFFER_SIZE; ++ fecp->fec_r_des_active = MCF_ESW_RDAR_R_DES_ACTIVE; ++ ++ /* setup MII interface */ ++ if (plat && plat->set_mii) ++ plat->set_mii(dev); ++ ++ /* Clear and enable interrupts */ ++ fecp->switch_ievent = 0xffffffff; ++ fecp->switch_imask = MCF_ESW_IMR_RXB | MCF_ESW_IMR_TXB | ++ MCF_ESW_IMR_RXF | MCF_ESW_IMR_TXF; ++ esw_clear_atable(fep); ++ /* Queue up command to detect the PHY and initialize the ++ * remainder of the interface. ++ */ ++#ifndef CONFIG_FEC_SHARED_PHY ++ fep->phy_addr = 0; ++#else ++ fep->phy_addr = fep->index; ++#endif ++ ++ fep->sequence_done = 1; ++ return 0; ++} ++ ++/* This function is called to start or restart the FEC during a link ++ * change. This only happens when switching between half and full ++ * duplex. ++ */ ++static void switch_restart(struct net_device *dev, int duplex) ++{ ++ struct switch_enet_private *fep; ++ cbd_t *bdp; ++ volatile switch_t *fecp; ++ int i; ++ struct coldfire_switch_platform_data *plat; ++ ++ fep = netdev_priv(dev); ++ fecp = fep->hwp; ++ plat = fep->pdev->dev.platform_data; ++ /* Whack a reset. We should wait for this.*/ ++ MCF_FEC_ECR0 = 1; ++ MCF_FEC_ECR1 = 1; ++ udelay(10); ++ ++ fecp->ESW_MODE = MCF_ESW_MODE_SW_RST; ++ udelay(10); ++ fecp->ESW_MODE = MCF_ESW_MODE_STATRST; ++ fecp->ESW_MODE = MCF_ESW_MODE_SW_EN; ++ ++ /* Enable transmit/receive on all ports */ ++ fecp->ESW_PER = 0xffffffff; ++ ++ /* Management port configuration, ++ * make port 0 as management port */ ++ fecp->ESW_BMPC = 0; ++ ++ /* Clear any outstanding interrupt. ++ */ ++ fecp->switch_ievent = 0xffffffff; ++ ++ /* Set station address.*/ ++ switch_set_mac_address(dev); ++ ++ switch_hw_init(); ++ ++ /* Reset all multicast.*/ ++ ++ /* Set maximum receive buffer size. ++ */ ++ fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; ++ ++ if (plat && plat->localhw_setup) ++ plat->localhw_setup(); ++ /* Set receive and transmit descriptor base. ++ */ ++ fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); ++ fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); ++ ++ fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; ++ fep->cur_rx = fep->rx_bd_base; ++ ++ /* Reset SKB transmit buffers. ++ */ ++ fep->skb_cur = fep->skb_dirty = 0; ++ for (i = 0; i <= TX_RING_MOD_MASK; i++) { ++ if (fep->tx_skbuff[i] != NULL) { ++ dev_kfree_skb_any(fep->tx_skbuff[i]); ++ fep->tx_skbuff[i] = NULL; ++ } ++ } ++ ++ /* Initialize the receive buffer descriptors. ++ */ ++ bdp = fep->rx_bd_base; ++ for (i = 0; i < RX_RING_SIZE; i++) { ++ ++ /* Initialize the BD for every fragment in the page. ++ */ ++ bdp->cbd_sc = BD_ENET_RX_EMPTY; ++#ifdef MODELO_BUFFER ++ bdp->bdu = 0x00000000; ++ bdp->ebd_status = RX_BD_INT; ++#endif ++ bdp++; ++ } ++ ++ /* Set the last buffer to wrap. ++ */ ++ bdp--; ++ bdp->cbd_sc |= BD_SC_WRAP; ++ ++ /* ...and the same for transmmit. ++ */ ++ bdp = fep->tx_bd_base; ++ for (i = 0; i < TX_RING_SIZE; i++) { ++ ++ /* Initialize the BD for every fragment in the page.*/ ++ bdp->cbd_sc = 0; ++ bdp->cbd_bufaddr = 0; ++ bdp++; ++ } ++ ++ /* Set the last buffer to wrap.*/ ++ bdp--; ++ bdp->cbd_sc |= BD_SC_WRAP; ++ ++ fep->full_duplex = duplex; ++ ++ /* And last, enable the transmit and receive processing.*/ ++ fecp->fec_r_buff_size = RX_BUFFER_SIZE; ++ fecp->fec_r_des_active = MCF_ESW_RDAR_R_DES_ACTIVE; ++ ++ /* Enable interrupts we wish to service. ++ */ ++ fecp->switch_ievent = 0xffffffff; ++ fecp->switch_imask = MCF_ESW_IMR_RXF | MCF_ESW_IMR_TXF | ++ MCF_ESW_IMR_RXB | MCF_ESW_IMR_TXB; ++} ++ ++static void switch_stop(struct net_device *dev) ++{ ++ volatile switch_t *fecp; ++ struct switch_enet_private *fep; ++ struct coldfire_switch_platform_data *plat; ++ ++ fep = netdev_priv(dev); ++ fecp = fep->hwp; ++ plat = fep->pdev->dev.platform_data; ++ /* ++ ** We cannot expect a graceful transmit stop without link !!! ++ */ ++ if (fep->phy1_link) ++ udelay(10); ++ if (fep->phy2_link) ++ udelay(10); ++ ++ /* Whack a reset. We should wait for this. ++ */ ++ udelay(10); ++} ++ ++static int fec_mdio_register(struct net_device *dev) ++{ ++ int err = 0; ++ struct switch_enet_private *fep = netdev_priv(dev); ++ ++ fep->mdio_bus = mdiobus_alloc(); ++ if (!fep->mdio_bus) { ++ printk(KERN_ERR "ethernet switch mdiobus_alloc fail\n"); ++ return -ENOMEM; ++ } ++ ++ fep->mdio_bus->name = "Coldfire switch MII 0 Bus"; ++ strcpy(fep->mdio_bus->id, "0"); ++ ++ fep->mdio_bus->read = &coldfire_fec_mdio_read; ++ fep->mdio_bus->write = &coldfire_fec_mdio_write; ++ fep->mdio_bus->priv = dev; ++ err = mdiobus_register(fep->mdio_bus); ++ if (err) { ++ mdiobus_free(fep->mdio_bus); ++ printk(KERN_ERR "%s: ethernet mdiobus_register fail\n", ++ dev->name); ++ return -EIO; ++ } ++ ++ printk(KERN_INFO "mdiobus_register %s ok\n", ++ fep->mdio_bus->name); ++ return err; ++} ++ ++static int __devinit eth_switch_probe(struct platform_device *pdev) ++{ ++ struct net_device *dev; ++ int err; ++ struct switch_enet_private *fep; ++ struct task_struct *task; ++ ++ printk(KERN_INFO "Ethernet Switch Version 1.0\n"); ++ ++ dev = alloc_etherdev(sizeof(struct switch_enet_private)); ++ if (!dev) { ++ printk(KERN_ERR "%s: ethernet switch alloc_etherdev fail\n", ++ dev->name); ++ return -ENOMEM; ++ } ++ ++ SET_NETDEV_DEV(dev, &pdev->dev); ++ ++ fep = netdev_priv(dev); ++ memset(fep, 0, sizeof(*fep)); ++ ++ fep->pdev = pdev; ++ platform_set_drvdata(pdev, dev); ++ printk(KERN_ERR "%s: ethernet switch port 0 init\n", ++ __func__); ++ err = switch_enet_init(pdev); ++ if (err) { ++ free_netdev(dev); ++ platform_set_drvdata(pdev, NULL); ++ } ++ ++ err = fec_mdio_register(dev); ++ if (err) { ++ printk(KERN_ERR "%s: ethernet switch fec_mdio_register\n", ++ dev->name); ++ free_netdev(dev); ++ platform_set_drvdata(pdev, NULL); ++ return -ENOMEM; ++ } ++ ++ /* setup timer for Learning Aging function */ ++ init_timer(&fep->timer_aging); ++ fep->timer_aging.function = l2switch_aging_timer; ++ fep->timer_aging.data = (unsigned long) fep; ++ fep->timer_aging.expires = jiffies + LEARNING_AGING_TIMER; ++ add_timer(&fep->timer_aging); ++ ++ /* register network device*/ ++ if (register_netdev(dev) != 0) { ++ /* XXX: missing cleanup here */ ++ free_netdev(dev); ++ platform_set_drvdata(pdev, NULL); ++ printk(KERN_ERR "%s: ethernet switch register_netdev fail\n", ++ dev->name); ++ return -EIO; ++ } ++ ++ task = kthread_run(switch_enet_learning, fep, ++ "modelo l2switch"); ++ if (IS_ERR(task)) { ++ err = PTR_ERR(task); ++ return err; ++ } ++ ++ printk(KERN_INFO "%s: ethernet switch %pM\n", ++ dev->name, dev->dev_addr); ++ return 0; ++} ++ ++static int __devexit eth_switch_remove(struct platform_device *pdev) ++{ ++ int i; ++ struct net_device *dev; ++ struct switch_enet_private *fep; ++ struct switch_platform_private *chip; ++ ++ chip = platform_get_drvdata(pdev); ++ if (chip) { ++ for (i = 0; i < chip->num_slots; i++) { ++ fep = chip->fep_host[i]; ++ dev = fep->netdev; ++ fep->sequence_done = 1; ++ unregister_netdev(dev); ++ free_netdev(dev); ++ ++ del_timer_sync(&fep->timer_aging); ++ } ++ ++ platform_set_drvdata(pdev, NULL); ++ kfree(chip); ++ ++ } else ++ printk(KERN_ERR "%s: can not get the " ++ "switch_platform_private %x\n", __func__, ++ (unsigned int)chip); ++ ++ return 0; ++} ++ ++static struct platform_driver eth_switch_driver = { ++ .probe = eth_switch_probe, ++ .remove = __devexit_p(eth_switch_remove), ++ .driver = { ++ .name = "coldfire-switch", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init coldfire_switch_init(void) ++{ ++ return platform_driver_register(ð_switch_driver); ++} ++ ++static void __exit coldfire_switch_exit(void) ++{ ++ platform_driver_unregister(ð_switch_driver); ++} ++ ++module_init(coldfire_switch_init); ++module_exit(coldfire_switch_exit); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/modelo_switch.h +@@ -0,0 +1,1141 @@ ++/****************************************************************************/ ++ ++/* ++ * mcfswitch -- L2 Switch Controller for Modelo ColdFire SoC ++ * processors. ++ * ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * 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. ++ * ++ */ ++ ++/****************************************************************************/ ++#ifndef SWITCH_H ++#define SWITCH_H ++/****************************************************************************/ ++/* The Switch stores dest/src/type, data, and checksum for receive packets. ++ */ ++#define PKT_MAXBUF_SIZE 1518 ++#define PKT_MINBUF_SIZE 64 ++#define PKT_MAXBLR_SIZE 1520 ++ ++/* ++ * The 5441x RX control register also contains maximum frame ++ * size bits. ++ */ ++#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) ++ ++/* ++ * Some hardware gets it MAC address out of local flash memory. ++ * if this is non-zero then assume it is the address to get MAC from. ++ */ ++#define FEC_FLASHMAC 0 ++ ++/* The number of Tx and Rx buffers. These are allocated from the page ++ * pool. The code may assume these are power of two, so it it best ++ * to keep them that size. ++ * We don't need to allocate pages for the transmitter. We just use ++ * the skbuffer directly. ++ */ ++#ifdef CONFIG_SWITCH_DMA_USE_SRAM ++#define SWITCH_ENET_RX_PAGES 6 ++#else ++#define SWITCH_ENET_RX_PAGES 8 ++#endif ++ ++#define SWITCH_ENET_RX_FRSIZE 2048 ++#define SWITCH_ENET_RX_FRPPG (PAGE_SIZE / SWITCH_ENET_RX_FRSIZE) ++#define RX_RING_SIZE (SWITCH_ENET_RX_FRPPG * SWITCH_ENET_RX_PAGES) ++#define SWITCH_ENET_TX_FRSIZE 2048 ++#define SWITCH_ENET_TX_FRPPG (PAGE_SIZE / SWITCH_ENET_TX_FRSIZE) ++ ++#ifdef CONFIG_SWITCH_DMA_USE_SRAM ++#define TX_RING_SIZE 8 /* Must be power of two */ ++#define TX_RING_MOD_MASK 7 /* for this to work */ ++#else ++#define TX_RING_SIZE 16 /* Must be power of two */ ++#define TX_RING_MOD_MASK 15 /* for this to work */ ++#endif ++ ++#define SWITCH_EPORT_NUMBER 2 ++ ++#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE) ++#error "L2SWITCH: descriptor ring size constants too large" ++#endif ++/*-----------------------------------------------------------------------*/ ++typedef struct l2switch_output_queue_status { ++ unsigned long ESW_MMSR; ++ unsigned long ESW_LMT; ++ unsigned long ESW_LFC; ++ unsigned long ESW_PCSR; ++ unsigned long ESW_IOSR; ++ unsigned long ESW_QWT; ++ unsigned long esw_reserved; ++ unsigned long ESW_P0BCT; ++} esw_output_queue_status; ++ ++typedef struct l2switch_statistics_status { ++ /* ++ * Total number of incoming frames processed ++ * but discarded in switch ++ */ ++ unsigned long ESW_DISCN; ++ /*Sum of bytes of frames counted in ESW_DISCN*/ ++ unsigned long ESW_DISCB; ++ /* ++ * Total number of incoming frames processed ++ * but not discarded in switch ++ */ ++ unsigned long ESW_NDISCN; ++ /*Sum of bytes of frames counted in ESW_NDISCN*/ ++ unsigned long ESW_NDISCB; ++} esw_statistics_status; ++ ++typedef struct l2switch_port_statistics_status { ++ /*outgoing frames discarded due to transmit queue congestion*/ ++ unsigned long MCF_ESW_POQC; ++ /*incoming frames discarded due to VLAN domain mismatch*/ ++ unsigned long MCF_ESW_PMVID; ++ /*incoming frames discarded due to untagged discard*/ ++ unsigned long MCF_ESW_PMVTAG; ++ /*incoming frames discarded due port is in blocking state*/ ++ unsigned long MCF_ESW_PBL; ++} esw_port_statistics_status; ++ ++typedef struct l2switch { ++ unsigned long ESW_REVISION; ++ unsigned long ESW_SCRATCH; ++ unsigned long ESW_PER; ++ unsigned long reserved0[1]; ++ unsigned long ESW_VLANV; ++ unsigned long ESW_DBCR; ++ unsigned long ESW_DMCR; ++ unsigned long ESW_BKLR; ++ unsigned long ESW_BMPC; ++ unsigned long ESW_MODE; ++ unsigned long ESW_VIMSEL; ++ unsigned long ESW_VOMSEL; ++ unsigned long ESW_VIMEN; ++ unsigned long ESW_VID;/*0x34*/ ++ /*from 0x38 0x3C*/ ++ unsigned long esw_reserved0[2]; ++ unsigned long ESW_MCR;/*0x40*/ ++ unsigned long ESW_EGMAP; ++ unsigned long ESW_INGMAP; ++ unsigned long ESW_INGSAL; ++ unsigned long ESW_INGSAH; ++ unsigned long ESW_INGDAL; ++ unsigned long ESW_INGDAH; ++ unsigned long ESW_ENGSAL; ++ unsigned long ESW_ENGSAH; ++ unsigned long ESW_ENGDAL; ++ unsigned long ESW_ENGDAH; ++ unsigned long ESW_MCVAL;/*0x6C*/ ++ /*from 0x70--0x7C*/ ++ unsigned long esw_reserved1[4]; ++ unsigned long ESW_MMSR;/*0x80*/ ++ unsigned long ESW_LMT; ++ unsigned long ESW_LFC; ++ unsigned long ESW_PCSR; ++ unsigned long ESW_IOSR; ++ unsigned long ESW_QWT;/*0x94*/ ++ unsigned long esw_reserved2[1];/*0x98*/ ++ unsigned long ESW_P0BCT;/*0x9C*/ ++ /*from 0xA0-0xB8*/ ++ unsigned long esw_reserved3[7]; ++ unsigned long ESW_P0FFEN;/*0xBC*/ ++ unsigned long ESW_PSNP[8]; ++ unsigned long ESW_IPSNP[8]; ++ /*port0-port2 VLAN Priority resolution map 0xFC0D_C100-C108*/ ++ unsigned long ESW_PVRES[3]; ++ /*from 0x10C-0x13C*/ ++ unsigned long esw_reserved4[13]; ++ unsigned long ESW_IPRES;/*0x140*/ ++ /*from 0x144-0x17C*/ ++ unsigned long esw_reserved5[15]; ++ ++ /*port0-port2 Priority Configuration 0xFC0D_C180-C188*/ ++ unsigned long ESW_PRES[3]; ++ /*from 0x18C-0x1FC*/ ++ unsigned long esw_reserved6[29]; ++ ++ /*port0-port2 VLAN ID 0xFC0D_C200-C208*/ ++ unsigned long ESW_PID[3]; ++ /*from 0x20C-0x27C*/ ++ unsigned long esw_reserved7[29]; ++ ++ /*port0-port2 VLAN domain resolution entry 0xFC0D_C280-C2FC*/ ++ unsigned long ESW_VRES[32]; ++ ++ unsigned long ESW_DISCN;/*0x300*/ ++ unsigned long ESW_DISCB; ++ unsigned long ESW_NDISCN; ++ unsigned long ESW_NDISCB;/*0xFC0DC30C*/ ++ /*per port statistics 0xFC0DC310_C33C*/ ++ esw_port_statistics_status port_statistics_status[3]; ++ /*from 0x340-0x400*/ ++ unsigned long esw_reserved8[48]; ++ ++ /*0xFC0DC400---0xFC0DC418*/ ++ /*unsigned long MCF_ESW_ISR;*/ ++ unsigned long switch_ievent; /* Interrupt event reg */ ++ /*unsigned long MCF_ESW_IMR;*/ ++ unsigned long switch_imask; /* Interrupt mask reg */ ++ /*unsigned long MCF_ESW_RDSR;*/ ++ unsigned long fec_r_des_start; /* Receive descriptor ring */ ++ /*unsigned long MCF_ESW_TDSR;*/ ++ unsigned long fec_x_des_start; /* Transmit descriptor ring */ ++ /*unsigned long MCF_ESW_MRBR;*/ ++ unsigned long fec_r_buff_size; /* Maximum receive buff size */ ++ /*unsigned long MCF_ESW_RDAR;*/ ++ unsigned long fec_r_des_active; /* Receive descriptor reg */ ++ /*unsigned long MCF_ESW_TDAR;*/ ++ unsigned long fec_x_des_active; /* Transmit descriptor reg */ ++ /*from 0x420-0x4FC*/ ++ unsigned long esw_reserved9[57]; ++ ++ /*0xFC0DC500---0xFC0DC508*/ ++ unsigned long ESW_LREC0; ++ unsigned long ESW_LREC1; ++ unsigned long ESW_LSR; ++} switch_t; ++ ++typedef struct _64bTableEntry { ++ unsigned int lo; /* lower 32 bits */ ++ unsigned int hi; /* upper 32 bits */ ++} AddrTable64bEntry; ++ ++typedef struct l2switchaddrtable { ++ AddrTable64bEntry eswTable64bEntry[2048]; ++} eswAddrTable_t; ++ ++/*unsigned long MCF_ESW_LOOKUP_MEM;*/ ++#define MCF_ESW_REVISION (*(volatile unsigned long *)(0xFC0DC000)) ++#define MCF_ESW_PER (*(volatile unsigned long *)(0xFC0DC008)) ++#define MCF_ESW_VLANV (*(volatile unsigned long *)(0xFC0DC010)) ++#define MCF_ESW_DBCR (*(volatile unsigned long *)(0xFC0DC014)) ++#define MCF_ESW_DMCR (*(volatile unsigned long *)(0xFC0DC018)) ++#define MCF_ESW_BKLR (*(volatile unsigned long *)(0xFC0DC01C)) ++#define MCF_ESW_BMPC (*(volatile unsigned long *)(0xFC0DC020)) ++#define MCF_ESW_MODE (*(volatile unsigned long *)(0xFC0DC024)) ++ ++#define MCF_ESW_ISR (*(volatile unsigned long *)(0xFC0DC400)) ++#define MCF_ESW_IMR (*(volatile unsigned long *)(0xFC0DC404)) ++#define MCF_ESW_TDAR (*(volatile unsigned long *)(0xFC0DC418)) ++#define MCF_ESW_LOOKUP_MEM (*(volatile unsigned long *)(0xFC0E0000)) ++ ++#define MCF_PPMCR0 (*(volatile unsigned short *)(0xFC04002D)) ++#define MCF_PPMHR0 (*(volatile unsigned long *)(0xFC040030)) ++ ++#define MCF_FEC_EIR0 (*(volatile unsigned long *)(0xFC0D4004)) ++#define MCF_FEC_EIR1 (*(volatile unsigned long *)(0xFC0D8004)) ++#define MCF_FEC_EIMR0 (*(volatile unsigned long *)(0xFC0D4008)) ++#define MCF_FEC_EIMR1 (*(volatile unsigned long *)(0xFC0D8008)) ++#define MCF_FEC_MMFR0 (*(volatile unsigned long *)(0xFC0D4040)) ++#define MCF_FEC_MMFR1 (*(volatile unsigned long *)(0xFC0D8040)) ++#define MCF_FEC_MSCR0 (*(volatile unsigned long *)(0xFC0D4044)) ++#define MCF_FEC_MSCR1 (*(volatile unsigned long *)(0xFC0D8044)) ++#define MCF_FEC_RCR0 (*(volatile unsigned long *)(0xFC0D4084)) ++#define MCF_FEC_RCR1 (*(volatile unsigned long *)(0xFC0D8084)) ++#define MCF_FEC_TCR0 (*(volatile unsigned long *)(0xFC0D40C4)) ++#define MCF_FEC_TCR1 (*(volatile unsigned long *)(0xFC0D80C4)) ++#define MCF_FEC_ECR0 (*(volatile unsigned long *)(0xFC0D4024)) ++#define MCF_FEC_ECR1 (*(volatile unsigned long *)(0xFC0D8024)) ++ ++ ++#define MCF_FEC_RCR_PROM (0x00000008) ++#define MCF_FEC_RCR_RMII_MODE (0x00000100) ++#define MCF_FEC_RCR_MAX_FL(x) (((x)&0x00003FFF)<<16) ++#define MCF_FEC_RCR_CRC_FWD (0x00004000) ++ ++#define MCF_FEC_TCR_FDEN (0x00000004) ++ ++#define MCF_FEC_ECR_ETHER_EN (0x00000002) ++#define MCF_FEC_ECR_ENA_1588 (0x00000010) ++ ++/*-------------ioctl command ---------------------------------------*/ ++#define ESW_SET_LEARNING_CONF 0x9101 ++#define ESW_GET_LEARNING_CONF 0x9201 ++#define ESW_SET_BLOCKING_CONF 0x9102 ++#define ESW_GET_BLOCKING_CONF 0x9202 ++#define ESW_SET_MULTICAST_CONF 0x9103 ++#define ESW_GET_MULTICAST_CONF 0x9203 ++#define ESW_SET_BROADCAST_CONF 0x9104 ++#define ESW_GET_BROADCAST_CONF 0x9204 ++#define ESW_SET_PORTENABLE_CONF 0x9105 ++#define ESW_GET_PORTENABLE_CONF 0x9205 ++#define ESW_SET_IP_SNOOP_CONF 0x9106 ++#define ESW_GET_IP_SNOOP_CONF 0x9206 ++#define ESW_SET_PORT_SNOOP_CONF 0x9107 ++#define ESW_GET_PORT_SNOOP_CONF 0x9207 ++#define ESW_SET_PORT_MIRROR_CONF 0x9108 ++#define ESW_GET_PORT_MIRROR_CONF 0x9208 ++#define ESW_SET_PIRORITY_VLAN 0x9109 ++#define ESW_GET_PIRORITY_VLAN 0x9209 ++#define ESW_SET_PIRORITY_IP 0x910A ++#define ESW_GET_PIRORITY_IP 0x920A ++#define ESW_SET_PIRORITY_MAC 0x910B ++#define ESW_GET_PIRORITY_MAC 0x920B ++#define ESW_SET_PIRORITY_DEFAULT 0x910C ++#define ESW_GET_PIRORITY_DEFAULT 0x920C ++#define ESW_SET_P0_FORCED_FORWARD 0x910D ++#define ESW_GET_P0_FORCED_FORWARD 0x920D ++#define ESW_SET_SWITCH_MODE 0x910E ++#define ESW_GET_SWITCH_MODE 0x920E ++#define ESW_SET_BRIDGE_CONFIG 0x910F ++#define ESW_GET_BRIDGE_CONFIG 0x920F ++#define ESW_SET_VLAN_OUTPUT_PROCESS 0x9110 ++#define ESW_GET_VLAN_OUTPUT_PROCESS 0x9210 ++#define ESW_SET_VLAN_INPUT_PROCESS 0x9111 ++#define ESW_GET_VLAN_INPUT_PROCESS 0x9211 ++#define ESW_SET_VLAN_DOMAIN_VERIFICATION 0x9112 ++#define ESW_GET_VLAN_DOMAIN_VERIFICATION 0x9212 ++#define ESW_SET_VLAN_RESOLUTION_TABLE 0x9113 ++#define ESW_GET_VLAN_RESOLUTION_TABLE 0x9213 ++#define ESW_GET_ENTRY_PORT_NUMBER 0x9214 ++#define ESW_GET_LOOKUP_TABLE 0x9215 ++#define ESW_GET_PORT_STATUS 0x9216 ++#define ESW_SET_VLAN_ID 0x9114 ++#define ESW_SET_VLAN_ID_CLEARED 0x9115 ++#define ESW_SET_PORT_IN_VLAN_ID 0x9116 ++#define ESW_SET_PORT_ENTRY_EMPTY 0x9117 ++#define ESW_SET_OTHER_PORT_ENTRY_EMPTY 0x9118 ++#define ESW_GET_PORT_ALL_STATUS 0x9217 ++#define ESW_SET_PORT_MIRROR_CONF_PORT_MATCH 0x9119 ++#define ESW_SET_PORT_MIRROR_CONF_ADDR_MATCH 0x911A ++ ++#define ESW_GET_STATISTICS_STATUS 0x9221 ++#define ESW_SET_OUTPUT_QUEUE_MEMORY 0x9125 ++#define ESW_GET_OUTPUT_QUEUE_STATUS 0x9225 ++#define ESW_UPDATE_STATIC_MACTABLE 0x9226 ++#define ESW_CLEAR_ALL_MACTABLE 0x9227 ++#define ESW_GET_USER_PID 0x9228 ++ ++typedef struct _eswIOCTL_PORT_CONF { ++ int port; ++ int enable; ++} eswIoctlPortConfig; ++ ++typedef struct _eswIOCTL_PORT_EN_CONF { ++ int port; ++ int tx_enable; ++ int rx_enable; ++} eswIoctlPortEnableConfig; ++ ++typedef struct _eswIOCTL_IP_SNOOP_CONF { ++ int mode; ++ unsigned long ip_header_protocol; ++} eswIoctlIpsnoopConfig; ++ ++typedef struct _eswIOCTL_P0_FORCED_FORWARD_CONF { ++ int port1; ++ int port2; ++ int enable; ++} eswIoctlP0ForcedForwardConfig; ++ ++typedef struct _eswIOCTL_PORT_SNOOP_CONF { ++ int mode; ++ unsigned short compare_port; ++ int compare_num; ++} eswIoctlPortsnoopConfig; ++ ++typedef struct _eswIOCTL_PORT_Mirror_CONF { ++ int mirror_port; ++ int port; ++ int egress_en; ++ int ingress_en; ++ int egress_mac_src_en; ++ int egress_mac_des_en; ++ int ingress_mac_src_en; ++ int ingress_mac_des_en; ++ unsigned char *src_mac; ++ unsigned char *des_mac; ++ int mirror_enable; ++} eswIoctlPortMirrorConfig; ++ ++struct eswIoctlMirrorCfgPortMatch { ++ int mirror_port; ++ int port_match_en; ++ int port; ++}; ++ ++struct eswIoctlMirrorCfgAddrMatch { ++ int mirror_port; ++ int addr_match_en; ++ unsigned char *mac_addr; ++}; ++ ++typedef struct _eswIOCTL_PRIORITY_VLAN_CONF { ++ int port; ++ int func_enable; ++ int vlan_pri_table_num; ++ int vlan_pri_table_value; ++} eswIoctlPriorityVlanConfig; ++ ++typedef struct _eswIOCTL_PRIORITY_IP_CONF { ++ int port; ++ int func_enable; ++ int ipv4_en; ++ int ip_priority_num; ++ int ip_priority_value; ++} eswIoctlPriorityIPConfig; ++ ++typedef struct _eswIOCTL_PRIORITY_MAC_CONF { ++ int port; ++} eswIoctlPriorityMacConfig; ++ ++typedef struct _eswIOCTL_PRIORITY_DEFAULT_CONF { ++ int port; ++ unsigned char priority_value; ++} eswIoctlPriorityDefaultConfig; ++ ++typedef struct _eswIOCTL_IRQ_STATUS { ++ unsigned long isr; ++ unsigned long imr; ++ unsigned long rx_buf_pointer; ++ unsigned long tx_buf_pointer; ++ unsigned long rx_max_size; ++ unsigned long rx_buf_active; ++ unsigned long tx_buf_active; ++} eswIoctlIrqStatus; ++ ++typedef struct _eswIOCTL_PORT_Mirror_STATUS { ++ unsigned long ESW_MCR; ++ unsigned long ESW_EGMAP; ++ unsigned long ESW_INGMAP; ++ unsigned long ESW_INGSAL; ++ unsigned long ESW_INGSAH; ++ unsigned long ESW_INGDAL; ++ unsigned long ESW_INGDAH; ++ unsigned long ESW_ENGSAL; ++ unsigned long ESW_ENGSAH; ++ unsigned long ESW_ENGDAL; ++ unsigned long ESW_ENGDAH; ++ unsigned long ESW_MCVAL; ++} eswIoctlPortMirrorStatus; ++ ++typedef struct _eswIOCTL_VLAN_OUTPUT_CONF { ++ int port; ++ int mode; ++} eswIoctlVlanOutputConfig; ++ ++typedef struct _eswIOCTL_VLAN_INPUT_CONF { ++ int port; ++ int mode; ++ unsigned short port_vlanid; ++} eswIoctlVlanInputConfig; ++ ++typedef struct _eswIOCTL_VLAN_DOMAIN_VERIFY_CONF { ++ int port; ++ int vlan_domain_verify_en; ++ int vlan_discard_unknown_en; ++} eswIoctlVlanVerificationConfig; ++ ++typedef struct _eswIOCTL_VLAN_RESOULATION_TABLE { ++ unsigned short port_vlanid; ++ unsigned char vlan_domain_port; ++ unsigned char vlan_domain_num; ++} eswIoctlVlanResoultionTable; ++ ++struct eswVlanTableItem { ++ eswIoctlVlanResoultionTable table[32]; ++ unsigned char valid_num; ++}; ++ ++typedef struct _eswIOCTL_VLAN_INPUT_STATUS { ++ unsigned long ESW_VLANV; ++ unsigned long ESW_PID[3]; ++ unsigned long ESW_VIMSEL; ++ unsigned long ESW_VIMEN; ++ unsigned long ESW_VRES[32]; ++} eswIoctlVlanInputStatus; ++ ++typedef struct _eswIOCTL_Static_MACTable { ++ unsigned char *mac_addr; ++ int port; ++ int priority; ++} eswIoctlUpdateStaticMACtable; ++ ++typedef struct _eswIOCTL_OUTPUT_QUEUE { ++ int fun_num; ++ esw_output_queue_status sOutputQueue; ++} eswIoctlOutputQueue; ++ ++/*=============================================================*/ ++#define LEARNING_AGING_TIMER (10 * HZ) ++/* ++ * Info received from Hardware Learning FIFO, ++ * holding MAC address and corresponding Hash Value and ++ * port number where the frame was received (disassembled). ++ */ ++typedef struct _eswPortInfo { ++ /* MAC lower 32 bits (first byte is 7:0). */ ++ unsigned int maclo; ++ /* MAC upper 16 bits (47:32). */ ++ unsigned int machi; ++ /* the hash value for this MAC address. */ ++ unsigned int hash; ++ /* the port number this MAC address is associated with. */ ++ unsigned int port; ++} eswPortInfo; ++ ++/* ++ * Hardware Look up Address Table 64-bit element. ++ */ ++typedef volatile struct _64bitTableEntry { ++ unsigned int lo; /* lower 32 bits */ ++ unsigned int hi; /* upper 32 bits */ ++} eswTable64bitEntry; ++ ++struct eswAddrTableEntryExample { ++ /* the entry number */ ++ unsigned short entrynum; ++ /* mac address array */ ++ unsigned char mac_addr[6]; ++ unsigned char item1; ++ unsigned short item2; ++}; ++ ++/* ++ * Define the buffer descriptor structure. ++ */ ++typedef struct bufdesc { ++ unsigned short cbd_sc; /* Control and status info */ ++ unsigned short cbd_datlen; /* Data length */ ++ unsigned long cbd_bufaddr; /* Buffer address */ ++#ifdef MODELO_BUFFER ++ unsigned long ebd_status; ++ unsigned short length_proto_type; ++ unsigned short payload_checksum; ++ unsigned long bdu; ++ unsigned long timestamp; ++ unsigned long reserverd_word1; ++ unsigned long reserverd_word2; ++#endif ++} cbd_t; ++ ++/* Forward declarations of some structures to support different PHYs ++ */ ++typedef struct { ++ uint mii_data; ++ void (*funct)(uint mii_reg, struct net_device *dev); ++} phy_cmd_t; ++ ++typedef struct { ++ uint id; ++ char *name; ++ ++ const phy_cmd_t *config; ++ const phy_cmd_t *startup; ++ const phy_cmd_t *ack_int; ++ const phy_cmd_t *shutdown; ++} phy_info_t; ++ ++struct port_status { ++ /* 1: link is up, 0: link is down */ ++ int port1_link_status; ++ int port2_link_status; ++ /* 1: blocking, 0: unblocking */ ++ int port0_block_status; ++ int port1_block_status; ++ int port2_block_status; ++}; ++ ++struct port_all_status { ++ /* 1: link is up, 0: link is down */ ++ int link_status; ++ /* 1: blocking, 0: unblocking */ ++ int block_status; ++ /* 1: unlearning, 0: learning */ ++ int learn_status; ++ /* vlan domain verify 1: enable 0: disable */ ++ int vlan_verify; ++ /* discard unknow 1: enable 0: disable */ ++ int discard_unknown; ++ /* multicast resolution 1: enable 0: disable */ ++ int multi_reso; ++ /* broadcast resolution 1: enable 0: disalbe */ ++ int broad_reso; ++ /* transmit 1: enable 0: disable */ ++ int ftransmit; ++ /* receive 1: enable 0: disable */ ++ int freceive; ++}; ++ ++/* The switch buffer descriptors track the ring buffers. The rx_bd_base and ++ * tx_bd_base always point to the base of the buffer descriptors. The ++ * cur_rx and cur_tx point to the currently available buffer. ++ * The dirty_tx tracks the current buffer that is being sent by the ++ * controller. The cur_tx and dirty_tx are equal under both completely ++ * empty and completely full conditions. The empty/ready indicator in ++ * the buffer descriptor determines the actual condition. ++ */ ++struct switch_enet_private { ++ /* Hardware registers of the switch device */ ++ volatile switch_t *hwp; ++ volatile eswAddrTable_t *hwentry; ++ ++ struct net_device *netdev; ++ struct platform_device *pdev; ++ /* The saved address of a sent-in-place packet/buffer, for skfree(). */ ++ unsigned char *tx_bounce[TX_RING_SIZE]; ++ struct sk_buff *tx_skbuff[TX_RING_SIZE]; ++ ushort skb_cur; ++ ushort skb_dirty; ++ ++ /* CPM dual port RAM relative addresses. ++ */ ++ cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ ++ cbd_t *tx_bd_base; ++ cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ ++ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ ++ uint tx_full; ++ /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ ++ spinlock_t hw_lock; ++ ++ /* hold while accessing the mii_list_t() elements */ ++ spinlock_t mii_lock; ++ struct mii_bus *mdio_bus; ++ struct phy_device *phydev[SWITCH_EPORT_NUMBER]; ++ ++ uint phy_id; ++ uint phy_id_done; ++ uint phy_status; ++ uint phy_speed; ++ phy_info_t const *phy; ++ struct work_struct phy_task; ++ volatile switch_t *phy_hwp; ++ ++ uint sequence_done; ++ uint mii_phy_task_queued; ++ ++ uint phy_addr; ++ ++ int index; ++ int opened; ++ int full_duplex; ++ int msg_enable; ++ int phy1_link; ++ int phy1_old_link; ++ int phy1_duplex; ++ int phy1_speed; ++ ++ int phy2_link; ++ int phy2_old_link; ++ int phy2_duplex; ++ int phy2_speed; ++ /* --------------Statistics--------------------------- */ ++ /* when a new element deleted a element with in ++ * a block due to lack of space */ ++ int atBlockOverflows; ++ /* Peak number of valid entries in the address table */ ++ int atMaxEntries; ++ /* current number of valid entries in the address table */ ++ int atCurrEntries; ++ /* maximum entries within a block found ++ * (updated within ageing)*/ ++ int atMaxEntriesPerBlock; ++ ++ /* -------------------ageing function------------------ */ ++ /* maximum age allowed for an entry */ ++ int ageMax; ++ /* last LUT entry to block that was ++ * inspected by the Ageing task*/ ++ int ageLutIdx; ++ /* last element within block inspected by the Ageing task */ ++ int ageBlockElemIdx; ++ /* complete table has been processed by ageing process */ ++ int ageCompleted; ++ /* delay setting */ ++ int ageDelay; ++ /* current delay Counter */ ++ int ageDelayCnt; ++ ++ /* ----------------timer related---------------------------- */ ++ /* current time (for timestamping) */ ++ int currTime; ++ /* flag set by timer when currTime changed ++ * and cleared by serving function*/ ++ int timeChanged; ++ ++ /**/ ++ /* Timer for Aging */ ++ struct timer_list timer_aging; ++ int learning_irqhandle_enable; ++}; ++ ++struct switch_platform_private { ++ unsigned long quirks; ++ int num_slots; /* Slots on controller */ ++ struct switch_enet_private *fep_host[0]; /* Pointers to hosts */ ++}; ++ ++/******************************************************************************/ ++/* Recieve is empty */ ++#define BD_SC_EMPTY ((unsigned short)0x8000) ++/* Transmit is ready */ ++#define BD_SC_READY ((unsigned short)0x8000) ++/* Last buffer descriptor */ ++#define BD_SC_WRAP ((unsigned short)0x2000) ++/* Interrupt on change */ ++#define BD_SC_INTRPT ((unsigned short)0x1000) ++/* Continous mode */ ++#define BD_SC_CM ((unsigned short)0x0200) ++/* Rec'd too many idles */ ++#define BD_SC_ID ((unsigned short)0x0100) ++/* xmt preamble */ ++#define BD_SC_P ((unsigned short)0x0100) ++/* Break received */ ++#define BD_SC_BR ((unsigned short)0x0020) ++/* Framing error */ ++#define BD_SC_FR ((unsigned short)0x0010) ++/* Parity error */ ++#define BD_SC_PR ((unsigned short)0x0008) ++/* Overrun */ ++#define BD_SC_OV ((unsigned short)0x0002) ++#define BD_SC_CD ((unsigned short)0x0001) ++ ++/* Buffer descriptor control/status used by Ethernet receive. ++*/ ++#define BD_ENET_RX_EMPTY ((unsigned short)0x8000) ++#define BD_ENET_RX_WRAP ((unsigned short)0x2000) ++#define BD_ENET_RX_INTR ((unsigned short)0x1000) ++#define BD_ENET_RX_LAST ((unsigned short)0x0800) ++#define BD_ENET_RX_FIRST ((unsigned short)0x0400) ++#define BD_ENET_RX_MISS ((unsigned short)0x0100) ++#define BD_ENET_RX_LG ((unsigned short)0x0020) ++#define BD_ENET_RX_NO ((unsigned short)0x0010) ++#define BD_ENET_RX_SH ((unsigned short)0x0008) ++#define BD_ENET_RX_CR ((unsigned short)0x0004) ++#define BD_ENET_RX_OV ((unsigned short)0x0002) ++#define BD_ENET_RX_CL ((unsigned short)0x0001) ++/* All status bits */ ++#define BD_ENET_RX_STATS ((unsigned short)0x013f) ++ ++/* Buffer descriptor control/status used by Ethernet transmit. ++*/ ++#define BD_ENET_TX_READY ((unsigned short)0x8000) ++#define BD_ENET_TX_PAD ((unsigned short)0x4000) ++#define BD_ENET_TX_WRAP ((unsigned short)0x2000) ++#define BD_ENET_TX_INTR ((unsigned short)0x1000) ++#define BD_ENET_TX_LAST ((unsigned short)0x0800) ++#define BD_ENET_TX_TC ((unsigned short)0x0400) ++#define BD_ENET_TX_DEF ((unsigned short)0x0200) ++#define BD_ENET_TX_HB ((unsigned short)0x0100) ++#define BD_ENET_TX_LC ((unsigned short)0x0080) ++#define BD_ENET_TX_RL ((unsigned short)0x0040) ++#define BD_ENET_TX_RCMASK ((unsigned short)0x003c) ++#define BD_ENET_TX_UN ((unsigned short)0x0002) ++#define BD_ENET_TX_CSL ((unsigned short)0x0001) ++/* All status bits */ ++#define BD_ENET_TX_STATS ((unsigned short)0x03ff) ++ ++/*Copy from validation code */ ++#define RX_BUFFER_SIZE 1520 ++#define TX_BUFFER_SIZE 1520 ++#define NUM_RXBDS 20 ++#define NUM_TXBDS 20 ++ ++#define TX_BD_R 0x8000 ++#define TX_BD_TO1 0x4000 ++#define TX_BD_W 0x2000 ++#define TX_BD_TO2 0x1000 ++#define TX_BD_L 0x0800 ++#define TX_BD_TC 0x0400 ++ ++#define TX_BD_INT 0x40000000 ++#define TX_BD_TS 0x20000000 ++#define TX_BD_PINS 0x10000000 ++#define TX_BD_IINS 0x08000000 ++#define TX_BD_TXE 0x00008000 ++#define TX_BD_UE 0x00002000 ++#define TX_BD_EE 0x00001000 ++#define TX_BD_FE 0x00000800 ++#define TX_BD_LCE 0x00000400 ++#define TX_BD_OE 0x00000200 ++#define TX_BD_TSE 0x00000100 ++#define TX_BD_BDU 0x80000000 ++ ++#define RX_BD_E 0x8000 ++#define RX_BD_R01 0x4000 ++#define RX_BD_W 0x2000 ++#define RX_BD_R02 0x1000 ++#define RX_BD_L 0x0800 ++#define RX_BD_M 0x0100 ++#define RX_BD_BC 0x0080 ++#define RX_BD_MC 0x0040 ++#define RX_BD_LG 0x0020 ++#define RX_BD_NO 0x0010 ++#define RX_BD_CR 0x0004 ++#define RX_BD_OV 0x0002 ++#define RX_BD_TR 0x0001 ++ ++#define RX_BD_ME 0x80000000 ++#define RX_BD_PE 0x04000000 ++#define RX_BD_CE 0x02000000 ++#define RX_BD_UC 0x01000000 ++#define RX_BD_INT 0x00800000 ++#define RX_BD_ICE 0x00000020 ++#define RX_BD_PCR 0x00000010 ++#define RX_BD_VLAN 0x00000004 ++#define RX_BD_IPV6 0x00000002 ++#define RX_BD_FRAG 0x00000001 ++#define RX_BD_BDU 0x80000000 ++/****************************************************************************/ ++ ++/* Address Table size in bytes(2048 64bit entry ) */ ++#define ESW_ATABLE_MEM_SIZE (2048*8) ++/* How many 64-bit elements fit in the address table */ ++#define ESW_ATABLE_MEM_NUM_ENTRIES (2048) ++/* Address Table Maximum number of entries in each Slot */ ++#define ATABLE_ENTRY_PER_SLOT 8 ++/* log2(ATABLE_ENTRY_PER_SLOT)*/ ++#define ATABLE_ENTRY_PER_SLOT_bits 3 ++/* entry size in byte */ ++#define ATABLE_ENTRY_SIZE 8 ++/* slot size in byte */ ++#define ATABLE_SLOT_SIZE (ATABLE_ENTRY_PER_SLOT * ATABLE_ENTRY_SIZE) ++/* width of timestamp variable (bits) within address table entry */ ++#define AT_DENTRY_TIMESTAMP_WIDTH 10 ++/* number of bits for port number storage */ ++#define AT_DENTRY_PORT_WIDTH 4 ++/* number of bits for port bitmask number storage */ ++#define AT_SENTRY_PORT_WIDTH 7 ++/* address table static entry port bitmask start address bit */ ++#define AT_SENTRY_PORTMASK_shift 21 ++/* number of bits for port priority storage */ ++#define AT_SENTRY_PRIO_WIDTH 7 ++/* address table static entry priority start address bit */ ++#define AT_SENTRY_PRIO_shift 18 ++/* address table dynamic entry port start address bit */ ++#define AT_DENTRY_PORT_shift 28 ++/* address table dynamic entry timestamp start address bit */ ++#define AT_DENTRY_TIME_shift 18 ++/* address table entry record type start address bit */ ++#define AT_ENTRY_TYPE_shift 17 ++/* address table entry record type bit: 1 static, 0 dynamic */ ++#define AT_ENTRY_TYPE_STATIC 1 ++#define AT_ENTRY_TYPE_DYNAMIC 0 ++/* address table entry record valid start address bit */ ++#define AT_ENTRY_VALID_shift 16 ++#define AT_ENTRY_RECORD_VALID 1 ++ ++#define AT_EXTRACT_VALID(x) \ ++ ((x >> AT_ENTRY_VALID_shift) & AT_ENTRY_RECORD_VALID) ++ ++#define AT_EXTRACT_PORTMASK(x) \ ++ ((x >> AT_SENTRY_PORTMASK_shift) & AT_SENTRY_PORT_WIDTH) ++ ++#define AT_EXTRACT_PRIO(x) \ ++ ((x >> AT_SENTRY_PRIO_shift) & AT_SENTRY_PRIO_WIDTH) ++ ++/* return block corresponding to the 8 bit hash value calculated */ ++#define GET_BLOCK_PTR(hash) (hash << 3) ++#define AT_EXTRACT_TIMESTAMP(x) \ ++ ((x >> AT_DENTRY_TIME_shift) & ((1 << AT_DENTRY_TIMESTAMP_WIDTH)-1)) ++#define AT_EXTRACT_PORT(x) \ ++ ((x >> AT_DENTRY_PORT_shift) & ((1 << AT_DENTRY_PORT_WIDTH)-1)) ++#define AT_SEXTRACT_PORT(x) \ ++ ((~((x >> AT_SENTRY_PORTMASK_shift) & \ ++ ((1 << AT_DENTRY_PORT_WIDTH)-1))) >> 1) ++#define TIMEDELTA(newtime, oldtime) \ ++ ((newtime - oldtime) & \ ++ ((1 << AT_DENTRY_TIMESTAMP_WIDTH)-1)) ++ ++#define AT_EXTRACT_IP_PROTOCOL(x) ((x >> 8) & 0xff) ++#define AT_EXTRACT_TCP_UDP_PORT(x) ((x >> 16) & 0xffff) ++ ++/* increment time value respecting modulo. */ ++#define TIMEINCREMENT(time) \ ++ ((time) = ((time)+1) & ((1 << AT_DENTRY_TIMESTAMP_WIDTH)-1)) ++/* ------------------------------------------------------------------------- */ ++/* Bit definitions and macros for MCF_ESW_REVISION */ ++#define MCF_ESW_REVISION_CORE_REVISION(x) (((x)&0x0000FFFF)<<0) ++#define MCF_ESW_REVISION_CUSTOMER_REVISION(x) (((x)&0x0000FFFF)<<16) ++ ++/* Bit definitions and macros for MCF_ESW_PER */ ++#define MCF_ESW_PER_TE0 (0x00000001) ++#define MCF_ESW_PER_TE1 (0x00000002) ++#define MCF_ESW_PER_TE2 (0x00000004) ++#define MCF_ESW_PER_RE0 (0x00010000) ++#define MCF_ESW_PER_RE1 (0x00020000) ++#define MCF_ESW_PER_RE2 (0x00040000) ++ ++/* Bit definitions and macros for MCF_ESW_VLANV */ ++#define MCF_ESW_VLANV_VV0 (0x00000001) ++#define MCF_ESW_VLANV_VV1 (0x00000002) ++#define MCF_ESW_VLANV_VV2 (0x00000004) ++#define MCF_ESW_VLANV_DU0 (0x00010000) ++#define MCF_ESW_VLANV_DU1 (0x00020000) ++#define MCF_ESW_VLANV_DU2 (0x00040000) ++ ++/* Bit definitions and macros for MCF_ESW_DBCR */ ++#define MCF_ESW_DBCR_P0 (0x00000001) ++#define MCF_ESW_DBCR_P1 (0x00000002) ++#define MCF_ESW_DBCR_P2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_DMCR */ ++#define MCF_ESW_DMCR_P0 (0x00000001) ++#define MCF_ESW_DMCR_P1 (0x00000002) ++#define MCF_ESW_DMCR_P2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_BKLR */ ++#define MCF_ESW_BKLR_BE0 (0x00000001) ++#define MCF_ESW_BKLR_BE1 (0x00000002) ++#define MCF_ESW_BKLR_BE2 (0x00000004) ++#define MCF_ESW_BKLR_LD0 (0x00010000) ++#define MCF_ESW_BKLR_LD1 (0x00020000) ++#define MCF_ESW_BKLR_LD2 (0x00040000) ++ ++/* Bit definitions and macros for MCF_ESW_BMPC */ ++#define MCF_ESW_BMPC_PORT(x) (((x)&0x0000000F)<<0) ++#define MCF_ESW_BMPC_MSG_TX (0x00000020) ++#define MCF_ESW_BMPC_EN (0x00000040) ++#define MCF_ESW_BMPC_DIS (0x00000080) ++#define MCF_ESW_BMPC_PRIORITY(x) (((x)&0x00000007)<<13) ++#define MCF_ESW_BMPC_PORTMASK(x) (((x)&0x00000007)<<16) ++ ++/* Bit definitions and macros for MCF_ESW_MODE */ ++#define MCF_ESW_MODE_SW_RST (0x00000001) ++#define MCF_ESW_MODE_SW_EN (0x00000002) ++#define MCF_ESW_MODE_STOP (0x00000080) ++#define MCF_ESW_MODE_CRC_TRAN (0x00000100) ++#define MCF_ESW_MODE_P0CT (0x00000200) ++#define MCF_ESW_MODE_STATRST (0x80000000) ++ ++/* Bit definitions and macros for MCF_ESW_VIMSEL */ ++#define MCF_ESW_VIMSEL_IM0(x) (((x)&0x00000003)<<0) ++#define MCF_ESW_VIMSEL_IM1(x) (((x)&0x00000003)<<2) ++#define MCF_ESW_VIMSEL_IM2(x) (((x)&0x00000003)<<4) ++ ++/* Bit definitions and macros for MCF_ESW_VOMSEL */ ++#define MCF_ESW_VOMSEL_OM0(x) (((x)&0x00000003)<<0) ++#define MCF_ESW_VOMSEL_OM1(x) (((x)&0x00000003)<<2) ++#define MCF_ESW_VOMSEL_OM2(x) (((x)&0x00000003)<<4) ++ ++/* Bit definitions and macros for MCF_ESW_VIMEN */ ++#define MCF_ESW_VIMEN_EN0 (0x00000001) ++#define MCF_ESW_VIMEN_EN1 (0x00000002) ++#define MCF_ESW_VIMEN_EN2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_VID */ ++#define MCF_ESW_VID_TAG(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_MCR */ ++#define MCF_ESW_MCR_PORT(x) (((x)&0x0000000F)<<0) ++#define MCF_ESW_MCR_MEN (0x00000010) ++#define MCF_ESW_MCR_INGMAP (0x00000020) ++#define MCF_ESW_MCR_EGMAP (0x00000040) ++#define MCF_ESW_MCR_INGSA (0x00000080) ++#define MCF_ESW_MCR_INGDA (0x00000100) ++#define MCF_ESW_MCR_EGSA (0x00000200) ++#define MCF_ESW_MCR_EGDA (0x00000400) ++ ++/* Bit definitions and macros for MCF_ESW_EGMAP */ ++#define MCF_ESW_EGMAP_EG0 (0x00000001) ++#define MCF_ESW_EGMAP_EG1 (0x00000002) ++#define MCF_ESW_EGMAP_EG2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_INGMAP */ ++#define MCF_ESW_INGMAP_ING0 (0x00000001) ++#define MCF_ESW_INGMAP_ING1 (0x00000002) ++#define MCF_ESW_INGMAP_ING2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_INGSAL */ ++#define MCF_ESW_INGSAL_ADDLOW(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_INGSAH */ ++#define MCF_ESW_INGSAH_ADDHIGH(x) (((x)&0x0000FFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_INGDAL */ ++#define MCF_ESW_INGDAL_ADDLOW(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_INGDAH */ ++#define MCF_ESW_INGDAH_ADDHIGH(x) (((x)&0x0000FFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_ENGSAL */ ++#define MCF_ESW_ENGSAL_ADDLOW(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_ENGSAH */ ++#define MCF_ESW_ENGSAH_ADDHIGH(x) (((x)&0x0000FFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_ENGDAL */ ++#define MCF_ESW_ENGDAL_ADDLOW(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_ENGDAH */ ++#define MCF_ESW_ENGDAH_ADDHIGH(x) (((x)&0x0000FFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_MCVAL */ ++#define MCF_ESW_MCVAL_COUNT(x) (((x)&0x000000FF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_MMSR */ ++#define MCF_ESW_MMSR_BUSY (0x00000001) ++#define MCF_ESW_MMSR_NOCELL (0x00000002) ++#define MCF_ESW_MMSR_MEMFULL (0x00000004) ++#define MCF_ESW_MMSR_MFLATCH (0x00000008) ++#define MCF_ESW_MMSR_DQ_GRNT (0x00000040) ++#define MCF_ESW_MMSR_CELLS_AVAIL(x) (((x)&0x000000FF)<<16) ++ ++/* Bit definitions and macros for MCF_ESW_LMT */ ++#define MCF_ESW_LMT_THRESH(x) (((x)&0x000000FF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_LFC */ ++#define MCF_ESW_LFC_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_PCSR */ ++#define MCF_ESW_PCSR_PC0 (0x00000001) ++#define MCF_ESW_PCSR_PC1 (0x00000002) ++#define MCF_ESW_PCSR_PC2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_IOSR */ ++#define MCF_ESW_IOSR_OR0 (0x00000001) ++#define MCF_ESW_IOSR_OR1 (0x00000002) ++#define MCF_ESW_IOSR_OR2 (0x00000004) ++ ++/* Bit definitions and macros for MCF_ESW_QWT */ ++#define MCF_ESW_QWT_Q0WT(x) (((x)&0x0000001F)<<0) ++#define MCF_ESW_QWT_Q1WT(x) (((x)&0x0000001F)<<8) ++#define MCF_ESW_QWT_Q2WT(x) (((x)&0x0000001F)<<16) ++#define MCF_ESW_QWT_Q3WT(x) (((x)&0x0000001F)<<24) ++ ++/* Bit definitions and macros for MCF_ESW_P0BCT */ ++#define MCF_ESW_P0BCT_THRESH(x) (((x)&0x000000FF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_P0FFEN */ ++#define MCF_ESW_P0FFEN_FEN (0x00000001) ++#define MCF_ESW_P0FFEN_FD(x) (((x)&0x00000003)<<2) ++ ++/* Bit definitions and macros for MCF_ESW_PSNP */ ++#define MCF_ESW_PSNP_EN (0x00000001) ++#define MCF_ESW_PSNP_MODE(x) (((x)&0x00000003)<<1) ++#define MCF_ESW_PSNP_CD (0x00000008) ++#define MCF_ESW_PSNP_CS (0x00000010) ++#define MCF_ESW_PSNP_PORT_COMPARE(x) (((x)&0x0000FFFF)<<16) ++ ++/* Bit definitions and macros for MCF_ESW_IPSNP */ ++#define MCF_ESW_IPSNP_EN (0x00000001) ++#define MCF_ESW_IPSNP_MODE(x) (((x)&0x00000003)<<1) ++#define MCF_ESW_IPSNP_PROTOCOL(x) (((x)&0x000000FF)<<8) ++ ++/* Bit definitions and macros for MCF_ESW_PVRES */ ++#define MCF_ESW_PVRES_PRI0(x) (((x)&0x00000007)<<0) ++#define MCF_ESW_PVRES_PRI1(x) (((x)&0x00000007)<<3) ++#define MCF_ESW_PVRES_PRI2(x) (((x)&0x00000007)<<6) ++#define MCF_ESW_PVRES_PRI3(x) (((x)&0x00000007)<<9) ++#define MCF_ESW_PVRES_PRI4(x) (((x)&0x00000007)<<12) ++#define MCF_ESW_PVRES_PRI5(x) (((x)&0x00000007)<<15) ++#define MCF_ESW_PVRES_PRI6(x) (((x)&0x00000007)<<18) ++#define MCF_ESW_PVRES_PRI7(x) (((x)&0x00000007)<<21) ++ ++/* Bit definitions and macros for MCF_ESW_IPRES */ ++#define MCF_ESW_IPRES_ADDRESS(x) (((x)&0x000000FF)<<0) ++#define MCF_ESW_IPRES_IPV4SEL (0x00000100) ++#define MCF_ESW_IPRES_PRI0(x) (((x)&0x00000003)<<9) ++#define MCF_ESW_IPRES_PRI1(x) (((x)&0x00000003)<<11) ++#define MCF_ESW_IPRES_PRI2(x) (((x)&0x00000003)<<13) ++#define MCF_ESW_IPRES_READ (0x80000000) ++ ++/* Bit definitions and macros for MCF_ESW_PRES */ ++#define MCF_ESW_PRES_VLAN (0x00000001) ++#define MCF_ESW_PRES_IP (0x00000002) ++#define MCF_ESW_PRES_MAC (0x00000004) ++#define MCF_ESW_PRES_DFLT_PRI(x) (((x)&0x00000007)<<4) ++ ++/* Bit definitions and macros for MCF_ESW_PID */ ++#define MCF_ESW_PID_VLANID(x) (((x)&0x0000FFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_VRES */ ++#define MCF_ESW_VRES_P0 (0x00000001) ++#define MCF_ESW_VRES_P1 (0x00000002) ++#define MCF_ESW_VRES_P2 (0x00000004) ++#define MCF_ESW_VRES_VLANID(x) (((x)&0x00000FFF)<<3) ++ ++/* Bit definitions and macros for MCF_ESW_DISCN */ ++#define MCF_ESW_DISCN_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_DISCB */ ++#define MCF_ESW_DISCB_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_NDISCN */ ++#define MCF_ESW_NDISCN_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_NDISCB */ ++#define MCF_ESW_NDISCB_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_POQC */ ++#define MCF_ESW_POQC_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_PMVID */ ++#define MCF_ESW_PMVID_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_PMVTAG */ ++#define MCF_ESW_PMVTAG_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_PBL */ ++#define MCF_ESW_PBL_COUNT(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_ISR */ ++#define MCF_ESW_ISR_EBERR (0x00000001) ++#define MCF_ESW_ISR_RXB (0x00000002) ++#define MCF_ESW_ISR_RXF (0x00000004) ++#define MCF_ESW_ISR_TXB (0x00000008) ++#define MCF_ESW_ISR_TXF (0x00000010) ++#define MCF_ESW_ISR_QM (0x00000020) ++#define MCF_ESW_ISR_OD0 (0x00000040) ++#define MCF_ESW_ISR_OD1 (0x00000080) ++#define MCF_ESW_ISR_OD2 (0x00000100) ++#define MCF_ESW_ISR_LRN (0x00000200) ++ ++/* Bit definitions and macros for MCF_ESW_IMR */ ++#define MCF_ESW_IMR_EBERR (0x00000001) ++#define MCF_ESW_IMR_RXB (0x00000002) ++#define MCF_ESW_IMR_RXF (0x00000004) ++#define MCF_ESW_IMR_TXB (0x00000008) ++#define MCF_ESW_IMR_TXF (0x00000010) ++#define MCF_ESW_IMR_QM (0x00000020) ++#define MCF_ESW_IMR_OD0 (0x00000040) ++#define MCF_ESW_IMR_OD1 (0x00000080) ++#define MCF_ESW_IMR_OD2 (0x00000100) ++#define MCF_ESW_IMR_LRN (0x00000200) ++ ++/* Bit definitions and macros for MCF_ESW_RDSR */ ++#define MCF_ESW_RDSR_ADDRESS(x) (((x)&0x3FFFFFFF)<<2) ++ ++/* Bit definitions and macros for MCF_ESW_TDSR */ ++#define MCF_ESW_TDSR_ADDRESS(x) (((x)&0x3FFFFFFF)<<2) ++ ++/* Bit definitions and macros for MCF_ESW_MRBR */ ++#define MCF_ESW_MRBR_SIZE(x) (((x)&0x000003FF)<<4) ++ ++/* Bit definitions and macros for MCF_ESW_RDAR */ ++#define MCF_ESW_RDAR_R_DES_ACTIVE (0x01000000) ++ ++/* Bit definitions and macros for MCF_ESW_TDAR */ ++#define MCF_ESW_TDAR_X_DES_ACTIVE (0x01000000) ++ ++/* Bit definitions and macros for MCF_ESW_LREC0 */ ++#define MCF_ESW_LREC0_MACADDR0(x) (((x)&0xFFFFFFFF)<<0) ++ ++/* Bit definitions and macros for MCF_ESW_LREC1 */ ++#define MCF_ESW_LREC1_MACADDR1(x) (((x)&0x0000FFFF)<<0) ++#define MCF_ESW_LREC1_HASH(x) (((x)&0x000000FF)<<16) ++#define MCF_ESW_LREC1_SWPORT(x) (((x)&0x00000003)<<24) ++ ++/* Bit definitions and macros for MCF_ESW_LSR */ ++#define MCF_ESW_LSR_DA (0x00000001) ++ ++/* port mirroring port number match */ ++#define MIRROR_EGRESS_PORT_MATCH 1 ++#define MIRROR_INGRESS_PORT_MATCH 2 ++ ++/* port mirroring mac address match */ ++#define MIRROR_EGRESS_SOURCE_MATCH 1 ++#define MIRROR_INGRESS_SOURCE_MATCH 2 ++#define MIRROR_EGRESS_DESTINATION_MATCH 3 ++#define MIRROR_INGRESS_DESTINATION_MATCH 4 ++ ++#endif /* SWITCH_H */ +--- a/include/linux/fsl_devices.h ++++ b/include/linux/fsl_devices.h +@@ -129,4 +129,21 @@ struct fsl_ata_platform_data { + void (*exit)(void); + int (*get_clk_rate)(void); + }; ++ ++struct net_device; ++struct coldfire_switch_platform_data { ++ int hash_table; ++ unsigned int *switch_hw; ++ void (*request_intrs)(struct net_device *dev, ++ irqreturn_t (*)(int, void *), ++ void *irq_privatedata); ++ void (*set_mii)(struct net_device *dev); ++ void (*get_mac)(struct net_device *dev); ++ void (*enable_phy_intr)(void); ++ void (*disable_phy_intr)(void); ++ void (*phy_ack_intr)(void); ++ void (*localhw_setup)(void); ++ void (*uncache)(unsigned long addr); ++ void (*platform_flush_cache)(void); ++}; + #endif /* _FSL_DEVICE_H_ */ +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4756,6 +4756,10 @@ static int dev_ifsioc(struct net *net, s + default: + if ((cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) || ++#if defined(CONFIG_MODELO_SWITCH) ++ (cmd >= 0x9101 && ++ cmd <= 0x92ff) || ++#endif + cmd == SIOCBONDENSLAVE || + cmd == SIOCBONDRELEASE || + cmd == SIOCBONDSETHWADDR || +@@ -4948,6 +4952,10 @@ int dev_ioctl(struct net *net, unsigned + */ + default: + if (cmd == SIOCWANDEV || ++#if defined(CONFIG_MODELO_SWITCH) ++ (cmd >= 0x9101 && ++ cmd <= 0x92ff) || ++#endif + (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15)) { + dev_load(net, ifr.ifr_name); |