3333 lines · 82.1 KB · C
RawDownload
/* * 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.