#define REG_SUPP(x) (((x).m != ((u16)-1)) && ((x).p != (u16)-1))
+struct ip175c_state;
+
/*********** CONSTANTS ***********/
struct register_mappings {
char *NAME;
// set to 1 for 178C, 0 for 175C.
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);
+
static const struct register_mappings IP178C = {
.NAME = "IP178C",
.MODEL_NO = 0x18,
.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,
};
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,
};
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,
};
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
}
/** Get only the vlan and router flags on the router **/
-static int get_flags(struct ip175c_state *state)
+static int ip175c_get_flags(struct ip175c_state *state)
{
int val;
}
/** Get all state variables for VLAN mappings and port-based tagging **/
-static int get_state(struct ip175c_state *state)
+static int ip175c_get_state(struct ip175c_state *state)
{
int i, j;
int ret;
- ret = get_flags(state);
+
+ 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,
} else {
addr.m += j/2;
}
- GET_PORT_BITS(state, state->vlan_ports[j], addr, bit_lookup);
+ GET_PORT_BITS(state, state->vlans[j].ports, addr, bit_lookup);
}
} else {
for (j=0; j<MAX_VLANS; j++) {
- state->vlan_ports[j] = 0;
+ state->vlans[j].ports = 0;
for (i=0; i<state->regs->NUM_PORTS; i++) {
if ((state->ports[i].pvid == j) ||
(state->ports[i].pvid == 0)) {
- state->vlan_ports[j] |= (1<<i);
+ state->vlans[j].ports |= (1<<i);
}
}
}
return 0;
}
-/** Only update vlan and router flags in the switch **/
-static int update_flags(struct ip175c_state *state)
+/** Only set vlan and router flags in the switch **/
+static int ip175c_set_flags(struct ip175c_state *state)
{
int val;
return setPhy(state, state->regs->ROUTER_CONTROL_REG, val);
}
-/** Update all VLAN and port state. Usually you should call "correct_vlan_state" first. **/
-static int update_state(struct ip175c_state *state)
+/** Set all VLAN and port state. Usually you should call "correct_vlan_state" first. **/
+static int ip175c_set_state(struct ip175c_state *state)
{
int j;
int i;
} 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);
}
}
}
}
- return update_flags(state);
+ return ip175c_set_flags(state);
}
/**
* Uses only the VLAN port mask and the add tag mask to generate the other fields:
* which ports are part of the same VLAN, removing vlan tags, and VLAN tag ids.
*/
-static void correct_vlan_state(struct ip175c_state *state)
+static void ip175c_correct_vlan_state(struct ip175c_state *state)
{
int i, j;
state->num_vlans = 0;
for (i=0; i<MAX_VLANS; i++) {
- if (state->vlan_ports[i] != 0) {
+ if (state->vlans[i].ports != 0) {
state->num_vlans = i+1; // Hack -- we need to store the "set" vlans somewhere...
}
}
}
state->ports[i].shareports = portmask;
for (j=0; j<MAX_VLANS; j++) {
- if (state->vlan_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<<state->regs->NUM_PORTS)-1));
+}
+
+static int ip175c_update_state(struct ip175c_state *state)
+{
+ ip175c_correct_vlan_state(state);
+ return ip175c_set_state(state);
+}
+
+static int ip175c_set_vlan_mode(struct ip175c_state *state)
+{
+ return ip175c_update_state(state);
+}
+
+static int ip175c_do_reset(struct ip175c_state *state)
+{
+ int err;
+
+ if (REG_SUPP(state->regs->MODE_REG)) {
+ err = setPhy(state, state->regs->MODE_REG, state->regs->MODE_VAL);
+ if (err < 0)
+ return err;
+ err = getPhy(state, state->regs->MODE_REG);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
}
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 = get_state(state); // May be set in get_state.
+ 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<<state->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 = get_state(state);
+ err = state->regs->get_state(state);
if (err < 0)
return err;
enable = val->value.i;
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_PORTS; i++)
- state->ports[i].shareports = 0xffff;
+ ip175c_reset_vlan_config(state);
- for (i = 0; i < MAX_VLANS; i++)
- state->vlan_ports[i] = 0x0;
-
- if (state->vlan_enabled) {
- // Updates other fields only based off vlan_ports and add_tag fields.
- // Note that by default, no ports are in any vlans.
- correct_vlan_state(state);
- }
- // Ensure sane defaults?
- return update_state(state);
+ return state->regs->set_vlan_mode(state);
}
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 = get_state(state);
+ 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) {
if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
return -EINVAL;
- err = get_state(state);
+ 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<<val->value.ports[i].id);
- state->vlan_ports[val->port_vlan] |= bitmask;
+ unsigned int bitmask = (1<<val->value.ports[i].id);
+ state->vlans[val->port_vlan].ports |= bitmask;
if (val->value.ports[i].flags & (1<<SWITCH_PORT_FLAG_TAGGED)) {
state->add_tag |= bitmask;
+ state->remove_tag &= (~bitmask);
} else {
state->add_tag &= (~bitmask);
+ state->remove_tag |= bitmask;
}
}
- correct_vlan_state(state);
- err = update_state(state);
-
- return err;
+ return state->regs->update_state(state);
}
static int ip175c_apply(struct switch_dev *dev)
struct ip175c_state *state = dev->priv;
int err;
- err = get_flags(state);
+ err = state->regs->get_flags(state);
if (err < 0)
return err;
struct ip175c_state *state = dev->priv;
int i, err;
- err = get_flags(state);
+ err = state->regs->get_flags(state);
if (err < 0)
return err;
mdelay(2);
}
- if (REG_SUPP(state->regs->MODE_REG)) {
- err = setPhy(state, state->regs->MODE_REG, state->regs->RESET_VAL);
- if (err < 0)
- return err;
- err = getPhy(state, state->regs->MODE_REG);
- }
-
/* reset switch ports */
for (i = 0; i < state->regs->NUM_PORTS-1; i++) {
err = ip_phy_write(state, i, MII_BMCR, BMCR_RESET);
return err;
}
- return 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 = get_state(state);
+ err = state->regs->get_state(state);
if (err < 0)
return err;
struct ip175c_state *state = dev->priv;
int err;
- err = get_state(state);
+ err = state->regs->get_state(state);
if (err < 0)
return err;
if (val->value.i == 1)
state->add_tag |= (1<<val->port_vlan);
- SET_PORT_BITS(state, state->add_tag,
- state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT);
- SET_PORT_BITS(state, state->remove_tag,
- state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT);
-
- return err;
+ return state->regs->update_state(state);
}
/** Get the current phy address */
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;
static int ip175c_set_pvid(struct switch_dev *dev, int port, int val)
{
struct ip175c_state *state = dev->priv;
+ int err;
- state->ports[port].pvid = val;
+ if (val < 0 || val >= MAX_VLANS)
+ return -EINVAL;
- if (!REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[port]))
- return 0;
+ err = state->regs->get_state(state);
+ if (err < 0)
+ return err;
+
+ state->ports[port].pvid = val;
- return setPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[port], val);
+ return state->regs->update_state(state);
}
IP175C_REGISTER_ERRNO,
};
+enum Vlans {
+ IP175C_VLAN_TAG,
+};
+
static const struct switch_attr ip175c_global[] = {
[IP175C_ENABLE_VLAN] = {
.id = IP175C_ENABLE_VLAN,
};
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[] = {