diff --git a/patch/kernel/sunxi-dev/b53_switch.kernel_4.5+.patch b/patch/kernel/sunxi-dev/b53_switch.kernel_4.5+.patch deleted file mode 100644 index c535570b1..000000000 --- a/patch/kernel/sunxi-dev/b53_switch.kernel_4.5+.patch +++ /dev/null @@ -1,5541 +0,0 @@ -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -old mode 100644 -new mode 100755 -index 16adbc4..1af38f6 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -12,6 +12,16 @@ menuconfig PHYLIB - - if PHYLIB - -+config SWCONFIG -+ tristate "Switch configuration API" -+ ---help--- -+ Switch configuration API using netlink. This allows -+ you to configure the VLAN features of certain switches. -+ -+config SWCONFIG_LEDS -+ bool "Switch LED trigger support" -+ depends on (SWCONFIG && LEDS_TRIGGERS) -+ - comment "MII PHY device drivers" - - config AT803X_PHY -@@ -214,6 +224,8 @@ config MDIO_BCM_UNIMAC - - endif # PHYLIB - -+source "drivers/net/phy/b53/Kconfig" -+ - config MICREL_KS8995MA - tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch" - depends on SPI -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -old mode 100644 -new mode 100755 -index 501ea76..79cd3b6 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -17,6 +17,8 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o - obj-$(CONFIG_ICPLUS_PHY) += icplus.o - obj-$(CONFIG_REALTEK_PHY) += realtek.o - obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o -+ obj-$(CONFIG_SWCONFIG) += swconfig.o -+ obj-$(CONFIG_B53) += b53/ - obj-$(CONFIG_FIXED_PHY) += fixed_phy.o - obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o - obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o -diff --git a/drivers/net/phy/b53/Kconfig b/drivers/net/phy/b53/Kconfig -new file mode 100755 -index 0000000..67e053e ---- /dev/null -+++ b/drivers/net/phy/b53/Kconfig -@@ -0,0 +1,37 @@ -+menuconfig B53 -+ tristate "Broadcom bcm53xx managed switch support" -+ depends on SWCONFIG -+ help -+ This driver adds support for Broadcom managed switch chips. It supports -+ BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX -+ integrated switches. -+ -+config B53_SPI_DRIVER -+ tristate "B53 SPI connected switch driver" -+ depends on B53 && SPI -+ help -+ Select to enable support for registering switches configured through SPI. -+ -+config B53_PHY_DRIVER -+ tristate "B53 MDIO connected switch driver" -+ depends on B53 -+ select B53_PHY_FIXUP -+ help -+ Select to enable support for registering switches configured through MDIO. -+ -+config B53_MMAP_DRIVER -+ tristate "B53 MMAP connected switch driver" -+ depends on B53 -+ help -+ Select to enable support for memory-mapped switches like the BCM63XX -+ integrated switches. -+ -+config B53_SRAB_DRIVER -+ tristate "B53 SRAB connected switch driver" -+ depends on B53 -+ help -+ Select to enable support for memory-mapped Switch Register Access -+ Bridge Registers (SRAB) like it is found on the BCM53010 -+ -+config B53_PHY_FIXUP -+ bool -diff --git a/drivers/net/phy/b53/Makefile b/drivers/net/phy/b53/Makefile -new file mode 100755 -index 0000000..7cc39c7 ---- /dev/null -+++ b/drivers/net/phy/b53/Makefile -@@ -0,0 +1,10 @@ -+obj-$(CONFIG_B53) += b53_common.o -+ -+obj-$(CONFIG_B53_PHY_FIXUP) += b53_phy_fixup.o -+ -+obj-$(CONFIG_B53_MMAP_DRIVER) += b53_mmap.o -+obj-$(CONFIG_B53_SRAB_DRIVER) += b53_srab.o -+obj-$(CONFIG_B53_PHY_DRIVER) += b53_mdio.o -+obj-$(CONFIG_B53_SPI_DRIVER) += b53_spi.o -+ -+ccflags-y += -Werror -diff --git a/drivers/net/phy/b53/b53_common.c b/drivers/net/phy/b53/b53_common.c -new file mode 100755 -index 0000000..ac7c10c ---- /dev/null -+++ b/drivers/net/phy/b53/b53_common.c -@@ -0,0 +1,1457 @@ -+/* -+ * B53 switch driver main logic -+ * -+ * Copyright (C) 2011-2013 Jonas Gorski -+ * -+ * 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. -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "b53_regs.h" -+#include "b53_priv.h" -+ -+/* buffer size needed for displaying all MIBs with max'd values */ -+#define B53_BUF_SIZE 1188 -+ -+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" }, -+ { }, -+}; -+ -+/* 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" }, -+ { } -+}; -+ -+/* 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" }, -+ { } -+}; -+ -+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, u16 members, -+ u16 untag) -+{ -+ if (is5325(dev)) { -+ u32 entry = 0; -+ -+ if (members) { -+ entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) | -+ 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 (members) -+ entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) | -+ 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], -+ (untag << VTE_UNTAG_S) | members); -+ -+ b53_do_vlan_op(dev, VTA_CMD_WRITE); -+ } -+} -+ -+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); -+} -+ -+static void b53_enable_vlan(struct b53_device *dev, int enable) -+{ -+ 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); -+ } -+ -+ mgmt &= ~SM_SW_FWD_MODE; -+ -+ if (enable) { -+ vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; -+ vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN; -+ vc4 &= ~VC4_ING_VID_CHECK_MASK; -+ vc4 |= VC4_ING_VID_VIO_DROP << 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; -+ -+ if (!is5325(dev) && !is5365(dev)) { -+ if (dev->allow_vid_4095) -+ vc5 |= VC5_VID_FFF_EN; -+ else -+ vc5 &= ~VC5_VID_FFF_EN; -+ } -+ } else { -+ vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID); -+ vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_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); -+} -+ -+static int b53_set_jumbo(struct b53_device *dev, int enable, int 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) -+{ -+ unsigned int i; -+ -+ b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, -+ FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC); -+ -+ for (i = 0; i < 10; i++) { -+ u8 fast_age_ctrl; -+ -+ b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, -+ &fast_age_ctrl); -+ -+ if (!(fast_age_ctrl & FAST_AGE_DONE)) -+ return 0; -+ -+ mdelay(1); -+ } -+ -+ pr_warn("time out while flushing ARL\n"); -+ -+ return -EINVAL; -+} -+ -+static void b53_enable_ports(struct b53_device *dev) -+{ -+ unsigned i; -+ -+ b53_for_each_port(dev, i) { -+ u8 port_ctrl; -+ u16 pvlan_mask; -+ -+ /* -+ * prevent leaking packets between wan and lan in unmanaged -+ * mode through port vlans. -+ */ -+ if (dev->enable_vlan || is_cpu_port(dev, i)) -+ pvlan_mask = 0x1ff; -+ else if (is531x5(dev) || is5301x(dev)) -+ /* BCM53115 may use a different port as cpu port */ -+ pvlan_mask = BIT(dev->sw_dev.cpu_port); -+ else -+ pvlan_mask = BIT(B53_CPU_PORT); -+ -+ /* BCM5325 CPU port is at 8 */ -+ if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25) -+ i = B53_CPU_PORT; -+ -+ if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7)) -+ /* disable unused ports 6 & 7 */ -+ port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE; -+ else if (i == B53_CPU_PORT) -+ port_ctrl = PORT_CTRL_RX_BCST_EN | -+ PORT_CTRL_RX_MCST_EN | -+ PORT_CTRL_RX_UCST_EN; -+ else -+ port_ctrl = 0; -+ -+ b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), -+ pvlan_mask); -+ -+ /* port state is handled by bcm63xx_enet driver */ -+ if (!is63xx(dev) && !(is5301x(dev) && i == 6)) -+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i), -+ port_ctrl); -+ } -+} -+ -+static void b53_enable_mib(struct b53_device *dev) -+{ -+ u8 gc; -+ -+ b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc); -+ -+ gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN); -+ -+ b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc); -+} -+ -+static int b53_apply(struct b53_device *dev) -+{ -+ int i; -+ -+ /* clear all vlan entries */ -+ if (is5325(dev) || is5365(dev)) { -+ for (i = 1; i < dev->sw_dev.vlans; i++) -+ b53_set_vlan_entry(dev, i, 0, 0); -+ } else { -+ b53_do_vlan_op(dev, VTA_CMD_CLEAR); -+ } -+ -+ b53_enable_vlan(dev, dev->enable_vlan); -+ -+ /* fill VLAN table */ -+ if (dev->enable_vlan) { -+ for (i = 0; i < dev->sw_dev.vlans; i++) { -+ struct b53_vlan *vlan = &dev->vlans[i]; -+ -+ if (!vlan->members) -+ continue; -+ -+ b53_set_vlan_entry(dev, i, vlan->members, vlan->untag); -+ } -+ -+ b53_for_each_port(dev, i) -+ b53_write16(dev, B53_VLAN_PAGE, -+ B53_VLAN_PORT_DEF_TAG(i), -+ dev->ports[i].pvid); -+ } else { -+ b53_for_each_port(dev, i) -+ b53_write16(dev, B53_VLAN_PAGE, -+ B53_VLAN_PORT_DEF_TAG(i), 1); -+ -+ } -+ -+ b53_enable_ports(dev); -+ -+ if (!is5325(dev) && !is5365(dev)) -+ b53_set_jumbo(dev, dev->enable_jumbo, 1); -+ -+ return 0; -+} -+ -+static void b53_switch_reset_gpio(struct b53_device *dev) -+{ -+ int gpio = dev->reset_gpio; -+ -+ if (gpio < 0) -+ return; -+ -+ /* -+ * Reset sequence: RESET low(50ms)->high(20ms) -+ */ -+ gpio_set_value(gpio, 0); -+ mdelay(50); -+ -+ gpio_set_value(gpio, 1); -+ mdelay(20); -+ -+ dev->current_page = 0xff; -+} -+ -+static int b53_switch_reset(struct b53_device *dev) -+{ -+ u8 cpu_port = dev->sw_dev.cpu_port; -+ u8 mgmt; -+ -+ b53_switch_reset_gpio(dev); -+ -+ if (is539x(dev)) { -+ b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83); -+ b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00); -+ } -+ -+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); -+ -+ if (!(mgmt & SM_SW_FWD_EN)) { -+ mgmt &= ~SM_SW_FWD_MODE; -+ mgmt |= SM_SW_FWD_EN; -+ -+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); -+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); -+ -+ if (!(mgmt & SM_SW_FWD_EN)) { -+ pr_err("Failed to enable switch!\n"); -+ return -EINVAL; -+ } -+ } -+ -+ /* enable all ports */ -+ b53_enable_ports(dev); -+ -+ /* configure MII port if necessary */ -+ if (is5325(dev)) { -+ u8 mii_port_override; -+ -+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ &mii_port_override); -+ /* reverse mii needs to be enabled */ -+ if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) { -+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ mii_port_override | PORT_OVERRIDE_RV_MII_25); -+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ &mii_port_override); -+ -+ if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) { -+ pr_err("Failed to enable reverse MII mode\n"); -+ return -EINVAL; -+ } -+ } -+ } else if (is531x5(dev) && cpu_port == B53_CPU_PORT) { -+ u8 mii_port_override; -+ -+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ &mii_port_override); -+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ mii_port_override | PORT_OVERRIDE_EN | -+ PORT_OVERRIDE_LINK); -+ } else if (is5301x(dev)) { -+ if (cpu_port == 8) { -+ u8 mii_port_override; -+ -+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ &mii_port_override); -+ mii_port_override |= PORT_OVERRIDE_LINK | -+ PORT_OVERRIDE_RX_FLOW | -+ PORT_OVERRIDE_TX_FLOW | -+ PORT_OVERRIDE_SPEED_2000M | -+ PORT_OVERRIDE_EN; -+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, -+ mii_port_override); -+ -+ /* TODO: Ports 5 & 7 require some extra handling */ -+ } else { -+ u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(cpu_port); -+ u8 gmii_po; -+ -+ b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po); -+ gmii_po |= GMII_PO_LINK | -+ GMII_PO_RX_FLOW | -+ GMII_PO_TX_FLOW | -+ GMII_PO_EN | -+ GMII_PO_SPEED_2000M; -+ b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po); -+ } -+ } -+ -+ b53_enable_mib(dev); -+ -+ return b53_flush_arl(dev); -+} -+ -+/* -+ * Swconfig glue functions -+ */ -+ -+static int b53_global_get_vlan_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ val->value.i = priv->enable_vlan; -+ -+ return 0; -+} -+ -+static int b53_global_set_vlan_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ priv->enable_vlan = val->value.i; -+ -+ return 0; -+} -+ -+static int b53_global_get_jumbo_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ val->value.i = priv->enable_jumbo; -+ -+ return 0; -+} -+ -+static int b53_global_set_jumbo_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ priv->enable_jumbo = val->value.i; -+ -+ return 0; -+} -+ -+static int b53_global_get_4095_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ val->value.i = priv->allow_vid_4095; -+ -+ return 0; -+} -+ -+static int b53_global_set_4095_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ priv->allow_vid_4095 = val->value.i; -+ -+ return 0; -+} -+ -+static int b53_global_get_ports(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x", -+ priv->enabled_ports); -+ val->value.s = priv->buf; -+ -+ return 0; -+} -+ -+static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ *val = priv->ports[port].pvid; -+ -+ return 0; -+} -+ -+static int b53_port_set_pvid(struct switch_dev *dev, int port, int val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ if (val > 15 && is5325(priv)) -+ return -EINVAL; -+ if (val == 4095 && !priv->allow_vid_4095) -+ return -EINVAL; -+ -+ priv->ports[port].pvid = val; -+ -+ return 0; -+} -+ -+static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ struct switch_port *port = &val->value.ports[0]; -+ struct b53_vlan *vlan = &priv->vlans[val->port_vlan]; -+ int i; -+ -+ val->len = 0; -+ -+ if (!vlan->members) -+ return 0; -+ -+ for (i = 0; i < dev->ports; i++) { -+ if (!(vlan->members & BIT(i))) -+ continue; -+ -+ -+ if (!(vlan->untag & BIT(i))) -+ port->flags = BIT(SWITCH_PORT_FLAG_TAGGED); -+ else -+ port->flags = 0; -+ -+ port->id = i; -+ val->len++; -+ port++; -+ } -+ -+ return 0; -+} -+ -+static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ struct switch_port *port; -+ struct b53_vlan *vlan = &priv->vlans[val->port_vlan]; -+ int i; -+ -+ /* only BCM5325 and BCM5365 supports VID 0 */ -+ if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv)) -+ return -EINVAL; -+ -+ /* VLAN 4095 needs special handling */ -+ if (val->port_vlan == 4095 && !priv->allow_vid_4095) -+ return -EINVAL; -+ -+ port = &val->value.ports[0]; -+ vlan->members = 0; -+ vlan->untag = 0; -+ for (i = 0; i < val->len; i++, port++) { -+ vlan->members |= BIT(port->id); -+ -+ if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) { -+ vlan->untag |= BIT(port->id); -+ priv->ports[port->id].pvid = val->port_vlan; -+ }; -+ } -+ -+ /* ignore disabled ports */ -+ vlan->members &= priv->enabled_ports; -+ vlan->untag &= priv->enabled_ports; -+ -+ return 0; -+} -+ -+static int b53_port_get_link(struct switch_dev *dev, int port, -+ struct switch_port_link *link) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ if (is_cpu_port(priv, port)) { -+ link->link = 1; -+ link->duplex = 1; -+ link->speed = is5325(priv) || is5365(priv) ? -+ SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000; -+ link->aneg = 0; -+ } else if (priv->enabled_ports & BIT(port)) { -+ u32 speed; -+ u16 lnk, duplex; -+ -+ b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk); -+ b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex); -+ -+ lnk = (lnk >> port) & 1; -+ duplex = (duplex >> port) & 1; -+ -+ if (is5325(priv) || is5365(priv)) { -+ u16 tmp; -+ -+ b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp); -+ speed = SPEED_PORT_FE(tmp, port); -+ } else { -+ b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed); -+ speed = SPEED_PORT_GE(speed, port); -+ } -+ -+ link->link = lnk; -+ if (lnk) { -+ link->duplex = duplex; -+ switch (speed) { -+ case SPEED_STAT_10M: -+ link->speed = SWITCH_PORT_SPEED_10; -+ break; -+ case SPEED_STAT_100M: -+ link->speed = SWITCH_PORT_SPEED_100; -+ break; -+ case SPEED_STAT_1000M: -+ link->speed = SWITCH_PORT_SPEED_1000; -+ break; -+ } -+ } -+ -+ link->aneg = 1; -+ } else { -+ link->link = 0; -+ } -+ -+ return 0; -+ -+} -+ -+static int b53_global_reset_switch(struct switch_dev *dev) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ /* reset vlans */ -+ priv->enable_vlan = 0; -+ priv->enable_jumbo = 0; -+ priv->allow_vid_4095 = 0; -+ -+ memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans); -+ memset(priv->ports, 0, sizeof(priv->ports) * dev->ports); -+ -+ return b53_switch_reset(priv); -+} -+ -+static int b53_global_apply_config(struct switch_dev *dev) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ -+ /* disable switching */ -+ b53_set_forwarding(priv, 0); -+ -+ b53_apply(priv); -+ -+ /* enable switching */ -+ b53_set_forwarding(priv, 1); -+ -+ return 0; -+} -+ -+ -+static int b53_global_reset_mib(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *priv = sw_to_b53(dev); -+ u8 gc; -+ -+ b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc); -+ -+ b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB); -+ mdelay(1); -+ b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB); -+ mdelay(1); -+ -+ return 0; -+} -+ -+static int b53_port_get_mib(struct switch_dev *sw_dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct b53_device *dev = sw_to_b53(sw_dev); -+ const struct b53_mib_desc *mibs; -+ int port = val->port_vlan; -+ int len = 0; -+ -+ if (!(BIT(port) & dev->enabled_ports)) -+ return -1; -+ -+ if (is5365(dev)) { -+ if (port == 5) -+ port = 8; -+ -+ mibs = b53_mibs_65; -+ } else if (is63xx(dev)) { -+ mibs = b53_mibs_63xx; -+ } else { -+ mibs = b53_mibs; -+ } -+ -+ dev->buf[0] = 0; -+ -+ for (; mibs->size > 0; mibs++) { -+ u64 val; -+ -+ if (mibs->size == 8) { -+ b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val); -+ } else { -+ u32 val32; -+ -+ b53_read32(dev, B53_MIB_PAGE(port), mibs->offset, -+ &val32); -+ val = val32; -+ } -+ -+ len += snprintf(dev->buf + len, B53_BUF_SIZE - len, -+ "%-20s: %llu\n", mibs->name, val); -+ } -+ -+ val->len = len; -+ val->value.s = dev->buf; -+ -+ return 0; -+} -+ -+static struct switch_attr b53_global_ops_25[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "Enable VLAN mode", -+ .set = b53_global_set_vlan_enable, -+ .get = b53_global_get_vlan_enable, -+ .max = 1, -+ }, -+ { -+ .type = SWITCH_TYPE_STRING, -+ .name = "ports", -+ .description = "Available ports (as bitmask)", -+ .get = b53_global_get_ports, -+ }, -+}; -+ -+static struct switch_attr b53_global_ops_65[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "Enable VLAN mode", -+ .set = b53_global_set_vlan_enable, -+ .get = b53_global_get_vlan_enable, -+ .max = 1, -+ }, -+ { -+ .type = SWITCH_TYPE_STRING, -+ .name = "ports", -+ .description = "Available ports (as bitmask)", -+ .get = b53_global_get_ports, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "reset_mib", -+ .description = "Reset MIB counters", -+ .set = b53_global_reset_mib, -+ }, -+}; -+ -+static struct switch_attr b53_global_ops[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "Enable VLAN mode", -+ .set = b53_global_set_vlan_enable, -+ .get = b53_global_get_vlan_enable, -+ .max = 1, -+ }, -+ { -+ .type = SWITCH_TYPE_STRING, -+ .name = "ports", -+ .description = "Available Ports (as bitmask)", -+ .get = b53_global_get_ports, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "reset_mib", -+ .description = "Reset MIB counters", -+ .set = b53_global_reset_mib, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_jumbo", -+ .description = "Enable Jumbo Frames", -+ .set = b53_global_set_jumbo_enable, -+ .get = b53_global_get_jumbo_enable, -+ .max = 1, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "allow_vid_4095", -+ .description = "Allow VID 4095", -+ .set = b53_global_set_4095_enable, -+ .get = b53_global_get_4095_enable, -+ .max = 1, -+ }, -+}; -+ -+static struct switch_attr b53_port_ops[] = { -+ { -+ .type = SWITCH_TYPE_STRING, -+ .name = "mib", -+ .description = "Get port's MIB counters", -+ .get = b53_port_get_mib, -+ }, -+}; -+ -+static struct switch_attr b53_no_ops[] = { -+}; -+ -+static const struct switch_dev_ops b53_switch_ops_25 = { -+ .attr_global = { -+ .attr = b53_global_ops_25, -+ .n_attr = ARRAY_SIZE(b53_global_ops_25), -+ }, -+ .attr_port = { -+ .attr = b53_no_ops, -+ .n_attr = ARRAY_SIZE(b53_no_ops), -+ }, -+ .attr_vlan = { -+ .attr = b53_no_ops, -+ .n_attr = ARRAY_SIZE(b53_no_ops), -+ }, -+ -+ .get_vlan_ports = b53_vlan_get_ports, -+ .set_vlan_ports = b53_vlan_set_ports, -+ .get_port_pvid = b53_port_get_pvid, -+ .set_port_pvid = b53_port_set_pvid, -+ .apply_config = b53_global_apply_config, -+ .reset_switch = b53_global_reset_switch, -+ .get_port_link = b53_port_get_link, -+}; -+ -+static const struct switch_dev_ops b53_switch_ops_65 = { -+ .attr_global = { -+ .attr = b53_global_ops_65, -+ .n_attr = ARRAY_SIZE(b53_global_ops_65), -+ }, -+ .attr_port = { -+ .attr = b53_port_ops, -+ .n_attr = ARRAY_SIZE(b53_port_ops), -+ }, -+ .attr_vlan = { -+ .attr = b53_no_ops, -+ .n_attr = ARRAY_SIZE(b53_no_ops), -+ }, -+ -+ .get_vlan_ports = b53_vlan_get_ports, -+ .set_vlan_ports = b53_vlan_set_ports, -+ .get_port_pvid = b53_port_get_pvid, -+ .set_port_pvid = b53_port_set_pvid, -+ .apply_config = b53_global_apply_config, -+ .reset_switch = b53_global_reset_switch, -+ .get_port_link = b53_port_get_link, -+}; -+ -+static const struct switch_dev_ops b53_switch_ops = { -+ .attr_global = { -+ .attr = b53_global_ops, -+ .n_attr = ARRAY_SIZE(b53_global_ops), -+ }, -+ .attr_port = { -+ .attr = b53_port_ops, -+ .n_attr = ARRAY_SIZE(b53_port_ops), -+ }, -+ .attr_vlan = { -+ .attr = b53_no_ops, -+ .n_attr = ARRAY_SIZE(b53_no_ops), -+ }, -+ -+ .get_vlan_ports = b53_vlan_get_ports, -+ .set_vlan_ports = b53_vlan_set_ports, -+ .get_port_pvid = b53_port_get_pvid, -+ .set_port_pvid = b53_port_set_pvid, -+ .apply_config = b53_global_apply_config, -+ .reset_switch = b53_global_reset_switch, -+ .get_port_link = b53_port_get_link, -+}; -+ -+struct b53_chip_data { -+ u32 chip_id; -+ const char *dev_name; -+ const char *alias; -+ u16 vlans; -+ u16 enabled_ports; -+ u8 cpu_port; -+ u8 vta_regs[3]; -+ u8 duplex_reg; -+ u8 jumbo_pm_reg; -+ u8 jumbo_size_reg; -+ const struct switch_dev_ops *sw_ops; -+}; -+ -+#define B53_VTA_REGS \ -+ { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY } -+#define B53_VTA_REGS_9798 \ -+ { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 } -+#define B53_VTA_REGS_63XX \ -+ { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX } -+ -+static const struct b53_chip_data b53_switch_chips[] = { -+ { -+ .chip_id = BCM5325_DEVICE_ID, -+ .dev_name = "BCM5325", -+ .alias = "bcm5325", -+ .vlans = 16, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, -+ .duplex_reg = B53_DUPLEX_STAT_FE, -+ .sw_ops = &b53_switch_ops_25, -+ }, -+ { -+ .chip_id = BCM5365_DEVICE_ID, -+ .dev_name = "BCM5365", -+ .alias = "bcm5365", -+ .vlans = 256, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, -+ .duplex_reg = B53_DUPLEX_STAT_FE, -+ .sw_ops = &b53_switch_ops_65, -+ }, -+ { -+ .chip_id = BCM5395_DEVICE_ID, -+ .dev_name = "BCM5395", -+ .alias = "bcm5395", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT, -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM5397_DEVICE_ID, -+ .dev_name = "BCM5397", -+ .alias = "bcm5397", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT, -+ .vta_regs = B53_VTA_REGS_9798, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM5398_DEVICE_ID, -+ .dev_name = "BCM5398", -+ .alias = "bcm5398", -+ .vlans = 4096, -+ .enabled_ports = 0x7f, -+ .cpu_port = B53_CPU_PORT, -+ .vta_regs = B53_VTA_REGS_9798, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53115_DEVICE_ID, -+ .dev_name = "BCM53115", -+ .alias = "bcm53115", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .vta_regs = B53_VTA_REGS, -+ .cpu_port = B53_CPU_PORT, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53125_DEVICE_ID, -+ .dev_name = "BCM53125", -+ .alias = "bcm53125", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT, -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53128_DEVICE_ID, -+ .dev_name = "BCM53128", -+ .alias = "bcm53128", -+ .vlans = 4096, -+ .enabled_ports = 0x1ff, -+ .cpu_port = B53_CPU_PORT, -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM63XX_DEVICE_ID, -+ .dev_name = "BCM63xx", -+ .alias = "bcm63xx", -+ .vlans = 4096, -+ .enabled_ports = 0, /* pdata must provide them */ -+ .cpu_port = B53_CPU_PORT, -+ .vta_regs = B53_VTA_REGS_63XX, -+ .duplex_reg = B53_DUPLEX_STAT_63XX, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53010_DEVICE_ID, -+ .dev_name = "BCM53010", -+ .alias = "bcm53011", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53011_DEVICE_ID, -+ .dev_name = "BCM53011", -+ .alias = "bcm53011", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53012_DEVICE_ID, -+ .dev_name = "BCM53012", -+ .alias = "bcm53011", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53018_DEVICE_ID, -+ .dev_name = "BCM53018", -+ .alias = "bcm53018", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+ { -+ .chip_id = BCM53019_DEVICE_ID, -+ .dev_name = "BCM53019", -+ .alias = "bcm53019", -+ .vlans = 4096, -+ .enabled_ports = 0x1f, -+ .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ -+ .vta_regs = B53_VTA_REGS, -+ .duplex_reg = B53_DUPLEX_STAT_GE, -+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, -+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, -+ .sw_ops = &b53_switch_ops, -+ }, -+}; -+ -+static int b53_switch_init(struct b53_device *dev) -+{ -+ struct switch_dev *sw_dev = &dev->sw_dev; -+ unsigned i; -+ int ret; -+ -+ for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) { -+ const struct b53_chip_data *chip = &b53_switch_chips[i]; -+ -+ if (chip->chip_id == dev->chip_id) { -+ sw_dev->name = chip->dev_name; -+ if (!sw_dev->alias) -+ sw_dev->alias = chip->alias; -+ if (!dev->enabled_ports) -+ dev->enabled_ports = chip->enabled_ports; -+ dev->duplex_reg = chip->duplex_reg; -+ dev->vta_regs[0] = chip->vta_regs[0]; -+ dev->vta_regs[1] = chip->vta_regs[1]; -+ dev->vta_regs[2] = chip->vta_regs[2]; -+ dev->jumbo_pm_reg = chip->jumbo_pm_reg; -+ sw_dev->ops = chip->sw_ops; -+ sw_dev->cpu_port = chip->cpu_port; -+ sw_dev->vlans = chip->vlans; -+ break; -+ } -+ } -+ -+ if (!sw_dev->name) -+ return -EINVAL; -+ -+ /* check which BCM5325x version we have */ -+ if (is5325(dev)) { -+ u8 vc4; -+ -+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); -+ -+ /* check reserved bits */ -+ switch (vc4 & 3) { -+ case 1: -+ /* BCM5325E */ -+ break; -+ case 3: -+ /* BCM5325F - do not use port 4 */ -+ dev->enabled_ports &= ~BIT(4); -+ break; -+ default: -+/* On the BCM47XX SoCs this is the supported internal switch.*/ -+#ifndef CONFIG_BCM47XX -+ /* BCM5325M */ -+ return -EINVAL; -+#else -+ break; -+#endif -+ } -+ } else if (dev->chip_id == BCM53115_DEVICE_ID) { -+ u64 strap_value; -+ -+ b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value); -+ /* use second IMP port if GMII is enabled */ -+ if (strap_value & SV_GMII_CTRL_115) -+ sw_dev->cpu_port = 5; -+ } -+ -+ /* cpu port is always last */ -+ sw_dev->ports = sw_dev->cpu_port + 1; -+ dev->enabled_ports |= BIT(sw_dev->cpu_port); -+ -+ dev->ports = devm_kzalloc(dev->dev, -+ sizeof(struct b53_port) * sw_dev->ports, -+ GFP_KERNEL); -+ if (!dev->ports) -+ return -ENOMEM; -+ -+ dev->vlans = devm_kzalloc(dev->dev, -+ sizeof(struct b53_vlan) * sw_dev->vlans, -+ GFP_KERNEL); -+ if (!dev->vlans) -+ return -ENOMEM; -+ -+ dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL); -+ if (!dev->buf) -+ return -ENOMEM; -+ -+ dev->reset_gpio = b53_switch_get_reset_gpio(dev); -+ if (dev->reset_gpio >= 0) { -+ ret = devm_gpio_request_one(dev->dev, dev->reset_gpio, -+ GPIOF_OUT_INIT_HIGH, "robo_reset"); -+ if (ret) -+ return ret; -+ } -+ -+ return b53_switch_reset(dev); -+} -+ -+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, -+ void *priv) -+{ -+ struct b53_device *dev; -+ -+ dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); -+ if (!dev) -+ return NULL; -+ -+ dev->dev = base; -+ dev->ops = ops; -+ dev->priv = priv; -+ mutex_init(&dev->reg_mutex); -+ -+ return dev; -+} -+EXPORT_SYMBOL(b53_switch_alloc); -+ -+int b53_switch_detect(struct b53_device *dev) -+{ -+ u32 id32; -+ u16 tmp; -+ u8 id8; -+ int ret; -+ -+ ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8); -+ if (ret) -+ return ret; -+ -+ switch (id8) { -+ case 0: -+ /* -+ * BCM5325 and BCM5365 do not have this register so reads -+ * return 0. But the read operation did succeed, so assume -+ * this is one of them. -+ * -+ * Next check if we can write to the 5325's VTA register; for -+ * 5365 it is read only. -+ */ -+ -+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf); -+ b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp); -+ -+ if (tmp == 0xf) -+ dev->chip_id = BCM5325_DEVICE_ID; -+ else -+ dev->chip_id = BCM5365_DEVICE_ID; -+ break; -+ case BCM5395_DEVICE_ID: -+ case BCM5397_DEVICE_ID: -+ case BCM5398_DEVICE_ID: -+ dev->chip_id = id8; -+ break; -+ default: -+ ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32); -+ if (ret) -+ return ret; -+ -+ switch (id32) { -+ case BCM53115_DEVICE_ID: -+ case BCM53125_DEVICE_ID: -+ case BCM53128_DEVICE_ID: -+ case BCM53010_DEVICE_ID: -+ case BCM53011_DEVICE_ID: -+ case BCM53012_DEVICE_ID: -+ case BCM53018_DEVICE_ID: -+ case BCM53019_DEVICE_ID: -+ dev->chip_id = id32; -+ break; -+ default: -+ pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n", -+ id8, id32); -+ return -ENODEV; -+ } -+ } -+ -+ if (dev->chip_id == BCM5325_DEVICE_ID) -+ return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25, -+ &dev->core_rev); -+ else -+ return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID, -+ &dev->core_rev); -+} -+EXPORT_SYMBOL(b53_switch_detect); -+ -+int b53_switch_register(struct b53_device *dev) -+{ -+ int ret; -+ -+ if (dev->pdata) { -+ dev->chip_id = dev->pdata->chip_id; -+ dev->enabled_ports = dev->pdata->enabled_ports; -+ dev->sw_dev.alias = dev->pdata->alias; -+ } -+ -+ if (!dev->chip_id && b53_switch_detect(dev)) -+ return -EINVAL; -+ -+ ret = b53_switch_init(dev); -+ if (ret) -+ return ret; -+ -+ pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev); -+ -+ return register_switch(&dev->sw_dev, NULL); -+} -+EXPORT_SYMBOL(b53_switch_register); -+ -+MODULE_AUTHOR("Jonas Gorski "); -+MODULE_DESCRIPTION("B53 switch library"); -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/phy/b53/b53_mdio.c b/drivers/net/phy/b53/b53_mdio.c -new file mode 100755 -index 0000000..3c25f0e ---- /dev/null -+++ b/drivers/net/phy/b53/b53_mdio.c -@@ -0,0 +1,396 @@ -+/* -+ * B53 register access through MII registers -+ * -+ * Copyright (C) 2011-2013 Jonas Gorski -+ * -+ * 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 -+#include -+#include -+ -+#include "b53_priv.h" -+ -+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */ -+ -+/* MII registers */ -+#define REG_MII_PAGE 0x10 /* MII Page register */ -+#define REG_MII_ADDR 0x11 /* MII Address register */ -+#define REG_MII_DATA0 0x18 /* MII Data register 0 */ -+#define REG_MII_DATA1 0x19 /* MII Data register 1 */ -+#define REG_MII_DATA2 0x1a /* MII Data register 2 */ -+#define REG_MII_DATA3 0x1b /* MII Data register 3 */ -+ -+#define REG_MII_PAGE_ENABLE BIT(0) -+#define REG_MII_ADDR_WRITE BIT(0) -+#define REG_MII_ADDR_READ BIT(1) -+ -+static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op) -+{ -+ int i; -+ u16 v; -+ int ret; -+ struct mii_bus *bus = dev->priv; -+ -+ if (dev->current_page != page) { -+ /* set page number */ -+ v = (page << 8) | REG_MII_PAGE_ENABLE; -+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v); -+ if (ret) -+ return ret; -+ dev->current_page = page; -+ } -+ -+ /* set register address */ -+ v = (reg << 8) | op; -+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v); -+ if (ret) -+ return ret; -+ -+ /* check if operation completed */ -+ for (i = 0; i < 5; ++i) { -+ v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR); -+ if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ))) -+ break; -+ usleep_range(10, 100); -+ } -+ -+ if (WARN_ON(i == 5)) -+ return -EIO; -+ -+ return 0; -+} -+ -+static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) -+{ -+ struct mii_bus *bus = dev->priv; -+ int ret; -+ -+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); -+ if (ret) -+ return ret; -+ -+ *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff; -+ -+ return 0; -+} -+ -+static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) -+{ -+ struct mii_bus *bus = dev->priv; -+ int ret; -+ -+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); -+ if (ret) -+ return ret; -+ -+ *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0); -+ -+ return 0; -+} -+ -+static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) -+{ -+ struct mii_bus *bus = dev->priv; -+ int ret; -+ -+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); -+ if (ret) -+ return ret; -+ -+ *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0); -+ *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16; -+ -+ return 0; -+} -+ -+static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ struct mii_bus *bus = dev->priv; -+ u64 temp = 0; -+ int i; -+ int ret; -+ -+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); -+ if (ret) -+ return ret; -+ -+ for (i = 2; i >= 0; i--) { -+ temp <<= 16; -+ temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i); -+ } -+ -+ *val = temp; -+ -+ return 0; -+} -+ -+static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ struct mii_bus *bus = dev->priv; -+ u64 temp = 0; -+ int i; -+ int ret; -+ -+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); -+ if (ret) -+ return ret; -+ -+ for (i = 3; i >= 0; i--) { -+ temp <<= 16; -+ temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i); -+ } -+ -+ *val = temp; -+ -+ return 0; -+} -+ -+static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) -+{ -+ struct mii_bus *bus = dev->priv; -+ int ret; -+ -+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value); -+ if (ret) -+ return ret; -+ -+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); -+} -+ -+static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg, -+ u16 value) -+{ -+ struct mii_bus *bus = dev->priv; -+ int ret; -+ -+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value); -+ if (ret) -+ return ret; -+ -+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); -+} -+ -+static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg, -+ u32 value) -+{ -+ struct mii_bus *bus = dev->priv; -+ unsigned int i; -+ u32 temp = value; -+ -+ for (i = 0; i < 2; i++) { -+ int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, -+ temp & 0xffff); -+ if (ret) -+ return ret; -+ temp >>= 16; -+ } -+ -+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); -+ -+} -+ -+static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ struct mii_bus *bus = dev->priv; -+ unsigned i; -+ u64 temp = value; -+ -+ for (i = 0; i < 3; i++) { -+ int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, -+ temp & 0xffff); -+ if (ret) -+ return ret; -+ temp >>= 16; -+ } -+ -+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); -+ -+} -+ -+static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ struct mii_bus *bus = dev->priv; -+ unsigned i; -+ u64 temp = value; -+ -+ for (i = 0; i < 4; i++) { -+ int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, -+ temp & 0xffff); -+ if (ret) -+ return ret; -+ temp >>= 16; -+ } -+ -+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); -+} -+ -+static struct b53_io_ops b53_mdio_ops = { -+ .read8 = b53_mdio_read8, -+ .read16 = b53_mdio_read16, -+ .read32 = b53_mdio_read32, -+ .read48 = b53_mdio_read48, -+ .read64 = b53_mdio_read64, -+ .write8 = b53_mdio_write8, -+ .write16 = b53_mdio_write16, -+ .write32 = b53_mdio_write32, -+ .write48 = b53_mdio_write48, -+ .write64 = b53_mdio_write64, -+}; -+ -+static int b53_phy_probe(struct phy_device *phydev) -+{ -+ struct b53_device dev; -+ int ret; -+ -+ /* allow the generic phy driver to take over */ -+ if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0) -+ return -ENODEV; -+ -+ dev.current_page = 0xff; -+ dev.priv = phydev->mdio.bus; -+ dev.ops = &b53_mdio_ops; -+ dev.pdata = NULL; -+ mutex_init(&dev.reg_mutex); -+ -+ ret = b53_switch_detect(&dev); -+ if (ret) -+ return ret; -+ -+ if (is5325(&dev) || is5365(&dev)) -+ phydev->supported = SUPPORTED_100baseT_Full; -+ else -+ phydev->supported = SUPPORTED_1000baseT_Full; -+ -+ phydev->advertising = phydev->supported; -+ -+ return 0; -+} -+ -+static int b53_phy_config_init(struct phy_device *phydev) -+{ -+ struct b53_device *dev; -+ int ret; -+ -+ dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus); -+ if (!dev) -+ return -ENOMEM; -+ -+ /* we don't use page 0xff, so force a page set */ -+ dev->current_page = 0xff; -+ /* force the ethX as alias */ -+ dev->sw_dev.alias = phydev->attached_dev->name; -+ -+ ret = b53_switch_register(dev); -+ if (ret) { -+ dev_err(dev->dev, "failed to register switch: %i\n", ret); -+ return ret; -+ } -+ -+ phydev->priv = dev; -+ -+ return 0; -+} -+ -+static void b53_phy_remove(struct phy_device *phydev) -+{ -+ struct b53_device *priv = phydev->priv; -+ -+ if (!priv) -+ return; -+ -+ b53_switch_remove(priv); -+ -+ phydev->priv = NULL; -+} -+ -+static int b53_phy_config_aneg(struct phy_device *phydev) -+{ -+ return 0; -+} -+ -+static int b53_phy_read_status(struct phy_device *phydev) -+{ -+ struct b53_device *priv = phydev->priv; -+ -+ if (is5325(priv) || is5365(priv)) -+ phydev->speed = 100; -+ else -+ phydev->speed = 1000; -+ -+ phydev->duplex = DUPLEX_FULL; -+ phydev->link = 1; -+ phydev->state = PHY_RUNNING; -+ -+ netif_carrier_on(phydev->attached_dev); -+ phydev->adjust_link(phydev->attached_dev); -+ -+ return 0; -+} -+ -+static struct phy_driver b53_phy_drivers[] = { -+ { /* BCM5325, BCM539x */ -+ .name = "Broadcom B53 (1)", -+ .phy_id = 0x0143bc00, -+ .phy_id_mask = 0x1ffffc00, -+ .features = 0, -+ .probe = b53_phy_probe, -+ .remove = b53_phy_remove, -+ .config_aneg = b53_phy_config_aneg, -+ .config_init = b53_phy_config_init, -+ .read_status = b53_phy_read_status, -+ }, -+ { /* BCM53125, BCM53128 */ -+ .name = "Broadcom B53 (2)", -+ .phy_id = 0x03625c00, -+ .phy_id_mask = 0x1ffffc00, -+ .features = 0, -+ .probe = b53_phy_probe, -+ .remove = b53_phy_remove, -+ .config_aneg = b53_phy_config_aneg, -+ .config_init = b53_phy_config_init, -+ .read_status = b53_phy_read_status, -+ }, -+ { /* BCM5365 */ -+ .name = "Broadcom B53 (3)", -+ .phy_id = 0x00406000, -+ .phy_id_mask = 0x1ffffc00, -+ .features = 0, -+ .probe = b53_phy_probe, -+ .remove = b53_phy_remove, -+ .config_aneg = b53_phy_config_aneg, -+ .config_init = b53_phy_config_init, -+ .read_status = b53_phy_read_status, -+ } -+}; -+ -+int __init b53_phy_driver_register(void) -+{ -+ return phy_drivers_register(b53_phy_drivers, -+ ARRAY_SIZE(b53_phy_drivers), THIS_MODULE); -+} -+ -+void __exit b53_phy_driver_unregister(void) -+{ -+ phy_drivers_unregister(b53_phy_drivers, -+ ARRAY_SIZE(b53_phy_drivers)); -+} -+ -+module_init(b53_phy_driver_register); -+module_exit(b53_phy_driver_unregister); -+ -+MODULE_DESCRIPTION("B53 MDIO access driver"); -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/phy/b53/b53_mmap.c b/drivers/net/phy/b53/b53_mmap.c -new file mode 100755 -index 0000000..ab1895e ---- /dev/null -+++ b/drivers/net/phy/b53/b53_mmap.c -@@ -0,0 +1,241 @@ -+/* -+ * B53 register access through memory mapped registers -+ * -+ * Copyright (C) 2012-2013 Jonas Gorski -+ * -+ * 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 -+#include -+#include -+#include -+ -+#include "b53_priv.h" -+ -+static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ -+ *val = readb(regs + (page << 8) + reg); -+ -+ return 0; -+} -+ -+static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ -+ if (WARN_ON(reg % 2)) -+ return -EINVAL; -+ -+ if (dev->pdata && dev->pdata->big_endian) -+ *val = be16_to_cpu(readw(regs + (page << 8) + reg)); -+ else -+ *val = readw(regs + (page << 8) + reg); -+ -+ return 0; -+} -+ -+static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ -+ if (WARN_ON(reg % 4)) -+ return -EINVAL; -+ -+ if (dev->pdata && dev->pdata->big_endian) -+ *val = be16_to_cpu(readl(regs + (page << 8) + reg)); -+ else -+ *val = readl(regs + (page << 8) + reg); -+ -+ return 0; -+} -+ -+static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ if (WARN_ON(reg % 2)) -+ return -EINVAL; -+ -+ if (reg % 4) { -+ u16 lo; -+ u32 hi; -+ -+ b53_mmap_read16(dev, page, reg, &lo); -+ b53_mmap_read32(dev, page, reg + 2, &hi); -+ -+ *val = ((u64)hi << 16) | lo; -+ } else { -+ u32 lo; -+ u16 hi; -+ -+ b53_mmap_read32(dev, page, reg, &lo); -+ b53_mmap_read16(dev, page, reg + 4, &hi); -+ -+ *val = ((u64)hi << 32) | lo; -+ } -+ -+ return 0; -+} -+ -+static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ u32 hi, lo; -+ -+ if (WARN_ON(reg % 4)) -+ return -EINVAL; -+ -+ b53_mmap_read32(dev, page, reg, &lo); -+ b53_mmap_read32(dev, page, reg + 4, &hi); -+ -+ *val = ((u64)hi << 32) | lo; -+ -+ return 0; -+} -+ -+static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ -+ writeb(value, regs + (page << 8) + reg); -+ -+ return 0; -+} -+ -+static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, -+ u16 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ -+ if (WARN_ON(reg % 2)) -+ return -EINVAL; -+ -+ if (dev->pdata && dev->pdata->big_endian) -+ writew(cpu_to_be16((value)), regs + (page << 8) + reg); -+ else -+ writew(value, regs + (page << 8) + reg); -+ -+ return 0; -+} -+ -+static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, -+ u32 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ -+ if (WARN_ON(reg % 4)) -+ return -EINVAL; -+ -+ if (dev->pdata && dev->pdata->big_endian) -+ writel(cpu_to_be32((value)), regs + (page << 8) + reg); -+ else -+ writel(value, regs + (page << 8) + reg); -+ -+ return 0; -+} -+ -+static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ if (WARN_ON(reg % 2)) -+ return -EINVAL; -+ -+ if (reg % 4) { -+ u32 hi = (u32)(value >> 16); -+ u16 lo = (u16)value; -+ -+ b53_mmap_write16(dev, page, reg, lo); -+ b53_mmap_write32(dev, page, reg + 2, hi); -+ } else { -+ u16 hi = (u16)(value >> 32); -+ u32 lo = (u32)value; -+ -+ b53_mmap_write32(dev, page, reg, lo); -+ b53_mmap_write16(dev, page, reg + 4, hi); -+ } -+ -+ return 0; -+} -+ -+static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ u32 hi, lo; -+ -+ hi = (u32)(value >> 32); -+ lo = (u32)value; -+ -+ if (WARN_ON(reg % 4)) -+ return -EINVAL; -+ -+ b53_mmap_write32(dev, page, reg, lo); -+ b53_mmap_write32(dev, page, reg + 4, hi); -+ -+ return 0; -+} -+ -+static struct b53_io_ops b53_mmap_ops = { -+ .read8 = b53_mmap_read8, -+ .read16 = b53_mmap_read16, -+ .read32 = b53_mmap_read32, -+ .read48 = b53_mmap_read48, -+ .read64 = b53_mmap_read64, -+ .write8 = b53_mmap_write8, -+ .write16 = b53_mmap_write16, -+ .write32 = b53_mmap_write32, -+ .write48 = b53_mmap_write48, -+ .write64 = b53_mmap_write64, -+}; -+ -+static int b53_mmap_probe(struct platform_device *pdev) -+{ -+ struct b53_platform_data *pdata = pdev->dev.platform_data; -+ struct b53_device *dev; -+ -+ if (!pdata) -+ return -EINVAL; -+ -+ dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs); -+ if (!dev) -+ return -ENOMEM; -+ -+ if (pdata) -+ dev->pdata = pdata; -+ -+ platform_set_drvdata(pdev, dev); -+ -+ return b53_switch_register(dev); -+} -+ -+static int b53_mmap_remove(struct platform_device *pdev) -+{ -+ struct b53_device *dev = platform_get_drvdata(pdev); -+ -+ if (dev) -+ b53_switch_remove(dev); -+ -+ return 0; -+} -+ -+static struct platform_driver b53_mmap_driver = { -+ .probe = b53_mmap_probe, -+ .remove = b53_mmap_remove, -+ .driver = { -+ .name = "b53-switch", -+ }, -+}; -+ -+module_platform_driver(b53_mmap_driver); -+MODULE_AUTHOR("Jonas Gorski "); -+MODULE_DESCRIPTION("B53 MMAP access driver"); -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/phy/b53/b53_phy_fixup.c b/drivers/net/phy/b53/b53_phy_fixup.c -new file mode 100755 -index 0000000..72d1373 ---- /dev/null -+++ b/drivers/net/phy/b53/b53_phy_fixup.c -@@ -0,0 +1,55 @@ -+/* -+ * B53 PHY Fixup call -+ * -+ * Copyright (C) 2013 Jonas Gorski -+ * -+ * 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 -+#include -+#include -+ -+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */ -+ -+#define B53_BRCM_OUI_1 0x0143bc00 -+#define B53_BRCM_OUI_2 0x03625c00 -+#define B53_BRCM_OUI_3 0x00406000 -+ -+static int b53_phy_fixup(struct phy_device *dev) -+{ -+ u32 phy_id; -+ struct mii_bus *bus = dev->mdio.bus; -+ -+ if (dev->mdio.addr != B53_PSEUDO_PHY) -+ return 0; -+ -+ /* read the first port's id */ -+ phy_id = mdiobus_read(bus, 0, 2) << 16; -+ phy_id |= mdiobus_read(bus, 0, 3); -+ -+ if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 || -+ (phy_id & 0xfffffc00) == B53_BRCM_OUI_2 || -+ (phy_id & 0xfffffc00) == B53_BRCM_OUI_3) { -+ dev->phy_id = phy_id; -+ } -+ -+ return 0; -+} -+ -+int __init b53_phy_fixup_register(void) -+{ -+ return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup); -+} -+ -+subsys_initcall(b53_phy_fixup_register); -diff --git a/drivers/net/phy/b53/b53_priv.h b/drivers/net/phy/b53/b53_priv.h -new file mode 100755 -index 0000000..4336fdb ---- /dev/null -+++ b/drivers/net/phy/b53/b53_priv.h -@@ -0,0 +1,324 @@ -+/* -+ * B53 common definitions -+ * -+ * Copyright (C) 2011-2013 Jonas Gorski -+ * -+ * 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. -+ */ -+ -+#ifndef __B53_PRIV_H -+#define __B53_PRIV_H -+ -+#include -+#include -+#include -+ -+struct b53_device; -+ -+struct b53_io_ops { -+ int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value); -+ int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value); -+ int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value); -+ int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value); -+ int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value); -+ int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value); -+ int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value); -+ int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value); -+ int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value); -+ int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value); -+}; -+ -+enum { -+ BCM5325_DEVICE_ID = 0x25, -+ BCM5365_DEVICE_ID = 0x65, -+ BCM5395_DEVICE_ID = 0x95, -+ BCM5397_DEVICE_ID = 0x97, -+ BCM5398_DEVICE_ID = 0x98, -+ BCM53115_DEVICE_ID = 0x53115, -+ BCM53125_DEVICE_ID = 0x53125, -+ BCM53128_DEVICE_ID = 0x53128, -+ BCM63XX_DEVICE_ID = 0x6300, -+ BCM53010_DEVICE_ID = 0x53010, -+ BCM53011_DEVICE_ID = 0x53011, -+ BCM53012_DEVICE_ID = 0x53012, -+ BCM53018_DEVICE_ID = 0x53018, -+ BCM53019_DEVICE_ID = 0x53019, -+}; -+ -+#define B53_N_PORTS 9 -+#define B53_N_PORTS_25 6 -+ -+struct b53_vlan { -+ unsigned int members:B53_N_PORTS; -+ unsigned int untag:B53_N_PORTS; -+}; -+ -+struct b53_port { -+ unsigned int pvid:12; -+}; -+ -+struct b53_device { -+ struct switch_dev sw_dev; -+ struct b53_platform_data *pdata; -+ -+ struct mutex reg_mutex; -+ const struct b53_io_ops *ops; -+ -+ /* chip specific data */ -+ u32 chip_id; -+ u8 core_rev; -+ u8 vta_regs[3]; -+ u8 duplex_reg; -+ u8 jumbo_pm_reg; -+ u8 jumbo_size_reg; -+ int reset_gpio; -+ -+ /* used ports mask */ -+ u16 enabled_ports; -+ -+ /* connect specific data */ -+ u8 current_page; -+ struct device *dev; -+ void *priv; -+ -+ /* run time configuration */ -+ unsigned enable_vlan:1; -+ unsigned enable_jumbo:1; -+ unsigned allow_vid_4095:1; -+ -+ struct b53_port *ports; -+ struct b53_vlan *vlans; -+ -+ char *buf; -+}; -+ -+#define b53_for_each_port(dev, i) \ -+ for (i = 0; i < B53_N_PORTS; i++) \ -+ if (dev->enabled_ports & BIT(i)) -+ -+ -+ -+static inline int is5325(struct b53_device *dev) -+{ -+ return dev->chip_id == BCM5325_DEVICE_ID; -+} -+ -+static inline int is5365(struct b53_device *dev) -+{ -+#ifdef CONFIG_BCM47XX -+ return dev->chip_id == BCM5365_DEVICE_ID; -+#else -+ return 0; -+#endif -+} -+ -+static inline int is5397_98(struct b53_device *dev) -+{ -+ return dev->chip_id == BCM5397_DEVICE_ID || -+ dev->chip_id == BCM5398_DEVICE_ID; -+} -+ -+static inline int is539x(struct b53_device *dev) -+{ -+ return dev->chip_id == BCM5395_DEVICE_ID || -+ dev->chip_id == BCM5397_DEVICE_ID || -+ dev->chip_id == BCM5398_DEVICE_ID; -+} -+ -+static inline int is531x5(struct b53_device *dev) -+{ -+ return dev->chip_id == BCM53115_DEVICE_ID || -+ dev->chip_id == BCM53125_DEVICE_ID || -+ dev->chip_id == BCM53128_DEVICE_ID; -+} -+ -+static inline int is63xx(struct b53_device *dev) -+{ -+#ifdef CONFIG_BCM63XX -+ return dev->chip_id == BCM63XX_DEVICE_ID; -+#else -+ return 0; -+#endif -+} -+ -+static inline int is5301x(struct b53_device *dev) -+{ -+ return dev->chip_id == BCM53010_DEVICE_ID || -+ dev->chip_id == BCM53011_DEVICE_ID || -+ dev->chip_id == BCM53012_DEVICE_ID || -+ dev->chip_id == BCM53018_DEVICE_ID || -+ dev->chip_id == BCM53019_DEVICE_ID; -+} -+ -+#define B53_CPU_PORT_25 5 -+#define B53_CPU_PORT 8 -+ -+static inline int is_cpu_port(struct b53_device *dev, int port) -+{ -+ return dev->sw_dev.cpu_port == port; -+} -+ -+static inline struct b53_device *sw_to_b53(struct switch_dev *sw) -+{ -+ return container_of(sw, struct b53_device, sw_dev); -+} -+ -+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, -+ void *priv); -+ -+int b53_switch_detect(struct b53_device *dev); -+ -+int b53_switch_register(struct b53_device *dev); -+ -+static inline void b53_switch_remove(struct b53_device *dev) -+{ -+ unregister_switch(&dev->sw_dev); -+} -+ -+static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->read8(dev, page, reg, val); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->read16(dev, page, reg, val); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->read32(dev, page, reg, val); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->read48(dev, page, reg, val); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->read64(dev, page, reg, val); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->write8(dev, page, reg, value); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg, -+ u16 value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->write16(dev, page, reg, value); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg, -+ u32 value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->write32(dev, page, reg, value); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->write48(dev, page, reg, value); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ int ret; -+ -+ mutex_lock(&dev->reg_mutex); -+ ret = dev->ops->write64(dev, page, reg, value); -+ mutex_unlock(&dev->reg_mutex); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_BCM47XX -+ -+#include -+#include -+static inline int b53_switch_get_reset_gpio(struct b53_device *dev) -+{ -+ enum bcm47xx_board board = bcm47xx_board_get(); -+ -+ switch (board) { -+ case BCM47XX_BOARD_LINKSYS_WRT300NV11: -+ case BCM47XX_BOARD_LINKSYS_WRT310NV1: -+ return 8; -+ default: -+ return bcm47xx_nvram_gpio_pin("robo_reset"); -+ } -+} -+#else -+static inline int b53_switch_get_reset_gpio(struct b53_device *dev) -+{ -+ return -ENOENT; -+} -+#endif -+#endif -diff --git a/drivers/net/phy/b53/b53_regs.h b/drivers/net/phy/b53/b53_regs.h -new file mode 100755 -index 0000000..eef5c81 ---- /dev/null -+++ b/drivers/net/phy/b53/b53_regs.h -@@ -0,0 +1,347 @@ -+/* -+ * B53 register definitions -+ * -+ * Copyright (C) 2004 Broadcom Corporation -+ * Copyright (C) 2011-2013 Jonas Gorski -+ * -+ * 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. -+ */ -+ -+#ifndef __B53_REGS_H -+#define __B53_REGS_H -+ -+/* Management Port (SMP) Page offsets */ -+#define B53_CTRL_PAGE 0x00 /* Control */ -+#define B53_STAT_PAGE 0x01 /* Status */ -+#define B53_MGMT_PAGE 0x02 /* Management Mode */ -+#define B53_MIB_AC_PAGE 0x03 /* MIB Autocast */ -+#define B53_ARLCTRL_PAGE 0x04 /* ARL Control */ -+#define B53_ARLIO_PAGE 0x05 /* ARL Access */ -+#define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */ -+#define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */ -+ -+/* PHY Registers */ -+#define B53_PORT_MII_PAGE(i) (0x10 + (i)) /* Port i MII Registers */ -+#define B53_IM_PORT_PAGE 0x18 /* Inverse MII Port (to EMAC) */ -+#define B53_ALL_PORT_PAGE 0x19 /* All ports MII (broadcast) */ -+ -+/* MIB registers */ -+#define B53_MIB_PAGE(i) (0x20 + (i)) -+ -+/* Quality of Service (QoS) Registers */ -+#define B53_QOS_PAGE 0x30 -+ -+/* Port VLAN Page */ -+#define B53_PVLAN_PAGE 0x31 -+ -+/* VLAN Registers */ -+#define B53_VLAN_PAGE 0x34 -+ -+/* Jumbo Frame Registers */ -+#define B53_JUMBO_PAGE 0x40 -+ -+/* CFP Configuration Registers Page */ -+#define B53_CFP_PAGE 0xa1 -+ -+/************************************************************************* -+ * Control Page registers -+ *************************************************************************/ -+ -+/* Port Control Register (8 bit) */ -+#define B53_PORT_CTRL(i) (0x00 + (i)) -+#define PORT_CTRL_RX_DISABLE BIT(0) -+#define PORT_CTRL_TX_DISABLE BIT(1) -+#define PORT_CTRL_RX_BCST_EN BIT(2) /* Broadcast RX (P8 only) */ -+#define PORT_CTRL_RX_MCST_EN BIT(3) /* Multicast RX (P8 only) */ -+#define PORT_CTRL_RX_UCST_EN BIT(4) /* Unicast RX (P8 only) */ -+#define PORT_CTRL_STP_STATE_S 5 -+#define PORT_CTRL_STP_STATE_MASK (0x3 << PORT_CTRL_STP_STATE_S) -+ -+/* SMP Control Register (8 bit) */ -+#define B53_SMP_CTRL 0x0a -+ -+/* Switch Mode Control Register (8 bit) */ -+#define B53_SWITCH_MODE 0x0b -+#define SM_SW_FWD_MODE BIT(0) /* 1 = Managed Mode */ -+#define SM_SW_FWD_EN BIT(1) /* Forwarding Enable */ -+ -+/* IMP Port state override register (8 bit) */ -+#define B53_PORT_OVERRIDE_CTRL 0x0e -+#define PORT_OVERRIDE_LINK BIT(0) -+#define PORT_OVERRIDE_FULL_DUPLEX BIT(1) /* 0 = Half Duplex */ -+#define PORT_OVERRIDE_SPEED_S 2 -+#define PORT_OVERRIDE_SPEED_10M (0 << PORT_OVERRIDE_SPEED_S) -+#define PORT_OVERRIDE_SPEED_100M (1 << PORT_OVERRIDE_SPEED_S) -+#define PORT_OVERRIDE_SPEED_1000M (2 << PORT_OVERRIDE_SPEED_S) -+#define PORT_OVERRIDE_RV_MII_25 BIT(4) /* BCM5325 only */ -+#define PORT_OVERRIDE_RX_FLOW BIT(4) -+#define PORT_OVERRIDE_TX_FLOW BIT(5) -+#define PORT_OVERRIDE_SPEED_2000M BIT(6) /* BCM5301X only, requires setting 1000M */ -+#define PORT_OVERRIDE_EN BIT(7) /* Use the register contents */ -+ -+/* Power-down mode control */ -+#define B53_PD_MODE_CTRL_25 0x0f -+ -+/* IP Multicast control (8 bit) */ -+#define B53_IP_MULTICAST_CTRL 0x21 -+#define B53_IPMC_FWD_EN BIT(1) -+#define B53_UC_FWD_EN BIT(6) -+#define B53_MC_FWD_EN BIT(7) -+ -+/* (16 bit) */ -+#define B53_UC_FLOOD_MASK 0x32 -+#define B53_MC_FLOOD_MASK 0x34 -+#define B53_IPMC_FLOOD_MASK 0x36 -+ -+/* -+ * Override Ports 0-7 State on devices with xMII interfaces (8 bit) -+ * -+ * For port 8 still use B53_PORT_OVERRIDE_CTRL -+ * Please note that not all ports are available on every hardware, e.g. BCM5301X -+ * don't include overriding port 6, BCM63xx also have some limitations. -+ */ -+#define B53_GMII_PORT_OVERRIDE_CTRL(i) (0x58 + (i)) -+#define GMII_PO_LINK BIT(0) -+#define GMII_PO_FULL_DUPLEX BIT(1) /* 0 = Half Duplex */ -+#define GMII_PO_SPEED_S 2 -+#define GMII_PO_SPEED_10M (0 << GMII_PO_SPEED_S) -+#define GMII_PO_SPEED_100M (1 << GMII_PO_SPEED_S) -+#define GMII_PO_SPEED_1000M (2 << GMII_PO_SPEED_S) -+#define GMII_PO_RX_FLOW BIT(4) -+#define GMII_PO_TX_FLOW BIT(5) -+#define GMII_PO_EN BIT(6) /* Use the register contents */ -+#define GMII_PO_SPEED_2000M BIT(7) /* BCM5301X only, requires setting 1000M */ -+ -+/* Software reset register (8 bit) */ -+#define B53_SOFTRESET 0x79 -+ -+/* Fast Aging Control register (8 bit) */ -+#define B53_FAST_AGE_CTRL 0x88 -+#define FAST_AGE_STATIC BIT(0) -+#define FAST_AGE_DYNAMIC BIT(1) -+#define FAST_AGE_PORT BIT(2) -+#define FAST_AGE_VLAN BIT(3) -+#define FAST_AGE_STP BIT(4) -+#define FAST_AGE_MC BIT(5) -+#define FAST_AGE_DONE BIT(7) -+ -+/************************************************************************* -+ * Status Page registers -+ *************************************************************************/ -+ -+/* Link Status Summary Register (16bit) */ -+#define B53_LINK_STAT 0x00 -+ -+/* Link Status Change Register (16 bit) */ -+#define B53_LINK_STAT_CHANGE 0x02 -+ -+/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */ -+#define B53_SPEED_STAT 0x04 -+#define SPEED_PORT_FE(reg, port) (((reg) >> (port)) & 1) -+#define SPEED_PORT_GE(reg, port) (((reg) >> 2 * (port)) & 3) -+#define SPEED_STAT_10M 0 -+#define SPEED_STAT_100M 1 -+#define SPEED_STAT_1000M 2 -+ -+/* Duplex Status Summary (16 bit) */ -+#define B53_DUPLEX_STAT_FE 0x06 -+#define B53_DUPLEX_STAT_GE 0x08 -+#define B53_DUPLEX_STAT_63XX 0x0c -+ -+/* Revision ID register for BCM5325 */ -+#define B53_REV_ID_25 0x50 -+ -+/* Strap Value (48 bit) */ -+#define B53_STRAP_VALUE 0x70 -+#define SV_GMII_CTRL_115 BIT(27) -+ -+/************************************************************************* -+ * Management Mode Page Registers -+ *************************************************************************/ -+ -+/* Global Management Config Register (8 bit) */ -+#define B53_GLOBAL_CONFIG 0x00 -+#define GC_RESET_MIB 0x01 -+#define GC_RX_BPDU_EN 0x02 -+#define GC_MIB_AC_HDR_EN 0x10 -+#define GC_MIB_AC_EN 0x20 -+#define GC_FRM_MGMT_PORT_M 0xC0 -+#define GC_FRM_MGMT_PORT_04 0x00 -+#define GC_FRM_MGMT_PORT_MII 0x80 -+ -+/* Broadcom Header control register (8 bit) */ -+#define B53_BRCM_HDR 0x03 -+#define BRCM_HDR_EN BIT(0) /* Enable tagging on IMP port */ -+ -+/* Device ID register (8 or 32 bit) */ -+#define B53_DEVICE_ID 0x30 -+ -+/* Revision ID register (8 bit) */ -+#define B53_REV_ID 0x40 -+ -+/************************************************************************* -+ * ARL Access Page Registers -+ *************************************************************************/ -+ -+/* VLAN Table Access Register (8 bit) */ -+#define B53_VT_ACCESS 0x80 -+#define B53_VT_ACCESS_9798 0x60 /* for BCM5397/BCM5398 */ -+#define B53_VT_ACCESS_63XX 0x60 /* for BCM6328/62/68 */ -+#define VTA_CMD_WRITE 0 -+#define VTA_CMD_READ 1 -+#define VTA_CMD_CLEAR 2 -+#define VTA_START_CMD BIT(7) -+ -+/* VLAN Table Index Register (16 bit) */ -+#define B53_VT_INDEX 0x81 -+#define B53_VT_INDEX_9798 0x61 -+#define B53_VT_INDEX_63XX 0x62 -+ -+/* VLAN Table Entry Register (32 bit) */ -+#define B53_VT_ENTRY 0x83 -+#define B53_VT_ENTRY_9798 0x63 -+#define B53_VT_ENTRY_63XX 0x64 -+#define VTE_MEMBERS 0x1ff -+#define VTE_UNTAG_S 9 -+#define VTE_UNTAG (0x1ff << 9) -+ -+/************************************************************************* -+ * Port VLAN Registers -+ *************************************************************************/ -+ -+/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */ -+#define B53_PVLAN_PORT_MASK(i) ((i) * 2) -+ -+/************************************************************************* -+ * 802.1Q Page Registers -+ *************************************************************************/ -+ -+/* Global QoS Control (8 bit) */ -+#define B53_QOS_GLOBAL_CTL 0x00 -+ -+/* Enable 802.1Q for individual Ports (16 bit) */ -+#define B53_802_1P_EN 0x04 -+ -+/************************************************************************* -+ * VLAN Page Registers -+ *************************************************************************/ -+ -+/* VLAN Control 0 (8 bit) */ -+#define B53_VLAN_CTRL0 0x00 -+#define VC0_8021PF_CTRL_MASK 0x3 -+#define VC0_8021PF_CTRL_NONE 0x0 -+#define VC0_8021PF_CTRL_CHANGE_PRI 0x1 -+#define VC0_8021PF_CTRL_CHANGE_VID 0x2 -+#define VC0_8021PF_CTRL_CHANGE_BOTH 0x3 -+#define VC0_8021QF_CTRL_MASK 0xc -+#define VC0_8021QF_CTRL_CHANGE_PRI 0x1 -+#define VC0_8021QF_CTRL_CHANGE_VID 0x2 -+#define VC0_8021QF_CTRL_CHANGE_BOTH 0x3 -+#define VC0_RESERVED_1 BIT(1) -+#define VC0_DROP_VID_MISS BIT(4) -+#define VC0_VID_HASH_VID BIT(5) -+#define VC0_VID_CHK_EN BIT(6) /* Use VID,DA or VID,SA */ -+#define VC0_VLAN_EN BIT(7) /* 802.1Q VLAN Enabled */ -+ -+/* VLAN Control 1 (8 bit) */ -+#define B53_VLAN_CTRL1 0x01 -+#define VC1_RX_MCST_TAG_EN BIT(1) -+#define VC1_RX_MCST_FWD_EN BIT(2) -+#define VC1_RX_MCST_UNTAG_EN BIT(3) -+ -+/* VLAN Control 2 (8 bit) */ -+#define B53_VLAN_CTRL2 0x02 -+ -+/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */ -+#define B53_VLAN_CTRL3 0x03 -+#define B53_VLAN_CTRL3_63XX 0x04 -+#define VC3_MAXSIZE_1532 BIT(6) /* 5325 only */ -+#define VC3_HIGH_8BIT_EN BIT(7) /* 5325 only */ -+ -+/* VLAN Control 4 (8 bit) */ -+#define B53_VLAN_CTRL4 0x05 -+#define B53_VLAN_CTRL4_25 0x04 -+#define B53_VLAN_CTRL4_63XX 0x06 -+#define VC4_ING_VID_CHECK_S 6 -+#define VC4_ING_VID_CHECK_MASK (0x3 << VC4_ING_VID_CHECK_S) -+#define VC4_ING_VID_VIO_FWD 0 /* forward, but do not learn */ -+#define VC4_ING_VID_VIO_DROP 1 /* drop VID violations */ -+#define VC4_NO_ING_VID_CHK 2 /* do not check */ -+#define VC4_ING_VID_VIO_TO_IMP 3 /* redirect to MII port */ -+ -+/* VLAN Control 5 (8 bit) */ -+#define B53_VLAN_CTRL5 0x06 -+#define B53_VLAN_CTRL5_25 0x05 -+#define B53_VLAN_CTRL5_63XX 0x07 -+#define VC5_VID_FFF_EN BIT(2) -+#define VC5_DROP_VTABLE_MISS BIT(3) -+ -+/* VLAN Control 6 (8 bit) */ -+#define B53_VLAN_CTRL6 0x07 -+#define B53_VLAN_CTRL6_63XX 0x08 -+ -+/* VLAN Table Access Register (16 bit) */ -+#define B53_VLAN_TABLE_ACCESS_25 0x06 /* BCM5325E/5350 */ -+#define B53_VLAN_TABLE_ACCESS_65 0x08 /* BCM5365 */ -+#define VTA_VID_LOW_MASK_25 0xf -+#define VTA_VID_LOW_MASK_65 0xff -+#define VTA_VID_HIGH_S_25 4 -+#define VTA_VID_HIGH_S_65 8 -+#define VTA_VID_HIGH_MASK_25 (0xff << VTA_VID_HIGH_S_25E) -+#define VTA_VID_HIGH_MASK_65 (0xf << VTA_VID_HIGH_S_65) -+#define VTA_RW_STATE BIT(12) -+#define VTA_RW_STATE_RD 0 -+#define VTA_RW_STATE_WR BIT(12) -+#define VTA_RW_OP_EN BIT(13) -+ -+/* VLAN Read/Write Registers for (16/32 bit) */ -+#define B53_VLAN_WRITE_25 0x08 -+#define B53_VLAN_WRITE_65 0x0a -+#define B53_VLAN_READ 0x0c -+#define VA_MEMBER_MASK 0x3f -+#define VA_UNTAG_S_25 6 -+#define VA_UNTAG_MASK_25 0x3f -+#define VA_UNTAG_S_65 7 -+#define VA_UNTAG_MASK_65 0x1f -+#define VA_VID_HIGH_S 12 -+#define VA_VID_HIGH_MASK (0xffff << VA_VID_HIGH_S) -+#define VA_VALID_25 BIT(20) -+#define VA_VALID_25_R4 BIT(24) -+#define VA_VALID_65 BIT(14) -+ -+/* VLAN Port Default Tag (16 bit) */ -+#define B53_VLAN_PORT_DEF_TAG(i) (0x10 + 2 * (i)) -+ -+/************************************************************************* -+ * Jumbo Frame Page Registers -+ *************************************************************************/ -+ -+/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */ -+#define B53_JUMBO_PORT_MASK 0x01 -+#define B53_JUMBO_PORT_MASK_63XX 0x04 -+#define JPM_10_100_JUMBO_EN BIT(24) /* GigE always enabled */ -+ -+/* Good Frame Max Size without 802.1Q TAG (16 bit) */ -+#define B53_JUMBO_MAX_SIZE 0x05 -+#define B53_JUMBO_MAX_SIZE_63XX 0x08 -+#define JMS_MIN_SIZE 1518 -+#define JMS_MAX_SIZE 9724 -+ -+/************************************************************************* -+ * CFP Configuration Page Registers -+ *************************************************************************/ -+ -+/* CFP Control Register with ports map (8 bit) */ -+#define B53_CFP_CTRL 0x00 -+ -+#endif /* !__B53_REGS_H */ -diff --git a/drivers/net/phy/b53/b53_spi.c b/drivers/net/phy/b53/b53_spi.c -new file mode 100755 -index 0000000..469a8dd ---- /dev/null -+++ b/drivers/net/phy/b53/b53_spi.c -@@ -0,0 +1,330 @@ -+/* -+ * B53 register access through SPI -+ * -+ * Copyright (C) 2011-2013 Jonas Gorski -+ * -+ * 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 -+ -+#include -+#include -+#include -+#include -+ -+#include "b53_priv.h" -+ -+#define B53_SPI_DATA 0xf0 -+ -+#define B53_SPI_STATUS 0xfe -+#define B53_SPI_CMD_SPIF BIT(7) -+#define B53_SPI_CMD_RACK BIT(5) -+ -+#define B53_SPI_CMD_READ 0x00 -+#define B53_SPI_CMD_WRITE 0x01 -+#define B53_SPI_CMD_NORMAL 0x60 -+#define B53_SPI_CMD_FAST 0x10 -+ -+#define B53_SPI_PAGE_SELECT 0xff -+ -+static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val, -+ unsigned len) -+{ -+ u8 txbuf[2]; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ; -+ txbuf[1] = reg; -+ -+ return spi_write_then_read(spi, txbuf, 2, val, len); -+} -+ -+static inline int b53_spi_clear_status(struct spi_device *spi) -+{ -+ unsigned int i; -+ u8 rxbuf; -+ int ret; -+ -+ for (i = 0; i < 10; i++) { -+ ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); -+ if (ret) -+ return ret; -+ -+ if (!(rxbuf & B53_SPI_CMD_SPIF)) -+ break; -+ -+ mdelay(1); -+ } -+ -+ if (i == 10) -+ return -EIO; -+ -+ return 0; -+} -+ -+static inline int b53_spi_set_page(struct spi_device *spi, u8 page) -+{ -+ u8 txbuf[3]; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; -+ txbuf[1] = B53_SPI_PAGE_SELECT; -+ txbuf[2] = page; -+ -+ return spi_write(spi, txbuf, sizeof(txbuf)); -+} -+ -+static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page) -+{ -+ int ret = b53_spi_clear_status(spi); -+ -+ if (ret) -+ return ret; -+ -+ return b53_spi_set_page(spi, page); -+} -+ -+static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg) -+{ -+ u8 rxbuf; -+ int retry_count; -+ int ret; -+ -+ ret = b53_spi_read_reg(spi, reg, &rxbuf, 1); -+ if (ret) -+ return ret; -+ -+ for (retry_count = 0; retry_count < 10; retry_count++) { -+ ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); -+ if (ret) -+ return ret; -+ -+ if (rxbuf & B53_SPI_CMD_RACK) -+ break; -+ -+ mdelay(1); -+ } -+ -+ if (retry_count == 10) -+ return -EIO; -+ -+ return 0; -+} -+ -+static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data, -+ unsigned len) -+{ -+ struct spi_device *spi = dev->priv; -+ int ret; -+ -+ ret = b53_prepare_reg_access(spi, page); -+ if (ret) -+ return ret; -+ -+ ret = b53_spi_prepare_reg_read(spi, reg); -+ if (ret) -+ return ret; -+ -+ return b53_spi_read_reg(spi, B53_SPI_DATA, data, len); -+} -+ -+static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) -+{ -+ return b53_spi_read(dev, page, reg, val, 1); -+} -+ -+static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) -+{ -+ int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2); -+ -+ if (!ret) -+ *val = le16_to_cpu(*val); -+ -+ return ret; -+} -+ -+static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) -+{ -+ int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4); -+ -+ if (!ret) -+ *val = le32_to_cpu(*val); -+ -+ return ret; -+} -+ -+static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ int ret; -+ -+ *val = 0; -+ ret = b53_spi_read(dev, page, reg, (u8 *)val, 6); -+ if (!ret) -+ *val = le64_to_cpu(*val); -+ -+ return ret; -+} -+ -+static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8); -+ -+ if (!ret) -+ *val = le64_to_cpu(*val); -+ -+ return ret; -+} -+ -+static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) -+{ -+ struct spi_device *spi = dev->priv; -+ int ret; -+ u8 txbuf[3]; -+ -+ ret = b53_prepare_reg_access(spi, page); -+ if (ret) -+ return ret; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; -+ txbuf[1] = reg; -+ txbuf[2] = value; -+ -+ return spi_write(spi, txbuf, sizeof(txbuf)); -+} -+ -+static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value) -+{ -+ struct spi_device *spi = dev->priv; -+ int ret; -+ u8 txbuf[4]; -+ -+ ret = b53_prepare_reg_access(spi, page); -+ if (ret) -+ return ret; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; -+ txbuf[1] = reg; -+ put_unaligned_le16(value, &txbuf[2]); -+ -+ return spi_write(spi, txbuf, sizeof(txbuf)); -+} -+ -+static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value) -+{ -+ struct spi_device *spi = dev->priv; -+ int ret; -+ u8 txbuf[6]; -+ -+ ret = b53_prepare_reg_access(spi, page); -+ if (ret) -+ return ret; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; -+ txbuf[1] = reg; -+ put_unaligned_le32(value, &txbuf[2]); -+ -+ return spi_write(spi, txbuf, sizeof(txbuf)); -+} -+ -+static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value) -+{ -+ struct spi_device *spi = dev->priv; -+ int ret; -+ u8 txbuf[10]; -+ -+ ret = b53_prepare_reg_access(spi, page); -+ if (ret) -+ return ret; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; -+ txbuf[1] = reg; -+ put_unaligned_le64(value, &txbuf[2]); -+ -+ return spi_write(spi, txbuf, sizeof(txbuf) - 2); -+} -+ -+static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value) -+{ -+ struct spi_device *spi = dev->priv; -+ int ret; -+ u8 txbuf[10]; -+ -+ ret = b53_prepare_reg_access(spi, page); -+ if (ret) -+ return ret; -+ -+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; -+ txbuf[1] = reg; -+ put_unaligned_le64(value, &txbuf[2]); -+ -+ return spi_write(spi, txbuf, sizeof(txbuf)); -+} -+ -+static struct b53_io_ops b53_spi_ops = { -+ .read8 = b53_spi_read8, -+ .read16 = b53_spi_read16, -+ .read32 = b53_spi_read32, -+ .read48 = b53_spi_read48, -+ .read64 = b53_spi_read64, -+ .write8 = b53_spi_write8, -+ .write16 = b53_spi_write16, -+ .write32 = b53_spi_write32, -+ .write48 = b53_spi_write48, -+ .write64 = b53_spi_write64, -+}; -+ -+static int b53_spi_probe(struct spi_device *spi) -+{ -+ struct b53_device *dev; -+ int ret; -+ -+ dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi); -+ if (!dev) -+ return -ENOMEM; -+ -+ if (spi->dev.platform_data) -+ dev->pdata = spi->dev.platform_data; -+ -+ ret = b53_switch_register(dev); -+ if (ret) -+ return ret; -+ -+ spi_set_drvdata(spi, dev); -+ -+ return 0; -+} -+ -+static int b53_spi_remove(struct spi_device *spi) -+{ -+ struct b53_device *dev = spi_get_drvdata(spi); -+ -+ if (dev) -+ b53_switch_remove(dev); -+ -+ return 0; -+} -+ -+static struct spi_driver b53_spi_driver = { -+ .driver = { -+ .name = "b53-switch", -+ .bus = &spi_bus_type, -+ .owner = THIS_MODULE, -+ }, -+ .probe = b53_spi_probe, -+ .remove = b53_spi_remove, -+}; -+ -+module_spi_driver(b53_spi_driver); -+ -+MODULE_AUTHOR("Jonas Gorski "); -+MODULE_DESCRIPTION("B53 SPI access driver"); -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/phy/b53/b53_srab.c b/drivers/net/phy/b53/b53_srab.c -new file mode 100755 -index 0000000..012daa3 ---- /dev/null -+++ b/drivers/net/phy/b53/b53_srab.c -@@ -0,0 +1,378 @@ -+/* -+ * B53 register access through Switch Register Access Bridge Registers -+ * -+ * Copyright (C) 2013 Hauke Mehrtens -+ * -+ * 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 -+#include -+#include -+#include -+ -+#include "b53_priv.h" -+ -+/* command and status register of the SRAB */ -+#define B53_SRAB_CMDSTAT 0x2c -+#define B53_SRAB_CMDSTAT_RST BIT(2) -+#define B53_SRAB_CMDSTAT_WRITE BIT(1) -+#define B53_SRAB_CMDSTAT_GORDYN BIT(0) -+#define B53_SRAB_CMDSTAT_PAGE 24 -+#define B53_SRAB_CMDSTAT_REG 16 -+ -+/* high order word of write data to switch registe */ -+#define B53_SRAB_WD_H 0x30 -+ -+/* low order word of write data to switch registe */ -+#define B53_SRAB_WD_L 0x34 -+ -+/* high order word of read data from switch register */ -+#define B53_SRAB_RD_H 0x38 -+ -+/* low order word of read data from switch register */ -+#define B53_SRAB_RD_L 0x3c -+ -+/* command and status register of the SRAB */ -+#define B53_SRAB_CTRLS 0x40 -+#define B53_SRAB_CTRLS_RCAREQ BIT(3) -+#define B53_SRAB_CTRLS_RCAGNT BIT(4) -+#define B53_SRAB_CTRLS_SW_INIT_DONE BIT(6) -+ -+/* the register captures interrupt pulses from the switch */ -+#define B53_SRAB_INTR 0x44 -+ -+static int b53_srab_request_grant(struct b53_device *dev) -+{ -+ u8 __iomem *regs = dev->priv; -+ u32 ctrls; -+ int i; -+ -+ ctrls = readl(regs + B53_SRAB_CTRLS); -+ ctrls |= B53_SRAB_CTRLS_RCAREQ; -+ writel(ctrls, regs + B53_SRAB_CTRLS); -+ -+ for (i = 0; i < 20; i++) { -+ ctrls = readl(regs + B53_SRAB_CTRLS); -+ if (ctrls & B53_SRAB_CTRLS_RCAGNT) -+ break; -+ usleep_range(10, 100); -+ } -+ if (WARN_ON(i == 5)) -+ return -EIO; -+ -+ return 0; -+} -+ -+static void b53_srab_release_grant(struct b53_device *dev) -+{ -+ u8 __iomem *regs = dev->priv; -+ u32 ctrls; -+ -+ ctrls = readl(regs + B53_SRAB_CTRLS); -+ ctrls &= ~B53_SRAB_CTRLS_RCAREQ; -+ writel(ctrls, regs + B53_SRAB_CTRLS); -+} -+ -+static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op) -+{ -+ int i; -+ u32 cmdstat; -+ u8 __iomem *regs = dev->priv; -+ -+ /* set register address */ -+ cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) | -+ (reg << B53_SRAB_CMDSTAT_REG) | -+ B53_SRAB_CMDSTAT_GORDYN | -+ op; -+ writel(cmdstat, regs + B53_SRAB_CMDSTAT); -+ -+ /* check if operation completed */ -+ for (i = 0; i < 5; ++i) { -+ cmdstat = readl(regs + B53_SRAB_CMDSTAT); -+ if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN)) -+ break; -+ usleep_range(10, 100); -+ } -+ -+ if (WARN_ON(i == 5)) -+ return -EIO; -+ -+ return 0; -+} -+ -+static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ ret = b53_srab_op(dev, page, reg, 0); -+ if (ret) -+ goto err; -+ -+ *val = readl(regs + B53_SRAB_RD_L) & 0xff; -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ ret = b53_srab_op(dev, page, reg, 0); -+ if (ret) -+ goto err; -+ -+ *val = readl(regs + B53_SRAB_RD_L) & 0xffff; -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ ret = b53_srab_op(dev, page, reg, 0); -+ if (ret) -+ goto err; -+ -+ *val = readl(regs + B53_SRAB_RD_L); -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ ret = b53_srab_op(dev, page, reg, 0); -+ if (ret) -+ goto err; -+ -+ *val = readl(regs + B53_SRAB_RD_L); -+ *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32; -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ ret = b53_srab_op(dev, page, reg, 0); -+ if (ret) -+ goto err; -+ -+ *val = readl(regs + B53_SRAB_RD_L); -+ *val += (u64)readl(regs + B53_SRAB_RD_H) << 32; -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ writel(value, regs + B53_SRAB_WD_L); -+ -+ ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg, -+ u16 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ writel(value, regs + B53_SRAB_WD_L); -+ -+ ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg, -+ u32 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ writel(value, regs + B53_SRAB_WD_L); -+ -+ ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+ -+} -+ -+static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ writel((u32)value, regs + B53_SRAB_WD_L); -+ writel((u16)(value >> 32), regs + B53_SRAB_WD_H); -+ -+ ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+ -+} -+ -+static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg, -+ u64 value) -+{ -+ u8 __iomem *regs = dev->priv; -+ int ret = 0; -+ -+ ret = b53_srab_request_grant(dev); -+ if (ret) -+ goto err; -+ -+ writel((u32)value, regs + B53_SRAB_WD_L); -+ writel((u32)(value >> 32), regs + B53_SRAB_WD_H); -+ -+ ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); -+ -+err: -+ b53_srab_release_grant(dev); -+ -+ return ret; -+} -+ -+static struct b53_io_ops b53_srab_ops = { -+ .read8 = b53_srab_read8, -+ .read16 = b53_srab_read16, -+ .read32 = b53_srab_read32, -+ .read48 = b53_srab_read48, -+ .read64 = b53_srab_read64, -+ .write8 = b53_srab_write8, -+ .write16 = b53_srab_write16, -+ .write32 = b53_srab_write32, -+ .write48 = b53_srab_write48, -+ .write64 = b53_srab_write64, -+}; -+ -+static int b53_srab_probe(struct platform_device *pdev) -+{ -+ struct b53_platform_data *pdata = pdev->dev.platform_data; -+ struct b53_device *dev; -+ -+ if (!pdata) -+ return -EINVAL; -+ -+ dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs); -+ if (!dev) -+ return -ENOMEM; -+ -+ if (pdata) -+ dev->pdata = pdata; -+ -+ platform_set_drvdata(pdev, dev); -+ -+ return b53_switch_register(dev); -+} -+ -+static int b53_srab_remove(struct platform_device *pdev) -+{ -+ struct b53_device *dev = platform_get_drvdata(pdev); -+ -+ if (dev) -+ b53_switch_remove(dev); -+ -+ return 0; -+} -+ -+static struct platform_driver b53_srab_driver = { -+ .probe = b53_srab_probe, -+ .remove = b53_srab_remove, -+ .driver = { -+ .name = "b53-srab-switch", -+ }, -+}; -+ -+module_platform_driver(b53_srab_driver); -+MODULE_AUTHOR("Hauke Mehrtens "); -+MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver"); -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/net/phy/swconfig.c b/drivers/net/phy/swconfig.c -new file mode 100755 -index 0000000..6bb3be1 ---- /dev/null -+++ b/drivers/net/phy/swconfig.c -@@ -0,0 +1,1153 @@ -+/* -+ * swconfig.c: Switch configuration API -+ * -+ * Copyright (C) 2008 Felix Fietkau -+ * -+ * 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SWCONFIG_DEVNAME "switch%d" -+ -+#include "swconfig_leds.c" -+ -+MODULE_AUTHOR("Felix Fietkau "); -+MODULE_LICENSE("GPL"); -+ -+static int swdev_id; -+static struct list_head swdevs; -+static DEFINE_SPINLOCK(swdevs_lock); -+struct swconfig_callback; -+ -+struct swconfig_callback { -+ struct sk_buff *msg; -+ struct genlmsghdr *hdr; -+ struct genl_info *info; -+ int cmd; -+ -+ /* callback for filling in the message data */ -+ int (*fill)(struct swconfig_callback *cb, void *arg); -+ -+ /* callback for closing the message before sending it */ -+ int (*close)(struct swconfig_callback *cb, void *arg); -+ -+ struct nlattr *nest[4]; -+ int args[4]; -+}; -+ -+/* defaults */ -+ -+static int -+swconfig_get_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ int ret; -+ if (val->port_vlan >= dev->vlans) -+ return -EINVAL; -+ -+ if (!dev->ops->get_vlan_ports) -+ return -EOPNOTSUPP; -+ -+ ret = dev->ops->get_vlan_ports(dev, val); -+ return ret; -+} -+ -+static int -+swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct switch_port *ports = val->value.ports; -+ const struct switch_dev_ops *ops = dev->ops; -+ int i; -+ -+ if (val->port_vlan >= dev->vlans) -+ return -EINVAL; -+ -+ /* validate ports */ -+ if (val->len > dev->ports) -+ return -EINVAL; -+ -+ if (!ops->set_vlan_ports) -+ return -EOPNOTSUPP; -+ -+ for (i = 0; i < val->len; i++) { -+ if (ports[i].id >= dev->ports) -+ return -EINVAL; -+ -+ if (ops->set_port_pvid && -+ !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED))) -+ ops->set_port_pvid(dev, ports[i].id, val->port_vlan); -+ } -+ -+ return ops->set_vlan_ports(dev, val); -+} -+ -+static int -+swconfig_set_pvid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ if (val->port_vlan >= dev->ports) -+ return -EINVAL; -+ -+ if (!dev->ops->set_port_pvid) -+ return -EOPNOTSUPP; -+ -+ return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i); -+} -+ -+static int -+swconfig_get_pvid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ if (val->port_vlan >= dev->ports) -+ return -EINVAL; -+ -+ if (!dev->ops->get_port_pvid) -+ return -EOPNOTSUPP; -+ -+ return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i); -+} -+ -+static const char * -+swconfig_speed_str(enum switch_port_speed speed) -+{ -+ switch (speed) { -+ case SWITCH_PORT_SPEED_10: -+ return "10baseT"; -+ case SWITCH_PORT_SPEED_100: -+ return "100baseT"; -+ case SWITCH_PORT_SPEED_1000: -+ return "1000baseT"; -+ default: -+ break; -+ } -+ -+ return "unknown"; -+} -+ -+static int -+swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct switch_port_link link; -+ int len; -+ int ret; -+ -+ if (val->port_vlan >= dev->ports) -+ return -EINVAL; -+ -+ if (!dev->ops->get_port_link) -+ return -EOPNOTSUPP; -+ -+ memset(&link, 0, sizeof(link)); -+ ret = dev->ops->get_port_link(dev, val->port_vlan, &link); -+ if (ret) -+ return ret; -+ -+ memset(dev->buf, 0, sizeof(dev->buf)); -+ -+ if (link.link) -+ len = snprintf(dev->buf, sizeof(dev->buf), -+ "port:%d link:up speed:%s %s-duplex %s%s%s%s%s", -+ val->port_vlan, -+ swconfig_speed_str(link.speed), -+ link.duplex ? "full" : "half", -+ link.tx_flow ? "txflow " : "", -+ link.rx_flow ? "rxflow " : "", -+ link.eee & ADVERTISED_100baseT_Full ? "eee100 " : "", -+ link.eee & ADVERTISED_1000baseT_Full ? "eee1000 " : "", -+ link.aneg ? "auto" : ""); -+ else -+ len = snprintf(dev->buf, sizeof(dev->buf), "port:%d link:down", -+ val->port_vlan); -+ -+ val->value.s = dev->buf; -+ val->len = len; -+ -+ return 0; -+} -+ -+static int -+swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ /* don't complain if not supported by the switch driver */ -+ if (!dev->ops->apply_config) -+ return 0; -+ -+ return dev->ops->apply_config(dev); -+} -+ -+static int -+swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ /* don't complain if not supported by the switch driver */ -+ if (!dev->ops->reset_switch) -+ return 0; -+ -+ return dev->ops->reset_switch(dev); -+} -+ -+enum global_defaults { -+ GLOBAL_APPLY, -+ GLOBAL_RESET, -+}; -+ -+enum vlan_defaults { -+ VLAN_PORTS, -+}; -+ -+enum port_defaults { -+ PORT_PVID, -+ PORT_LINK, -+}; -+ -+static struct switch_attr default_global[] = { -+ [GLOBAL_APPLY] = { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "apply", -+ .description = "Activate changes in the hardware", -+ .set = swconfig_apply_config, -+ }, -+ [GLOBAL_RESET] = { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "reset", -+ .description = "Reset the switch", -+ .set = swconfig_reset_switch, -+ } -+}; -+ -+static struct switch_attr default_port[] = { -+ [PORT_PVID] = { -+ .type = SWITCH_TYPE_INT, -+ .name = "pvid", -+ .description = "Primary VLAN ID", -+ .set = swconfig_set_pvid, -+ .get = swconfig_get_pvid, -+ }, -+ [PORT_LINK] = { -+ .type = SWITCH_TYPE_STRING, -+ .name = "link", -+ .description = "Get port link information", -+ .set = NULL, -+ .get = swconfig_get_link, -+ } -+}; -+ -+static struct switch_attr default_vlan[] = { -+ [VLAN_PORTS] = { -+ .type = SWITCH_TYPE_PORTS, -+ .name = "ports", -+ .description = "VLAN port mapping", -+ .set = swconfig_set_vlan_ports, -+ .get = swconfig_get_vlan_ports, -+ }, -+}; -+ -+static const struct switch_attr * -+swconfig_find_attr_by_name(const struct switch_attrlist *alist, -+ const char *name) -+{ -+ int i; -+ -+ for (i = 0; i < alist->n_attr; i++) -+ if (strcmp(name, alist->attr[i].name) == 0) -+ return &alist->attr[i]; -+ -+ return NULL; -+} -+ -+static void swconfig_defaults_init(struct switch_dev *dev) -+{ -+ const struct switch_dev_ops *ops = dev->ops; -+ -+ dev->def_global = 0; -+ dev->def_vlan = 0; -+ dev->def_port = 0; -+ -+ if (ops->get_vlan_ports || ops->set_vlan_ports) -+ set_bit(VLAN_PORTS, &dev->def_vlan); -+ -+ if (ops->get_port_pvid || ops->set_port_pvid) -+ set_bit(PORT_PVID, &dev->def_port); -+ -+ if (ops->get_port_link && -+ !swconfig_find_attr_by_name(&ops->attr_port, "link")) -+ set_bit(PORT_LINK, &dev->def_port); -+ -+ /* always present, can be no-op */ -+ set_bit(GLOBAL_APPLY, &dev->def_global); -+ set_bit(GLOBAL_RESET, &dev->def_global); -+} -+ -+ -+static struct genl_family switch_fam = { -+ .id = GENL_ID_GENERATE, -+ .name = "switch", -+ .hdrsize = 0, -+ .version = 1, -+ .maxattr = SWITCH_ATTR_MAX, -+}; -+ -+static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = { -+ [SWITCH_ATTR_ID] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING }, -+ [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED }, -+ [SWITCH_ATTR_TYPE] = { .type = NLA_U32 }, -+}; -+ -+static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = { -+ [SWITCH_PORT_ID] = { .type = NLA_U32 }, -+ [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG }, -+}; -+ -+static inline void -+swconfig_lock(void) -+{ -+ spin_lock(&swdevs_lock); -+} -+ -+static inline void -+swconfig_unlock(void) -+{ -+ spin_unlock(&swdevs_lock); -+} -+ -+static struct switch_dev * -+swconfig_get_dev(struct genl_info *info) -+{ -+ struct switch_dev *dev = NULL; -+ struct switch_dev *p; -+ int id; -+ -+ if (!info->attrs[SWITCH_ATTR_ID]) -+ goto done; -+ -+ id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]); -+ swconfig_lock(); -+ list_for_each_entry(p, &swdevs, dev_list) { -+ if (id != p->id) -+ continue; -+ -+ dev = p; -+ break; -+ } -+ if (dev) -+ mutex_lock(&dev->sw_mutex); -+ else -+ pr_debug("device %d not found\n", id); -+ swconfig_unlock(); -+done: -+ return dev; -+} -+ -+static inline void -+swconfig_put_dev(struct switch_dev *dev) -+{ -+ mutex_unlock(&dev->sw_mutex); -+} -+ -+static int -+swconfig_dump_attr(struct swconfig_callback *cb, void *arg) -+{ -+ struct switch_attr *op = arg; -+ struct genl_info *info = cb->info; -+ struct sk_buff *msg = cb->msg; -+ int id = cb->args[0]; -+ void *hdr; -+ -+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam, -+ NLM_F_MULTI, SWITCH_CMD_NEW_ATTR); -+ if (IS_ERR(hdr)) -+ return -1; -+ -+ if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name)) -+ goto nla_put_failure; -+ if (op->description) -+ if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION, -+ op->description)) -+ goto nla_put_failure; -+ -+ genlmsg_end(msg, hdr); -+ return msg->len; -+nla_put_failure: -+ genlmsg_cancel(msg, hdr); -+ return -EMSGSIZE; -+} -+ -+/* spread multipart messages across multiple message buffers */ -+static int -+swconfig_send_multipart(struct swconfig_callback *cb, void *arg) -+{ -+ struct genl_info *info = cb->info; -+ int restart = 0; -+ int err; -+ -+ do { -+ if (!cb->msg) { -+ cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); -+ if (cb->msg == NULL) -+ goto error; -+ } -+ -+ if (!(cb->fill(cb, arg) < 0)) -+ break; -+ -+ /* fill failed, check if this was already the second attempt */ -+ if (restart) -+ goto error; -+ -+ /* try again in a new message, send the current one */ -+ restart = 1; -+ if (cb->close) { -+ if (cb->close(cb, arg) < 0) -+ goto error; -+ } -+ err = genlmsg_reply(cb->msg, info); -+ cb->msg = NULL; -+ if (err < 0) -+ goto error; -+ -+ } while (restart); -+ -+ return 0; -+ -+error: -+ if (cb->msg) -+ nlmsg_free(cb->msg); -+ return -1; -+} -+ -+static int -+swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info) -+{ -+ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); -+ const struct switch_attrlist *alist; -+ struct switch_dev *dev; -+ struct swconfig_callback cb; -+ int err = -EINVAL; -+ int i; -+ -+ /* defaults */ -+ struct switch_attr *def_list; -+ unsigned long *def_active; -+ int n_def; -+ -+ dev = swconfig_get_dev(info); -+ if (!dev) -+ return -EINVAL; -+ -+ switch (hdr->cmd) { -+ case SWITCH_CMD_LIST_GLOBAL: -+ alist = &dev->ops->attr_global; -+ def_list = default_global; -+ def_active = &dev->def_global; -+ n_def = ARRAY_SIZE(default_global); -+ break; -+ case SWITCH_CMD_LIST_VLAN: -+ alist = &dev->ops->attr_vlan; -+ def_list = default_vlan; -+ def_active = &dev->def_vlan; -+ n_def = ARRAY_SIZE(default_vlan); -+ break; -+ case SWITCH_CMD_LIST_PORT: -+ alist = &dev->ops->attr_port; -+ def_list = default_port; -+ def_active = &dev->def_port; -+ n_def = ARRAY_SIZE(default_port); -+ break; -+ default: -+ WARN_ON(1); -+ goto out; -+ } -+ -+ memset(&cb, 0, sizeof(cb)); -+ cb.info = info; -+ cb.fill = swconfig_dump_attr; -+ for (i = 0; i < alist->n_attr; i++) { -+ if (alist->attr[i].disabled) -+ continue; -+ cb.args[0] = i; -+ err = swconfig_send_multipart(&cb, (void *) &alist->attr[i]); -+ if (err < 0) -+ goto error; -+ } -+ -+ /* defaults */ -+ for (i = 0; i < n_def; i++) { -+ if (!test_bit(i, def_active)) -+ continue; -+ cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i; -+ err = swconfig_send_multipart(&cb, (void *) &def_list[i]); -+ if (err < 0) -+ goto error; -+ } -+ swconfig_put_dev(dev); -+ -+ if (!cb.msg) -+ return 0; -+ -+ return genlmsg_reply(cb.msg, info); -+ -+error: -+ if (cb.msg) -+ nlmsg_free(cb.msg); -+out: -+ swconfig_put_dev(dev); -+ return err; -+} -+ -+static const struct switch_attr * -+swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info, -+ struct switch_val *val) -+{ -+ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); -+ const struct switch_attrlist *alist; -+ const struct switch_attr *attr = NULL; -+ int attr_id; -+ -+ /* defaults */ -+ struct switch_attr *def_list; -+ unsigned long *def_active; -+ int n_def; -+ -+ if (!info->attrs[SWITCH_ATTR_OP_ID]) -+ goto done; -+ -+ switch (hdr->cmd) { -+ case SWITCH_CMD_SET_GLOBAL: -+ case SWITCH_CMD_GET_GLOBAL: -+ alist = &dev->ops->attr_global; -+ def_list = default_global; -+ def_active = &dev->def_global; -+ n_def = ARRAY_SIZE(default_global); -+ break; -+ case SWITCH_CMD_SET_VLAN: -+ case SWITCH_CMD_GET_VLAN: -+ alist = &dev->ops->attr_vlan; -+ def_list = default_vlan; -+ def_active = &dev->def_vlan; -+ n_def = ARRAY_SIZE(default_vlan); -+ if (!info->attrs[SWITCH_ATTR_OP_VLAN]) -+ goto done; -+ val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]); -+ if (val->port_vlan >= dev->vlans) -+ goto done; -+ break; -+ case SWITCH_CMD_SET_PORT: -+ case SWITCH_CMD_GET_PORT: -+ alist = &dev->ops->attr_port; -+ def_list = default_port; -+ def_active = &dev->def_port; -+ n_def = ARRAY_SIZE(default_port); -+ if (!info->attrs[SWITCH_ATTR_OP_PORT]) -+ goto done; -+ val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]); -+ if (val->port_vlan >= dev->ports) -+ goto done; -+ break; -+ default: -+ WARN_ON(1); -+ goto done; -+ } -+ -+ if (!alist) -+ goto done; -+ -+ attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]); -+ if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) { -+ attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET; -+ if (attr_id >= n_def) -+ goto done; -+ if (!test_bit(attr_id, def_active)) -+ goto done; -+ attr = &def_list[attr_id]; -+ } else { -+ if (attr_id >= alist->n_attr) -+ goto done; -+ attr = &alist->attr[attr_id]; -+ } -+ -+ if (attr->disabled) -+ attr = NULL; -+ -+done: -+ if (!attr) -+ pr_debug("attribute lookup failed\n"); -+ val->attr = attr; -+ return attr; -+} -+ -+static int -+swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head, -+ struct switch_val *val, int max) -+{ -+ struct nlattr *nla; -+ int rem; -+ -+ val->len = 0; -+ nla_for_each_nested(nla, head, rem) { -+ struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1]; -+ struct switch_port *port = &val->value.ports[val->len]; -+ -+ if (val->len >= max) -+ return -EINVAL; -+ -+ if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla, -+ port_policy)) -+ return -EINVAL; -+ -+ if (!tb[SWITCH_PORT_ID]) -+ return -EINVAL; -+ -+ port->id = nla_get_u32(tb[SWITCH_PORT_ID]); -+ if (tb[SWITCH_PORT_FLAG_TAGGED]) -+ port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED); -+ val->len++; -+ } -+ -+ return 0; -+} -+ -+static int -+swconfig_set_attr(struct sk_buff *skb, struct genl_info *info) -+{ -+ const struct switch_attr *attr; -+ struct switch_dev *dev; -+ struct switch_val val; -+ int err = -EINVAL; -+ -+ dev = swconfig_get_dev(info); -+ if (!dev) -+ return -EINVAL; -+ -+ memset(&val, 0, sizeof(val)); -+ attr = swconfig_lookup_attr(dev, info, &val); -+ if (!attr || !attr->set) -+ goto error; -+ -+ val.attr = attr; -+ switch (attr->type) { -+ case SWITCH_TYPE_NOVAL: -+ break; -+ case SWITCH_TYPE_INT: -+ if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT]) -+ goto error; -+ val.value.i = -+ nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]); -+ break; -+ case SWITCH_TYPE_STRING: -+ if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR]) -+ goto error; -+ val.value.s = -+ nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]); -+ break; -+ case SWITCH_TYPE_PORTS: -+ val.value.ports = dev->portbuf; -+ memset(dev->portbuf, 0, -+ sizeof(struct switch_port) * dev->ports); -+ -+ /* TODO: implement multipart? */ -+ if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) { -+ err = swconfig_parse_ports(skb, -+ info->attrs[SWITCH_ATTR_OP_VALUE_PORTS], -+ &val, dev->ports); -+ if (err < 0) -+ goto error; -+ } else { -+ val.len = 0; -+ err = 0; -+ } -+ break; -+ default: -+ goto error; -+ } -+ -+ err = attr->set(dev, attr, &val); -+error: -+ swconfig_put_dev(dev); -+ return err; -+} -+ -+static int -+swconfig_close_portlist(struct swconfig_callback *cb, void *arg) -+{ -+ if (cb->nest[0]) -+ nla_nest_end(cb->msg, cb->nest[0]); -+ return 0; -+} -+ -+static int -+swconfig_send_port(struct swconfig_callback *cb, void *arg) -+{ -+ const struct switch_port *port = arg; -+ struct nlattr *p = NULL; -+ -+ if (!cb->nest[0]) { -+ cb->nest[0] = nla_nest_start(cb->msg, cb->cmd); -+ if (!cb->nest[0]) -+ return -1; -+ } -+ -+ p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT); -+ if (!p) -+ goto error; -+ -+ if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id)) -+ goto nla_put_failure; -+ if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { -+ if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED)) -+ goto nla_put_failure; -+ } -+ -+ nla_nest_end(cb->msg, p); -+ return 0; -+ -+nla_put_failure: -+ nla_nest_cancel(cb->msg, p); -+error: -+ nla_nest_cancel(cb->msg, cb->nest[0]); -+ return -1; -+} -+ -+static int -+swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr, -+ const struct switch_val *val) -+{ -+ struct swconfig_callback cb; -+ int err = 0; -+ int i; -+ -+ if (!val->value.ports) -+ return -EINVAL; -+ -+ memset(&cb, 0, sizeof(cb)); -+ cb.cmd = attr; -+ cb.msg = *msg; -+ cb.info = info; -+ cb.fill = swconfig_send_port; -+ cb.close = swconfig_close_portlist; -+ -+ cb.nest[0] = nla_nest_start(cb.msg, cb.cmd); -+ for (i = 0; i < val->len; i++) { -+ err = swconfig_send_multipart(&cb, &val->value.ports[i]); -+ if (err) -+ goto done; -+ } -+ err = val->len; -+ swconfig_close_portlist(&cb, NULL); -+ *msg = cb.msg; -+ -+done: -+ return err; -+} -+ -+static int -+swconfig_get_attr(struct sk_buff *skb, struct genl_info *info) -+{ -+ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); -+ const struct switch_attr *attr; -+ struct switch_dev *dev; -+ struct sk_buff *msg = NULL; -+ struct switch_val val; -+ int err = -EINVAL; -+ int cmd = hdr->cmd; -+ -+ dev = swconfig_get_dev(info); -+ if (!dev) -+ return -EINVAL; -+ -+ memset(&val, 0, sizeof(val)); -+ attr = swconfig_lookup_attr(dev, info, &val); -+ if (!attr || !attr->get) -+ goto error; -+ -+ if (attr->type == SWITCH_TYPE_PORTS) { -+ val.value.ports = dev->portbuf; -+ memset(dev->portbuf, 0, -+ sizeof(struct switch_port) * dev->ports); -+ } -+ -+ err = attr->get(dev, attr, &val); -+ if (err) -+ goto error; -+ -+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); -+ if (!msg) -+ goto error; -+ -+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam, -+ 0, cmd); -+ if (IS_ERR(hdr)) -+ goto nla_put_failure; -+ -+ switch (attr->type) { -+ case SWITCH_TYPE_INT: -+ if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i)) -+ goto nla_put_failure; -+ break; -+ case SWITCH_TYPE_STRING: -+ if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s)) -+ goto nla_put_failure; -+ break; -+ case SWITCH_TYPE_PORTS: -+ err = swconfig_send_ports(&msg, info, -+ SWITCH_ATTR_OP_VALUE_PORTS, &val); -+ if (err < 0) -+ goto nla_put_failure; -+ break; -+ default: -+ pr_debug("invalid type in attribute\n"); -+ err = -EINVAL; -+ goto error; -+ } -+ genlmsg_end(msg, hdr); -+ err = msg->len; -+ if (err < 0) -+ goto nla_put_failure; -+ -+ swconfig_put_dev(dev); -+ return genlmsg_reply(msg, info); -+ -+nla_put_failure: -+ if (msg) -+ nlmsg_free(msg); -+error: -+ swconfig_put_dev(dev); -+ if (!err) -+ err = -ENOMEM; -+ return err; -+} -+ -+static int -+swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags, -+ const struct switch_dev *dev) -+{ -+ struct nlattr *p = NULL, *m = NULL; -+ void *hdr; -+ int i; -+ -+ hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags, -+ SWITCH_CMD_NEW_ATTR); -+ if (IS_ERR(hdr)) -+ return -1; -+ -+ if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port)) -+ goto nla_put_failure; -+ -+ m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP); -+ if (!m) -+ goto nla_put_failure; -+ for (i = 0; i < dev->ports; i++) { -+ p = nla_nest_start(msg, SWITCH_ATTR_PORTS); -+ if (!p) -+ continue; -+ if (dev->portmap[i].s) { -+ if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT, -+ dev->portmap[i].s)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT, -+ dev->portmap[i].virt)) -+ goto nla_put_failure; -+ } -+ nla_nest_end(msg, p); -+ } -+ nla_nest_end(msg, m); -+ genlmsg_end(msg, hdr); -+ return msg->len; -+nla_put_failure: -+ genlmsg_cancel(msg, hdr); -+ return -EMSGSIZE; -+} -+ -+static int swconfig_dump_switches(struct sk_buff *skb, -+ struct netlink_callback *cb) -+{ -+ struct switch_dev *dev; -+ int start = cb->args[0]; -+ int idx = 0; -+ -+ swconfig_lock(); -+ list_for_each_entry(dev, &swdevs, dev_list) { -+ if (++idx <= start) -+ continue; -+ if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid, -+ cb->nlh->nlmsg_seq, NLM_F_MULTI, -+ dev) < 0) -+ break; -+ } -+ swconfig_unlock(); -+ cb->args[0] = idx; -+ -+ return skb->len; -+} -+ -+static int -+swconfig_done(struct netlink_callback *cb) -+{ -+ return 0; -+} -+ -+static struct genl_ops swconfig_ops[] = { -+ { -+ .cmd = SWITCH_CMD_LIST_GLOBAL, -+ .doit = swconfig_list_attrs, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_LIST_VLAN, -+ .doit = swconfig_list_attrs, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_LIST_PORT, -+ .doit = swconfig_list_attrs, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_GLOBAL, -+ .doit = swconfig_get_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_VLAN, -+ .doit = swconfig_get_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_PORT, -+ .doit = swconfig_get_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_SET_GLOBAL, -+ .doit = swconfig_set_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_SET_VLAN, -+ .doit = swconfig_set_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_SET_PORT, -+ .doit = swconfig_set_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_SWITCH, -+ .dumpit = swconfig_dump_switches, -+ .policy = switch_policy, -+ .done = swconfig_done, -+ } -+}; -+ -+#ifdef CONFIG_OF -+void -+of_switch_load_portmap(struct switch_dev *dev) -+{ -+ struct device_node *port; -+ -+ if (!dev->of_node) -+ return; -+ -+ for_each_child_of_node(dev->of_node, port) { -+ const __be32 *prop; -+ const char *segment; -+ int size, phys; -+ -+ if (!of_device_is_compatible(port, "swconfig,port")) -+ continue; -+ -+ if (of_property_read_string(port, "swconfig,segment", &segment)) -+ continue; -+ -+ prop = of_get_property(port, "swconfig,portmap", &size); -+ if (!prop) -+ continue; -+ -+ if (size != (2 * sizeof(*prop))) { -+ pr_err("%s: failed to parse port mapping\n", -+ port->name); -+ continue; -+ } -+ -+ phys = be32_to_cpup(prop++); -+ if ((phys < 0) | (phys >= dev->ports)) { -+ pr_err("%s: physical port index out of range\n", -+ port->name); -+ continue; -+ } -+ -+ dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL); -+ dev->portmap[phys].virt = be32_to_cpup(prop); -+ pr_debug("Found port: %s, physical: %d, virtual: %d\n", -+ segment, phys, dev->portmap[phys].virt); -+ } -+} -+#endif -+ -+int -+register_switch(struct switch_dev *dev, struct net_device *netdev) -+{ -+ struct switch_dev *sdev; -+ const int max_switches = 8 * sizeof(unsigned long); -+ unsigned long in_use = 0; -+ int err; -+ int i; -+ -+ INIT_LIST_HEAD(&dev->dev_list); -+ if (netdev) { -+ dev->netdev = netdev; -+ if (!dev->alias) -+ dev->alias = netdev->name; -+ } -+ BUG_ON(!dev->alias); -+ -+ if (dev->ports > 0) { -+ dev->portbuf = kzalloc(sizeof(struct switch_port) * -+ dev->ports, GFP_KERNEL); -+ if (!dev->portbuf) -+ return -ENOMEM; -+ dev->portmap = kzalloc(sizeof(struct switch_portmap) * -+ dev->ports, GFP_KERNEL); -+ if (!dev->portmap) { -+ kfree(dev->portbuf); -+ return -ENOMEM; -+ } -+ } -+ swconfig_defaults_init(dev); -+ mutex_init(&dev->sw_mutex); -+ swconfig_lock(); -+ dev->id = ++swdev_id; -+ -+ list_for_each_entry(sdev, &swdevs, dev_list) { -+ if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i)) -+ continue; -+ if (i < 0 || i > max_switches) -+ continue; -+ -+ set_bit(i, &in_use); -+ } -+ i = find_first_zero_bit(&in_use, max_switches); -+ -+ if (i == max_switches) { -+ swconfig_unlock(); -+ return -ENFILE; -+ } -+ -+#ifdef CONFIG_OF -+ if (dev->ports) -+ of_switch_load_portmap(dev); -+#endif -+ -+ /* fill device name */ -+ snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i); -+ -+ list_add_tail(&dev->dev_list, &swdevs); -+ swconfig_unlock(); -+ -+ err = swconfig_create_led_trigger(dev); -+ if (err) -+ return err; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(register_switch); -+ -+void -+unregister_switch(struct switch_dev *dev) -+{ -+ swconfig_destroy_led_trigger(dev); -+ kfree(dev->portbuf); -+ mutex_lock(&dev->sw_mutex); -+ swconfig_lock(); -+ list_del(&dev->dev_list); -+ swconfig_unlock(); -+ mutex_unlock(&dev->sw_mutex); -+} -+EXPORT_SYMBOL_GPL(unregister_switch); -+ -+ -+static int __init -+swconfig_init(void) -+{ -+ int err; -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)) -+ int i; -+#endif -+ -+ INIT_LIST_HEAD(&swdevs); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)) -+ err = genl_register_family(&switch_fam); -+ if (err) -+ return err; -+ -+ for (i = 0; i < ARRAY_SIZE(swconfig_ops); i++) { -+ err = genl_register_ops(&switch_fam, &swconfig_ops[i]); -+ if (err) -+ goto unregister; -+ } -+ return 0; -+ -+unregister: -+ genl_unregister_family(&switch_fam); -+ return err; -+#else -+ err = genl_register_family_with_ops(&switch_fam, swconfig_ops); -+ if (err) -+ return err; -+ return 0; -+#endif -+} -+ -+static void __exit -+swconfig_exit(void) -+{ -+ genl_unregister_family(&switch_fam); -+} -+ -+module_init(swconfig_init); -+module_exit(swconfig_exit); -+ -diff --git a/drivers/net/phy/swconfig_leds.c b/drivers/net/phy/swconfig_leds.c -new file mode 100755 -index 0000000..abd7bed ---- /dev/null -+++ b/drivers/net/phy/swconfig_leds.c -@@ -0,0 +1,354 @@ -+/* -+ * swconfig_led.c: LED trigger support for the switch configuration API -+ * -+ * Copyright (C) 2011 Gabor Juhos -+ * -+ * 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. -+ * -+ */ -+ -+#ifdef CONFIG_SWCONFIG_LEDS -+ -+#include -+#include -+#include -+#include -+ -+#define SWCONFIG_LED_TIMER_INTERVAL (HZ / 10) -+#define SWCONFIG_LED_NUM_PORTS 32 -+ -+struct switch_led_trigger { -+ struct led_trigger trig; -+ struct switch_dev *swdev; -+ -+ struct delayed_work sw_led_work; -+ u32 port_mask; -+ u32 port_link; -+ unsigned long port_traffic[SWCONFIG_LED_NUM_PORTS]; -+}; -+ -+struct swconfig_trig_data { -+ struct led_classdev *led_cdev; -+ struct switch_dev *swdev; -+ -+ rwlock_t lock; -+ u32 port_mask; -+ -+ bool prev_link; -+ unsigned long prev_traffic; -+ enum led_brightness prev_brightness; -+}; -+ -+static void -+swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data, -+ enum led_brightness brightness) -+{ -+ led_set_brightness(trig_data->led_cdev, brightness); -+ trig_data->prev_brightness = brightness; -+} -+ -+static void -+swconfig_trig_update_port_mask(struct led_trigger *trigger) -+{ -+ struct list_head *entry; -+ struct switch_led_trigger *sw_trig; -+ u32 port_mask; -+ -+ if (!trigger) -+ return; -+ -+ sw_trig = (void *) trigger; -+ -+ port_mask = 0; -+ read_lock(&trigger->leddev_list_lock); -+ list_for_each(entry, &trigger->led_cdevs) { -+ struct led_classdev *led_cdev; -+ struct swconfig_trig_data *trig_data; -+ -+ led_cdev = list_entry(entry, struct led_classdev, trig_list); -+ trig_data = led_cdev->trigger_data; -+ if (trig_data) { -+ read_lock(&trig_data->lock); -+ port_mask |= trig_data->port_mask; -+ read_unlock(&trig_data->lock); -+ } -+ } -+ read_unlock(&trigger->leddev_list_lock); -+ -+ sw_trig->port_mask = port_mask; -+ -+ if (port_mask) -+ schedule_delayed_work(&sw_trig->sw_led_work, -+ SWCONFIG_LED_TIMER_INTERVAL); -+ else -+ cancel_delayed_work_sync(&sw_trig->sw_led_work); -+} -+ -+static ssize_t -+swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct led_classdev *led_cdev = dev_get_drvdata(dev); -+ struct swconfig_trig_data *trig_data = led_cdev->trigger_data; -+ unsigned long port_mask; -+ ssize_t ret = -EINVAL; -+ char *after; -+ size_t count; -+ -+ port_mask = simple_strtoul(buf, &after, 16); -+ count = after - buf; -+ -+ if (*after && isspace(*after)) -+ count++; -+ -+ if (count == size) { -+ bool changed; -+ -+ write_lock(&trig_data->lock); -+ -+ changed = (trig_data->port_mask != port_mask); -+ if (changed) { -+ trig_data->port_mask = port_mask; -+ if (port_mask == 0) -+ swconfig_trig_set_brightness(trig_data, LED_OFF); -+ } -+ -+ write_unlock(&trig_data->lock); -+ -+ if (changed) -+ swconfig_trig_update_port_mask(led_cdev->trigger); -+ -+ ret = count; -+ } -+ -+ return ret; -+} -+ -+static ssize_t -+swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct led_classdev *led_cdev = dev_get_drvdata(dev); -+ struct swconfig_trig_data *trig_data = led_cdev->trigger_data; -+ -+ read_lock(&trig_data->lock); -+ sprintf(buf, "%#x\n", trig_data->port_mask); -+ read_unlock(&trig_data->lock); -+ -+ return strlen(buf) + 1; -+} -+ -+static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show, -+ swconfig_trig_port_mask_store); -+ -+static void -+swconfig_trig_activate(struct led_classdev *led_cdev) -+{ -+ struct switch_led_trigger *sw_trig; -+ struct swconfig_trig_data *trig_data; -+ int err; -+ -+ if (led_cdev->trigger->activate != swconfig_trig_activate) -+ return; -+ -+ trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL); -+ if (!trig_data) -+ return; -+ -+ sw_trig = (void *) led_cdev->trigger; -+ -+ rwlock_init(&trig_data->lock); -+ trig_data->led_cdev = led_cdev; -+ trig_data->swdev = sw_trig->swdev; -+ led_cdev->trigger_data = trig_data; -+ -+ err = device_create_file(led_cdev->dev, &dev_attr_port_mask); -+ if (err) -+ goto err_free; -+ -+ return; -+ -+err_free: -+ led_cdev->trigger_data = NULL; -+ kfree(trig_data); -+} -+ -+static void -+swconfig_trig_deactivate(struct led_classdev *led_cdev) -+{ -+ struct swconfig_trig_data *trig_data; -+ -+ swconfig_trig_update_port_mask(led_cdev->trigger); -+ -+ trig_data = (void *) led_cdev->trigger_data; -+ if (trig_data) { -+ device_remove_file(led_cdev->dev, &dev_attr_port_mask); -+ kfree(trig_data); -+ } -+} -+ -+static void -+swconfig_trig_led_event(struct switch_led_trigger *sw_trig, -+ struct led_classdev *led_cdev) -+{ -+ struct swconfig_trig_data *trig_data; -+ u32 port_mask; -+ bool link; -+ -+ trig_data = led_cdev->trigger_data; -+ if (!trig_data) -+ return; -+ -+ read_lock(&trig_data->lock); -+ port_mask = trig_data->port_mask; -+ read_unlock(&trig_data->lock); -+ -+ link = !!(sw_trig->port_link & port_mask); -+ if (!link) { -+ if (link != trig_data->prev_link) -+ swconfig_trig_set_brightness(trig_data, LED_OFF); -+ } else { -+ unsigned long traffic; -+ int i; -+ -+ traffic = 0; -+ for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) { -+ if (port_mask & (1 << i)) -+ traffic += sw_trig->port_traffic[i]; -+ } -+ -+ if (trig_data->prev_brightness != LED_FULL) -+ swconfig_trig_set_brightness(trig_data, LED_FULL); -+ else if (traffic != trig_data->prev_traffic) -+ swconfig_trig_set_brightness(trig_data, LED_OFF); -+ -+ trig_data->prev_traffic = traffic; -+ } -+ -+ trig_data->prev_link = link; -+} -+ -+static void -+swconfig_trig_update_leds(struct switch_led_trigger *sw_trig) -+{ -+ struct list_head *entry; -+ struct led_trigger *trigger; -+ -+ trigger = &sw_trig->trig; -+ read_lock(&trigger->leddev_list_lock); -+ list_for_each(entry, &trigger->led_cdevs) { -+ struct led_classdev *led_cdev; -+ -+ led_cdev = list_entry(entry, struct led_classdev, trig_list); -+ swconfig_trig_led_event(sw_trig, led_cdev); -+ } -+ read_unlock(&trigger->leddev_list_lock); -+} -+ -+static void -+swconfig_led_work_func(struct work_struct *work) -+{ -+ struct switch_led_trigger *sw_trig; -+ struct switch_dev *swdev; -+ u32 port_mask; -+ u32 link; -+ int i; -+ -+ sw_trig = container_of(work, struct switch_led_trigger, -+ sw_led_work.work); -+ -+ port_mask = sw_trig->port_mask; -+ swdev = sw_trig->swdev; -+ -+ link = 0; -+ for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) { -+ u32 port_bit; -+ -+ port_bit = BIT(i); -+ if ((port_mask & port_bit) == 0) -+ continue; -+ -+ if (swdev->ops->get_port_link) { -+ struct switch_port_link port_link; -+ -+ memset(&port_link, '\0', sizeof(port_link)); -+ swdev->ops->get_port_link(swdev, i, &port_link); -+ -+ if (port_link.link) -+ link |= port_bit; -+ } -+ -+ if (swdev->ops->get_port_stats) { -+ struct switch_port_stats port_stats; -+ -+ memset(&port_stats, '\0', sizeof(port_stats)); -+ swdev->ops->get_port_stats(swdev, i, &port_stats); -+ sw_trig->port_traffic[i] = port_stats.tx_bytes + -+ port_stats.rx_bytes; -+ } -+ } -+ -+ sw_trig->port_link = link; -+ -+ swconfig_trig_update_leds(sw_trig); -+ -+ schedule_delayed_work(&sw_trig->sw_led_work, -+ SWCONFIG_LED_TIMER_INTERVAL); -+} -+ -+static int -+swconfig_create_led_trigger(struct switch_dev *swdev) -+{ -+ struct switch_led_trigger *sw_trig; -+ int err; -+ -+ if (!swdev->ops->get_port_link) -+ return 0; -+ -+ sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL); -+ if (!sw_trig) -+ return -ENOMEM; -+ -+ sw_trig->swdev = swdev; -+ sw_trig->trig.name = swdev->devname; -+ sw_trig->trig.activate = swconfig_trig_activate; -+ sw_trig->trig.deactivate = swconfig_trig_deactivate; -+ -+ INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func); -+ -+ err = led_trigger_register(&sw_trig->trig); -+ if (err) -+ goto err_free; -+ -+ swdev->led_trigger = sw_trig; -+ -+ return 0; -+ -+err_free: -+ kfree(sw_trig); -+ return err; -+} -+ -+static void -+swconfig_destroy_led_trigger(struct switch_dev *swdev) -+{ -+ struct switch_led_trigger *sw_trig; -+ -+ sw_trig = swdev->led_trigger; -+ if (sw_trig) { -+ cancel_delayed_work_sync(&sw_trig->sw_led_work); -+ led_trigger_unregister(&sw_trig->trig); -+ kfree(sw_trig); -+ } -+} -+ -+#else /* SWCONFIG_LEDS */ -+static inline int -+swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; } -+ -+static inline void -+swconfig_destroy_led_trigger(struct switch_dev *swdev) { } -+#endif /* CONFIG_SWCONFIG_LEDS */ -diff --git a/include/linux/platform_data/b53.h b/include/linux/platform_data/b53.h -new file mode 100755 -index 0000000..7842741 ---- /dev/null -+++ b/include/linux/platform_data/b53.h -@@ -0,0 +1,36 @@ -+/* -+ * B53 platform data -+ * -+ * Copyright (C) 2013 Jonas Gorski -+ * -+ * 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. -+ */ -+ -+#ifndef __B53_H -+#define __B53_H -+ -+#include -+ -+struct b53_platform_data { -+ u32 chip_id; -+ u16 enabled_ports; -+ -+ /* allow to specify an ethX alias */ -+ const char *alias; -+ -+ /* only used by MMAP'd driver */ -+ unsigned big_endian:1; -+ void __iomem *regs; -+}; -+ -+#endif -diff --git a/include/linux/switch.h b/include/linux/switch.h -new file mode 100755 -index 0000000..4291364 ---- /dev/null -+++ b/include/linux/switch.h -@@ -0,0 +1,169 @@ -+/* -+ * switch.h: Switch configuration API -+ * -+ * Copyright (C) 2008 Felix Fietkau -+ * -+ * 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. -+ */ -+#ifndef _LINUX_SWITCH_H -+#define _LINUX_SWITCH_H -+ -+#include -+#include -+ -+struct switch_dev; -+struct switch_op; -+struct switch_val; -+struct switch_attr; -+struct switch_attrlist; -+struct switch_led_trigger; -+ -+int register_switch(struct switch_dev *dev, struct net_device *netdev); -+void unregister_switch(struct switch_dev *dev); -+ -+/** -+ * struct switch_attrlist - attribute list -+ * -+ * @n_attr: number of attributes -+ * @attr: pointer to the attributes array -+ */ -+struct switch_attrlist { -+ int n_attr; -+ const struct switch_attr *attr; -+}; -+ -+enum switch_port_speed { -+ SWITCH_PORT_SPEED_UNKNOWN = 0, -+ SWITCH_PORT_SPEED_10 = 10, -+ SWITCH_PORT_SPEED_100 = 100, -+ SWITCH_PORT_SPEED_1000 = 1000, -+}; -+ -+struct switch_port_link { -+ bool link; -+ bool duplex; -+ bool aneg; -+ bool tx_flow; -+ bool rx_flow; -+ enum switch_port_speed speed; -+ /* in ethtool adv_t format */ -+ u32 eee; -+}; -+ -+struct switch_port_stats { -+ unsigned long tx_bytes; -+ unsigned long rx_bytes; -+}; -+ -+/** -+ * struct switch_dev_ops - switch driver operations -+ * -+ * @attr_global: global switch attribute list -+ * @attr_port: port attribute list -+ * @attr_vlan: vlan attribute list -+ * -+ * Callbacks: -+ * -+ * @get_vlan_ports: read the port list of a VLAN -+ * @set_vlan_ports: set the port list of a VLAN -+ * -+ * @get_port_pvid: get the primary VLAN ID of a port -+ * @set_port_pvid: set the primary VLAN ID of a port -+ * -+ * @apply_config: apply all changed settings to the switch -+ * @reset_switch: resetting the switch -+ */ -+struct switch_dev_ops { -+ struct switch_attrlist attr_global, attr_port, attr_vlan; -+ -+ int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val); -+ int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val); -+ -+ int (*get_port_pvid)(struct switch_dev *dev, int port, int *val); -+ int (*set_port_pvid)(struct switch_dev *dev, int port, int val); -+ -+ int (*apply_config)(struct switch_dev *dev); -+ int (*reset_switch)(struct switch_dev *dev); -+ -+ int (*get_port_link)(struct switch_dev *dev, int port, -+ struct switch_port_link *link); -+ int (*get_port_stats)(struct switch_dev *dev, int port, -+ struct switch_port_stats *stats); -+}; -+ -+struct switch_dev { -+ struct device_node *of_node; -+ const struct switch_dev_ops *ops; -+ /* will be automatically filled */ -+ char devname[IFNAMSIZ]; -+ -+ const char *name; -+ /* NB: either alias or netdev must be set */ -+ const char *alias; -+ struct net_device *netdev; -+ -+ int ports; -+ int vlans; -+ int cpu_port; -+ -+ /* the following fields are internal for swconfig */ -+ int id; -+ struct list_head dev_list; -+ unsigned long def_global, def_port, def_vlan; -+ -+ struct mutex sw_mutex; -+ struct switch_port *portbuf; -+ struct switch_portmap *portmap; -+ -+ char buf[128]; -+ -+#ifdef CONFIG_SWCONFIG_LEDS -+ struct switch_led_trigger *led_trigger; -+#endif -+}; -+ -+struct switch_port { -+ u32 id; -+ u32 flags; -+}; -+ -+struct switch_portmap { -+ u32 virt; -+ const char *s; -+}; -+ -+struct switch_val { -+ const struct switch_attr *attr; -+ int port_vlan; -+ int len; -+ union { -+ const char *s; -+ u32 i; -+ struct switch_port *ports; -+ } value; -+}; -+ -+struct switch_attr { -+ int disabled; -+ int type; -+ const char *name; -+ const char *description; -+ -+ int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val); -+ int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val); -+ -+ /* for driver internal use */ -+ int id; -+ int ofs; -+ int max; -+}; -+ -+#endif /* _LINUX_SWITCH_H */ -diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -old mode 100644 -new mode 100755 -index 68ceb97..40fa85b ---- a/include/uapi/linux/Kbuild -+++ b/include/uapi/linux/Kbuild -@@ -379,6 +379,7 @@ header-y += stddef.h - header-y += string.h - header-y += suspend_ioctls.h - header-y += swab.h -+header-y += switch.h - header-y += synclink.h - header-y += sysctl.h - header-y += sysinfo.h -diff --git a/include/uapi/linux/switch.h b/include/uapi/linux/switch.h -new file mode 100755 -index 0000000..a59b239 ---- /dev/null -+++ b/include/uapi/linux/switch.h -@@ -0,0 +1,103 @@ -+/* -+ * switch.h: Switch configuration API -+ * -+ * Copyright (C) 2008 Felix Fietkau -+ * -+ * 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. -+ */ -+ -+#ifndef _UAPI_LINUX_SWITCH_H -+#define _UAPI_LINUX_SWITCH_H -+ -+#include -+#include -+#include -+#include -+#ifndef __KERNEL__ -+#include -+#include -+#include -+#endif -+ -+/* main attributes */ -+enum { -+ SWITCH_ATTR_UNSPEC, -+ /* global */ -+ SWITCH_ATTR_TYPE, -+ /* device */ -+ SWITCH_ATTR_ID, -+ SWITCH_ATTR_DEV_NAME, -+ SWITCH_ATTR_ALIAS, -+ SWITCH_ATTR_NAME, -+ SWITCH_ATTR_VLANS, -+ SWITCH_ATTR_PORTS, -+ SWITCH_ATTR_PORTMAP, -+ SWITCH_ATTR_CPU_PORT, -+ /* attributes */ -+ SWITCH_ATTR_OP_ID, -+ SWITCH_ATTR_OP_TYPE, -+ SWITCH_ATTR_OP_NAME, -+ SWITCH_ATTR_OP_PORT, -+ SWITCH_ATTR_OP_VLAN, -+ SWITCH_ATTR_OP_VALUE_INT, -+ SWITCH_ATTR_OP_VALUE_STR, -+ SWITCH_ATTR_OP_VALUE_PORTS, -+ SWITCH_ATTR_OP_DESCRIPTION, -+ /* port lists */ -+ SWITCH_ATTR_PORT, -+ SWITCH_ATTR_MAX -+}; -+ -+enum { -+ /* port map */ -+ SWITCH_PORTMAP_PORTS, -+ SWITCH_PORTMAP_SEGMENT, -+ SWITCH_PORTMAP_VIRT, -+ SWITCH_PORTMAP_MAX -+}; -+ -+/* commands */ -+enum { -+ SWITCH_CMD_UNSPEC, -+ SWITCH_CMD_GET_SWITCH, -+ SWITCH_CMD_NEW_ATTR, -+ SWITCH_CMD_LIST_GLOBAL, -+ SWITCH_CMD_GET_GLOBAL, -+ SWITCH_CMD_SET_GLOBAL, -+ SWITCH_CMD_LIST_PORT, -+ SWITCH_CMD_GET_PORT, -+ SWITCH_CMD_SET_PORT, -+ SWITCH_CMD_LIST_VLAN, -+ SWITCH_CMD_GET_VLAN, -+ SWITCH_CMD_SET_VLAN -+}; -+ -+/* data types */ -+enum switch_val_type { -+ SWITCH_TYPE_UNSPEC, -+ SWITCH_TYPE_INT, -+ SWITCH_TYPE_STRING, -+ SWITCH_TYPE_PORTS, -+ SWITCH_TYPE_NOVAL, -+}; -+ -+/* port nested attributes */ -+enum { -+ SWITCH_PORT_UNSPEC, -+ SWITCH_PORT_ID, -+ SWITCH_PORT_FLAG_TAGGED, -+ SWITCH_PORT_ATTR_MAX -+}; -+ -+#define SWITCH_ATTR_DEFAULTS_OFFSET 0x1000 -+ -+ -+#endif /* _UAPI_LINUX_SWITCH_H */