+static int
+ar8216_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ priv->vlan = !!val->value.i;
+ return 0;
+}
+
+static int
+ar8216_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ val->value.i = priv->vlan;
+ return 0;
+}
+
+
+static int
+ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+
+ /* make sure no invalid PVIDs get set */
+
+ if (vlan >= dev->vlans)
+ return -EINVAL;
+
+ priv->pvid[port] = vlan;
+ return 0;
+}
+
+static int
+ar8216_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ *vlan = priv->pvid[port];
+ return 0;
+}
+
+static int
+ar8216_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ priv->vlan_id[val->port_vlan] = val->value.i;
+ return 0;
+}
+
+static int
+ar8216_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ val->value.i = priv->vlan_id[val->port_vlan];
+ return 0;
+}
+
+static int
+ar8216_sw_get_port_link(struct switch_dev *dev, int port,
+ struct switch_port_link *link)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+
+ ar8216_read_port_link(priv, port, link);
+ return 0;
+}
+
+static int
+ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ u8 ports = priv->vlan_table[val->port_vlan];
+ int i;
+
+ val->len = 0;
+ for (i = 0; i < dev->ports; i++) {
+ struct switch_port *p;
+
+ if (!(ports & (1 << i)))
+ continue;
+
+ p = &val->value.ports[val->len++];
+ p->id = i;
+ if (priv->vlan_tagged & (1 << i))
+ p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+ else
+ p->flags = 0;
+ }
+ return 0;
+}
+
+static int
+ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ u8 *vt = &priv->vlan_table[val->port_vlan];
+ int i, j;
+
+ *vt = 0;
+ for (i = 0; i < val->len; i++) {
+ struct switch_port *p = &val->value.ports[i];
+
+ if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+ priv->vlan_tagged |= (1 << p->id);
+ } else {
+ priv->vlan_tagged &= ~(1 << p->id);
+ priv->pvid[p->id] = val->port_vlan;
+
+ /* make sure that an untagged port does not
+ * appear in other vlans */
+ for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+ if (j == val->port_vlan)
+ continue;
+ priv->vlan_table[j] &= ~(1 << p->id);
+ }
+ }
+
+ *vt |= 1 << p->id;
+ }
+ return 0;
+}
+
+static int
+ar8216_sw_hw_apply(struct switch_dev *dev)
+{
+ struct ar8216_priv *priv = to_ar8216(dev);
+ u8 portmask[AR8216_NUM_PORTS];
+ int i, j;
+
+ mutex_lock(&priv->reg_mutex);
+ /* flush all vlan translation unit entries */
+ priv->chip->vtu_flush(priv);
+
+ memset(portmask, 0, sizeof(portmask));
+ if (!priv->init) {
+ /* calculate the port destination masks and load vlans
+ * into the vlan translation unit */
+ for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+ u8 vp = priv->vlan_table[j];
+
+ if (!vp)
+ continue;
+
+ for (i = 0; i < dev->ports; i++) {
+ u8 mask = (1 << i);
+ if (vp & mask)
+ portmask[i] |= vp & ~mask;
+ }
+
+ priv->chip->vtu_load_vlan(priv, priv->vlan_id[j],
+ priv->vlan_table[j]);
+ }
+ } else {
+ /* vlan disabled:
+ * isolate all ports, but connect them to the cpu port */
+ for (i = 0; i < dev->ports; i++) {
+ if (i == AR8216_PORT_CPU)
+ continue;
+
+ portmask[i] = 1 << AR8216_PORT_CPU;
+ portmask[AR8216_PORT_CPU] |= (1 << i);
+ }
+ }
+
+ /* update the port destination mask registers and tag settings */
+ for (i = 0; i < dev->ports; i++) {
+ int egress, ingress;
+ int pvid;
+
+ if (priv->vlan) {
+ pvid = priv->vlan_id[priv->pvid[i]];
+ if (priv->vlan_tagged & (1 << i))
+ egress = AR8216_OUT_ADD_VLAN;
+ else
+ egress = AR8216_OUT_STRIP_VLAN;
+ ingress = AR8216_IN_SECURE;
+ } else {
+ pvid = i;
+ egress = AR8216_OUT_KEEP;
+ ingress = AR8216_IN_PORT_ONLY;
+ }
+
+ priv->chip->setup_port(priv, i, egress, ingress, portmask[i],
+ pvid);
+ }
+ mutex_unlock(&priv->reg_mutex);
+ return 0;
+}
+