net: dsa: add Broadcom SF2 switch driver
Add support for the Broadcom Starfigther 2 switch chip using a DSA driver. This switch driver supports the following features: - configuration of the external switch port interface: MII, RevMII, RGMII and RGMII_NO_ID are supported - support for the per-port MIB counters - support for link interrupts for special ports (e.g: MoCA) - powering up/down of switch memories to conserve power when ports are unused Finally, update the compatible property for the DSA core code to match our switch top-level compatible node. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									5037d532b8
								
							
						
					
					
						commit
						246d7f773c
					
				| @ -36,4 +36,15 @@ config NET_DSA_MV88E6123_61_65 | ||||
| 	  This enables support for the Marvell 88E6123/6161/6165 | ||||
| 	  ethernet switch chips. | ||||
| 
 | ||||
| config NET_DSA_BCM_SF2 | ||||
| 	tristate "Broadcom Starfighter 2 Ethernet switch support" | ||||
| 	select NET_DSA | ||||
| 	select NET_DSA_TAG_BRCM | ||||
| 	select FIXED_PHY if NET_DSA_BCM_SF2=y | ||||
| 	select BCM7XXX_PHY | ||||
| 	select MDIO_BCM_UNIMAC | ||||
| 	---help--- | ||||
| 	  This enables support for the Broadcom Starfighter 2 Ethernet | ||||
| 	  switch chips. | ||||
| 
 | ||||
| endmenu | ||||
|  | ||||
| @ -7,3 +7,4 @@ endif | ||||
| ifdef CONFIG_NET_DSA_MV88E6131 | ||||
| mv88e6xxx_drv-y += mv88e6131.o | ||||
| endif | ||||
| obj-$(CONFIG_NET_DSA_BCM_SF2)	+= bcm_sf2.o | ||||
|  | ||||
							
								
								
									
										626
									
								
								drivers/net/dsa/bcm_sf2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										626
									
								
								drivers/net/dsa/bcm_sf2.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,626 @@ | ||||
| /*
 | ||||
|  * Broadcom Starfighter 2 DSA switch driver | ||||
|  * | ||||
|  * Copyright (C) 2014, Broadcom Corporation | ||||
|  * | ||||
|  * 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/list.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/phy.h> | ||||
| #include <linux/phy_fixed.h> | ||||
| #include <linux/mii.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_irq.h> | ||||
| #include <linux/of_address.h> | ||||
| #include <net/dsa.h> | ||||
| 
 | ||||
| #include "bcm_sf2.h" | ||||
| #include "bcm_sf2_regs.h" | ||||
| 
 | ||||
| /* String, offset, and register size in bytes if different from 4 bytes */ | ||||
| static const struct bcm_sf2_hw_stats bcm_sf2_mib[] = { | ||||
| 	{ "TxOctets",		0x000, 8	}, | ||||
| 	{ "TxDropPkts",		0x020		}, | ||||
| 	{ "TxQPKTQ0",		0x030		}, | ||||
| 	{ "TxBroadcastPkts",	0x040		}, | ||||
| 	{ "TxMulticastPkts",	0x050		}, | ||||
| 	{ "TxUnicastPKts",	0x060		}, | ||||
| 	{ "TxCollisions",	0x070		}, | ||||
| 	{ "TxSingleCollision",	0x080		}, | ||||
| 	{ "TxMultipleCollision", 0x090		}, | ||||
| 	{ "TxDeferredCollision", 0x0a0		}, | ||||
| 	{ "TxLateCollision",	0x0b0		}, | ||||
| 	{ "TxExcessiveCollision", 0x0c0		}, | ||||
| 	{ "TxFrameInDisc",	0x0d0		}, | ||||
| 	{ "TxPausePkts",	0x0e0		}, | ||||
| 	{ "TxQPKTQ1",		0x0f0		}, | ||||
| 	{ "TxQPKTQ2",		0x100		}, | ||||
| 	{ "TxQPKTQ3",		0x110		}, | ||||
| 	{ "TxQPKTQ4",		0x120		}, | ||||
| 	{ "TxQPKTQ5",		0x130		}, | ||||
| 	{ "RxOctets",		0x140, 8	}, | ||||
| 	{ "RxUndersizePkts",	0x160		}, | ||||
| 	{ "RxPausePkts",	0x170		}, | ||||
| 	{ "RxPkts64Octets",	0x180		}, | ||||
| 	{ "RxPkts65to127Octets", 0x190		}, | ||||
| 	{ "RxPkts128to255Octets", 0x1a0		}, | ||||
| 	{ "RxPkts256to511Octets", 0x1b0		}, | ||||
| 	{ "RxPkts512to1023Octets", 0x1c0	}, | ||||
| 	{ "RxPkts1024toMaxPktsOctets", 0x1d0	}, | ||||
| 	{ "RxOversizePkts",	0x1e0		}, | ||||
| 	{ "RxJabbers",		0x1f0		}, | ||||
| 	{ "RxAlignmentErrors",	0x200		}, | ||||
| 	{ "RxFCSErrors",	0x210		}, | ||||
| 	{ "RxGoodOctets",	0x220, 8	}, | ||||
| 	{ "RxDropPkts",		0x240		}, | ||||
| 	{ "RxUnicastPkts",	0x250		}, | ||||
| 	{ "RxMulticastPkts",	0x260		}, | ||||
| 	{ "RxBroadcastPkts",	0x270		}, | ||||
| 	{ "RxSAChanges",	0x280		}, | ||||
| 	{ "RxFragments",	0x290		}, | ||||
| 	{ "RxJumboPkt",		0x2a0		}, | ||||
| 	{ "RxSymblErr",		0x2b0		}, | ||||
| 	{ "InRangeErrCount",	0x2c0		}, | ||||
| 	{ "OutRangeErrCount",	0x2d0		}, | ||||
| 	{ "EEELpiEvent",	0x2e0		}, | ||||
| 	{ "EEELpiDuration",	0x2f0		}, | ||||
| 	{ "RxDiscard",		0x300, 8	}, | ||||
| 	{ "TxQPKTQ6",		0x320		}, | ||||
| 	{ "TxQPKTQ7",		0x330		}, | ||||
| 	{ "TxPkts64Octets",	0x340		}, | ||||
| 	{ "TxPkts65to127Octets", 0x350		}, | ||||
| 	{ "TxPkts128to255Octets", 0x360		}, | ||||
| 	{ "TxPkts256to511Ocets", 0x370		}, | ||||
| 	{ "TxPkts512to1023Ocets", 0x380		}, | ||||
| 	{ "TxPkts1024toMaxPktOcets", 0x390	}, | ||||
| }; | ||||
| 
 | ||||
| #define BCM_SF2_STATS_SIZE	ARRAY_SIZE(bcm_sf2_mib) | ||||
| 
 | ||||
| static void bcm_sf2_sw_get_strings(struct dsa_switch *ds, | ||||
| 				   int port, uint8_t *data) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	for (i = 0; i < BCM_SF2_STATS_SIZE; i++) | ||||
| 		memcpy(data + i * ETH_GSTRING_LEN, | ||||
| 		       bcm_sf2_mib[i].string, ETH_GSTRING_LEN); | ||||
| } | ||||
| 
 | ||||
| static void bcm_sf2_sw_get_ethtool_stats(struct dsa_switch *ds, | ||||
| 					 int port, uint64_t *data) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	const struct bcm_sf2_hw_stats *s; | ||||
| 	unsigned int i; | ||||
| 	u64 val = 0; | ||||
| 	u32 offset; | ||||
| 
 | ||||
| 	mutex_lock(&priv->stats_mutex); | ||||
| 
 | ||||
| 	/* Now fetch the per-port counters */ | ||||
| 	for (i = 0; i < BCM_SF2_STATS_SIZE; i++) { | ||||
| 		s = &bcm_sf2_mib[i]; | ||||
| 
 | ||||
| 		/* Do a latched 64-bit read if needed */ | ||||
| 		offset = s->reg + CORE_P_MIB_OFFSET(port); | ||||
| 		if (s->sizeof_stat == 8) | ||||
| 			val = core_readq(priv, offset); | ||||
| 		else | ||||
| 			val = core_readl(priv, offset); | ||||
| 
 | ||||
| 		data[i] = (u64)val; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&priv->stats_mutex); | ||||
| } | ||||
| 
 | ||||
| static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds) | ||||
| { | ||||
| 	return BCM_SF2_STATS_SIZE; | ||||
| } | ||||
| 
 | ||||
| static char *bcm_sf2_sw_probe(struct mii_bus *bus, int sw_addr) | ||||
| { | ||||
| 	return "Broadcom Starfighter 2"; | ||||
| } | ||||
| 
 | ||||
| static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	unsigned int i; | ||||
| 	u32 reg, val; | ||||
| 
 | ||||
| 	/* Enable the port memories */ | ||||
| 	reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL); | ||||
| 	reg &= ~P_TXQ_PSM_VDD(port); | ||||
| 	core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); | ||||
| 
 | ||||
| 	/* Enable Broadcast, Multicast, Unicast forwarding to IMP port */ | ||||
| 	reg = core_readl(priv, CORE_IMP_CTL); | ||||
| 	reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN); | ||||
| 	reg &= ~(RX_DIS | TX_DIS); | ||||
| 	core_writel(priv, reg, CORE_IMP_CTL); | ||||
| 
 | ||||
| 	/* Enable forwarding */ | ||||
| 	core_writel(priv, SW_FWDG_EN, CORE_SWMODE); | ||||
| 
 | ||||
| 	/* Enable IMP port in dumb mode */ | ||||
| 	reg = core_readl(priv, CORE_SWITCH_CTRL); | ||||
| 	reg |= MII_DUMB_FWDG_EN; | ||||
| 	core_writel(priv, reg, CORE_SWITCH_CTRL); | ||||
| 
 | ||||
| 	/* Resolve which bit controls the Broadcom tag */ | ||||
| 	switch (port) { | ||||
| 	case 8: | ||||
| 		val = BRCM_HDR_EN_P8; | ||||
| 		break; | ||||
| 	case 7: | ||||
| 		val = BRCM_HDR_EN_P7; | ||||
| 		break; | ||||
| 	case 5: | ||||
| 		val = BRCM_HDR_EN_P5; | ||||
| 		break; | ||||
| 	default: | ||||
| 		val = 0; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Enable Broadcom tags for IMP port */ | ||||
| 	reg = core_readl(priv, CORE_BRCM_HDR_CTRL); | ||||
| 	reg |= val; | ||||
| 	core_writel(priv, reg, CORE_BRCM_HDR_CTRL); | ||||
| 
 | ||||
| 	/* Enable reception Broadcom tag for CPU TX (switch RX) to
 | ||||
| 	 * allow us to tag outgoing frames | ||||
| 	 */ | ||||
| 	reg = core_readl(priv, CORE_BRCM_HDR_RX_DIS); | ||||
| 	reg &= ~(1 << port); | ||||
| 	core_writel(priv, reg, CORE_BRCM_HDR_RX_DIS); | ||||
| 
 | ||||
| 	/* Enable transmission of Broadcom tags from the switch (CPU RX) to
 | ||||
| 	 * allow delivering frames to the per-port net_devices | ||||
| 	 */ | ||||
| 	reg = core_readl(priv, CORE_BRCM_HDR_TX_DIS); | ||||
| 	reg &= ~(1 << port); | ||||
| 	core_writel(priv, reg, CORE_BRCM_HDR_TX_DIS); | ||||
| 
 | ||||
| 	/* Force link status for IMP port */ | ||||
| 	reg = core_readl(priv, CORE_STS_OVERRIDE_IMP); | ||||
| 	reg |= (MII_SW_OR | LINK_STS); | ||||
| 	core_writel(priv, reg, CORE_STS_OVERRIDE_IMP); | ||||
| 
 | ||||
| 	/* Enable the IMP Port to be in the same VLAN as the other ports
 | ||||
| 	 * on a per-port basis such that we only have Port i and IMP in | ||||
| 	 * the same VLAN. | ||||
| 	 */ | ||||
| 	for (i = 0; i < priv->hw_params.num_ports; i++) { | ||||
| 		if (!((1 << i) & ds->phys_port_mask)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); | ||||
| 		reg |= (1 << port); | ||||
| 		core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void bcm_sf2_port_setup(struct dsa_switch *ds, int port) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	/* Clear the memory power down */ | ||||
| 	reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL); | ||||
| 	reg &= ~P_TXQ_PSM_VDD(port); | ||||
| 	core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); | ||||
| 
 | ||||
| 	/* Clear the Rx and Tx disable bits and set to no spanning tree */ | ||||
| 	core_writel(priv, 0, CORE_G_PCTL_PORT(port)); | ||||
| 
 | ||||
| 	/* Enable port 7 interrupts to get notified */ | ||||
| 	if (port == 7) | ||||
| 		intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); | ||||
| 
 | ||||
| 	/* Set this port, and only this one to be in the default VLAN */ | ||||
| 	reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); | ||||
| 	reg &= ~PORT_VLAN_CTRL_MASK; | ||||
| 	reg |= (1 << port); | ||||
| 	core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); | ||||
| } | ||||
| 
 | ||||
| static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	u32 off, reg; | ||||
| 
 | ||||
| 	if (dsa_is_cpu_port(ds, port)) | ||||
| 		off = CORE_IMP_CTL; | ||||
| 	else | ||||
| 		off = CORE_G_PCTL_PORT(port); | ||||
| 
 | ||||
| 	reg = core_readl(priv, off); | ||||
| 	reg |= RX_DIS | TX_DIS; | ||||
| 	core_writel(priv, reg, off); | ||||
| 
 | ||||
| 	/* Power down the port memory */ | ||||
| 	reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL); | ||||
| 	reg |= P_TXQ_PSM_VDD(port); | ||||
| 	core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = dev_id; | ||||
| 
 | ||||
| 	priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) & | ||||
| 				~priv->irq0_mask; | ||||
| 	intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = dev_id; | ||||
| 
 | ||||
| 	priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) & | ||||
| 				~priv->irq1_mask; | ||||
| 	intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); | ||||
| 
 | ||||
| 	if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) | ||||
| 		priv->port_sts[7].link = 1; | ||||
| 	if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) | ||||
| 		priv->port_sts[7].link = 0; | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static int bcm_sf2_sw_setup(struct dsa_switch *ds) | ||||
| { | ||||
| 	const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	struct device_node *dn; | ||||
| 	void __iomem **base; | ||||
| 	unsigned int port; | ||||
| 	unsigned int i; | ||||
| 	u32 reg, rev; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	spin_lock_init(&priv->indir_lock); | ||||
| 	mutex_init(&priv->stats_mutex); | ||||
| 
 | ||||
| 	/* All the interesting properties are at the parent device_node
 | ||||
| 	 * level | ||||
| 	 */ | ||||
| 	dn = ds->pd->of_node->parent; | ||||
| 
 | ||||
| 	priv->irq0 = irq_of_parse_and_map(dn, 0); | ||||
| 	priv->irq1 = irq_of_parse_and_map(dn, 1); | ||||
| 
 | ||||
| 	base = &priv->core; | ||||
| 	for (i = 0; i < BCM_SF2_REGS_NUM; i++) { | ||||
| 		*base = of_iomap(dn, i); | ||||
| 		if (*base == NULL) { | ||||
| 			pr_err("unable to find register: %s\n", reg_names[i]); | ||||
| 			return -ENODEV; | ||||
| 		} | ||||
| 		base++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Disable all interrupts and request them */ | ||||
| 	intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | ||||
| 	intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||||
| 	intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||||
| 	intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | ||||
| 	intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||||
| 	intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||||
| 
 | ||||
| 	ret = request_irq(priv->irq0, bcm_sf2_switch_0_isr, 0, | ||||
| 			  "switch_0", priv); | ||||
| 	if (ret < 0) { | ||||
| 		pr_err("failed to request switch_0 IRQ\n"); | ||||
| 		goto out_unmap; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = request_irq(priv->irq1, bcm_sf2_switch_1_isr, 0, | ||||
| 			  "switch_1", priv); | ||||
| 	if (ret < 0) { | ||||
| 		pr_err("failed to request switch_1 IRQ\n"); | ||||
| 		goto out_free_irq0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Reset the MIB counters */ | ||||
| 	reg = core_readl(priv, CORE_GMNCFGCFG); | ||||
| 	reg |= RST_MIB_CNT; | ||||
| 	core_writel(priv, reg, CORE_GMNCFGCFG); | ||||
| 	reg &= ~RST_MIB_CNT; | ||||
| 	core_writel(priv, reg, CORE_GMNCFGCFG); | ||||
| 
 | ||||
| 	/* Get the maximum number of ports for this switch */ | ||||
| 	priv->hw_params.num_ports = core_readl(priv, CORE_IMP0_PRT_ID) + 1; | ||||
| 	if (priv->hw_params.num_ports > DSA_MAX_PORTS) | ||||
| 		priv->hw_params.num_ports = DSA_MAX_PORTS; | ||||
| 
 | ||||
| 	/* Assume a single GPHY setup if we can't read that property */ | ||||
| 	if (of_property_read_u32(dn, "brcm,num-gphy", | ||||
| 				 &priv->hw_params.num_gphy)) | ||||
| 		priv->hw_params.num_gphy = 1; | ||||
| 
 | ||||
| 	/* Enable all valid ports and disable those unused */ | ||||
| 	for (port = 0; port < priv->hw_params.num_ports; port++) { | ||||
| 		/* IMP port receives special treatment */ | ||||
| 		if ((1 << port) & ds->phys_port_mask) | ||||
| 			bcm_sf2_port_setup(ds, port); | ||||
| 		else if (dsa_is_cpu_port(ds, port)) | ||||
| 			bcm_sf2_imp_setup(ds, port); | ||||
| 		else | ||||
| 			bcm_sf2_port_disable(ds, port); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Include the pseudo-PHY address and the broadcast PHY address to
 | ||||
| 	 * divert reads towards our workaround | ||||
| 	 */ | ||||
| 	ds->phys_mii_mask |= ((1 << 30) | (1 << 0)); | ||||
| 
 | ||||
| 	rev = reg_readl(priv, REG_SWITCH_REVISION); | ||||
| 	priv->hw_params.top_rev = (rev >> SWITCH_TOP_REV_SHIFT) & | ||||
| 					SWITCH_TOP_REV_MASK; | ||||
| 	priv->hw_params.core_rev = (rev & SF2_REV_MASK); | ||||
| 
 | ||||
| 	pr_info("Starfighter 2 top: %x.%02x, core: %x.%02x base: 0x%p, IRQs: %d, %d\n", | ||||
| 		priv->hw_params.top_rev >> 8, priv->hw_params.top_rev & 0xff, | ||||
| 		priv->hw_params.core_rev >> 8, priv->hw_params.core_rev & 0xff, | ||||
| 		priv->core, priv->irq0, priv->irq1); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_free_irq0: | ||||
| 	free_irq(priv->irq0, priv); | ||||
| out_unmap: | ||||
| 	base = &priv->core; | ||||
| 	for (i = 0; i < BCM_SF2_REGS_NUM; i++) { | ||||
| 		iounmap(*base); | ||||
| 		base++; | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int bcm_sf2_sw_set_addr(struct dsa_switch *ds, u8 *addr) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int bcm_sf2_sw_indir_rw(struct dsa_switch *ds, int op, int addr, | ||||
| 			       int regnum, u16 val) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	int ret = 0; | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	reg = reg_readl(priv, REG_SWITCH_CNTRL); | ||||
| 	reg |= MDIO_MASTER_SEL; | ||||
| 	reg_writel(priv, reg, REG_SWITCH_CNTRL); | ||||
| 
 | ||||
| 	/* Page << 8 | offset */ | ||||
| 	reg = 0x70; | ||||
| 	reg <<= 2; | ||||
| 	core_writel(priv, addr, reg); | ||||
| 
 | ||||
| 	/* Page << 8 | offset */ | ||||
| 	reg = 0x80 << 8 | regnum << 1; | ||||
| 	reg <<= 2; | ||||
| 
 | ||||
| 	if (op) | ||||
| 		ret = core_readl(priv, reg); | ||||
| 	else | ||||
| 		core_writel(priv, val, reg); | ||||
| 
 | ||||
| 	reg = reg_readl(priv, REG_SWITCH_CNTRL); | ||||
| 	reg &= ~MDIO_MASTER_SEL; | ||||
| 	reg_writel(priv, reg, REG_SWITCH_CNTRL); | ||||
| 
 | ||||
| 	return ret & 0xffff; | ||||
| } | ||||
| 
 | ||||
| static int bcm_sf2_sw_phy_read(struct dsa_switch *ds, int addr, int regnum) | ||||
| { | ||||
| 	/* Intercept reads from the MDIO broadcast address or Broadcom
 | ||||
| 	 * pseudo-PHY address | ||||
| 	 */ | ||||
| 	switch (addr) { | ||||
| 	case 0: | ||||
| 	case 30: | ||||
| 		return bcm_sf2_sw_indir_rw(ds, 1, addr, regnum, 0); | ||||
| 	default: | ||||
| 		return 0xffff; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int bcm_sf2_sw_phy_write(struct dsa_switch *ds, int addr, int regnum, | ||||
| 				u16 val) | ||||
| { | ||||
| 	/* Intercept writes to the MDIO broadcast address or Broadcom
 | ||||
| 	 * pseudo-PHY address | ||||
| 	 */ | ||||
| 	switch (addr) { | ||||
| 	case 0: | ||||
| 	case 30: | ||||
| 		bcm_sf2_sw_indir_rw(ds, 0, addr, regnum, val); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, | ||||
| 				   struct phy_device *phydev) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	u32 id_mode_dis = 0, port_mode; | ||||
| 	const char *str = NULL; | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	switch (phydev->interface) { | ||||
| 	case PHY_INTERFACE_MODE_RGMII: | ||||
| 		str = "RGMII (no delay)"; | ||||
| 		id_mode_dis = 1; | ||||
| 	case PHY_INTERFACE_MODE_RGMII_TXID: | ||||
| 		if (!str) | ||||
| 			str = "RGMII (TX delay)"; | ||||
| 		port_mode = EXT_GPHY; | ||||
| 		break; | ||||
| 	case PHY_INTERFACE_MODE_MII: | ||||
| 		str = "MII"; | ||||
| 		port_mode = EXT_EPHY; | ||||
| 		break; | ||||
| 	case PHY_INTERFACE_MODE_REVMII: | ||||
| 		str = "Reverse MII"; | ||||
| 		port_mode = EXT_REVMII; | ||||
| 		break; | ||||
| 	default: | ||||
| 		goto force_link; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Clear id_mode_dis bit, and the existing port mode, but
 | ||||
| 	 * make sure we enable the RGMII block for data to pass | ||||
| 	 */ | ||||
| 	reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); | ||||
| 	reg &= ~ID_MODE_DIS; | ||||
| 	reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); | ||||
| 	reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); | ||||
| 
 | ||||
| 	reg |= port_mode | RGMII_MODE_EN; | ||||
| 	if (id_mode_dis) | ||||
| 		reg |= ID_MODE_DIS; | ||||
| 
 | ||||
| 	if (phydev->pause) { | ||||
| 		if (phydev->asym_pause) | ||||
| 			reg |= TX_PAUSE_EN; | ||||
| 		reg |= RX_PAUSE_EN; | ||||
| 	} | ||||
| 
 | ||||
| 	reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); | ||||
| 
 | ||||
| 	pr_info("Port %d configured for %s\n", port, str); | ||||
| 
 | ||||
| force_link: | ||||
| 	/* Force link settings detected from the PHY */ | ||||
| 	reg = SW_OVERRIDE; | ||||
| 	switch (phydev->speed) { | ||||
| 	case SPEED_1000: | ||||
| 		reg |= SPDSTS_1000 << SPEED_SHIFT; | ||||
| 		break; | ||||
| 	case SPEED_100: | ||||
| 		reg |= SPDSTS_100 << SPEED_SHIFT; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (phydev->link) | ||||
| 		reg |= LINK_STS; | ||||
| 	if (phydev->duplex == DUPLEX_FULL) | ||||
| 		reg |= DUPLX_MODE; | ||||
| 
 | ||||
| 	core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); | ||||
| } | ||||
| 
 | ||||
| static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, | ||||
| 					 struct fixed_phy_status *status) | ||||
| { | ||||
| 	struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||||
| 	u32 link, duplex, pause, speed; | ||||
| 	u32 reg; | ||||
| 
 | ||||
| 	link = core_readl(priv, CORE_LNKSTS); | ||||
| 	duplex = core_readl(priv, CORE_DUPSTS); | ||||
| 	pause = core_readl(priv, CORE_PAUSESTS); | ||||
| 	speed = core_readl(priv, CORE_SPDSTS); | ||||
| 
 | ||||
| 	speed >>= (port * SPDSTS_SHIFT); | ||||
| 	speed &= SPDSTS_MASK; | ||||
| 
 | ||||
| 	status->link = 0; | ||||
| 
 | ||||
| 	/* Port 7 is special as we do not get link status from CORE_LNKSTS,
 | ||||
| 	 * which means that we need to force the link at the port override | ||||
| 	 * level to get the data to flow. We do use what the interrupt handler | ||||
| 	 * did determine before. | ||||
| 	 */ | ||||
| 	if (port == 7) { | ||||
| 		status->link = priv->port_sts[port].link; | ||||
| 		reg = core_readl(priv, CORE_STS_OVERRIDE_GMIIP_PORT(7)); | ||||
| 		reg |= SW_OVERRIDE; | ||||
| 		if (status->link) | ||||
| 			reg |= LINK_STS; | ||||
| 		else | ||||
| 			reg &= ~LINK_STS; | ||||
| 		core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(7)); | ||||
| 		status->duplex = 1; | ||||
| 	} else { | ||||
| 		status->link = !!(link & (1 << port)); | ||||
| 		status->duplex = !!(duplex & (1 << port)); | ||||
| 	} | ||||
| 
 | ||||
| 	switch (speed) { | ||||
| 	case SPDSTS_10: | ||||
| 		status->speed = SPEED_10; | ||||
| 		break; | ||||
| 	case SPDSTS_100: | ||||
| 		status->speed = SPEED_100; | ||||
| 		break; | ||||
| 	case SPDSTS_1000: | ||||
| 		status->speed = SPEED_1000; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((pause & (1 << port)) && | ||||
| 	    (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) { | ||||
| 		status->asym_pause = 1; | ||||
| 		status->pause = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (pause & (1 << port)) | ||||
| 		status->pause = 1; | ||||
| } | ||||
| 
 | ||||
| static struct dsa_switch_driver bcm_sf2_switch_driver = { | ||||
| 	.tag_protocol		= htons(ETH_P_BRCMTAG), | ||||
| 	.priv_size		= sizeof(struct bcm_sf2_priv), | ||||
| 	.probe			= bcm_sf2_sw_probe, | ||||
| 	.setup			= bcm_sf2_sw_setup, | ||||
| 	.set_addr		= bcm_sf2_sw_set_addr, | ||||
| 	.phy_read		= bcm_sf2_sw_phy_read, | ||||
| 	.phy_write		= bcm_sf2_sw_phy_write, | ||||
| 	.get_strings		= bcm_sf2_sw_get_strings, | ||||
| 	.get_ethtool_stats	= bcm_sf2_sw_get_ethtool_stats, | ||||
| 	.get_sset_count		= bcm_sf2_sw_get_sset_count, | ||||
| 	.adjust_link		= bcm_sf2_sw_adjust_link, | ||||
| 	.fixed_link_update	= bcm_sf2_sw_fixed_link_update, | ||||
| }; | ||||
| 
 | ||||
| static int __init bcm_sf2_init(void) | ||||
| { | ||||
| 	register_switch_driver(&bcm_sf2_switch_driver); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| module_init(bcm_sf2_init); | ||||
| 
 | ||||
| static void __exit bcm_sf2_exit(void) | ||||
| { | ||||
| 	unregister_switch_driver(&bcm_sf2_switch_driver); | ||||
| } | ||||
| module_exit(bcm_sf2_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Broadcom Corporation"); | ||||
| MODULE_DESCRIPTION("Driver for Broadcom Starfighter 2 ethernet switch chip"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_ALIAS("platform:brcm-sf2"); | ||||
							
								
								
									
										140
									
								
								drivers/net/dsa/bcm_sf2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								drivers/net/dsa/bcm_sf2.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| /*
 | ||||
|  * Broadcom Starfighter2 private context | ||||
|  * | ||||
|  * Copyright (C) 2014, Broadcom Corporation | ||||
|  * | ||||
|  * 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 __BCM_SF2_H | ||||
| #define __BCM_SF2_H | ||||
| 
 | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/mii.h> | ||||
| 
 | ||||
| #include <net/dsa.h> | ||||
| 
 | ||||
| #include "bcm_sf2_regs.h" | ||||
| 
 | ||||
| struct bcm_sf2_hw_params { | ||||
| 	u16	top_rev; | ||||
| 	u16	core_rev; | ||||
| 	u32	num_gphy; | ||||
| 	u8	num_acb_queue; | ||||
| 	u8	num_rgmii; | ||||
| 	u8	num_ports; | ||||
| 	u8	fcb_pause_override:1; | ||||
| 	u8	acb_packets_inflight:1; | ||||
| }; | ||||
| 
 | ||||
| #define BCM_SF2_REGS_NAME {\ | ||||
| 	"core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb" \ | ||||
| } | ||||
| 
 | ||||
| #define BCM_SF2_REGS_NUM	6 | ||||
| 
 | ||||
| struct bcm_sf2_port_status { | ||||
| 	unsigned int link; | ||||
| }; | ||||
| 
 | ||||
| struct bcm_sf2_priv { | ||||
| 	/* Base registers, keep those in order with BCM_SF2_REGS_NAME */ | ||||
| 	void __iomem			*core; | ||||
| 	void __iomem			*reg; | ||||
| 	void __iomem			*intrl2_0; | ||||
| 	void __iomem			*intrl2_1; | ||||
| 	void __iomem			*fcb; | ||||
| 	void __iomem			*acb; | ||||
| 
 | ||||
| 	/* spinlock protecting access to the indirect registers */ | ||||
| 	spinlock_t			indir_lock; | ||||
| 
 | ||||
| 	int				irq0; | ||||
| 	int				irq1; | ||||
| 	u32				irq0_stat; | ||||
| 	u32				irq0_mask; | ||||
| 	u32				irq1_stat; | ||||
| 	u32				irq1_mask; | ||||
| 
 | ||||
| 	/* Mutex protecting access to the MIB counters */ | ||||
| 	struct mutex			stats_mutex; | ||||
| 
 | ||||
| 	struct bcm_sf2_hw_params	hw_params; | ||||
| 
 | ||||
| 	struct bcm_sf2_port_status	port_sts[DSA_MAX_PORTS]; | ||||
| }; | ||||
| 
 | ||||
| struct bcm_sf2_hw_stats { | ||||
| 	const char	*string; | ||||
| 	u16		reg; | ||||
| 	u8		sizeof_stat; | ||||
| }; | ||||
| 
 | ||||
| #define SF2_IO_MACRO(name) \ | ||||
| static inline u32 name##_readl(struct bcm_sf2_priv *priv, u32 off)	\ | ||||
| {									\ | ||||
| 	return __raw_readl(priv->name + off);				\ | ||||
| }									\ | ||||
| static inline void name##_writel(struct bcm_sf2_priv *priv,		\ | ||||
| 				  u32 val, u32 off)			\ | ||||
| {									\ | ||||
| 	__raw_writel(val, priv->name + off);				\ | ||||
| }									\ | ||||
| 
 | ||||
| /* Accesses to 64-bits register requires us to latch the hi/lo pairs
 | ||||
|  * using the REG_DIR_DATA_{READ,WRITE} ancillary registers. The 'indir_lock' | ||||
|  * spinlock is automatically grabbed and released to provide relative | ||||
|  * atomiticy with latched reads/writes. | ||||
|  */ | ||||
| #define SF2_IO64_MACRO(name) \ | ||||
| static inline u64 name##_readq(struct bcm_sf2_priv *priv, u32 off)	\ | ||||
| {									\ | ||||
| 	u32 indir, dir;							\ | ||||
| 	spin_lock(&priv->indir_lock);					\ | ||||
| 	indir = reg_readl(priv, REG_DIR_DATA_READ);			\ | ||||
| 	dir = __raw_readl(priv->name + off);				\ | ||||
| 	spin_unlock(&priv->indir_lock);					\ | ||||
| 	return (u64)indir << 32 | dir;					\ | ||||
| }									\ | ||||
| static inline void name##_writeq(struct bcm_sf2_priv *priv, u32 off,	\ | ||||
| 							u64 val)	\ | ||||
| {									\ | ||||
| 	spin_lock(&priv->indir_lock);					\ | ||||
| 	reg_writel(priv, upper_32_bits(val), REG_DIR_DATA_WRITE);	\ | ||||
| 	__raw_writel(lower_32_bits(val), priv->name + off);		\ | ||||
| 	spin_unlock(&priv->indir_lock);					\ | ||||
| } | ||||
| 
 | ||||
| #define SWITCH_INTR_L2(which)						\ | ||||
| static inline void intrl2_##which##_mask_clear(struct bcm_sf2_priv *priv, \ | ||||
| 						u32 mask)		\ | ||||
| {									\ | ||||
| 	intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR);	\ | ||||
| 	priv->irq##which##_mask &= ~(mask);				\ | ||||
| }									\ | ||||
| static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \ | ||||
| 						u32 mask)		\ | ||||
| {									\ | ||||
| 	intrl2_## which##_writel(priv, mask, INTRL2_CPU_MASK_SET);	\ | ||||
| 	priv->irq##which##_mask |= (mask);				\ | ||||
| }									\ | ||||
| 
 | ||||
| SF2_IO_MACRO(core); | ||||
| SF2_IO_MACRO(reg); | ||||
| SF2_IO64_MACRO(core); | ||||
| SF2_IO_MACRO(intrl2_0); | ||||
| SF2_IO_MACRO(intrl2_1); | ||||
| SF2_IO_MACRO(fcb); | ||||
| SF2_IO_MACRO(acb); | ||||
| 
 | ||||
| SWITCH_INTR_L2(0); | ||||
| SWITCH_INTR_L2(1); | ||||
| 
 | ||||
| #endif /* __BCM_SF2_H */ | ||||
							
								
								
									
										227
									
								
								drivers/net/dsa/bcm_sf2_regs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								drivers/net/dsa/bcm_sf2_regs.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,227 @@ | ||||
| /*
 | ||||
|  * Broadcom Starfighter 2 switch register defines | ||||
|  * | ||||
|  * Copyright (C) 2014, Broadcom Corporation | ||||
|  * | ||||
|  * 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 __BCM_SF2_REGS_H | ||||
| #define __BCM_SF2_REGS_H | ||||
| 
 | ||||
| /* Register set relative to 'REG' */ | ||||
| #define REG_SWITCH_CNTRL		0x00 | ||||
| #define  MDIO_MASTER_SEL		(1 << 0) | ||||
| 
 | ||||
| #define REG_SWITCH_STATUS		0x04 | ||||
| #define REG_DIR_DATA_WRITE		0x08 | ||||
| #define REG_DIR_DATA_READ		0x0C | ||||
| 
 | ||||
| #define REG_SWITCH_REVISION		0x18 | ||||
| #define  SF2_REV_MASK			0xffff | ||||
| #define  SWITCH_TOP_REV_SHIFT		16 | ||||
| #define  SWITCH_TOP_REV_MASK		0xffff | ||||
| 
 | ||||
| #define REG_PHY_REVISION		0x1C | ||||
| 
 | ||||
| #define REG_SPHY_CNTRL			0x2C | ||||
| #define  IDDQ_BIAS			(1 << 0) | ||||
| #define  EXT_PWR_DOWN			(1 << 1) | ||||
| #define  FORCE_DLL_EN			(1 << 2) | ||||
| #define  IDDQ_GLOBAL_PWR		(1 << 3) | ||||
| #define  CK25_DIS			(1 << 4) | ||||
| #define  PHY_RESET			(1 << 5) | ||||
| #define  PHY_PHYAD_SHIFT		8 | ||||
| #define  PHY_PHYAD_MASK			0x1F | ||||
| 
 | ||||
| #define REG_RGMII_0_BASE		0x34 | ||||
| #define REG_RGMII_CNTRL			0x00 | ||||
| #define REG_RGMII_IB_STATUS		0x04 | ||||
| #define REG_RGMII_RX_CLOCK_DELAY_CNTRL	0x08 | ||||
| #define REG_RGMII_CNTRL_SIZE		0x0C | ||||
| #define REG_RGMII_CNTRL_P(x)		(REG_RGMII_0_BASE + \ | ||||
| 					((x) * REG_RGMII_CNTRL_SIZE)) | ||||
| /* Relative to REG_RGMII_CNTRL */ | ||||
| #define  RGMII_MODE_EN			(1 << 0) | ||||
| #define  ID_MODE_DIS			(1 << 1) | ||||
| #define  PORT_MODE_SHIFT		2 | ||||
| #define  INT_EPHY			(0 << PORT_MODE_SHIFT) | ||||
| #define  INT_GPHY			(1 << PORT_MODE_SHIFT) | ||||
| #define  EXT_EPHY			(2 << PORT_MODE_SHIFT) | ||||
| #define  EXT_GPHY			(3 << PORT_MODE_SHIFT) | ||||
| #define  EXT_REVMII			(4 << PORT_MODE_SHIFT) | ||||
| #define  PORT_MODE_MASK			0x7 | ||||
| #define  RVMII_REF_SEL			(1 << 5) | ||||
| #define  RX_PAUSE_EN			(1 << 6) | ||||
| #define  TX_PAUSE_EN			(1 << 7) | ||||
| #define  TX_CLK_STOP_EN			(1 << 8) | ||||
| #define  LPI_COUNT_SHIFT		9 | ||||
| #define  LPI_COUNT_MASK			0x3F | ||||
| 
 | ||||
| /* Register set relative to 'INTRL2_0' and 'INTRL2_1' */ | ||||
| #define INTRL2_CPU_STATUS		0x00 | ||||
| #define INTRL2_CPU_SET			0x04 | ||||
| #define INTRL2_CPU_CLEAR		0x08 | ||||
| #define INTRL2_CPU_MASK_STATUS		0x0c | ||||
| #define INTRL2_CPU_MASK_SET		0x10 | ||||
| #define INTRL2_CPU_MASK_CLEAR		0x14 | ||||
| 
 | ||||
| /* Shared INTRL2_0 and INTRL2_ interrupt sources macros */ | ||||
| #define P_LINK_UP_IRQ(x)		(1 << (0 + (x))) | ||||
| #define P_LINK_DOWN_IRQ(x)		(1 << (1 + (x))) | ||||
| #define P_ENERGY_ON_IRQ(x)		(1 << (2 + (x))) | ||||
| #define P_ENERGY_OFF_IRQ(x)		(1 << (3 + (x))) | ||||
| #define P_GPHY_IRQ(x)			(1 << (4 + (x))) | ||||
| #define P_NUM_IRQ			5 | ||||
| #define P_IRQ_MASK(x)			(P_LINK_UP_IRQ((x)) | \ | ||||
| 					 P_LINK_DOWN_IRQ((x)) | \ | ||||
| 					 P_ENERGY_ON_IRQ((x)) | \ | ||||
| 					 P_ENERGY_OFF_IRQ((x)) | \ | ||||
| 					 P_GPHY_IRQ((x))) | ||||
| 
 | ||||
| /* INTRL2_0 interrupt sources */ | ||||
| #define P0_IRQ_OFF			0 | ||||
| #define MEM_DOUBLE_IRQ			(1 << 5) | ||||
| #define EEE_LPI_IRQ			(1 << 6) | ||||
| #define P5_CPU_WAKE_IRQ			(1 << 7) | ||||
| #define P8_CPU_WAKE_IRQ			(1 << 8) | ||||
| #define P7_CPU_WAKE_IRQ			(1 << 9) | ||||
| #define IEEE1588_IRQ			(1 << 10) | ||||
| #define MDIO_ERR_IRQ			(1 << 11) | ||||
| #define MDIO_DONE_IRQ			(1 << 12) | ||||
| #define GISB_ERR_IRQ			(1 << 13) | ||||
| #define UBUS_ERR_IRQ			(1 << 14) | ||||
| #define FAILOVER_ON_IRQ			(1 << 15) | ||||
| #define FAILOVER_OFF_IRQ		(1 << 16) | ||||
| #define TCAM_SOFT_ERR_IRQ		(1 << 17) | ||||
| 
 | ||||
| /* INTRL2_1 interrupt sources */ | ||||
| #define P7_IRQ_OFF			0 | ||||
| #define P_IRQ_OFF(x)			((6 - (x)) * P_NUM_IRQ) | ||||
| 
 | ||||
| /* Register set relative to 'CORE' */ | ||||
| #define CORE_G_PCTL_PORT0		0x00000 | ||||
| #define CORE_G_PCTL_PORT(x)		(CORE_G_PCTL_PORT0 + (x * 0x4)) | ||||
| #define CORE_IMP_CTL			0x00020 | ||||
| #define  RX_DIS				(1 << 0) | ||||
| #define  TX_DIS				(1 << 1) | ||||
| #define  RX_BCST_EN			(1 << 2) | ||||
| #define  RX_MCST_EN			(1 << 3) | ||||
| #define  RX_UCST_EN			(1 << 4) | ||||
| #define  G_MISTP_STATE_SHIFT		5 | ||||
| #define  G_MISTP_NO_STP			(0 << G_MISTP_STATE_SHIFT) | ||||
| #define  G_MISTP_DIS_STATE		(1 << G_MISTP_STATE_SHIFT) | ||||
| #define  G_MISTP_BLOCK_STATE		(2 << G_MISTP_STATE_SHIFT) | ||||
| #define  G_MISTP_LISTEN_STATE		(3 << G_MISTP_STATE_SHIFT) | ||||
| #define  G_MISTP_LEARN_STATE		(4 << G_MISTP_STATE_SHIFT) | ||||
| #define  G_MISTP_FWD_STATE		(5 << G_MISTP_STATE_SHIFT) | ||||
| #define  G_MISTP_STATE_MASK		0x7 | ||||
| 
 | ||||
| #define CORE_SWMODE			0x0002c | ||||
| #define  SW_FWDG_MODE			(1 << 0) | ||||
| #define  SW_FWDG_EN			(1 << 1) | ||||
| #define  RTRY_LMT_DIS			(1 << 2) | ||||
| 
 | ||||
| #define CORE_STS_OVERRIDE_IMP		0x00038 | ||||
| #define  GMII_SPEED_UP_2G		(1 << 6) | ||||
| #define  MII_SW_OR			(1 << 7) | ||||
| 
 | ||||
| #define CORE_NEW_CTRL			0x00084 | ||||
| #define  IP_MC				(1 << 0) | ||||
| #define  OUTRANGEERR_DISCARD		(1 << 1) | ||||
| #define  INRANGEERR_DISCARD		(1 << 2) | ||||
| #define  CABLE_DIAG_LEN			(1 << 3) | ||||
| #define  OVERRIDE_AUTO_PD_WAR		(1 << 4) | ||||
| #define  EN_AUTO_PD_WAR			(1 << 5) | ||||
| #define  UC_FWD_EN			(1 << 6) | ||||
| #define  MC_FWD_EN			(1 << 7) | ||||
| 
 | ||||
| #define CORE_SWITCH_CTRL		0x00088 | ||||
| #define  MII_DUMB_FWDG_EN		(1 << 6) | ||||
| 
 | ||||
| #define CORE_SFT_LRN_CTRL		0x000f8 | ||||
| #define  SW_LEARN_CNTL(x)		(1 << (x)) | ||||
| 
 | ||||
| #define CORE_STS_OVERRIDE_GMIIP_PORT(x)	(0x160 + (x) * 4) | ||||
| #define  LINK_STS			(1 << 0) | ||||
| #define  DUPLX_MODE			(1 << 1) | ||||
| #define  SPEED_SHIFT			2 | ||||
| #define  SPEED_MASK			0x3 | ||||
| #define  RXFLOW_CNTL			(1 << 4) | ||||
| #define  TXFLOW_CNTL			(1 << 5) | ||||
| #define  SW_OVERRIDE			(1 << 6) | ||||
| 
 | ||||
| #define CORE_WATCHDOG_CTRL		0x001e4 | ||||
| #define  SOFTWARE_RESET			(1 << 7) | ||||
| #define  EN_CHIP_RST			(1 << 6) | ||||
| #define  EN_SW_RESET			(1 << 4) | ||||
| 
 | ||||
| #define CORE_LNKSTS			0x00400 | ||||
| #define  LNK_STS_MASK			0x1ff | ||||
| 
 | ||||
| #define CORE_SPDSTS			0x00410 | ||||
| #define  SPDSTS_10			0 | ||||
| #define  SPDSTS_100			1 | ||||
| #define  SPDSTS_1000			2 | ||||
| #define  SPDSTS_SHIFT			2 | ||||
| #define  SPDSTS_MASK			0x3 | ||||
| 
 | ||||
| #define CORE_DUPSTS			0x00420 | ||||
| #define  CORE_DUPSTS_MASK		0x1ff | ||||
| 
 | ||||
| #define CORE_PAUSESTS			0x00428 | ||||
| #define  PAUSESTS_TX_PAUSE_SHIFT	9 | ||||
| 
 | ||||
| #define CORE_GMNCFGCFG			0x0800 | ||||
| #define  RST_MIB_CNT			(1 << 0) | ||||
| #define  RXBPDU_EN			(1 << 1) | ||||
| 
 | ||||
| #define CORE_IMP0_PRT_ID		0x0804 | ||||
| 
 | ||||
| #define CORE_BRCM_HDR_CTRL		0x0080c | ||||
| #define  BRCM_HDR_EN_P8			(1 << 0) | ||||
| #define  BRCM_HDR_EN_P5			(1 << 1) | ||||
| #define  BRCM_HDR_EN_P7			(1 << 2) | ||||
| 
 | ||||
| #define CORE_BRCM_HDR_CTRL2		0x0828 | ||||
| 
 | ||||
| #define CORE_HL_PRTC_CTRL		0x0940 | ||||
| #define  ARP_EN				(1 << 0) | ||||
| #define  RARP_EN			(1 << 1) | ||||
| #define  DHCP_EN			(1 << 2) | ||||
| #define  ICMPV4_EN			(1 << 3) | ||||
| #define  ICMPV6_EN			(1 << 4) | ||||
| #define  ICMPV6_FWD_MODE		(1 << 5) | ||||
| #define  IGMP_DIP_EN			(1 << 8) | ||||
| #define  IGMP_RPTLVE_EN			(1 << 9) | ||||
| #define  IGMP_RTPLVE_FWD_MODE		(1 << 10) | ||||
| #define  IGMP_QRY_EN			(1 << 11) | ||||
| #define  IGMP_QRY_FWD_MODE		(1 << 12) | ||||
| #define  IGMP_UKN_EN			(1 << 13) | ||||
| #define  IGMP_UKN_FWD_MODE		(1 << 14) | ||||
| #define  MLD_RPTDONE_EN			(1 << 15) | ||||
| #define  MLD_RPTDONE_FWD_MODE		(1 << 16) | ||||
| #define  MLD_QRY_EN			(1 << 17) | ||||
| #define  MLD_QRY_FWD_MODE		(1 << 18) | ||||
| 
 | ||||
| #define CORE_RST_MIB_CNT_EN		0x0950 | ||||
| 
 | ||||
| #define CORE_BRCM_HDR_RX_DIS		0x0980 | ||||
| #define CORE_BRCM_HDR_TX_DIS		0x0988 | ||||
| 
 | ||||
| #define CORE_MEM_PSM_VDD_CTRL		0x2380 | ||||
| #define  P_TXQ_PSM_VDD_SHIFT		2 | ||||
| #define  P_TXQ_PSM_VDD_MASK		0x3 | ||||
| #define  P_TXQ_PSM_VDD(x)		(P_TXQ_PSM_VDD_MASK << \ | ||||
| 					((x) * P_TXQ_PSM_VDD_SHIFT)) | ||||
| 
 | ||||
| #define	CORE_P0_MIB_OFFSET		0x8000 | ||||
| #define P_MIB_SIZE			0x400 | ||||
| #define CORE_P_MIB_OFFSET(x)		(CORE_P0_MIB_OFFSET + (x) * P_MIB_SIZE) | ||||
| 
 | ||||
| #define CORE_PORT_VLAN_CTL_PORT(x)	(0xc400 + ((x) * 0x8)) | ||||
| #define  PORT_VLAN_CTRL_MASK		0x1ff | ||||
| 
 | ||||
| #endif /* __BCM_SF2_REGS_H */ | ||||
| @ -635,6 +635,7 @@ struct packet_type dsa_pack_type __read_mostly = { | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id dsa_of_match_table[] = { | ||||
| 	{ .compatible = "brcm,bcm7445-switch-v4.0" }, | ||||
| 	{ .compatible = "marvell,dsa", }, | ||||
| 	{} | ||||
| }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user