X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Fb53%2Fb53_common.c;h=670588c84e0a1a1957bcca06ff2832e837005b8b;hb=b3f95490b9bec020314eb32016988ee262d52884;hp=39027c1771066e6eb9cdba0f088e84de7eec9885;hpb=72b47e69c406227055e4bb942e0cb813ae615200;p=openwrt%2Fstaging%2Fwigyori.git 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 39027c1771..670588c84e 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,9 @@ #include #include #include +#include +#include +#include #include #include "b53_regs.h" @@ -38,7 +41,6 @@ struct b53_mib_desc { const char *name; }; - /* BCM5365 MIB counters */ static const struct b53_mib_desc b53_mibs_65[] = { { 8, 0x00, "TxOctets" }, @@ -76,6 +78,9 @@ static const struct b53_mib_desc b53_mibs_65[] = { { }, }; +#define B63XX_MIB_TXB_ID 0 /* TxOctets */ +#define B63XX_MIB_RXB_ID 14 /* RxOctets */ + /* BCM63xx MIB counters */ static const struct b53_mib_desc b53_mibs_63xx[] = { { 8, 0x00, "TxOctets" }, @@ -123,6 +128,9 @@ static const struct b53_mib_desc b53_mibs_63xx[] = { { } }; +#define B53XX_MIB_TXB_ID 0 /* TxOctets */ +#define B53XX_MIB_RXB_ID 12 /* RxOctets */ + /* MIB counters */ static const struct b53_mib_desc b53_mibs[] = { { 8, 0x00, "TxOctets" }, @@ -189,7 +197,8 @@ static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members, u32 entry = 0; if (members) { - entry = (untag << VA_UNTAG_S) | 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 @@ -203,7 +212,8 @@ static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members, u16 entry = 0; if (members) - entry = (untag << VA_UNTAG_S) | members | VA_VALID_65; + 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 | @@ -406,11 +416,11 @@ static void b53_enable_mib(struct b53_device *dev) { u8 gc; - b53_read8(dev, B53_CTRL_PAGE, B53_GLOBAL_CONFIG, &gc); + b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc); gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN); - b53_write8(dev, B53_CTRL_PAGE, B53_GLOBAL_CONFIG, gc); + b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc); } static int b53_apply(struct b53_device *dev) @@ -457,7 +467,7 @@ static int b53_apply(struct b53_device *dev) return 0; } -void b53_switch_reset_gpio(struct b53_device *dev) +static void b53_switch_reset_gpio(struct b53_device *dev) { int gpio = dev->reset_gpio; @@ -476,34 +486,88 @@ 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 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)) { @@ -523,7 +587,7 @@ static int b53_switch_reset(struct b53_device *dev) return -EINVAL; } } - } else if ((is531x5(dev) || is5301x(dev)) && dev->sw_dev.cpu_port == B53_CPU_PORT) { + } else if (is531x5(dev) && cpu_port == B53_CPU_PORT) { u8 mii_port_override; b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, @@ -531,8 +595,89 @@ static int b53_switch_reset(struct b53_device *dev) b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, mii_port_override | PORT_OVERRIDE_EN | PORT_OVERRIDE_LINK); + + /* BCM47189 has another interface connected to the port 5 */ + if (dev->enabled_ports & BIT(5)) { + u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(5); + 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; + b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po); + } + } 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); + } + } + + 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); @@ -764,6 +909,54 @@ static int b53_port_get_link(struct switch_dev *dev, int port, } +static int b53_port_set_link(struct switch_dev *sw_dev, int port, + struct switch_port_link *link) +{ + struct b53_device *dev = sw_to_b53(sw_dev); + + /* + * TODO: BCM63XX requires special handling as it can have external phys + * and ports might be GE or only FE + */ + if (is63xx(dev)) + return -ENOTSUPP; + + if (port == sw_dev->cpu_port) + return -EINVAL; + + if (!(BIT(port) & dev->enabled_ports)) + return -EINVAL; + + if (link->speed == SWITCH_PORT_SPEED_1000 && + (is5325(dev) || is5365(dev))) + return -EINVAL; + + if (link->speed == SWITCH_PORT_SPEED_1000 && !link->duplex) + return -EINVAL; + + return switch_generic_set_link(sw_dev, port, link); +} + +static int b53_phy_read16(struct switch_dev *dev, int addr, u8 reg, u16 *value) +{ + struct b53_device *priv = sw_to_b53(dev); + + if (priv->ops->phy_read16) + return priv->ops->phy_read16(priv, addr, reg, value); + + return b53_read16(priv, B53_PORT_MII_PAGE(addr), reg, value); +} + +static int b53_phy_write16(struct switch_dev *dev, int addr, u8 reg, u16 value) +{ + struct b53_device *priv = sw_to_b53(dev); + + if (priv->ops->phy_write16) + return priv->ops->phy_write16(priv, addr, reg, value); + + return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg, value); +} + static int b53_global_reset_switch(struct switch_dev *dev) { struct b53_device *priv = sw_to_b53(dev); @@ -773,8 +966,8 @@ static int b53_global_reset_switch(struct switch_dev *dev) 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); + memset(priv->vlans, 0, sizeof(*priv->vlans) * dev->vlans); + memset(priv->ports, 0, sizeof(*priv->ports) * dev->ports); return b53_switch_reset(priv); } @@ -860,6 +1053,54 @@ static int b53_port_get_mib(struct switch_dev *sw_dev, return 0; } +static int b53_port_get_stats(struct switch_dev *sw_dev, int port, + struct switch_port_stats *stats) +{ + struct b53_device *dev = sw_to_b53(sw_dev); + const struct b53_mib_desc *mibs; + int txb_id, rxb_id; + u64 rxb, txb; + + if (!(BIT(port) & dev->enabled_ports)) + return -EINVAL; + + txb_id = B53XX_MIB_TXB_ID; + rxb_id = B53XX_MIB_RXB_ID; + + if (is5365(dev)) { + if (port == 5) + port = 8; + + mibs = b53_mibs_65; + } else if (is63xx(dev)) { + mibs = b53_mibs_63xx; + txb_id = B63XX_MIB_TXB_ID; + rxb_id = B63XX_MIB_RXB_ID; + } else { + mibs = b53_mibs; + } + + dev->buf[0] = 0; + + if (mibs->size == 8) { + b53_read64(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &txb); + b53_read64(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &rxb); + } else { + u32 val32; + + b53_read32(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &val32); + txb = val32; + + b53_read32(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &val32); + rxb = val32; + } + + stats->tx_bytes = txb; + stats->rx_bytes = rxb; + + return 0; +} + static struct switch_attr b53_global_ops_25[] = { { .type = SWITCH_TYPE_INT, @@ -972,6 +1213,10 @@ static const struct switch_dev_ops b53_switch_ops_25 = { .apply_config = b53_global_apply_config, .reset_switch = b53_global_reset_switch, .get_port_link = b53_port_get_link, + .set_port_link = b53_port_set_link, + .get_port_stats = b53_port_get_stats, + .phy_read16 = b53_phy_read16, + .phy_write16 = b53_phy_write16, }; static const struct switch_dev_ops b53_switch_ops_65 = { @@ -995,6 +1240,10 @@ static const struct switch_dev_ops b53_switch_ops_65 = { .apply_config = b53_global_apply_config, .reset_switch = b53_global_reset_switch, .get_port_link = b53_port_get_link, + .set_port_link = b53_port_set_link, + .get_port_stats = b53_port_get_stats, + .phy_read16 = b53_phy_read16, + .phy_write16 = b53_phy_write16, }; static const struct switch_dev_ops b53_switch_ops = { @@ -1018,6 +1267,10 @@ static const struct switch_dev_ops b53_switch_ops = { .apply_config = b53_global_apply_config, .reset_switch = b53_global_reset_switch, .get_port_link = b53_port_get_link, + .set_port_link = b53_port_set_link, + .get_port_stats = b53_port_get_stats, + .phy_read16 = b53_phy_read16, + .phy_write16 = b53_phy_write16, }; struct b53_chip_data { @@ -1127,6 +1380,19 @@ static const struct b53_chip_data b53_switch_chips[] = { .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", @@ -1146,7 +1412,7 @@ static const struct b53_chip_data b53_switch_chips[] = { .alias = "bcm53011", .vlans = 4096, .enabled_ports = 0x1f, - .cpu_port = B53_CPU_PORT_25, // TODO: auto detect + .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, @@ -1158,8 +1424,8 @@ static const struct b53_chip_data b53_switch_chips[] = { .dev_name = "BCM53011", .alias = "bcm53011", .vlans = 4096, - .enabled_ports = 0x1f, - .cpu_port = B53_CPU_PORT_25, // TODO: auto detect + .enabled_ports = 0x1bf, + .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, @@ -1171,8 +1437,8 @@ static const struct b53_chip_data b53_switch_chips[] = { .dev_name = "BCM53012", .alias = "bcm53011", .vlans = 4096, - .enabled_ports = 0x1f, - .cpu_port = B53_CPU_PORT_25, // TODO: auto detect + .enabled_ports = 0x1bf, + .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, @@ -1185,7 +1451,7 @@ static const struct b53_chip_data b53_switch_chips[] = { .alias = "bcm53018", .vlans = 4096, .enabled_ports = 0x1f, - .cpu_port = B53_CPU_PORT_25, // TODO: auto detect + .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, @@ -1198,7 +1464,7 @@ static const struct b53_chip_data b53_switch_chips[] = { .alias = "bcm53019", .vlans = 4096, .enabled_ports = 0x1f, - .cpu_port = B53_CPU_PORT_25, // TODO: auto detect + .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, @@ -1207,7 +1473,44 @@ static const struct b53_chip_data b53_switch_chips[] = { }, }; -int b53_switch_init(struct b53_device *dev) +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; unsigned i; @@ -1270,9 +1573,14 @@ int b53_switch_init(struct b53_device *dev) sw_dev->cpu_port = 5; } - /* cpu port is always last */ - sw_dev->ports = sw_dev->cpu_port + 1; + 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); dev->ports = devm_kzalloc(dev->dev, sizeof(struct b53_port) * sw_dev->ports, @@ -1292,7 +1600,8 @@ int b53_switch_init(struct b53_device *dev) 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"); + ret = devm_gpio_request_one(dev->dev, dev->reset_gpio, + GPIOF_OUT_INIT_HIGH, "robo_reset"); if (ret) return ret; } @@ -1361,6 +1670,7 @@ int b53_switch_detect(struct b53_device *dev) 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: