X-Git-Url: http://git.rohieb.name/openwrt.git/blobdiff_plain/398de7c230a76347689439c07dfac738ff0e7ab0..08171f79fc8271d1e8fbb829af4f64c6695c670d:/target/linux/generic-2.6/files/drivers/net/phy/ip175c.c 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 d911c860f..58ee675f0 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 @@ -25,6 +25,7 @@ #include #include #include +#include #define MAX_VLANS 16 #define MAX_PORTS 9 @@ -100,15 +101,11 @@ struct register_mappings { bitnum SIMPLE_VLAN_REGISTERS; // 175C has two vlans per register but 178C has only one. // Pointers to functions which manipulate hardware state - int (*get_flags)(struct ip175c_state *state); - int (*get_state)(struct ip175c_state *state); int (*update_state)(struct ip175c_state *state); int (*set_vlan_mode)(struct ip175c_state *state); int (*reset)(struct ip175c_state *state); }; -static int ip175c_get_flags(struct ip175c_state *state); -static int ip175c_get_state(struct ip175c_state *state); static int ip175c_update_state(struct ip175c_state *state); static int ip175c_set_vlan_mode(struct ip175c_state *state); static int ip175c_do_reset(struct ip175c_state *state); @@ -155,8 +152,6 @@ static const struct register_mappings IP178C = { .MII_REGISTER_EN = NOTSUPPORTED, - .get_flags = ip175c_get_flags, - .get_state = ip175c_get_state, .update_state = ip175c_update_state, .set_vlan_mode = ip175c_set_vlan_mode, .reset = ip175c_do_reset, @@ -204,8 +199,6 @@ static const struct register_mappings IP175C = { .MII_REGISTER_EN = NOTSUPPORTED, - .get_flags = ip175c_get_flags, - .get_state = ip175c_get_state, .update_state = ip175c_update_state, .set_vlan_mode = ip175c_set_vlan_mode, .reset = ip175c_do_reset, @@ -254,14 +247,58 @@ static const struct register_mappings IP175A = { .MII_REGISTER_EN = {0, 18}, .MII_REGISTER_EN_BIT = 7, - .get_flags = ip175c_get_flags, - .get_state = ip175c_get_state, .update_state = ip175c_update_state, .set_vlan_mode = ip175c_set_vlan_mode, .reset = ip175c_do_reset, }; +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, + + .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; @@ -276,7 +313,10 @@ struct ip175c_state { unsigned int add_tag; unsigned int remove_tag; int num_vlans; - unsigned int vlan_ports[MAX_VLANS]; + struct vlan_state { + unsigned int ports; + unsigned int tag; // VLAN tag (IP175D only) + } vlans[MAX_VLANS]; const struct register_mappings *regs; reg proc_mii; // phy/reg for the low level register access via swconfig @@ -375,183 +415,48 @@ static int setPhy(struct ip175c_state *state, reg mii, u16 value) static int get_model(struct ip175c_state *state) { - reg oui_id_reg = {0, 2}; - int oui_id; - reg model_no_reg = {0, 3}; - int model_no, model_no_orig; - - // 175 and 178 have the same oui ID. - reg oui_id_reg_178c = {5, 2}; // returns error on IP175C. - int is_178c = 0; - - oui_id = getPhy(state, oui_id_reg); - if (oui_id != 0x0243) { - // non - return -ENODEV; // Not a IC+ chip. - } - oui_id = getPhy(state, oui_id_reg_178c); - if (oui_id == 0x0243) { - is_178c = 1; - } + int id1, id2; + int oui_id, model_no, rev_no, chip_no; + + id1 = ip_phy_read(state, 0, 2); + id2 = ip_phy_read(state, 0, 3); + oui_id = (id1 << 6) | ((id2 >> 10) & 0x3f); + model_no = (id2 >> 4) & 0x3f; + rev_no = id2 & 0xf; + pr_debug("IP175C: Identified oui=%06x model=%02x rev=%X\n", oui_id, model_no, rev_no); - model_no_orig = getPhy(state, model_no_reg); - if (model_no_orig < 0) { + if (oui_id != 0x0090c3) // No other oui_id should have reached us anyway return -ENODEV; - } - model_no = model_no_orig >> 4; // shift out revision number. - model_no &= 0x3f; // only take the model number (low 6 bits). + if (model_no == IP175A.MODEL_NO) { state->regs = &IP175A; } else if (model_no == IP175C.MODEL_NO) { - if (is_178c) { + /* + * Several models share the same model_no: + * 178C has more PHYs, so we try whether the device responds to a read from PHY5 + * 175D has a new chip ID register + * 175C has neither + */ + if (ip_phy_read(state, 5, 2) == 0x0243) { state->regs = &IP178C; } else { - state->regs = &IP175C; - } - } else { - printk(KERN_WARNING "ip175c: Found an unknown IC+ switch with model number %02Xh.\n", model_no_orig); - return -EPERM; - } - return 0; -} - -/** Get only the vlan and router flags on the router **/ -static int ip175c_get_flags(struct ip175c_state *state) -{ - int val; - - state->router_mode = 0; - state->vlan_enabled = -1; // hack - state->num_vlans = 0; - - if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) { - return 0; // not an error if it doesn't support enable vlan. - } - - val = getPhy(state, state->regs->ROUTER_CONTROL_REG); - if (val < 0) { - return val; - } - if (state->regs->ROUTER_EN_BIT >= 0) - state->router_mode = ((val>>state->regs->ROUTER_EN_BIT) & 1); - - if (state->regs->NUMLAN_GROUPS_BIT >= 0) { - state->num_vlans = (val >> state->regs->NUMLAN_GROUPS_BIT); - state->num_vlans &= (state->regs->NUMLAN_GROUPS_MAX-1); - state->num_vlans+=1; // does not include WAN. - } - - val = getPhy(state, state->regs->VLAN_CONTROL_REG); - if (val < 0) { - return 0; - } - if (state->regs->TAG_VLAN_BIT >= 0) - state->vlan_enabled = ((val>>state->regs->TAG_VLAN_BIT) & 1); - - return 0; -} - -/** Get all state variables for VLAN mappings and port-based tagging **/ -static int ip175c_get_state(struct ip175c_state *state) -{ - int i, j; - int ret; - - ret = ip175c_get_flags(state); - if (ret < 0) { - return ret; - } - - GET_PORT_BITS(state, state->remove_tag, - state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT); - GET_PORT_BITS(state, state->add_tag, - state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT); - - if (state->vlan_enabled == -1) { - // not sure how to get this... - state->vlan_enabled = (state->remove_tag || state->add_tag); - } - - if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) { - for (j=0; jports[j].shareports = 0; // initialize them in case. - } - for (j=0; jregs->NUM_PORTS; j++) { - reg addr; - const bitnum *bit_lookup = (j%2==0)? - state->regs->VLAN_LOOKUP_EVEN_BIT: - state->regs->VLAN_LOOKUP_ODD_BIT; - addr = state->regs->VLAN_LOOKUP_REG; - if (state->regs->SIMPLE_VLAN_REGISTERS) { - addr.m += j; - } else { - switch (j) { - case 0: - case 1: - break; - case 2: - case 3: - addr.m+=1; - break; - case 4: - addr.m+=2; - break; - case 5: - addr = state->regs->VLAN_LOOKUP_REG_5; - break; - } - } - - if (REG_SUPP(addr)) { - GET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup); - } - } - } else { - for (j=0; jports[j].shareports = 0xff; - } - } - - for (i=0; iregs->VLAN_DEFAULT_TAG_REG[i])) { - int val = getPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i]); - if (val < 0) { - return val; - } - state->ports[i].pvid = val; - } else { - state->ports[i].pvid = 0; - } - } - - if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) { - for (j=0; jregs->TAG_VLAN_MASK_REG; - const bitnum *bit_lookup = (j%2==0)? - state->regs->TAG_VLAN_MASK_EVEN_BIT: - state->regs->TAG_VLAN_MASK_ODD_BIT; - if (state->regs->SIMPLE_VLAN_REGISTERS) { - addr.m += j; + 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 = &IP175D; } else { - addr.m += j/2; + state->regs = &IP175C; } - GET_PORT_BITS(state, state->vlan_ports[j], addr, bit_lookup); } } else { - for (j=0; jvlan_ports[j] = 0; - for (i=0; iregs->NUM_PORTS; i++) { - if ((state->ports[i].pvid == j) || - (state->ports[i].pvid == 0)) { - state->vlan_ports[j] |= (1<regs->VLAN_LOOKUP_EVEN_BIT: state->regs->VLAN_LOOKUP_ODD_BIT; - // duplicate code -- sorry addr = state->regs->VLAN_LOOKUP_REG; if (state->regs->SIMPLE_VLAN_REGISTERS) { addr.m += j; @@ -649,7 +553,7 @@ static int ip175c_set_state(struct ip175c_state *state) } else { addr.m += j/2; } - vlan_mask = state->vlan_ports[j]; + vlan_mask = state->vlans[j].ports; SET_PORT_BITS(state, vlan_mask, addr, bit_lookup); } } @@ -676,7 +580,7 @@ static void ip175c_correct_vlan_state(struct ip175c_state *state) int i, j; state->num_vlans = 0; for (i=0; ivlan_ports[i] != 0) { + if (state->vlans[i].ports != 0) { state->num_vlans = i+1; // Hack -- we need to store the "set" vlans somewhere... } } @@ -690,11 +594,10 @@ static void ip175c_correct_vlan_state(struct ip175c_state *state) } state->ports[i].shareports = portmask; for (j=0; jvlan_ports[j] & portmask) - state->ports[i].shareports |= state->vlan_ports[j]; + if (state->vlans[j].ports & portmask) + state->ports[i].shareports |= state->vlans[j].ports; } } - state->remove_tag = ((~state->add_tag) & ((1<regs->NUM_PORTS)-1)); } static int ip175c_update_state(struct ip175c_state *state) @@ -721,33 +624,141 @@ static int ip175c_do_reset(struct ip175c_state *state) return err; } - return 0; + return ip175c_update_state(state); +} + +/*** Low-level functions for IP175D ***/ + +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; - int err; - err = state->regs->get_state(state); // May be set in get_state. - if (err < 0) - return err; val->value.i = state->vlan_enabled; return 0; } +static void ip175c_reset_vlan_config(struct ip175c_state *state) +{ + int i; + + state->remove_tag = (state->vlan_enabled ? ((1<regs->NUM_PORTS)-1) : 0x0000); + state->add_tag = 0x0000; + for (i = 0; i < MAX_VLANS; i++) { + state->vlans[i].ports = 0x0000; + state->vlans[i].tag = (i ? i : 16); + } + for (i = 0; i < MAX_PORTS; i++) + state->ports[i].pvid = 0; +} + static int ip175c_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ip175c_state *state = dev->priv; - int err; int enable; - int i; - err = state->regs->get_state(state); - if (err < 0) - return err; enable = val->value.i; - if (state->vlan_enabled == enable) { // Do not change any state. return 0; @@ -755,10 +766,7 @@ static int ip175c_set_enable_vlan(struct switch_dev *dev, const struct switch_at state->vlan_enabled = enable; // Otherwise, if we are switching state, set fields to a known default. - state->remove_tag = 0x0000; - state->add_tag = 0x0000; - for (i = 0; i < MAX_VLANS; i++) - state->vlan_ports[i] = 0x0; + ip175c_reset_vlan_config(state); return state->regs->set_vlan_mode(state); } @@ -766,7 +774,6 @@ static int ip175c_set_enable_vlan(struct switch_dev *dev, const struct switch_at static int ip175c_get_ports(struct switch_dev *dev, struct switch_val *val) { struct ip175c_state *state = dev->priv; - int err; int b; int ind; unsigned int ports; @@ -774,11 +781,7 @@ static int ip175c_get_ports(struct switch_dev *dev, struct switch_val *val) if (val->port_vlan >= dev->vlans || val->port_vlan < 0) return -EINVAL; - err = state->regs->get_state(state); - if (err<0) - return err; - - ports = state->vlan_ports[val->port_vlan]; + ports = state->vlans[val->port_vlan].ports; b = 0; ind = 0; while (b < MAX_PORTS) { @@ -800,23 +803,20 @@ static int ip175c_set_ports(struct switch_dev *dev, struct switch_val *val) { struct ip175c_state *state = dev->priv; int i; - int err; if (val->port_vlan >= dev->vlans || val->port_vlan < 0) return -EINVAL; - err = state->regs->get_state(state); - if (err < 0) - return err; - - state->vlan_ports[val->port_vlan] = 0; + state->vlans[val->port_vlan].ports = 0; for (i = 0; i < val->len; i++) { - int bitmask = (1<value.ports[i].id); - state->vlan_ports[val->port_vlan] |= bitmask; + unsigned int bitmask = (1<value.ports[i].id); + state->vlans[val->port_vlan].ports |= bitmask; if (val->value.ports[i].flags & (1<add_tag |= bitmask; + state->remove_tag &= (~bitmask); } else { state->add_tag &= (~bitmask); + state->remove_tag |= bitmask; } } @@ -826,11 +826,6 @@ static int ip175c_set_ports(struct switch_dev *dev, struct switch_val *val) static int ip175c_apply(struct switch_dev *dev) { struct ip175c_state *state = dev->priv; - int err; - - err = state->regs->get_flags(state); - if (err < 0) - return err; if (REG_SUPP(state->regs->MII_REGISTER_EN)) { int val = getPhy(state, state->regs->MII_REGISTER_EN); @@ -848,10 +843,6 @@ static int ip175c_reset(struct switch_dev *dev) struct ip175c_state *state = dev->priv; int i, err; - err = state->regs->get_flags(state); - if (err < 0) - return err; - if (REG_SUPP(state->regs->RESET_REG)) { err = setPhy(state, state->regs->RESET_REG, state->regs->RESET_VAL); if (err < 0) @@ -873,17 +864,16 @@ static int ip175c_reset(struct switch_dev *dev) return err; } + state->router_mode = 0; + state->vlan_enabled = 0; + ip175c_reset_vlan_config(state); + return state->regs->reset(state); } static int ip175c_get_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ip175c_state *state = dev->priv; - int err; - - err = state->regs->get_state(state); - if (err < 0) - return err; if (state->add_tag & (1<port_vlan)) { if (state->remove_tag & (1<port_vlan)) @@ -902,11 +892,6 @@ static int ip175c_get_tagged(struct switch_dev *dev, const struct switch_attr *a static int ip175c_set_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ip175c_state *state = dev->priv; - int err; - - err = state->regs->get_state(state); - if (err < 0) - return err; state->add_tag &= ~(1<port_vlan); state->remove_tag &= ~(1<port_vlan); @@ -999,6 +984,34 @@ static int ip175c_read_name(struct switch_dev *dev, const struct switch_attr *at return 0; } +static int ip175c_get_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct ip175c_state *state = dev->priv; + int vlan = val->port_vlan; + + if (vlan < 0 || vlan >= MAX_VLANS) + return -EINVAL; + + val->value.i = state->vlans[vlan].tag; + return 0; +} + +static int ip175c_set_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct ip175c_state *state = dev->priv; + int vlan = val->port_vlan; + int tag = val->value.i; + + if (vlan < 0 || vlan >= MAX_VLANS) + return -EINVAL; + + if (tag < 0 || tag > 4095) + return -EINVAL; + + state->vlans[vlan].tag = tag; + return state->regs->update_state(state); +} + static int ip175c_set_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ip175c_state *state = dev->priv; @@ -1118,17 +1131,11 @@ static int ip175c_get_pvid(struct switch_dev *dev, int port, int *val) static int ip175c_set_pvid(struct switch_dev *dev, int port, int val) { struct ip175c_state *state = dev->priv; - int err; if (val < 0 || val >= MAX_VLANS) return -EINVAL; - err = state->regs->get_state(state); - if (err < 0) - return err; - state->ports[port].pvid = val; - return state->regs->update_state(state); } @@ -1149,6 +1156,10 @@ enum Globals { IP175C_REGISTER_ERRNO, }; +enum Vlans { + IP175C_VLAN_TAG, +}; + static const struct switch_attr ip175c_global[] = { [IP175C_ENABLE_VLAN] = { .id = IP175C_ENABLE_VLAN, @@ -1194,6 +1205,14 @@ static const struct switch_attr ip175c_global[] = { }; static const struct switch_attr ip175c_vlan[] = { + [IP175C_VLAN_TAG] = { + .id = IP175C_VLAN_TAG, + .type = SWITCH_TYPE_INT, + .description = "VLAN tag (0-4095) [IP175D only]", + .name = "tag", + .get = ip175c_get_tag, + .set = ip175c_set_tag, + } }; static const struct switch_attr ip175c_port[] = { @@ -1265,6 +1284,7 @@ static int ip175c_probe(struct phy_device *pdev) dev->ports = state->regs->NUM_PORTS; dev->name = state->regs->NAME; + pr_info("IP175C: Found %s at %s\n", dev->name, dev_name(&pdev->dev)); return 0; error: @@ -1286,6 +1306,11 @@ static int ip175c_config_init(struct phy_device *pdev) ip175c_reset(&state->dev); state->registered = true; + + pdev->state = PHY_RUNNING; + pdev->speed = SPEED_100; + pdev->duplex = DUPLEX_FULL; + pdev->pause = pdev->asym_pause = 0; netif_carrier_on(pdev->attached_dev); return 0; @@ -1307,16 +1332,13 @@ static int ip175c_config_aneg(struct phy_device *pdev) static int ip175c_read_status(struct phy_device *pdev) { - pdev->speed = SPEED_100; - pdev->duplex = DUPLEX_FULL; - pdev->pause = pdev->asym_pause = 0; return 0; } static struct phy_driver ip175c_driver = { .name = "IC+ IP175C", - .phy_id = 0x02430d80, - .phy_id_mask = 0x0ffffff0, + .phy_id = 0x02430c00, + .phy_id_mask = 0x0ffffc00, .features = PHY_BASIC_FEATURES, .probe = ip175c_probe, .remove = ip175c_remove,