X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Fmvsw61xx.c;h=9a689e6c81b69cbe29995c929a0de9d35344663d;hp=7199de0eb21b05b6afc76f8a5379bd73fdd7e044;hb=f24ffb901e0408917748773b883841eca52eea05;hpb=a1872182bb252ac9d731cbc8af0e9ddb75fc8cb0 diff --git a/target/linux/generic/files/drivers/net/phy/mvsw61xx.c b/target/linux/generic/files/drivers/net/phy/mvsw61xx.c index 7199de0eb2..9a689e6c81 100644 --- a/target/linux/generic/files/drivers/net/phy/mvsw61xx.c +++ b/target/linux/generic/files/drivers/net/phy/mvsw61xx.c @@ -2,8 +2,9 @@ * Marvell 88E61xx switch driver * * Copyright (c) 2014 Claudio Leite + * Copyright (c) 2014 Nikita Nazarenko * - * Based on code (c) 2008 Felix Fietkau + * Based on code (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 v2 as published by the @@ -27,6 +28,7 @@ MODULE_DESCRIPTION("Marvell 88E61xx Switch driver"); MODULE_AUTHOR("Claudio Leite "); +MODULE_AUTHOR("Nikita Nazarenko "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:mvsw61xx"); @@ -145,6 +147,52 @@ mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr, return -ETIMEDOUT; } +static int +mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg) +{ + sw16(dev, MV_GLOBAL2REG(SMI_OP), + MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg); + + if (mvsw61xx_wait_mask_s(dev, MV_GLOBAL2REG(SMI_OP), + MV_INDIRECT_INPROGRESS, 0) < 0) + return -ETIMEDOUT; + + return sr16(dev, MV_GLOBAL2REG(SMI_DATA)); +} + +static int +mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val) +{ + sw16(dev, MV_GLOBAL2REG(SMI_DATA), val); + + sw16(dev, MV_GLOBAL2REG(SMI_OP), + MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg); + + return mvsw61xx_wait_mask_s(dev, MV_GLOBAL2REG(SMI_OP), + MV_INDIRECT_INPROGRESS, 0) < 0; +} + +static int +mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg) +{ + int ret; + + mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page); + ret = mvsw61xx_mdio_read(dev, port, reg); + mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0); + + return ret; +} + +static void +mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg, + u16 val) +{ + mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page); + mvsw61xx_mdio_write(dev, port, reg, val); + mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0); +} + static int mvsw61xx_get_port_mask(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -194,7 +242,7 @@ mvsw61xx_set_port_qmode(struct switch_dev *dev, } static int -mvsw61xx_get_pvid(struct switch_dev *dev, int port, int *val) +mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val) { struct mvsw61xx_state *state = get_state(dev); @@ -204,7 +252,7 @@ mvsw61xx_get_pvid(struct switch_dev *dev, int port, int *val) } static int -mvsw61xx_set_pvid(struct switch_dev *dev, int port, int val) +mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val) { struct mvsw61xx_state *state = get_state(dev); @@ -217,73 +265,32 @@ mvsw61xx_set_pvid(struct switch_dev *dev, int port, int val) } static int -mvsw61xx_get_port_status(struct switch_dev *dev, - const struct switch_attr *attr, struct switch_val *val) +mvsw61xx_get_port_link(struct switch_dev *dev, int port, + struct switch_port_link *link) { - struct mvsw61xx_state *state = get_state(dev); - char *buf = state->buf; u16 status, speed; - int len; - - status = sr16(dev, MV_PORTREG(STATUS, val->port_vlan)); - speed = (status & MV_PORT_STATUS_SPEED_MASK) >> - MV_PORT_STATUS_SPEED_SHIFT; - - len = sprintf(buf, "link: "); - if (status & MV_PORT_STATUS_LINK) { - len += sprintf(buf + len, "up, speed: "); - - switch (speed) { - case MV_PORT_STATUS_SPEED_10: - len += sprintf(buf + len, "10"); - break; - case MV_PORT_STATUS_SPEED_100: - len += sprintf(buf + len, "100"); - break; - case MV_PORT_STATUS_SPEED_1000: - len += sprintf(buf + len, "1000"); - break; - } - - len += sprintf(buf + len, " Mbps, duplex: "); - - if (status & MV_PORT_STATUS_FDX) - len += sprintf(buf + len, "full"); - else - len += sprintf(buf + len, "half"); - } else { - len += sprintf(buf + len, "down"); - } - val->value.s = buf; + status = sr16(dev, MV_PORTREG(STATUS, port)); - return 0; -} + link->link = status & MV_PORT_STATUS_LINK; + if (!link->link) + return 0; -static int -mvsw61xx_get_port_speed(struct switch_dev *dev, - const struct switch_attr *attr, struct switch_val *val) -{ - u16 status, speed; + link->duplex = status & MV_PORT_STATUS_FDX; - status = sr16(dev, MV_PORTREG(STATUS, val->port_vlan)); speed = (status & MV_PORT_STATUS_SPEED_MASK) >> MV_PORT_STATUS_SPEED_SHIFT; - val->value.i = 0; - - if (status & MV_PORT_STATUS_LINK) { - switch (speed) { - case MV_PORT_STATUS_SPEED_10: - val->value.i = 10; - break; - case MV_PORT_STATUS_SPEED_100: - val->value.i = 100; - break; - case MV_PORT_STATUS_SPEED_1000: - val->value.i = 1000; - break; - } + switch (speed) { + case MV_PORT_STATUS_SPEED_10: + link->speed = SWITCH_PORT_SPEED_10; + break; + case MV_PORT_STATUS_SPEED_100: + link->speed = SWITCH_PORT_SPEED_100; + break; + case MV_PORT_STATUS_SPEED_1000: + link->speed = SWITCH_PORT_SPEED_1000; + break; } return 0; @@ -333,6 +340,7 @@ static int mvsw61xx_set_vlan_ports(struct switch_dev *dev, state->vlans[vno].mask = 0; state->vlans[vno].port_mode = 0; + state->vlans[vno].port_sstate = 0; if(state->vlans[vno].vid == 0) state->vlans[vno].vid = vno; @@ -348,6 +356,8 @@ static int mvsw61xx_set_vlan_ports(struct switch_dev *dev, mode = MV_VTUCTL_EGRESS_UNTAGGED; state->vlans[vno].port_mode |= mode << (pno * 4); + state->vlans[vno].port_sstate |= + MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2); } /* @@ -447,14 +457,14 @@ static int mvsw61xx_set_enable_vlan(struct switch_dev *dev, static int mvsw61xx_vtu_program(struct switch_dev *dev) { struct mvsw61xx_state *state = get_state(dev); - u16 v1, v2; + u16 v1, v2, s1, s2; int i; /* Flush */ mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), MV_VTUOP_INPROGRESS, 0); sw16(dev, MV_GLOBALREG(VTU_OP), - MV_VTUOP_INPROGRESS | MV_VTUOP_VALID); + MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE); /* Write VLAN table */ for (i = 1; i < dev->vlans; i++) { @@ -466,14 +476,32 @@ static int mvsw61xx_vtu_program(struct switch_dev *dev) mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), MV_VTUOP_INPROGRESS, 0); - sw16(dev, MV_GLOBALREG(VTU_VID), - MV_VTUOP_VALID | state->vlans[i].vid); + /* Write per-VLAN port state into STU */ + s1 = (u16) (state->vlans[i].port_sstate & 0xffff); + s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff); + + sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID); + sw16(dev, MV_GLOBALREG(VTU_SID), i); + sw16(dev, MV_GLOBALREG(VTU_DATA1), s1); + sw16(dev, MV_GLOBALREG(VTU_DATA2), s2); + sw16(dev, MV_GLOBALREG(VTU_DATA3), 0); + + sw16(dev, MV_GLOBALREG(VTU_OP), + MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD); + mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), + MV_VTUOP_INPROGRESS, 0); - v1 = (u16)(state->vlans[i].port_mode & 0xffff); - v2 = (u16)((state->vlans[i].port_mode >> 16) & 0xffff); + /* Write VLAN information into VTU */ + v1 = (u16) (state->vlans[i].port_mode & 0xffff); + v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff); + sw16(dev, MV_GLOBALREG(VTU_VID), + MV_VTU_VID_VALID | state->vlans[i].vid); + sw16(dev, MV_GLOBALREG(VTU_SID), i); + sw16(dev, MV_GLOBALREG(VTU_FID), i); sw16(dev, MV_GLOBALREG(VTU_DATA1), v1); sw16(dev, MV_GLOBALREG(VTU_DATA2), v2); + sw16(dev, MV_GLOBALREG(VTU_DATA3), 0); sw16(dev, MV_GLOBALREG(VTU_OP), MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD); @@ -498,8 +526,10 @@ static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno) if(mode != MV_VTUCTL_EGRESS_TAGGED) state->ports[i].pvid = state->vlans[vno].vid; - if (state->vlans[vno].port_based) + if (state->vlans[vno].port_based) { state->ports[i].mask |= state->vlans[vno].mask; + state->ports[i].fdb = vno; + } else state->ports[i].qmode = MV_8021Q_MODE_SECURE; } @@ -514,8 +544,6 @@ static int mvsw61xx_update_state(struct switch_dev *dev) if (!state->registered) return -EINVAL; - mvsw61xx_vtu_program(dev); - /* * Set 802.1q-only mode if vlan_enabled is true. * @@ -558,8 +586,14 @@ static int mvsw61xx_update_state(struct switch_dev *dev) state->ports[i].mask &= ~(1 << i); - reg = sr16(dev, MV_PORTREG(VLANMAP, i)) & ~MV_PORTS_MASK; - reg |= state->ports[i].mask; + /* set default forwarding DB number and port mask */ + reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK; + reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) & + MV_FDB_HI_MASK; + sw16(dev, MV_PORTREG(CONTROL1, i), reg); + + reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) | + state->ports[i].mask; sw16(dev, MV_PORTREG(VLANMAP, i), reg); reg = sr16(dev, MV_PORTREG(CONTROL2, i)) & @@ -568,6 +602,8 @@ static int mvsw61xx_update_state(struct switch_dev *dev) sw16(dev, MV_PORTREG(CONTROL2, i), reg); } + mvsw61xx_vtu_program(dev); + return 0; } @@ -576,7 +612,20 @@ static int mvsw61xx_apply(struct switch_dev *dev) return mvsw61xx_update_state(dev); } -static int mvsw61xx_reset(struct switch_dev *dev) +static void mvsw61xx_enable_serdes(struct switch_dev *dev) +{ + int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES, + MV_PAGE_FIBER_SERDES, MII_BMCR); + if (bmcr < 0) + return; + + if (bmcr & BMCR_PDOWN) + mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES, + MV_PAGE_FIBER_SERDES, MII_BMCR, + bmcr & ~BMCR_PDOWN); +} + +static int _mvsw61xx_reset(struct switch_dev *dev, bool full) { struct mvsw61xx_state *state = get_state(dev); int i; @@ -585,7 +634,7 @@ static int mvsw61xx_reset(struct switch_dev *dev) /* Disable all ports before reset */ for (i = 0; i < dev->ports; i++) { reg = sr16(dev, MV_PORTREG(CONTROL, i)) & - ~MV_PORTCTRL_ENABLED; + ~MV_PORTCTRL_FORWARDING; sw16(dev, MV_PORTREG(CONTROL, i), reg); } @@ -597,17 +646,41 @@ static int mvsw61xx_reset(struct switch_dev *dev) return -ETIMEDOUT; for (i = 0; i < dev->ports; i++) { + state->ports[i].fdb = 0; state->ports[i].qmode = 0; state->ports[i].mask = 0; state->ports[i].pvid = 0; /* Force flow control off */ - reg = sr16(dev, MV_PORTREG(FORCE, i)) & ~MV_FORCE_FC_MASK; - reg |= MV_FORCE_FC_DISABLE; - sw16(dev, MV_PORTREG(FORCE, i), reg); + reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK; + reg |= MV_PHYCTL_FC_DISABLE; + sw16(dev, MV_PORTREG(PHYCTL, i), reg); /* Set port association vector */ sw16(dev, MV_PORTREG(ASSOC, i), (1 << i)); + + /* power up phys */ + if (full && i < 5) { + mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL, + MV_SPEC_MDI_CROSS_AUTO | + MV_SPEC_ENERGY_DETECT | + MV_SPEC_DOWNSHIFT_COUNTER); + mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET | + BMCR_ANENABLE | BMCR_FULLDPLX | + BMCR_SPEED1000); + } + + /* enable SerDes if necessary */ + if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) { + u16 sts = sr16(dev, MV_PORTREG(STATUS, i)); + u16 mode = sts & MV_PORT_STATUS_CMODE_MASK; + + if (mode == MV_PORT_STATUS_CMODE_100BASE_X || + mode == MV_PORT_STATUS_CMODE_1000BASE_X || + mode == MV_PORT_STATUS_CMODE_SGMII) { + mvsw61xx_enable_serdes(dev); + } + } } for (i = 0; i < dev->vlans; i++) { @@ -615,6 +688,7 @@ static int mvsw61xx_reset(struct switch_dev *dev) state->vlans[i].mask = 0; state->vlans[i].vid = 0; state->vlans[i].port_mode = 0; + state->vlans[i].port_sstate = 0; } state->vlan_enabled = 0; @@ -624,13 +698,18 @@ static int mvsw61xx_reset(struct switch_dev *dev) /* Re-enable ports */ for (i = 0; i < dev->ports; i++) { reg = sr16(dev, MV_PORTREG(CONTROL, i)) | - MV_PORTCTRL_ENABLED; + MV_PORTCTRL_FORWARDING; sw16(dev, MV_PORTREG(CONTROL, i), reg); } return 0; } +static int mvsw61xx_reset(struct switch_dev *dev) +{ + return _mvsw61xx_reset(dev, false); +} + enum { MVSW61XX_ENABLE_VLAN, }; @@ -643,8 +722,6 @@ enum { enum { MVSW61XX_PORT_MASK, MVSW61XX_PORT_QMODE, - MVSW61XX_PORT_STATUS, - MVSW61XX_PORT_LINK, }; static const struct switch_attr mvsw61xx_global[] = { @@ -694,22 +771,6 @@ static const struct switch_attr mvsw61xx_port[] = { .get = mvsw61xx_get_port_qmode, .set = mvsw61xx_set_port_qmode, }, - [MVSW61XX_PORT_STATUS] = { - .id = MVSW61XX_PORT_STATUS, - .type = SWITCH_TYPE_STRING, - .description = "Return port status", - .name = "status", - .get = mvsw61xx_get_port_status, - .set = NULL, - }, - [MVSW61XX_PORT_LINK] = { - .id = MVSW61XX_PORT_LINK, - .type = SWITCH_TYPE_INT, - .description = "Get link speed", - .name = "link", - .get = mvsw61xx_get_port_speed, - .set = NULL, - }, }; static const struct switch_dev_ops mvsw61xx_ops = { @@ -725,8 +786,9 @@ static const struct switch_dev_ops mvsw61xx_ops = { .attr = mvsw61xx_port, .n_attr = ARRAY_SIZE(mvsw61xx_port), }, - .get_port_pvid = mvsw61xx_get_pvid, - .set_port_pvid = mvsw61xx_set_pvid, + .get_port_link = mvsw61xx_get_port_link, + .get_port_pvid = mvsw61xx_get_port_pvid, + .set_port_pvid = mvsw61xx_set_port_pvid, .get_vlan_ports = mvsw61xx_get_vlan_ports, .set_vlan_ports = mvsw61xx_set_vlan_ports, .apply_config = mvsw61xx_apply, @@ -740,8 +802,8 @@ static int mvsw61xx_probe(struct platform_device *pdev) struct mvsw61xx_state *state; struct device_node *np = pdev->dev.of_node; struct device_node *mdio; + char *model_str; u32 val; - u16 reg; int err; state = kzalloc(sizeof(*state), GFP_KERNEL); @@ -776,17 +838,31 @@ static int mvsw61xx_probe(struct platform_device *pdev) state->base_addr = MV_BASE; } - reg = r16(state->bus, state->is_indirect, state->base_addr, - MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; - if (reg != MV_IDENT_VALUE) { - dev_err(&pdev->dev, "No switch found at 0x%02x\n", + state->model = r16(state->bus, state->is_indirect, state->base_addr, + MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; + + switch(state->model) { + case MV_IDENT_VALUE_6171: + model_str = MV_IDENT_STR_6171; + break; + case MV_IDENT_VALUE_6172: + model_str = MV_IDENT_STR_6172; + break; + case MV_IDENT_VALUE_6176: + model_str = MV_IDENT_STR_6176; + break; + case MV_IDENT_VALUE_6352: + model_str = MV_IDENT_STR_6352; + break; + default: + dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n", state->base_addr); err = -ENODEV; goto out_err; } platform_set_drvdata(pdev, state); - dev_info(&pdev->dev, "Found %s at %s:%02x\n", MV_IDENT_STR, + dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str, state->bus->id, state->base_addr); dev_info(&pdev->dev, "Using %sdirect addressing\n", @@ -808,10 +884,12 @@ static int mvsw61xx_probe(struct platform_device *pdev) state->dev.vlans = MV_VLANS; state->dev.cpu_port = state->cpu_port0; state->dev.ports = MV_PORTS; - state->dev.name = MV_IDENT_STR; + state->dev.name = model_str; state->dev.ops = &mvsw61xx_ops; state->dev.alias = dev_name(&pdev->dev); + _mvsw61xx_reset(&state->dev, true); + err = register_switch(&state->dev, NULL); if (err < 0) goto out_err; @@ -839,6 +917,9 @@ mvsw61xx_remove(struct platform_device *pdev) static const struct of_device_id mvsw61xx_match[] = { { .compatible = "marvell,88e6171" }, + { .compatible = "marvell,88e6172" }, + { .compatible = "marvell,88e6176" }, + { .compatible = "marvell,88e6352" }, { } }; MODULE_DEVICE_TABLE(of, mvsw61xx_match);