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

Reply via email to