Excellent. This chip is used on the Ubiquity Routerstation (not the Pro) and I have some of those and I will test this code. As has been pointed out elsewhere there are several variants on the AMD6996, the Routerstation uses the F variant.
David On Saturday 08 January 2011, Peter Lebbing wrote: > Hello developers, > > I have written a switch driver for the Infineon ADM6996 switch chip, which > in my case is in a D-Link DSL-G624T modem/router. It implements 802.1Q > based VLAN support, via the swconfig interface. Other fancy features of > the switch chip are not (yet) implemented. > > Because the datasheet isn't that well written, it took some experimentation > to get the functionality right. The real functionality of the driver is > obviously in the setting of the necessary bits in the configuration > registers of the chip. Everything else is pretty much boilerplate code, > and can easily be changed if something is deemed not to be the best way. > > So let's move to one such design decision which really is debatable. > > The switch chip allows 16 VLAN definitions. But any such definition can be > for any 12-bits VLAN ID. The swconfig infrastructure seems to unify the > concept of an entry in the list of VLAN definitions and it's VLAN ID. This > shows strongest in the fact that when you do the following invocation, for > example: > > # swconfig dev eth0 vlan 2 set ports '0 1 2 3t 4t 5t' > > It will not only invoke the responsible handler "set_vlan_ports" from the > struct switch_dev_ops, but also automatically the handler "set_port_pvid" > from same struct, setting the Primary VLAN ID for the untagged ports 0, 1 > and 2 to VLAN ID 2. Mind you, that is the actual 12-bit identifier from > 802.1Q, which is independent of the fact that it is entry number 2 in the > table of VLAN definitions. > > This is all fine if all the VLAN IDs we need are 0-15, but it is a > perfectly valid use case to have VLAN IDs with much higher numbers. The > ADM6996 chip can handle that use case fine, so I did not want to exclude > it. > > I've seen similar things in other switch chips in OpenWRT while browsing, > where it was coined "4k VLANs". It wasn't completely clear to me how it > was handled there, however. > > I chose to handle this case as follows: > > In addition to the standard attributes, vlan in switch_dev_ops has an > attribute named "vid", which gives the VLAN ID for that entry in the table > of VLAN definitions. By default it is set to the number of the entry. So > by default it is: > > # swconfig dev eth0 show > > ... > VLAN 0: > vid: 0 > ports: > VLAN 1: > vid: 1 > ports: > VLAN 2: > vid: 2 > ports: > ... > > VLAN 15: > vid: 15 > ports: > > This means that as long as people don't change the "vid", presumably > because they don't need more than the 16 lowest VLAN IDs, it all works > completely intuitively, and Primary VLAN IDs for the ports are set > correctly automatically. Once you change the "vid" for a vlan, you become > responsible for correcting the mistakes the automatic invocation of > "set_port_pvid" makes. > > But as I said, if there is a more elegant solution, it is easily changed in > the code. > > A change with respect to the original code (which only detected the switch > chip, but had almost no functionality), is that I moved allocation of the > phy_device->priv structure from adm6996_probe() to adm6996_config_init(). > The reason is that config_init() now has a chance to fail (registering > swconfig interface), and I wasn't sure about free()ing of the private > structure. So I simply copied the behaviour from ar8216.c. > > Another change is that I changed the number of ports from 5 to 6. The thing > is, it has 6 ports, of which number 5 (zero-based counting) is connected > to the CPU. You want to be able to config VLANs for that port as well. On > the D-Link modem, port number 4 is not connected to anything. The > datasheet of the ADM6996 chip suggests using that port as WAN port, and > the D-Link does not have an Ethernet WAN port, so it makes sense. > > The original code sets the Primary VLAN ID of port 0, which it assumes is > the WAN port, to 1, and the others to 0. I have left it like this; I also > initialize the swconfig Primary VLAN ID to that. But I wonder why this was > done; in the original code, it was effectively a no-op, since it would not > make a difference unless a bunch of other registers were also changed. > > Finally, I'd like to make a little personal note. To my great regret, I > have very little free time. So it might take a while before I get back to > you when you ask something or wish me to make an adjustment or > improvement. This is very unfortunate, and obviously I do not ask you to > wait for me on anything; I only wish to contribute, not stall. I cannot > change my circumstances, and simply have to accept them. Nonetheless, I > hope to give the community useful code. > > Oh, and this is my first code contribution to a community project. Do not > hesitate to point out what I should do differently, if I got something > wrong. Thank you for OpenWRT! > > Signed-off-by: Peter Lebbing <pe...@digitalbrains.com> > > --- > > Index: backfire/target/linux/generic-2.6/files/drivers/net/phy/adm6996.c > =================================================================== > --- > backfire/target/linux/generic-2.6/files/drivers/net/phy/adm6996.c (revisio > n 24930) +++ > backfire/target/linux/generic-2.6/files/drivers/net/phy/adm6996.c (working > copy) @@ -1,12 +1,18 @@ > /* > * ADM6996 switch driver > * > + * swconfig interface based on ar8216.c > + * > * Copyright (c) 2008 Felix Fietkau <n...@openwrt.org> > + * VLAN support Copyright (c) 2010, 2011 Peter Lebbing > <pe...@digitalbrains.com> * > * This program is free software; you can redistribute it and/or modify > it * under the terms of the GNU General Public License v2 as published by > the * Free Software Foundation > */ > + > +/*#define DEBUG 1 */ > + > #include <linux/kernel.h> > #include <linux/string.h> > #include <linux/errno.h> > @@ -24,6 +30,7 @@ > #include <linux/mii.h> > #include <linux/ethtool.h> > #include <linux/phy.h> > +#include <linux/switch.h> > > #include <asm/io.h> > #include <asm/irq.h> > @@ -35,24 +42,42 @@ > MODULE_LICENSE("GPL"); > > struct adm6996_priv { > + struct switch_dev dev; > + struct phy_device *phydev; > + > + bool enable_vlan; > + bool vlan_enabled; /* Current hardware state */ > + > +#ifdef DEBUG > + u16 addr; /* Debugging: register address to operate on */ > +#endif > + > + u16 pvid[ADM_PHY_PORTS]; /* Primary VLAN ID */ > + > + u16 vlan_id[ADM_NUM_VLANS]; > + u8 vlan_table[ADM_NUM_VLANS]; /* bitmap, 1 = port is member */ > + u8 vlan_tagged[ADM_NUM_VLANS]; /* bitmap, 1 = tagged member */ > + > + struct mutex reg_mutex; > + > /* use abstraction for regops, we want to add gpio support in the future > */ - u16 (*read)(struct phy_device *phydev, enum admreg reg); > - void (*write)(struct phy_device *phydev, enum admreg reg, u16 val); > + u16 (*read) (struct phy_device *phydev, enum admreg reg); > + void (*write) (struct phy_device *phydev, enum admreg reg, u16 val); > }; > > -#define to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv) > +#define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev) > +#define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv) > > - > static inline u16 > r16(struct phy_device *pdev, enum admreg reg) > { > - return to_adm(pdev)->read(pdev, reg); > + return phy_to_adm(pdev)->read(pdev, reg); > } > > static inline void > w16(struct phy_device *pdev, enum admreg reg, u16 val) > { > - to_adm(pdev)->write(pdev, reg, val); > + phy_to_adm(pdev)->write(pdev, reg, val); > } > > > @@ -68,27 +93,473 @@ > phydev->bus->write(phydev->bus, PHYADDR(reg), val); > } > > +static int > +adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr > *attr, + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > > -static int adm6996_config_init(struct phy_device *pdev) > + if (val->value.i > 1) > + return -EINVAL; > + > + priv->enable_vlan = val->value.i; > + > + return 0; > +}; > + > +static int > +adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr > *attr, + struct switch_val *val) > { > + struct adm6996_priv *priv = to_adm(dev); > + > + val->value.i = priv->enable_vlan; > + > + return 0; > +}; > + > +#ifdef DEBUG > + > +static int > +adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr, > + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + if (val->value.i > 1023) > + return -EINVAL; > + > + priv->addr = val->value.i; > + > + return 0; > +}; > + > +static int > +adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr, > + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + val->value.i = priv->addr; > + > + return 0; > +}; > + > +static int > +adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr, > + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + if (val->value.i > 65535) > + return -EINVAL; > + > + w16(priv->phydev, priv->addr, val->value.i); > + > + return 0; > +}; > + > +static int > +adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr, > + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + val->value.i = r16(priv->phydev, priv->addr); > + > + return 0; > +}; > + > +#endif > + > +static int > +adm6996_set_pvid(struct switch_dev *dev, int port, int vlan) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + dev_dbg (&priv->phydev->dev, "set_pvid port %d vlan %d\n", port > + , vlan); > + > + if (vlan > ADM_VLAN_MAX_ID) > + return -EINVAL; > + > + priv->pvid[port] = vlan; > + > + return 0; > +} > + > +static int > +adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + dev_dbg (&priv->phydev->dev, "get_pvid port %d\n", port); > + *vlan = priv->pvid[port]; > + > + return 0; > +} > + > +static int > +adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr, > + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + dev_dbg (&priv->phydev->dev, "set_vid port %d vid %d\n", val->port_vlan, > + val->value.i); > + > + if (val->value.i > ADM_VLAN_MAX_ID) > + return -EINVAL; > + > + priv->vlan_id[val->port_vlan] = val->value.i; > + > + return 0; > +}; > + > +static int > +adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr, > + struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + dev_dbg (&priv->phydev->dev, "get_vid port %d\n", val->port_vlan); > + > + val->value.i = priv->vlan_id[val->port_vlan]; > + > + return 0; > +}; > + > +static int > +adm6996_get_ports(struct switch_dev *dev, struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + u8 ports = priv->vlan_table[val->port_vlan]; > + u8 tagged = priv->vlan_tagged[val->port_vlan]; > int i; > > + dev_dbg (&priv->phydev->dev, "get_ports port_vlan %d\n", > + val->port_vlan); > + > + val->len = 0; > + > + for (i = 0; i < ADM_PHY_PORTS; i++) { > + struct switch_port *p; > + > + if (!(ports & (1 << i))) > + continue; > + > + p = &val->value.ports[val->len++]; > + p->id = i; > + if (tagged & (1 << i)) > + p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); > + else > + p->flags = 0; > + } > + > + return 0; > +}; > + > +static int > +adm6996_set_ports(struct switch_dev *dev, struct switch_val *val) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + u8 *ports = &priv->vlan_table[val->port_vlan]; > + u8 *tagged = &priv->vlan_tagged[val->port_vlan]; > + int i; > + > + dev_dbg (&priv->phydev->dev, "set_ports port_vlan %d ports", > + val->port_vlan); > + > + *ports = 0; > + *tagged = 0; > + > + for (i = 0; i < val->len; i++) { > + struct switch_port *p = &val->value.ports[i]; > + > +#ifdef DEBUG > + printk(" %d%s", p->id, > + ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" : > + "")); > +#endif > + > + if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) > + *tagged |= (1 << p->id); > + > + *ports |= (1 << p->id); > + } > + > +#ifdef DEBUG > + printk("\n"); > +#endif > + > + return 0; > +}; > + > +/* > + * Precondition: reg_mutex must be held > + */ > +static void > +adm6996_enable_vlan(struct adm6996_priv *priv) > +{ > + u16 reg; > + > + reg = r16(priv->phydev, ADM_OTBE_P2_PVID); > + reg &= ~(ADM_OTBE_MASK); > + w16(priv->phydev, ADM_OTBE_P2_PVID, reg); > + reg = r16(priv->phydev, ADM_IFNTE); > + reg &= ~(ADM_IFNTE_MASK); > + w16(priv->phydev, ADM_IFNTE, reg); > + reg = r16(priv->phydev, ADM_VID_CHECK); > + reg |= ADM_VID_CHECK_MASK; > + w16(priv->phydev, ADM_VID_CHECK, reg); > + reg = r16(priv->phydev, ADM_SYSC0); > + reg |= ADM_NTTE; > + reg &= ~(ADM_RVID1); > + w16(priv->phydev, ADM_SYSC0, reg); > + reg = r16(priv->phydev, ADM_SYSC3); > + reg |= ADM_TBV; > + w16(priv->phydev, ADM_SYSC3, reg); > + > +}; > + > +/* > + * Disable VLANs > + * > + * Sets VLAN mapping for port-based VLAN with all ports connected to > + * eachother (this is also the power-on default). > + * > + * Precondition: reg_mutex must be held > + */ > +static void > +adm6996_disable_vlan(struct adm6996_priv *priv) > +{ > + u16 reg; > + int i; > + > + for (i = 0; i < ADM_PHY_PORTS; i++) { > + reg = ADM_VLAN_FILT_MEMBER_MASK; > + w16(priv->phydev, ADM_VLAN_FILT_L(i), reg); > + reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1); > + w16(priv->phydev, ADM_VLAN_FILT_H(i), reg); > + } > + > + reg = r16(priv->phydev, ADM_OTBE_P2_PVID); > + reg |= ADM_OTBE_MASK; > + w16(priv->phydev, ADM_OTBE_P2_PVID, reg); > + reg = r16(priv->phydev, ADM_IFNTE); > + reg |= ADM_IFNTE_MASK; > + w16(priv->phydev, ADM_IFNTE, reg); > + reg = r16(priv->phydev, ADM_VID_CHECK); > + reg &= ~(ADM_VID_CHECK_MASK); > + w16(priv->phydev, ADM_VID_CHECK, reg); > + reg = r16(priv->phydev, ADM_SYSC0); > + reg &= ~(ADM_NTTE); > + reg |= ADM_RVID1; > + w16(priv->phydev, ADM_SYSC0, reg); > + reg = r16(priv->phydev, ADM_SYSC3); > + reg &= ~(ADM_TBV); > + w16(priv->phydev, ADM_SYSC3, reg); > +} > + > +/* > + * Precondition: reg_mutex must be held > + */ > +static void > +adm6996_apply_port_pvids(struct adm6996_priv *priv) > +{ > + u16 reg; > + int i; > + > + for (i = 0; i < ADM_PHY_PORTS; i++) { > + reg = r16(priv->phydev, adm_portcfg[i]); > + reg &= ~(ADM_PORTCFG_PVID_MASK); > + reg |= ADM_PORTCFG_PVID(priv->pvid[i]); > + w16(priv->phydev, adm_portcfg[i], reg); > + } > + > + w16(priv->phydev, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0])); > + w16(priv->phydev, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1])); > + reg = r16(priv->phydev, ADM_OTBE_P2_PVID); > + reg &= ~(ADM_P2_PVID_MASK); > + reg |= ADM_P2_PVID_VAL(priv->pvid[2]); > + w16(priv->phydev, ADM_OTBE_P2_PVID, reg); > + reg = ADM_P3_PVID_VAL(priv->pvid[3]); > + reg |= ADM_P4_PVID_VAL(priv->pvid[4]); > + w16(priv->phydev, ADM_P3_P4_PVID, reg); > + w16(priv->phydev, ADM_P5_PVID, ADM_P5_PVID_VAL(priv->pvid[5])); > +} > + > +/* > + * Precondition: reg_mutex must be held > + */ > +static void > +adm6996_apply_vlan_filters(struct adm6996_priv *priv) > +{ > + u8 ports, tagged; > + u16 vid, reg; > + int i; > + > + for (i = 0; i < ADM_NUM_VLANS; i++) { > + vid = priv->vlan_id[i]; > + ports = priv->vlan_table[i]; > + tagged = priv->vlan_tagged[i]; > + > + if (ports == 0) { > + /* Disable VLAN entry */ > + w16(priv->phydev, ADM_VLAN_FILT_H(i), 0); > + w16(priv->phydev, ADM_VLAN_FILT_L(i), 0); > + continue; > + } > + > + reg = ADM_VLAN_FILT_MEMBER(ports); > + reg |= ADM_VLAN_FILT_TAGGED(tagged); > + w16(priv->phydev, ADM_VLAN_FILT_L(i), reg); > + reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid); > + w16(priv->phydev, ADM_VLAN_FILT_H(i), reg); > + } > +} > + > +static int > +adm6996_hw_apply(struct switch_dev *dev) > +{ > + struct adm6996_priv *priv = to_adm(dev); > + > + dev_dbg (&priv->phydev->dev, "hw_apply\n"); > + > + mutex_lock(&priv->reg_mutex); > + > + if (!priv->enable_vlan && priv->vlan_enabled) { > + adm6996_disable_vlan(priv); > + priv->vlan_enabled = 0; > + return 0; > + } else if (priv->enable_vlan && !priv->vlan_enabled) { > + adm6996_enable_vlan(priv); > + priv->vlan_enabled = 1; > + } > + > + adm6996_apply_port_pvids (priv); > + adm6996_apply_vlan_filters (priv); > + > + mutex_unlock(&priv->reg_mutex); > + > + return 0; > +} > + > +static struct switch_attr adm6996_globals[] = { > + { > + .type = SWITCH_TYPE_INT, > + .name = "enable_vlan", > + .description = "Enable VLANs", > + .set = adm6996_set_enable_vlan, > + .get = adm6996_get_enable_vlan, > + }, > +#ifdef DEBUG > + { > + .type = SWITCH_TYPE_INT, > + .name = "addr", > + .description = > + "Direct register access: set register address (0 - 1023)", > + .set = adm6996_set_addr, > + .get = adm6996_get_addr, > + }, > + { > + .type = SWITCH_TYPE_INT, > + .name = "data", > + .description = > + "Direct register access: read/write to register (0 - 65535)", > + .set = adm6996_set_data, > + .get = adm6996_get_data, > + }, > +#endif > +}; > + > +static struct switch_attr adm6996_port[] = { > +}; > + > +static struct switch_attr adm6996_vlan[] = { > + { > + .type = SWITCH_TYPE_INT, > + .name = "vid", > + .description = "VLAN ID", > + .set = adm6996_set_vid, > + .get = adm6996_get_vid, > + }, > +}; > + > +static const struct switch_dev_ops adm6996_ops = { > + .attr_global = { > + .attr = adm6996_globals, > + .n_attr = ARRAY_SIZE(adm6996_globals), > + }, > + .attr_port = { > + .attr = adm6996_port, > + .n_attr = ARRAY_SIZE(adm6996_port), > + }, > + .attr_vlan = { > + .attr = adm6996_vlan, > + .n_attr = ARRAY_SIZE(adm6996_vlan), > + }, > + .get_port_pvid = adm6996_get_pvid, > + .set_port_pvid = adm6996_set_pvid, > + .get_vlan_ports = adm6996_get_ports, > + .set_vlan_ports = adm6996_set_ports, > + .apply_config = adm6996_hw_apply, > +}; > + > +static int adm6996_config_init(struct phy_device *pdev) > +{ > + struct adm6996_priv *priv; > + struct switch_dev *swdev; > + > + int i, ret; > + > + priv = kzalloc(sizeof(struct adm6996_priv), GFP_KERNEL); > + if (priv == NULL) > + return -ENOMEM; > + > + mutex_init(&priv->reg_mutex); > + priv->phydev = pdev; > + priv->read = adm6996_read_mii_reg; > + priv->write = adm6996_write_mii_reg; > + pdev->priv = priv; > + > printk("%s: ADM6996 PHY driver attached.\n", pdev->attached_dev->name); > pdev->supported = ADVERTISED_100baseT_Full; > pdev->advertising = ADVERTISED_100baseT_Full; > > /* initialize port and vlan settings */ > - for (i = 0; i < ADM_PHY_PORTS; i++) { > + for (i = 0; i < ADM_PHY_PORTS - 1; i++) { > w16(pdev, adm_portcfg[i], ADM_PORTCFG_INIT | > ADM_PORTCFG_PVID((i == ADM_WAN_PORT) ? 1 : 0)); > } > w16(pdev, adm_portcfg[5], ADM_PORTCFG_CPU); > > /* reset all ports */ > - for (i = 0; i < ADM_PHY_PORTS; i++) { > + for (i = 0; i < ADM_PHY_PORTS - 1; i++) { > w16(pdev, ADM_PHY_PORT(i), ADM_PHYCFG_INIT); > } > > + /* Clear VLAN priority map so prio's are unused */ > + w16 (pdev, ADM_VLAN_PRIOMAP, 0); > + > + swdev = &priv->dev; > + swdev->name = "ADM6996"; > + swdev->cpu_port = ADM_CPU_PORT; > + swdev->ports = ADM_PHY_PORTS; > + swdev->vlans = ADM_NUM_VLANS; > + swdev->ops = &adm6996_ops; > + > + priv->pvid[ADM_WAN_PORT] = 1; > + > + for (i = 0; i < ADM_NUM_VLANS; i++) { > + priv->vlan_id[i] = i; > + } > + > + if ((ret = register_switch(swdev, pdev->attached_dev)) < 0) { > + kfree(priv); > + return ret; > + } > + > return 0; > } > > @@ -125,15 +596,6 @@ > > static int adm6996_probe(struct phy_device *pdev) > { > - struct adm6996_priv *priv; > - > - priv = kzalloc(sizeof(struct adm6996_priv), GFP_KERNEL); > - if (priv == NULL) > - return -ENOMEM; > - > - priv->read = adm6996_read_mii_reg; > - priv->write = adm6996_write_mii_reg; > - pdev->priv = priv; > return 0; > } > > Index: backfire/target/linux/generic-2.6/files/drivers/net/phy/adm6996.h > =================================================================== > --- > backfire/target/linux/generic-2.6/files/drivers/net/phy/adm6996.h (revisio > n 24930) +++ > backfire/target/linux/generic-2.6/files/drivers/net/phy/adm6996.h (working > copy) @@ -10,10 +10,13 @@ > #ifndef __ADM6996_H > #define __ADM6996_H > > -#define ADM_PHY_PORTS 5 > +#define ADM_PHY_PORTS 6 > #define ADM_CPU_PORT 5 > #define ADM_WAN_PORT 0 /* FIXME: dynamic ? */ > > +#define ADM_NUM_VLANS 16 > +#define ADM_VLAN_MAX_ID 4094 > + > enum admreg { > ADM_EEPROM_BASE = 0x0, > ADM_P0_CFG = ADM_EEPROM_BASE + 1, > @@ -22,7 +25,21 @@ > ADM_P3_CFG = ADM_EEPROM_BASE + 7, > ADM_P4_CFG = ADM_EEPROM_BASE + 8, > ADM_P5_CFG = ADM_EEPROM_BASE + 9, > + ADM_SYSC0 = ADM_EEPROM_BASE + 0xa, > + ADM_VLAN_PRIOMAP = ADM_EEPROM_BASE + 0xe, > + ADM_SYSC3 = ADM_EEPROM_BASE + 0x11, > + /* Input Force No Tag Enable */ > + ADM_IFNTE = ADM_EEPROM_BASE + 0x20, > + ADM_VID_CHECK = ADM_EEPROM_BASE + 0x26, > + ADM_P0_PVID = ADM_EEPROM_BASE + 0x28, > + ADM_P1_PVID = ADM_EEPROM_BASE + 0x29, > + /* Output Tag Bypass Enable and P2 PVID */ > + ADM_OTBE_P2_PVID = ADM_EEPROM_BASE + 0x2a, > + ADM_P3_P4_PVID = ADM_EEPROM_BASE + 0x2b, > + ADM_P5_PVID = ADM_EEPROM_BASE + 0x2c, > ADM_EEPROM_EXT_BASE = 0x40, > +#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n)) > +#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n)) > ADM_COUNTER_BASE = 0xa0, > ADM_SIG0 = ADM_COUNTER_BASE + 0, > ADM_SIG1 = ADM_COUNTER_BASE + 1, > @@ -84,9 +101,33 @@ > ), > }; > > -#define ADM_PORTCFG_PPID(N) ((n & 0x3) << 8) > +#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8) > #define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10) > +#define ADM_PORTCFG_PVID_MASK (0xf << 10) > > +#define ADM_IFNTE_MASK (0x3f << 9) > +#define ADM_VID_CHECK_MASK (0x3f << 6) > + > +#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0) > +#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0) > +#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0) > +#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0) > +#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8) > +#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0) > +#define ADM_P2_PVID_MASK 0xff > + > +#define ADM_OTBE(n) (((n) & 0x3f) << 8) > +#define ADM_OTBE_MASK (0x3f << 8) > + > +/* ADM_SYSC0 */ > +enum { > + ADM_NTTE = (1 << 2), /* New Tag Transmit Enable */ > + ADM_RVID1 = (1 << 8) /* Replace VLAN ID 1 */ > +}; > + > +/* Tag Based VLAN in ADM_SYSC3 */ > +#define ADM_TBV (1 << 5) > + > static const u8 adm_portcfg[] = { > [0] = ADM_P0_CFG, > [1] = ADM_P1_CFG, > @@ -96,6 +137,16 @@ > [5] = ADM_P5_CFG, > }; > > +/* Fields in ADM_VLAN_FILT_L(x) */ > +#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12) > +#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6) > +#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0) > +#define ADM_VLAN_FILT_MEMBER_MASK 0x3f > +/* Fields in ADM_VLAN_FILT_H(x) */ > +#define ADM_VLAN_FILT_VALID (1 << 15) > +#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0) > + > + > /* > * Split the register address in phy id and register > * it will get combined again by the mdio bus op > _______________________________________________ > openwrt-devel mailing list > openwrt-devel@lists.openwrt.org > https://lists.openwrt.org/mailman/listinfo/openwrt-devel _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel