From 5df15c06ccd105a19431bc32bd1d158b3ff3325e Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Thu, 9 Feb 2017 21:20:41 +0100 Subject: [PATCH] b53: allow configuration through device tree Add support for the same binding as upstream b53 to allow an easy switch. Signed-off-by: Jonas Gorski --- .../files/drivers/net/phy/b53/b53_common.c | 177 ++++++++++++++++-- .../files/drivers/net/phy/b53/b53_priv.h | 8 + .../files/drivers/net/phy/b53/b53_spi.c | 14 ++ 3 files changed, 180 insertions(+), 19 deletions(-) diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c index b884010d2826..482966a909de 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include "b53_regs.h" @@ -478,35 +480,88 @@ static void b53_switch_reset_gpio(struct b53_device *dev) dev->current_page = 0xff; } -static int b53_switch_reset(struct b53_device *dev) +static int b53_configure_ports_of(struct b53_device *dev) { - u8 cpu_port = dev->sw_dev.cpu_port; - u8 mgmt; + struct device_node *dn, *pn; + u32 port_num; - b53_switch_reset_gpio(dev); + dn = of_get_child_by_name(dev_of_node(dev->dev), "ports"); - if (is539x(dev)) { - b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83); - b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00); - } + for_each_available_child_of_node(dn, pn) { + struct device_node *fixed_link; - b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); + if (of_property_read_u32(pn, "reg", &port_num)) + continue; - if (!(mgmt & SM_SW_FWD_EN)) { - mgmt &= ~SM_SW_FWD_MODE; - mgmt |= SM_SW_FWD_EN; + if (port_num > B53_CPU_PORT) + continue; - b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); - b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); + fixed_link = of_get_child_by_name(pn, "fixed-link"); + if (fixed_link) { + u32 spd; + u8 po = GMII_PO_LINK; + int mode = of_get_phy_mode(pn); + + if (!of_property_read_u32(fixed_link, "speed", &spd)) { + switch (spd) { + case 10: + po |= GMII_PO_SPEED_10M; + break; + case 100: + po |= GMII_PO_SPEED_100M; + break; + case 2000: + if (is_imp_port(dev, port_num)) + po |= PORT_OVERRIDE_SPEED_2000M; + else + po |= GMII_PO_SPEED_2000M; + /* fall through */ + case 1000: + po |= GMII_PO_SPEED_1000M; + break; + } + } - if (!(mgmt & SM_SW_FWD_EN)) { - pr_err("Failed to enable switch!\n"); - return -EINVAL; + if (of_property_read_bool(fixed_link, "full-duplex")) + po |= PORT_OVERRIDE_FULL_DUPLEX; + if (of_property_read_bool(fixed_link, "pause")) + po |= GMII_PO_RX_FLOW; + if (of_property_read_bool(fixed_link, "asym-pause")) + po |= GMII_PO_TX_FLOW; + + if (is_imp_port(dev, port_num)) { + po |= PORT_OVERRIDE_EN; + + if (is5325(dev) && + mode == PHY_INTERFACE_MODE_REVMII) + po |= PORT_OVERRIDE_RV_MII_25; + + b53_write8(dev, B53_CTRL_PAGE, + B53_PORT_OVERRIDE_CTRL, po); + + if (is5325(dev) && + mode == PHY_INTERFACE_MODE_REVMII) { + b53_read8(dev, B53_CTRL_PAGE, + B53_PORT_OVERRIDE_CTRL, &po); + if (!(po & PORT_OVERRIDE_RV_MII_25)) + pr_err("Failed to enable reverse MII mode\n"); + return -EINVAL; + } + } else { + po |= GMII_PO_EN; + b53_write8(dev, B53_CTRL_PAGE, + B53_GMII_PORT_OVERRIDE_CTRL(port_num), + po); + } } } - /* enable all ports */ - b53_enable_ports(dev); + return 0; +} + +static int b53_configure_ports(struct b53_device *dev) +{ + u8 cpu_port = dev->sw_dev.cpu_port; /* configure MII port if necessary */ if (is5325(dev)) { @@ -576,6 +631,47 @@ static int b53_switch_reset(struct b53_device *dev) } } + return 0; +} + +static int b53_switch_reset(struct b53_device *dev) +{ + int ret = 0; + 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); + + if (dev->dev->of_node) + ret = b53_configure_ports_of(dev); + else + ret = b53_configure_ports(dev); + + if (ret) + return ret; + b53_enable_mib(dev); return b53_flush_arl(dev); @@ -1320,6 +1416,43 @@ static const struct b53_chip_data b53_switch_chips[] = { }, }; +static int b53_switch_init_of(struct b53_device *dev) +{ + struct device_node *dn, *pn; + const char *alias; + u32 port_num; + u16 ports = 0; + + dn = of_get_child_by_name(dev_of_node(dev->dev), "ports"); + if (!dn) + return -EINVAL; + + for_each_available_child_of_node(dn, pn) { + const char *label; + int len; + + if (of_property_read_u32(pn, "reg", &port_num)) + continue; + + if (port_num > B53_CPU_PORT) + continue; + + ports |= BIT(port_num); + + label = of_get_property(pn, "label", &len); + if (label && !strcmp(label, "cpu")) + dev->sw_dev.cpu_port = port_num; + } + + dev->enabled_ports = ports; + + if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias", + &alias)) + dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL); + + return 0; +} + static int b53_switch_init(struct b53_device *dev) { struct switch_dev *sw_dev = &dev->sw_dev; @@ -1383,6 +1516,12 @@ static int b53_switch_init(struct b53_device *dev) sw_dev->cpu_port = 5; } + if (dev_of_node(dev->dev)) { + ret = b53_switch_init_of(dev); + if (ret) + return ret; + } + dev->enabled_ports |= BIT(sw_dev->cpu_port); sw_dev->ports = fls(dev->enabled_ports); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h index 53da97ebbaf1..277c75d367a8 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h @@ -170,6 +170,14 @@ static inline int is_cpu_port(struct b53_device *dev, int port) return dev->sw_dev.cpu_port == port; } +static inline int is_imp_port(struct b53_device *dev, int port) +{ + if (is5325(dev) || is5365(dev)) + return port == B53_CPU_PORT_25; + else + return port == B53_CPU_PORT; +} + static inline struct b53_device *sw_to_b53(struct switch_dev *sw) { return container_of(sw, struct b53_device, sw_dev); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c index 469a8ddde382..efc8f7ee89f2 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "b53_priv.h" @@ -313,11 +314,24 @@ static int b53_spi_remove(struct spi_device *spi) return 0; } +static const struct of_device_id b53_of_match[] = { + { .compatible = "brcm,bcm5325" }, + { .compatible = "brcm,bcm53115" }, + { .compatible = "brcm,bcm53125" }, + { .compatible = "brcm,bcm53128" }, + { .compatible = "brcm,bcm5365" }, + { .compatible = "brcm,bcm5395" }, + { .compatible = "brcm,bcm5397" }, + { .compatible = "brcm,bcm5398" }, + { /* sentinel */ }, +}; + static struct spi_driver b53_spi_driver = { .driver = { .name = "b53-switch", .bus = &spi_bus_type, .owner = THIS_MODULE, + .of_match_table = b53_of_match, }, .probe = b53_spi_probe, .remove = b53_spi_remove, -- 2.30.2