/* * B53 switch driver main logic * * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <linux/delay.h>#include <linux/export.h>#include <linux/gpio/consumer.h>#include <linux/kernel.h>#include <linux/math.h>#include <linux/minmax.h>#include <linux/module.h>#include <linux/platform_data/b53.h>#include <linux/phy.h>#include <linux/phylink.h>#include <linux/etherdevice.h>#include <linux/if_bridge.h>#include <linux/if_vlan.h>#include <net/dsa.h> #include "b53_regs.h"#include "b53_priv.h" struct b53_mib_desc { u8 size; u8 offset; const char *name;}; /* BCM5365 MIB counters */static const struct b53_mib_desc b53_mibs_65[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPkts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredTransmit" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x38, "TxPausePkts" }, { 8, 0x44, "RxOctets" }, { 4, 0x4c, "RxUndersizePkts" }, { 4, 0x50, "RxPausePkts" }, { 4, 0x54, "Pkts64Octets" }, { 4, 0x58, "Pkts65to127Octets" }, { 4, 0x5c, "Pkts128to255Octets" }, { 4, 0x60, "Pkts256to511Octets" }, { 4, 0x64, "Pkts512to1023Octets" }, { 4, 0x68, "Pkts1024to1522Octets" }, { 4, 0x6c, "RxOversizePkts" }, { 4, 0x70, "RxJabbers" }, { 4, 0x74, "RxAlignmentErrors" }, { 4, 0x78, "RxFCSErrors" }, { 8, 0x7c, "RxGoodOctets" }, { 4, 0x84, "RxDropPkts" }, { 4, 0x88, "RxUnicastPkts" }, { 4, 0x8c, "RxMulticastPkts" }, { 4, 0x90, "RxBroadcastPkts" }, { 4, 0x94, "RxSAChanges" }, { 4, 0x98, "RxFragments" },}; #define B53_MIBS_65_SIZE ARRAY_SIZE(b53_mibs_65) /* BCM63xx MIB counters */static const struct b53_mib_desc b53_mibs_63xx[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x0c, "TxQoSPkts" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPkts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredTransmit" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x38, "TxPausePkts" }, { 8, 0x3c, "TxQoSOctets" }, { 8, 0x44, "RxOctets" }, { 4, 0x4c, "RxUndersizePkts" }, { 4, 0x50, "RxPausePkts" }, { 4, 0x54, "Pkts64Octets" }, { 4, 0x58, "Pkts65to127Octets" }, { 4, 0x5c, "Pkts128to255Octets" }, { 4, 0x60, "Pkts256to511Octets" }, { 4, 0x64, "Pkts512to1023Octets" }, { 4, 0x68, "Pkts1024to1522Octets" }, { 4, 0x6c, "RxOversizePkts" }, { 4, 0x70, "RxJabbers" }, { 4, 0x74, "RxAlignmentErrors" }, { 4, 0x78, "RxFCSErrors" }, { 8, 0x7c, "RxGoodOctets" }, { 4, 0x84, "RxDropPkts" }, { 4, 0x88, "RxUnicastPkts" }, { 4, 0x8c, "RxMulticastPkts" }, { 4, 0x90, "RxBroadcastPkts" }, { 4, 0x94, "RxSAChanges" }, { 4, 0x98, "RxFragments" }, { 4, 0xa0, "RxSymbolErrors" }, { 4, 0xa4, "RxQoSPkts" }, { 8, 0xa8, "RxQoSOctets" }, { 4, 0xb0, "Pkts1523to2047Octets" }, { 4, 0xb4, "Pkts2048to4095Octets" }, { 4, 0xb8, "Pkts4096to8191Octets" }, { 4, 0xbc, "Pkts8192to9728Octets" }, { 4, 0xc0, "RxDiscarded" },}; #define B53_MIBS_63XX_SIZE ARRAY_SIZE(b53_mibs_63xx) /* MIB counters */static const struct b53_mib_desc b53_mibs[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPkts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredTransmit" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x38, "TxPausePkts" }, { 8, 0x50, "RxOctets" }, { 4, 0x58, "RxUndersizePkts" }, { 4, 0x5c, "RxPausePkts" }, { 4, 0x60, "Pkts64Octets" }, { 4, 0x64, "Pkts65to127Octets" }, { 4, 0x68, "Pkts128to255Octets" }, { 4, 0x6c, "Pkts256to511Octets" }, { 4, 0x70, "Pkts512to1023Octets" }, { 4, 0x74, "Pkts1024to1522Octets" }, { 4, 0x78, "RxOversizePkts" }, { 4, 0x7c, "RxJabbers" }, { 4, 0x80, "RxAlignmentErrors" }, { 4, 0x84, "RxFCSErrors" }, { 8, 0x88, "RxGoodOctets" }, { 4, 0x90, "RxDropPkts" }, { 4, 0x94, "RxUnicastPkts" }, { 4, 0x98, "RxMulticastPkts" }, { 4, 0x9c, "RxBroadcastPkts" }, { 4, 0xa0, "RxSAChanges" }, { 4, 0xa4, "RxFragments" }, { 4, 0xa8, "RxJumboPkts" }, { 4, 0xac, "RxSymbolErrors" }, { 4, 0xc0, "RxDiscarded" },}; #define B53_MIBS_SIZE ARRAY_SIZE(b53_mibs) static const struct b53_mib_desc b53_mibs_58xx[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x0c, "TxQPKTQ0" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPKts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredCollision" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x34, "TxFrameInDisc" }, { 4, 0x38, "TxPausePkts" }, { 4, 0x3c, "TxQPKTQ1" }, { 4, 0x40, "TxQPKTQ2" }, { 4, 0x44, "TxQPKTQ3" }, { 4, 0x48, "TxQPKTQ4" }, { 4, 0x4c, "TxQPKTQ5" }, { 8, 0x50, "RxOctets" }, { 4, 0x58, "RxUndersizePkts" }, { 4, 0x5c, "RxPausePkts" }, { 4, 0x60, "RxPkts64Octets" }, { 4, 0x64, "RxPkts65to127Octets" }, { 4, 0x68, "RxPkts128to255Octets" }, { 4, 0x6c, "RxPkts256to511Octets" }, { 4, 0x70, "RxPkts512to1023Octets" }, { 4, 0x74, "RxPkts1024toMaxPktsOctets" }, { 4, 0x78, "RxOversizePkts" }, { 4, 0x7c, "RxJabbers" }, { 4, 0x80, "RxAlignmentErrors" }, { 4, 0x84, "RxFCSErrors" }, { 8, 0x88, "RxGoodOctets" }, { 4, 0x90, "RxDropPkts" }, { 4, 0x94, "RxUnicastPkts" }, { 4, 0x98, "RxMulticastPkts" }, { 4, 0x9c, "RxBroadcastPkts" }, { 4, 0xa0, "RxSAChanges" }, { 4, 0xa4, "RxFragments" }, { 4, 0xa8, "RxJumboPkt" }, { 4, 0xac, "RxSymblErr" }, { 4, 0xb0, "InRangeErrCount" }, { 4, 0xb4, "OutRangeErrCount" }, { 4, 0xb8, "EEELpiEvent" }, { 4, 0xbc, "EEELpiDuration" }, { 4, 0xc0, "RxDiscard" }, { 4, 0xc8, "TxQPKTQ6" }, { 4, 0xcc, "TxQPKTQ7" }, { 4, 0xd0, "TxPkts64Octets" }, { 4, 0xd4, "TxPkts65to127Octets" }, { 4, 0xd8, "TxPkts128to255Octets" }, { 4, 0xdc, "TxPkts256to511Ocets" }, { 4, 0xe0, "TxPkts512to1023Ocets" }, { 4, 0xe4, "TxPkts1024toMaxPktOcets" },}; #define B53_MIBS_58XX_SIZE ARRAY_SIZE(b53_mibs_58xx) #define B53_MAX_MTU_25 (1536 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN)#define B53_MAX_MTU (9720 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) static int b53_do_vlan_op(struct b53_device *dev, u8 op){ unsigned int i; b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op); for (i = 0; i < 10; i++) { u8 vta; b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta); if (!(vta & VTA_START_CMD)) return 0; usleep_range(100, 200); } return -EIO;} static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, struct b53_vlan *vlan){ if (is5325(dev)) { u32 entry = 0; if (vlan->members) { entry = ((vlan->untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) | vlan->members; if (dev->core_rev >= 3) entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S; else entry |= VA_VALID_25; } b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry); b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | VTA_RW_STATE_WR | VTA_RW_OP_EN); } else if (is5365(dev)) { u16 entry = 0; if (vlan->members) entry = ((vlan->untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) | vlan->members | VA_VALID_65; b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry); b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | VTA_RW_STATE_WR | VTA_RW_OP_EN); } else { b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], (vlan->untag << VTE_UNTAG_S) | vlan->members); b53_do_vlan_op(dev, VTA_CMD_WRITE); } dev_dbg(dev->ds->dev, "VID: %d, members: 0x%04x, untag: 0x%04x\n", vid, vlan->members, vlan->untag);} static void b53_get_vlan_entry(struct b53_device *dev, u16 vid, struct b53_vlan *vlan){ if (is5325(dev)) { u32 entry = 0; b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | VTA_RW_STATE_RD | VTA_RW_OP_EN); b53_read32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, &entry); if (dev->core_rev >= 3) vlan->valid = !!(entry & VA_VALID_25_R4); else vlan->valid = !!(entry & VA_VALID_25); vlan->members = entry & VA_MEMBER_MASK; vlan->untag = (entry >> VA_UNTAG_S_25) & VA_UNTAG_MASK_25; } else if (is5365(dev)) { u16 entry = 0; b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | VTA_RW_STATE_WR | VTA_RW_OP_EN); b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, &entry); vlan->valid = !!(entry & VA_VALID_65); vlan->members = entry & VA_MEMBER_MASK; vlan->untag = (entry >> VA_UNTAG_S_65) & VA_UNTAG_MASK_65; } else { u32 entry = 0; b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); b53_do_vlan_op(dev, VTA_CMD_READ); b53_read32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], &entry); vlan->members = entry & VTE_MEMBERS; vlan->untag = (entry >> VTE_UNTAG_S) & VTE_MEMBERS; vlan->valid = true; }} static void b53_set_eap_mode(struct b53_device *dev, int port, int mode){ u64 eap_conf; if (is5325(dev) || is5365(dev) || dev->chip_id == BCM5389_DEVICE_ID) return; b53_read64(dev, B53_EAP_PAGE, B53_PORT_EAP_CONF(port), &eap_conf); if (is63xx(dev)) { eap_conf &= ~EAP_MODE_MASK_63XX; eap_conf |= (u64)mode << EAP_MODE_SHIFT_63XX; } else { eap_conf &= ~EAP_MODE_MASK; eap_conf |= (u64)mode << EAP_MODE_SHIFT; } b53_write64(dev, B53_EAP_PAGE, B53_PORT_EAP_CONF(port), eap_conf);} static void b53_set_forwarding(struct b53_device *dev, int enable){ u8 mgmt; b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); if (enable) mgmt |= SM_SW_FWD_EN; else mgmt &= ~SM_SW_FWD_EN; b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); if (!is5325(dev)) { /* Include IMP port in dumb forwarding mode */ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt); mgmt |= B53_MII_DUMB_FWDG_EN; b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); /* Look at B53_UC_FWD_EN and B53_MC_FWD_EN to decide whether * frames should be flooded or not. */ b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IP_MC; b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); } else { b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); mgmt |= B53_IP_MC; b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); }} static void b53_enable_vlan(struct b53_device *dev, int port, bool enable, bool enable_filtering){ u8 mgmt, vc0, vc1, vc4 = 0, vc5; b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1); if (is5325(dev) || is5365(dev)) { b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5); } else if (is63xx(dev)) { b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5); } else { b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); } vc1 &= ~VC1_RX_MCST_FWD_EN; if (enable) { vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; vc1 |= VC1_RX_MCST_UNTAG_EN; vc4 &= ~VC4_ING_VID_CHECK_MASK; if (enable_filtering) { vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; vc5 |= VC5_DROP_VTABLE_MISS; } else { vc4 |= VC4_NO_ING_VID_CHK << VC4_ING_VID_CHECK_S; vc5 &= ~VC5_DROP_VTABLE_MISS; } if (is5325(dev)) vc0 &= ~VC0_RESERVED_1; if (is5325(dev) || is5365(dev)) vc1 |= VC1_RX_MCST_TAG_EN; } else { vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID); vc1 &= ~VC1_RX_MCST_UNTAG_EN; vc4 &= ~VC4_ING_VID_CHECK_MASK; vc5 &= ~VC5_DROP_VTABLE_MISS; if (is5325(dev) || is5365(dev)) vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; else vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S; if (is5325(dev) || is5365(dev)) vc1 &= ~VC1_RX_MCST_TAG_EN; } if (!is5325(dev) && !is5365(dev)) vc5 &= ~VC5_VID_FFF_EN; b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1); if (is5325(dev) || is5365(dev)) { /* enable the high 8 bit vid check on 5325 */ if (is5325(dev) && enable) b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, VC3_HIGH_8BIT_EN); else b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5); } else if (is63xx(dev)) { b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5); } else { b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5); } b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); dev->vlan_enabled = enable; dev_dbg(dev->dev, "Port %d VLAN enabled: %d, filtering: %d\n", port, enable, enable_filtering);} static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100){ u32 port_mask = 0; u16 max_size = JMS_MIN_SIZE; if (is5325(dev) || is5365(dev)) return -EINVAL; if (enable) { port_mask = dev->enabled_ports; max_size = JMS_MAX_SIZE; if (allow_10_100) port_mask |= JPM_10_100_JUMBO_EN; } b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask); return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);} static int b53_flush_arl(struct b53_device *dev, u8 mask){ unsigned int i; if (is5325(dev)) return 0; b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask); Lines 1–500 of 3333. Load more
/* * B53 switch driver main logic * * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <linux/delay.h> #include <linux/export.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/math.h> #include <linux/minmax.h> #include <linux/module.h> #include <linux/platform_data/b53.h> #include <linux/phy.h> #include <linux/phylink.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> #include <linux/if_vlan.h> #include <net/dsa.h> #include "b53_regs.h" #include "b53_priv.h" struct b53_mib_desc { u8 size; u8 offset; const char *name; }; /* BCM5365 MIB counters */ static const struct b53_mib_desc b53_mibs_65[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPkts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredTransmit" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x38, "TxPausePkts" }, { 8, 0x44, "RxOctets" }, { 4, 0x4c, "RxUndersizePkts" }, { 4, 0x50, "RxPausePkts" }, { 4, 0x54, "Pkts64Octets" }, { 4, 0x58, "Pkts65to127Octets" }, { 4, 0x5c, "Pkts128to255Octets" }, { 4, 0x60, "Pkts256to511Octets" }, { 4, 0x64, "Pkts512to1023Octets" }, { 4, 0x68, "Pkts1024to1522Octets" }, { 4, 0x6c, "RxOversizePkts" }, { 4, 0x70, "RxJabbers" }, { 4, 0x74, "RxAlignmentErrors" }, { 4, 0x78, "RxFCSErrors" }, { 8, 0x7c, "RxGoodOctets" }, { 4, 0x84, "RxDropPkts" }, { 4, 0x88, "RxUnicastPkts" }, { 4, 0x8c, "RxMulticastPkts" }, { 4, 0x90, "RxBroadcastPkts" }, { 4, 0x94, "RxSAChanges" }, { 4, 0x98, "RxFragments" }, }; #define B53_MIBS_65_SIZE ARRAY_SIZE(b53_mibs_65) /* BCM63xx MIB counters */ static const struct b53_mib_desc b53_mibs_63xx[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x0c, "TxQoSPkts" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPkts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredTransmit" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x38, "TxPausePkts" }, { 8, 0x3c, "TxQoSOctets" }, { 8, 0x44, "RxOctets" }, { 4, 0x4c, "RxUndersizePkts" }, { 4, 0x50, "RxPausePkts" }, { 4, 0x54, "Pkts64Octets" }, { 4, 0x58, "Pkts65to127Octets" }, { 4, 0x5c, "Pkts128to255Octets" }, { 4, 0x60, "Pkts256to511Octets" }, { 4, 0x64, "Pkts512to1023Octets" }, { 4, 0x68, "Pkts1024to1522Octets" }, { 4, 0x6c, "RxOversizePkts" }, { 4, 0x70, "RxJabbers" }, { 4, 0x74, "RxAlignmentErrors" }, { 4, 0x78, "RxFCSErrors" }, { 8, 0x7c, "RxGoodOctets" }, { 4, 0x84, "RxDropPkts" }, { 4, 0x88, "RxUnicastPkts" }, { 4, 0x8c, "RxMulticastPkts" }, { 4, 0x90, "RxBroadcastPkts" }, { 4, 0x94, "RxSAChanges" }, { 4, 0x98, "RxFragments" }, { 4, 0xa0, "RxSymbolErrors" }, { 4, 0xa4, "RxQoSPkts" }, { 8, 0xa8, "RxQoSOctets" }, { 4, 0xb0, "Pkts1523to2047Octets" }, { 4, 0xb4, "Pkts2048to4095Octets" }, { 4, 0xb8, "Pkts4096to8191Octets" }, { 4, 0xbc, "Pkts8192to9728Octets" }, { 4, 0xc0, "RxDiscarded" }, }; #define B53_MIBS_63XX_SIZE ARRAY_SIZE(b53_mibs_63xx) /* MIB counters */ static const struct b53_mib_desc b53_mibs[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPkts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredTransmit" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x38, "TxPausePkts" }, { 8, 0x50, "RxOctets" }, { 4, 0x58, "RxUndersizePkts" }, { 4, 0x5c, "RxPausePkts" }, { 4, 0x60, "Pkts64Octets" }, { 4, 0x64, "Pkts65to127Octets" }, { 4, 0x68, "Pkts128to255Octets" }, { 4, 0x6c, "Pkts256to511Octets" }, { 4, 0x70, "Pkts512to1023Octets" }, { 4, 0x74, "Pkts1024to1522Octets" }, { 4, 0x78, "RxOversizePkts" }, { 4, 0x7c, "RxJabbers" }, { 4, 0x80, "RxAlignmentErrors" }, { 4, 0x84, "RxFCSErrors" }, { 8, 0x88, "RxGoodOctets" }, { 4, 0x90, "RxDropPkts" }, { 4, 0x94, "RxUnicastPkts" }, { 4, 0x98, "RxMulticastPkts" }, { 4, 0x9c, "RxBroadcastPkts" }, { 4, 0xa0, "RxSAChanges" }, { 4, 0xa4, "RxFragments" }, { 4, 0xa8, "RxJumboPkts" }, { 4, 0xac, "RxSymbolErrors" }, { 4, 0xc0, "RxDiscarded" }, }; #define B53_MIBS_SIZE ARRAY_SIZE(b53_mibs) static const struct b53_mib_desc b53_mibs_58xx[] = { { 8, 0x00, "TxOctets" }, { 4, 0x08, "TxDropPkts" }, { 4, 0x0c, "TxQPKTQ0" }, { 4, 0x10, "TxBroadcastPkts" }, { 4, 0x14, "TxMulticastPkts" }, { 4, 0x18, "TxUnicastPKts" }, { 4, 0x1c, "TxCollisions" }, { 4, 0x20, "TxSingleCollision" }, { 4, 0x24, "TxMultipleCollision" }, { 4, 0x28, "TxDeferredCollision" }, { 4, 0x2c, "TxLateCollision" }, { 4, 0x30, "TxExcessiveCollision" }, { 4, 0x34, "TxFrameInDisc" }, { 4, 0x38, "TxPausePkts" }, { 4, 0x3c, "TxQPKTQ1" }, { 4, 0x40, "TxQPKTQ2" }, { 4, 0x44, "TxQPKTQ3" }, { 4, 0x48, "TxQPKTQ4" }, { 4, 0x4c, "TxQPKTQ5" }, { 8, 0x50, "RxOctets" }, { 4, 0x58, "RxUndersizePkts" }, { 4, 0x5c, "RxPausePkts" }, { 4, 0x60, "RxPkts64Octets" }, { 4, 0x64, "RxPkts65to127Octets" }, { 4, 0x68, "RxPkts128to255Octets" }, { 4, 0x6c, "RxPkts256to511Octets" }, { 4, 0x70, "RxPkts512to1023Octets" }, { 4, 0x74, "RxPkts1024toMaxPktsOctets" }, { 4, 0x78, "RxOversizePkts" }, { 4, 0x7c, "RxJabbers" }, { 4, 0x80, "RxAlignmentErrors" }, { 4, 0x84, "RxFCSErrors" }, { 8, 0x88, "RxGoodOctets" }, { 4, 0x90, "RxDropPkts" }, { 4, 0x94, "RxUnicastPkts" }, { 4, 0x98, "RxMulticastPkts" }, { 4, 0x9c, "RxBroadcastPkts" }, { 4, 0xa0, "RxSAChanges" }, { 4, 0xa4, "RxFragments" }, { 4, 0xa8, "RxJumboPkt" }, { 4, 0xac, "RxSymblErr" }, { 4, 0xb0, "InRangeErrCount" }, { 4, 0xb4, "OutRangeErrCount" }, { 4, 0xb8, "EEELpiEvent" }, { 4, 0xbc, "EEELpiDuration" }, { 4, 0xc0, "RxDiscard" }, { 4, 0xc8, "TxQPKTQ6" }, { 4, 0xcc, "TxQPKTQ7" }, { 4, 0xd0, "TxPkts64Octets" }, { 4, 0xd4, "TxPkts65to127Octets" }, { 4, 0xd8, "TxPkts128to255Octets" }, { 4, 0xdc, "TxPkts256to511Ocets" }, { 4, 0xe0, "TxPkts512to1023Ocets" }, { 4, 0xe4, "TxPkts1024toMaxPktOcets" }, }; #define B53_MIBS_58XX_SIZE ARRAY_SIZE(b53_mibs_58xx) #define B53_MAX_MTU_25 (1536 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) #define B53_MAX_MTU (9720 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) static int b53_do_vlan_op(struct b53_device *dev, u8 op) { unsigned int i; b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op); for (i = 0; i < 10; i++) { u8 vta; b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta); if (!(vta & VTA_START_CMD)) return 0; usleep_range(100, 200); } return -EIO; } static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, struct b53_vlan *vlan) { if (is5325(dev)) { u32 entry = 0; if (vlan->members) { entry = ((vlan->untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) | vlan->members; if (dev->core_rev >= 3) entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S; else entry |= VA_VALID_25; } b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry); b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | VTA_RW_STATE_WR | VTA_RW_OP_EN); } else if (is5365(dev)) { u16 entry = 0; if (vlan->members) entry = ((vlan->untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) | vlan->members | VA_VALID_65; b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry); b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | VTA_RW_STATE_WR | VTA_RW_OP_EN); } else { b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], (vlan->untag << VTE_UNTAG_S) | vlan->members); b53_do_vlan_op(dev, VTA_CMD_WRITE); } dev_dbg(dev->ds->dev, "VID: %d, members: 0x%04x, untag: 0x%04x\n", vid, vlan->members, vlan->untag); } static void b53_get_vlan_entry(struct b53_device *dev, u16 vid, struct b53_vlan *vlan) { if (is5325(dev)) { u32 entry = 0; b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | VTA_RW_STATE_RD | VTA_RW_OP_EN); b53_read32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, &entry); if (dev->core_rev >= 3) vlan->valid = !!(entry & VA_VALID_25_R4); else vlan->valid = !!(entry & VA_VALID_25); vlan->members = entry & VA_MEMBER_MASK; vlan->untag = (entry >> VA_UNTAG_S_25) & VA_UNTAG_MASK_25; } else if (is5365(dev)) { u16 entry = 0; b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | VTA_RW_STATE_WR | VTA_RW_OP_EN); b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, &entry); vlan->valid = !!(entry & VA_VALID_65); vlan->members = entry & VA_MEMBER_MASK; vlan->untag = (entry >> VA_UNTAG_S_65) & VA_UNTAG_MASK_65; } else { u32 entry = 0; b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); b53_do_vlan_op(dev, VTA_CMD_READ); b53_read32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], &entry); vlan->members = entry & VTE_MEMBERS; vlan->untag = (entry >> VTE_UNTAG_S) & VTE_MEMBERS; vlan->valid = true; } } static void b53_set_eap_mode(struct b53_device *dev, int port, int mode) { u64 eap_conf; if (is5325(dev) || is5365(dev) || dev->chip_id == BCM5389_DEVICE_ID) return; b53_read64(dev, B53_EAP_PAGE, B53_PORT_EAP_CONF(port), &eap_conf); if (is63xx(dev)) { eap_conf &= ~EAP_MODE_MASK_63XX; eap_conf |= (u64)mode << EAP_MODE_SHIFT_63XX; } else { eap_conf &= ~EAP_MODE_MASK; eap_conf |= (u64)mode << EAP_MODE_SHIFT; } b53_write64(dev, B53_EAP_PAGE, B53_PORT_EAP_CONF(port), eap_conf); } static void b53_set_forwarding(struct b53_device *dev, int enable) { u8 mgmt; b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); if (enable) mgmt |= SM_SW_FWD_EN; else mgmt &= ~SM_SW_FWD_EN; b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); if (!is5325(dev)) { /* Include IMP port in dumb forwarding mode */ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt); mgmt |= B53_MII_DUMB_FWDG_EN; b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); /* Look at B53_UC_FWD_EN and B53_MC_FWD_EN to decide whether * frames should be flooded or not. */ b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IP_MC; b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); } else { b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); mgmt |= B53_IP_MC; b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); } } static void b53_enable_vlan(struct b53_device *dev, int port, bool enable, bool enable_filtering) { u8 mgmt, vc0, vc1, vc4 = 0, vc5; b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1); if (is5325(dev) || is5365(dev)) { b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5); } else if (is63xx(dev)) { b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5); } else { b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); } vc1 &= ~VC1_RX_MCST_FWD_EN; if (enable) { vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; vc1 |= VC1_RX_MCST_UNTAG_EN; vc4 &= ~VC4_ING_VID_CHECK_MASK; if (enable_filtering) { vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; vc5 |= VC5_DROP_VTABLE_MISS; } else { vc4 |= VC4_NO_ING_VID_CHK << VC4_ING_VID_CHECK_S; vc5 &= ~VC5_DROP_VTABLE_MISS; } if (is5325(dev)) vc0 &= ~VC0_RESERVED_1; if (is5325(dev) || is5365(dev)) vc1 |= VC1_RX_MCST_TAG_EN; } else { vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID); vc1 &= ~VC1_RX_MCST_UNTAG_EN; vc4 &= ~VC4_ING_VID_CHECK_MASK; vc5 &= ~VC5_DROP_VTABLE_MISS; if (is5325(dev) || is5365(dev)) vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; else vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S; if (is5325(dev) || is5365(dev)) vc1 &= ~VC1_RX_MCST_TAG_EN; } if (!is5325(dev) && !is5365(dev)) vc5 &= ~VC5_VID_FFF_EN; b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1); if (is5325(dev) || is5365(dev)) { /* enable the high 8 bit vid check on 5325 */ if (is5325(dev) && enable) b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, VC3_HIGH_8BIT_EN); else b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5); } else if (is63xx(dev)) { b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5); } else { b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4); b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5); } b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); dev->vlan_enabled = enable; dev_dbg(dev->dev, "Port %d VLAN enabled: %d, filtering: %d\n", port, enable, enable_filtering); } static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100) { u32 port_mask = 0; u16 max_size = JMS_MIN_SIZE; if (is5325(dev) || is5365(dev)) return -EINVAL; if (enable) { port_mask = dev->enabled_ports; max_size = JMS_MAX_SIZE; if (allow_10_100) port_mask |= JPM_10_100_JUMBO_EN; } b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask); return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size); } static int b53_flush_arl(struct b53_device *dev, u8 mask) { unsigned int i; if (is5325(dev)) return 0; b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask);