From 4511da99b967a7598b2352dde3acf82cacb11978 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 8 Jun 2010 20:18:51 +0000 Subject: [PATCH] ip17xx: Add support for IP175D Add support for the IP175D chip. Since the register set is vastly different from the previous models, we cannot not use the register number tables in struct register_mappings (except for VLAN_DEFAULT_TAG_REG), so we supply a different set of low-level functions. Unlike with the previous models, we keep the VLAN setup in our state structure instead of querying the hardware (it would be much harder in case of IP175D, because the mapping between hardware and software state is not 1:1). Therefore, get_flags() and get_state() are no-ops. Signed-off-by: Martin Mares Signed-off-by: Patrick Horn SVN-Revision: 21721 --- .../files/drivers/net/phy/ip175c.c | 170 +++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) diff --git a/target/linux/generic-2.6/files/drivers/net/phy/ip175c.c b/target/linux/generic-2.6/files/drivers/net/phy/ip175c.c index ef4b4d909271..3f21d72b6640 100644 --- a/target/linux/generic-2.6/files/drivers/net/phy/ip175c.c +++ b/target/linux/generic-2.6/files/drivers/net/phy/ip175c.c @@ -263,6 +263,56 @@ static const struct register_mappings IP175A = { }; +static int ip175d_get_flags(struct ip175c_state *state); +static int ip175d_get_state(struct ip175c_state *state); +static int ip175d_update_state(struct ip175c_state *state); +static int ip175d_set_vlan_mode(struct ip175c_state *state); +static int ip175d_reset(struct ip175c_state *state); + +static const struct register_mappings IP175D = { + .NAME = "IP175D", + .MODEL_NO = 0x18, + + // The IP175D has a completely different interface, so we leave most + // of the registers undefined and switch to different code paths. + + .VLAN_DEFAULT_TAG_REG = { + NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED, + NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED, + }, + + .ADD_TAG_REG = NOTSUPPORTED, + .REMOVE_TAG_REG = NOTSUPPORTED, + + .SIMPLE_VLAN_REGISTERS = 0, + + .VLAN_LOOKUP_REG = NOTSUPPORTED, + .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, + .TAG_VLAN_MASK_REG = NOTSUPPORTED, + + .RESET_VAL = 0x175D, + .RESET_REG = {20,2}, + .MODE_REG = NOTSUPPORTED, + + .ROUTER_CONTROL_REG = NOTSUPPORTED, + .ROUTER_EN_BIT = -1, + .NUMLAN_GROUPS_BIT = -1, + + .VLAN_CONTROL_REG = NOTSUPPORTED, + .TAG_VLAN_BIT = -1, + + .NUM_PORTS = 6, + .CPU_PORT = 5, + + .MII_REGISTER_EN = NOTSUPPORTED, + + .get_flags = ip175d_get_flags, + .get_state = ip175d_get_state, + .update_state = ip175d_update_state, + .set_vlan_mode = ip175d_set_vlan_mode, + .reset = ip175d_reset, +}; + struct ip175c_state { struct switch_dev dev; struct mii_bus *mii_bus; @@ -407,7 +457,7 @@ static int get_model(struct ip175c_state *state) chip_no = ip_phy_read(state, 20, 0); pr_debug("IP175C: Chip ID register reads %04x\n", chip_no); if (chip_no == 0x175d) { - state->regs = &IP175C; + state->regs = &IP175D; } else { state->regs = &IP175C; } @@ -419,6 +469,8 @@ static int get_model(struct ip175c_state *state) return 0; } +/*** Low-level functions for the older models ***/ + /** Get only the vlan and router flags on the router **/ static int ip175c_get_flags(struct ip175c_state *state) { @@ -727,6 +779,122 @@ static int ip175c_do_reset(struct ip175c_state *state) return 0; } +/*** Low-level functions for IP175D ***/ + +static int ip175d_get_flags(struct ip175c_state *state) +{ + // We keep the configuration of the switch cached, so get doesn't do anything + return 0; +} + +static int ip175d_get_state(struct ip175c_state *state) +{ + // Again, the configuration is fully cached + return 0; +} + +static int ip175d_update_state(struct ip175c_state *state) +{ + unsigned int filter_mask = 0; + unsigned int ports[16], add[16], rem[16]; + int i, j; + int err = 0; + + for (i = 0; i < 16; i++) { + ports[i] = 0; + add[i] = 0; + rem[i] = 0; + if (!state->vlan_enabled) { + err |= ip_phy_write(state, 22, 14+i, i+1); // default tags + ports[i] = 0x3f; + continue; + } + if (!state->vlans[i].tag) { + // Reset the filter + err |= ip_phy_write(state, 22, 14+i, 0); // tag + continue; + } + filter_mask |= 1 << i; + err |= ip_phy_write(state, 22, 14+i, state->vlans[i].tag); + ports[i] = state->vlans[i].ports; + for (j = 0; j < 6; j++) { + if (ports[i] & (1 << j)) { + if (state->add_tag & (1 << j)) + add[i] |= 1 << j; + if (state->remove_tag & (1 << j)) + rem[i] |= 1 << j; + } + } + } + + // Port masks, tag adds and removals + for (i = 0; i < 8; i++) { + err |= ip_phy_write(state, 23, i, ports[2*i] | (ports[2*i+1] << 8)); + err |= ip_phy_write(state, 23, 8+i, add[2*i] | (add[2*i+1] << 8)); + err |= ip_phy_write(state, 23, 16+i, rem[2*i] | (rem[2*i+1] << 8)); + } + err |= ip_phy_write(state, 22, 10, filter_mask); + + // Default VLAN tag for each port + for (i = 0; i < 6; i++) + err |= ip_phy_write(state, 22, 4+i, state->vlans[state->ports[i].pvid].tag); + + return (err ? -EIO : 0); +} + +static int ip175d_set_vlan_mode(struct ip175c_state *state) +{ + int i; + int err = 0; + + if (state->vlan_enabled) { + // VLAN classification rules: tag-based VLANs, use VID to classify, + // drop packets that cannot be classified. + err |= ip_phy_write_masked(state, 22, 0, 0x3fff, 0x003f); + + // Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed, + // VID=0xfff discarded, admin both tagged and untagged, ingress + // filters enabled. + err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f); + + // Egress rules: IGMP processing off, keep VLAN header off + err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000); + } else { + // VLAN classification rules: everything off & clear table + err |= ip_phy_write_masked(state, 22, 0, 0xbfff, 0x8000); + + // Ingress and egress rules: set to defaults + err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f); + err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000); + } + + // Reset default VLAN for each port to 0 + for (i = 0; i < 6; i++) + state->ports[i].pvid = 0; + + err |= ip175d_update_state(state); + + return (err ? -EIO : 0); +} + +static int ip175d_reset(struct ip175c_state *state) +{ + int err = 0; + + // Disable the special tagging mode + err |= ip_phy_write_masked(state, 21, 22, 0x0003, 0x0000); + + // Set 802.1q protocol type + err |= ip_phy_write(state, 22, 3, 0x8100); + + state->vlan_enabled = 0; + err |= ip175d_set_vlan_mode(state); + + return (err ? -EIO : 0); +} + +/*** High-level functions ***/ + static int ip175c_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ip175c_state *state = dev->priv; -- 2.30.2