X-Git-Url: https://git.archive.openwrt.org/?p=openwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Fmvsw61xx.c;h=4d6dfc52cd2fb547f2ae9e8da61c309b896529a9;hp=7199de0eb21b05b6afc76f8a5379bd73fdd7e044;hb=484ecf816460f58fe589565ac88014f369db86ee;hpb=fdc1d1989982029b1888335c551b38278830f4a3 diff --git a/target/linux/generic/files/drivers/net/phy/mvsw61xx.c b/target/linux/generic/files/drivers/net/phy/mvsw61xx.c index 7199de0eb2..4d6dfc52cd 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"); @@ -194,7 +196,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 +206,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 +219,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; + status = sr16(dev, MV_PORTREG(STATUS, port)); - 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; - } + link->link = status & MV_PORT_STATUS_LINK; + if (!link->link) + return 0; - len += sprintf(buf + len, " Mbps, duplex: "); + link->duplex = status & MV_PORT_STATUS_FDX; - 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; - - return 0; -} - -static int -mvsw61xx_get_port_speed(struct switch_dev *dev, - const struct switch_attr *attr, struct switch_val *val) -{ - u16 status, speed; - - 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 +294,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 +310,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 +411,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 +430,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 +480,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 +498,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 +540,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 +556,8 @@ static int mvsw61xx_update_state(struct switch_dev *dev) sw16(dev, MV_PORTREG(CONTROL2, i), reg); } + mvsw61xx_vtu_program(dev); + return 0; } @@ -585,7 +575,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,14 +587,15 @@ 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)); @@ -615,6 +606,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,7 +616,7 @@ 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); } @@ -643,8 +635,6 @@ enum { enum { MVSW61XX_PORT_MASK, MVSW61XX_PORT_QMODE, - MVSW61XX_PORT_STATUS, - MVSW61XX_PORT_LINK, }; static const struct switch_attr mvsw61xx_global[] = { @@ -694,22 +684,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 +699,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 +715,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 +751,28 @@ 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; + 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,7 +794,7 @@ 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); @@ -839,6 +825,8 @@ mvsw61xx_remove(struct platform_device *pdev) static const struct of_device_id mvsw61xx_match[] = { { .compatible = "marvell,88e6171" }, + { .compatible = "marvell,88e6172" }, + { .compatible = "marvell,88e6176" }, { } }; MODULE_DEVICE_TABLE(of, mvsw61xx_match);