ralink: Update MT7530 switch driver.

- Support set VLAN ID of each vlan.
- Support untag feature.
- Replace register address with C MACRO.
- There are 8 ports for MT7530 actually.

Signed-off-by: Xiongfei Guo <xfguo@credosemi.com>

SVN-Revision: 41331
This commit is contained in:
John Crispin 2014-06-24 18:14:14 +00:00
parent 0708bda7c6
commit 021e728962

View file

@ -2814,7 +2814,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+#endif +#endif
--- /dev/null --- /dev/null
+++ b/drivers/net/ethernet/ralink/mt7530.c +++ b/drivers/net/ethernet/ralink/mt7530.c
@@ -0,0 +1,467 @@ @@ -0,0 +1,571 @@
+/* +/*
+ * This program is free software; you can redistribute it and/or + * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License + * modify it under the terms of the GNU General Public License
@ -2851,25 +2851,45 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+#include "mt7530.h" +#include "mt7530.h"
+ +
+#define MT7530_CPU_PORT 6 +#define MT7530_CPU_PORT 6
+#define MT7530_NUM_PORTS 7 +#define MT7530_NUM_PORTS 8
+#define MT7530_NUM_VLANS 16 +#define MT7530_NUM_VLANS 16
+#define MT7530_NUM_VIDS 16 +#define MT7530_MAX_VID 4095
+#define MT7530_MIN_VID 0
+ +
+#define REG_ESW_VLAN_VTCR 0x90 +/* registers */
+#define REG_ESW_VLAN_VAWD1 0x94 +#define REG_ESW_VLAN_VTCR 0x90
+#define REG_ESW_VLAN_VAWD2 0x98 +#define REG_ESW_VLAN_VAWD1 0x94
+#define REG_ESW_VLAN_VAWD2 0x98
+#define REG_ESW_VLAN_VTIM(x) (0x100 + 4 * ((x) / 2))
+ +
+#define REG_ESW_VLAN_VAWD1_IVL_MAC BIT(30)
+#define REG_ESW_VLAN_VAWD1_VTAG_EN BIT(28)
+#define REG_ESW_VLAN_VAWD1_VALID BIT(0)
+
+/* vlan egress mode */
+enum {
+ ETAG_CTRL_UNTAG = 0,
+ ETAG_CTRL_TAG = 2,
+ ETAG_CTRL_SWAP = 1,
+ ETAG_CTRL_STACK = 3,
+};
+
+#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
+#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
+#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
+enum { +enum {
+ /* Global attributes. */ + /* Global attributes. */
+ MT7530_ATTR_ENABLE_VLAN, + MT7530_ATTR_ENABLE_VLAN,
+}; +};
+ +
+struct mt7530_port { +struct mt7530_port_entry {
+ u16 pvid; + u16 pvid;
+}; +};
+ +
+struct mt7530_vlan { +struct mt7530_vlan_entry {
+ u8 ports; + u16 vid;
+ u8 member;
+ u8 etags;
+}; +};
+ +
+struct mt7530_priv { +struct mt7530_priv {
@ -2878,23 +2898,29 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ struct switch_dev swdev; + struct switch_dev swdev;
+ +
+ bool global_vlan_enable; + bool global_vlan_enable;
+ struct mt7530_vlan vlans[MT7530_NUM_VLANS]; + struct mt7530_vlan_entry vlan_entries[MT7530_NUM_VLANS];
+ struct mt7530_port ports[MT7530_NUM_PORTS]; + struct mt7530_port_entry port_entries[MT7530_NUM_PORTS];
+}; +};
+ +
+struct mt7530_mapping { +struct mt7530_mapping {
+ char *name; + char *name;
+ u8 pvids[6]; + u16 pvids[MT7530_NUM_PORTS];
+ u8 vlans[8]; + u8 members[MT7530_NUM_VLANS];
+ u8 etags[MT7530_NUM_VLANS];
+ u16 vids[MT7530_NUM_VLANS];
+} mt7530_defaults[] = { +} mt7530_defaults[] = {
+ { + {
+ .name = "llllw", + .name = "llllw",
+ .pvids = { 1, 1, 1, 1, 2, 1 }, + .pvids = { 1, 1, 1, 1, 2, 1, 1 },
+ .vlans = { 0, 0x6f, 0x50 }, + .members = { 0x6f, 0x50 },
+ .etags = { 0x40, 0x40 },
+ .vids = { 1, 2 },
+ }, { + }, {
+ .name = "wllll", + .name = "wllll",
+ .pvids = { 2, 1, 1, 1, 1, 1 }, + .pvids = { 2, 1, 1, 1, 1, 1, 1 },
+ .vlans = { 0, 0x7e, 0x41 }, + .members = { 0x7e, 0x41 },
+ .etags = { 0x40, 0x40 },
+ .vids = { 1, 2 },
+ }, + },
+}; +};
+ +
@ -2921,10 +2947,14 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ +
+ mt7530->global_vlan_enable = 1; + mt7530->global_vlan_enable = 1;
+ +
+ for (i = 0; i < 6; i++) + for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530->ports[i].pvid = map->pvids[i]; + mt7530->port_entries[i].pvid = map->pvids[i];
+ for (i = 0; i < 8; i++) +
+ mt7530->vlans[i].ports = map->vlans[i]; + for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ mt7530->vlan_entries[i].member = map->members[i];
+ mt7530->vlan_entries[i].etags = map->etags[i];
+ mt7530->vlan_entries[i].vid = map->vids[i];
+ }
+} +}
+ +
+static int +static int
@ -2932,8 +2962,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+{ +{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); + struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ +
+ memset(priv->ports, 0, sizeof(priv->ports)); + memset(priv->port_entries, 0, sizeof(priv->port_entries));
+ memset(priv->vlans, 0, sizeof(priv->vlans)); + memset(priv->vlan_entries, 0, sizeof(priv->vlan_entries));
+ +
+ return 0; + return 0;
+} +}
@ -2965,6 +2995,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+static u32 +static u32
+mt7530_r32(struct mt7530_priv *priv, u32 reg) +mt7530_r32(struct mt7530_priv *priv, u32 reg)
+{ +{
+ u32 val;
+ if (priv->bus) { + if (priv->bus) {
+ u16 high, low; + u16 high, low;
+ +
@ -2975,7 +3006,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ return (high << 16) | (low & 0xffff); + return (high << 16) | (low & 0xffff);
+ } + }
+ +
+ return ioread32(priv->base + reg); + val = ioread32(priv->base + reg);
+ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
+
+ return val;
+} +}
+ +
+static void +static void
@ -2988,6 +3022,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ return; + return;
+ } + }
+ +
+ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
+ iowrite32(val, priv->base + reg); + iowrite32(val, priv->base + reg);
+} +}
+ +
@ -3018,8 +3053,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ if (port >= MT7530_NUM_PORTS) + if (port >= MT7530_NUM_PORTS)
+ return -EINVAL; + return -EINVAL;
+ +
+ *val = mt7530_r32(priv, 0x2014 + (0x100 * port)); + *val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(port));
+ *val &= 0xff; + *val &= 0xfff;
+ +
+ return 0; + return 0;
+} +}
@ -3030,9 +3065,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); + struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ +
+ if (port >= MT7530_NUM_PORTS) + if (port >= MT7530_NUM_PORTS)
+ return -1; + return -EINVAL;
+ +
+ priv->ports[port].pvid = pvid; + if (pvid < MT7530_MIN_VID || pvid > MT7530_MAX_VID)
+ return -EINVAL;
+
+ priv->port_entries[port].pvid = pvid;
+ +
+ return 0; + return 0;
+} +}
@ -3042,26 +3080,38 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+{ +{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); + struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u32 member; + u32 member;
+ u32 etags;
+ int i; + int i;
+ +
+ val->len = 0; + val->len = 0;
+ +
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VIDS) + if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS)
+ return -EINVAL; + return -EINVAL;
+ +
+ mt7530_vtcr(priv, 0, val->port_vlan); + mt7530_vtcr(priv, 0, val->port_vlan);
+
+ member = mt7530_r32(priv, REG_ESW_VLAN_VAWD1); + member = mt7530_r32(priv, REG_ESW_VLAN_VAWD1);
+ member >>= 16; + member >>= 16;
+ member &= 0xff; + member &= 0xff;
+ +
+ etags = mt7530_r32(priv, REG_ESW_VLAN_VAWD2);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++) { + for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ struct switch_port *p; + struct switch_port *p;
+ int etag;
+
+ if (!(member & BIT(i))) + if (!(member & BIT(i)))
+ continue; + continue;
+ +
+ p = &val->value.ports[val->len++]; + p = &val->value.ports[val->len++];
+ p->id = i; + p->id = i;
+ p->flags = 0; +
+ etag = (etags >> (i * 2)) & 0x3;
+
+ if (etag == ETAG_CTRL_TAG)
+ p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
+ else if (etag != ETAG_CTRL_UNTAG)
+ printk("vlan egress tag control neither untag nor tag.\n");
+ } + }
+ +
+ return 0; + return 0;
@ -3071,10 +3121,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) +mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{ +{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); + struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int ports = 0; + u8 member = 0;
+ u8 etags = 0;
+ int i; + int i;
+ +
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VIDS || + if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS ||
+ val->len > MT7530_NUM_PORTS) + val->len > MT7530_NUM_PORTS)
+ return -EINVAL; + return -EINVAL;
+ +
@ -3084,10 +3135,54 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+ if (p->id >= MT7530_NUM_PORTS) + if (p->id >= MT7530_NUM_PORTS)
+ return -EINVAL; + return -EINVAL;
+ +
+ ports |= BIT(p->id); + member |= BIT(p->id);
+ }
+ priv->vlans[val->port_vlan].ports = ports;
+ +
+ if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
+ etags |= BIT(p->id);
+ }
+ priv->vlan_entries[val->port_vlan].member = member;
+ priv->vlan_entries[val->port_vlan].etags = etags;
+
+ return 0;
+}
+
+static int
+mt7530_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int vlan;
+ u16 vid;
+
+ vlan = val->port_vlan;
+ vid = (u16)val->value.i;
+
+ if (vlan < 0 || vlan >= MT7530_NUM_VLANS)
+ return -EINVAL;
+
+ if (vid < MT7530_MIN_VID || vid > MT7530_MAX_VID)
+ return -EINVAL;
+
+ priv->vlan_entries[vlan].vid = vid;
+ return 0;
+}
+
+static int
+mt7530_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u32 vid;
+ int vlan;
+
+ vlan = val->port_vlan;
+
+ vid = mt7530_r32(priv, REG_ESW_VLAN_VTIM(vlan));
+ if (vlan & 1)
+ vid = vid >> 12;
+ vid &= 0xfff;
+
+ val->value.i = vid;
+ return 0; + return 0;
+} +}
+ +
@ -3095,79 +3190,80 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+mt7530_apply_config(struct switch_dev *dev) +mt7530_apply_config(struct switch_dev *dev)
+{ +{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); + struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ int i; + int i, j;
+ +
+ if (!priv->global_vlan_enable) { + if (!priv->global_vlan_enable) {
+ mt7530_w32(priv, 0x2004, 0xff000); + for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, 0x2104, 0xff000); + mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0000);
+ mt7530_w32(priv, 0x2204, 0xff000); +
+ mt7530_w32(priv, 0x2304, 0xff000); + for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, 0x2404, 0xff000); + mt7530_w32(priv, REG_ESW_PORT_PVC(i), 0x8100000c);
+ mt7530_w32(priv, 0x2504, 0xff000); +
+ mt7530_w32(priv, 0x2604, 0xff000); + return 0;
+ mt7530_w32(priv, 0x2010, 0x810000c);
+ mt7530_w32(priv, 0x2110, 0x810000c);
+ mt7530_w32(priv, 0x2210, 0x810000c);
+ mt7530_w32(priv, 0x2310, 0x810000c);
+ mt7530_w32(priv, 0x2410, 0x810000c);
+ mt7530_w32(priv, 0x2510, 0x810000c);
+ mt7530_w32(priv, 0x2610, 0x810000c);
+ return 0;
+ } + }
+ +
+ // LAN/WAN ports as security mode + /* set all ports as security mode */
+ mt7530_w32(priv, 0x2004, 0xff0003); + for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, 0x2104, 0xff0003); + mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0003);
+ mt7530_w32(priv, 0x2204, 0xff0003);
+ mt7530_w32(priv, 0x2304, 0xff0003);
+ mt7530_w32(priv, 0x2404, 0xff0003);
+ mt7530_w32(priv, 0x2504, 0xff0003);
+ // LAN/WAN ports as transparent port
+ mt7530_w32(priv, 0x2010, 0x810000c0);
+ mt7530_w32(priv, 0x2110, 0x810000c0);
+ mt7530_w32(priv, 0x2210, 0x810000c0);
+ mt7530_w32(priv, 0x2310, 0x810000c0);
+ mt7530_w32(priv, 0x2410, 0x810000c0);
+ mt7530_w32(priv, 0x2510, 0x810000c0);
+ +
+ // set CPU/P7 port as user port + /* set all ports as user port */
+ mt7530_w32(priv, 0x2610, 0x81000000); + for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_w32(priv, 0x2710, 0x81000000); + mt7530_w32(priv, REG_ESW_PORT_PVC(i), 0x81000000);
+
+ mt7530_w32(priv, 0x2604, 0x20ff0003);
+ mt7530_w32(priv, 0x2704, 0x20ff0003);
+ mt7530_w32(priv, 0x2610, 0x81000000);
+ +
+ for (i = 0; i < MT7530_NUM_VLANS; i++) { + for (i = 0; i < MT7530_NUM_VLANS; i++) {
+ u8 ports = priv->vlans[i].ports; + u16 vid = priv->vlan_entries[i].vid;
+ u32 val = mt7530_r32(priv, 0x100 + 4 * (i / 2)); + u8 member = priv->vlan_entries[i].member;
+ u8 etags = priv->vlan_entries[i].etags;
+ u32 val;
+ +
+ /* vid of vlan */
+ val = mt7530_r32(priv, REG_ESW_VLAN_VTIM(i));
+ if (i % 2 == 0) { + if (i % 2 == 0) {
+ val &= 0xfff000; + val &= 0xfff000;
+ val |= i; + val |= vid;
+ } else { + } else {
+ val &= 0xfff; + val &= 0xfff;
+ val |= (i << 12); + val |= (vid << 12);
+ } + }
+ mt7530_w32(priv, 0x100 + 4 * (i / 2), val); + mt7530_w32(priv, REG_ESW_VLAN_VTIM(i), val);
+ +
+ if (ports) + /* vlan port membership */
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, BIT(30) | (ports << 16) | BIT(0)); + if (member)
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, REG_ESW_VLAN_VAWD1_IVL_MAC |
+ REG_ESW_VLAN_VAWD1_VTAG_EN | (member << 16) |
+ REG_ESW_VLAN_VAWD1_VALID);
+ else + else
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, 0); + mt7530_w32(priv, REG_ESW_VLAN_VAWD1, 0);
+ +
+ /* egress mode */
+ val = 0;
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
+ if (etags & BIT(j))
+ val |= ETAG_CTRL_TAG << (j * 2);
+ else
+ val |= ETAG_CTRL_UNTAG << (j * 2);
+ }
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD2, val);
+
+ /* write to vlan table */
+ mt7530_vtcr(priv, 1, i); + mt7530_vtcr(priv, 1, i);
+ } + }
+ +
+ for (i = 0; i < MT7530_NUM_PORTS; i++) + /* Port Default PVID */
+ mt7530_w32(priv, 0x2014 + (0x100 * i), 0x10000 | priv->ports[i].pvid); + for (i = 0; i < MT7530_NUM_PORTS; i++) {
+ u32 val;
+ val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(i));
+ val &= ~0xfff;
+ val |= priv->port_entries[i].pvid;
+ mt7530_w32(priv, REG_ESW_PORT_PPBV1(i), val);
+ }
+ +
+ return 0; + return 0;
+} +}
+ +
+static int +static int
+mt7530_get_port_link(struct switch_dev *dev, int port, +mt7530_get_port_link(struct switch_dev *dev, int port,
+ struct switch_port_link *link) + struct switch_port_link *link)
+{ +{
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); + struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
+ u32 speed, pmsr; + u32 speed, pmsr;
@ -3216,6 +3312,14 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
+}; +};
+ +
+static const struct switch_attr mt7530_vlan[] = { +static const struct switch_attr mt7530_vlan[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "vid",
+ .description = "VLAN ID (0-4094)",
+ .set = mt7530_set_vid,
+ .get = mt7530_get_vid,
+ .max = 4094,
+ },
+}; +};
+ +
+static const struct switch_dev_ops mt7530_ops = { +static const struct switch_dev_ops mt7530_ops = {