diff options
Diffstat (limited to 'target/linux/generic-2.6/files-2.6.30/drivers/net/phy')
-rw-r--r-- | target/linux/generic-2.6/files-2.6.30/drivers/net/phy/ar8216.c | 651 | ||||
-rw-r--r-- | target/linux/generic-2.6/files-2.6.30/drivers/net/phy/mvswitch.c | 470 |
2 files changed, 0 insertions, 1121 deletions
diff --git a/target/linux/generic-2.6/files-2.6.30/drivers/net/phy/ar8216.c b/target/linux/generic-2.6/files-2.6.30/drivers/net/phy/ar8216.c deleted file mode 100644 index 93e4b33d9..000000000 --- a/target/linux/generic-2.6/files-2.6.30/drivers/net/phy/ar8216.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - * ar8216.c: AR8216 switch driver - * - * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/if.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/if_ether.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/netlink.h> -#include <linux/bitops.h> -#include <net/genetlink.h> -#include <linux/switch.h> -#include <linux/delay.h> -#include <linux/phy.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include "ar8216.h" - - -struct ar8216_priv { - struct switch_dev dev; - struct phy_device *phy; - u32 (*read)(struct ar8216_priv *priv, int reg); - void (*write)(struct ar8216_priv *priv, int reg, u32 val); - const struct net_device_ops *ndo_old; - struct net_device_ops ndo; - - /* all fields below are cleared on reset */ - bool vlan; - u8 vlan_id[AR8216_NUM_VLANS]; - u8 vlan_table[AR8216_NUM_VLANS]; - u8 vlan_tagged; - u16 pvid[AR8216_NUM_PORTS]; -}; -static struct switch_dev athdev; - -#define to_ar8216(_dev) container_of(_dev, struct ar8216_priv, dev) - -static inline void -split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) -{ - regaddr >>= 1; - *r1 = regaddr & 0x1e; - - regaddr >>= 5; - *r2 = regaddr & 0x7; - - regaddr >>= 3; - *page = regaddr & 0x1ff; -} - -static u32 -ar8216_mii_read(struct ar8216_priv *priv, int reg) -{ - struct phy_device *phy = priv->phy; - u16 r1, r2, page; - u16 lo, hi; - - split_addr((u32) reg, &r1, &r2, &page); - phy->bus->write(phy->bus, 0x18, 0, page); - msleep(1); /* wait for the page switch to propagate */ - lo = phy->bus->read(phy->bus, 0x10 | r2, r1); - hi = phy->bus->read(phy->bus, 0x10 | r2, r1 + 1); - - return (hi << 16) | lo; -} - -static void -ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) -{ - struct phy_device *phy = priv->phy; - u16 r1, r2, r3; - u16 lo, hi; - - split_addr((u32) reg, &r1, &r2, &r3); - phy->bus->write(phy->bus, 0x18, 0, r3); - msleep(1); /* wait for the page switch to propagate */ - - lo = val & 0xffff; - hi = (u16) (val >> 16); - phy->bus->write(phy->bus, 0x10 | r2, r1 + 1, hi); - phy->bus->write(phy->bus, 0x10 | r2, r1, lo); -} - -static u32 -ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) -{ - u32 v; - - v = priv->read(priv, reg); - v &= ~mask; - v |= val; - priv->write(priv, reg, v); - - return v; -} - -static int -ar8216_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_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_set_pvid(struct switch_dev *dev, int port, int vlan) -{ - struct ar8216_priv *priv = to_ar8216(dev); - priv->pvid[port] = vlan; - return 0; -} - -static int -ar8216_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_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_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_mangle_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct ar8216_priv *priv = dev->phy_ptr; - unsigned char *buf; - - if (unlikely(!priv)) - goto error; - - if (!priv->vlan) - goto send; - - if (unlikely(skb_headroom(skb) < 2)) { - if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0) - goto error; - } - - buf = skb_push(skb, 2); - buf[0] = 0x10; - buf[1] = 0x80; - -send: - return priv->ndo_old->ndo_start_xmit(skb, dev); - -error: - dev_kfree_skb_any(skb); - return 0; -} - -static int -ar8216_mangle_rx(struct sk_buff *skb, int napi) -{ - struct ar8216_priv *priv; - struct net_device *dev; - unsigned char *buf; - int port, vlan; - - dev = skb->dev; - if (!dev) - goto error; - - priv = dev->phy_ptr; - if (!priv) - goto error; - - /* don't strip the header if vlan mode is disabled */ - if (!priv->vlan) - goto recv; - - /* strip header, get vlan id */ - buf = skb->data; - skb_pull(skb, 2); - - /* check for vlan header presence */ - if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) - goto recv; - - port = buf[0] & 0xf; - - /* no need to fix up packets coming from a tagged source */ - if (priv->vlan_tagged & (1 << port)) - goto recv; - - /* lookup port vid from local table, the switch passes an invalid vlan id */ - vlan = priv->pvid[port]; - - buf[14 + 2] &= 0xf0; - buf[14 + 2] |= vlan >> 8; - buf[15 + 2] = vlan & 0xff; - -recv: - skb->protocol = eth_type_trans(skb, skb->dev); - - if (napi) - return netif_receive_skb(skb); - else - return netif_rx(skb); - -error: - /* no vlan? eat the packet! */ - dev_kfree_skb_any(skb); - return 0; -} - -static int -ar8216_netif_rx(struct sk_buff *skb) -{ - return ar8216_mangle_rx(skb, 0); -} - -static int -ar8216_netif_receive_skb(struct sk_buff *skb) -{ - return ar8216_mangle_rx(skb, 1); -} - - -static struct switch_attr ar8216_globals[] = { - { - .type = SWITCH_TYPE_INT, - .name = "enable_vlan", - .description = "Enable VLAN mode", - .set = ar8216_set_vlan, - .get = ar8216_get_vlan, - .max = 1 - }, -}; - -static struct switch_attr ar8216_port[] = { -}; - -static struct switch_attr ar8216_vlan[] = { - { - .type = SWITCH_TYPE_INT, - .name = "pvid", - .description = "VLAN ID", - .set = ar8216_set_vid, - .get = ar8216_get_vid, - .max = 4095, - }, -}; - - -static int -ar8216_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 < AR8216_NUM_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_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 < AR8216_NUM_VLANS; j++) { - if (j == val->port_vlan) - continue; - priv->vlan_table[j] &= ~(1 << p->id); - } - } - - *vt |= 1 << p->id; - } - return 0; -} - -static int -ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val) -{ - int timeout = 20; - - while ((priv->read(priv, reg) & mask) != val) { - if (timeout-- <= 0) { - printk(KERN_ERR "ar8216: timeout waiting for operation to complete\n"); - return 1; - } - } - return 0; -} - -static void -ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) -{ - if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0)) - return; - if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) { - val &= AR8216_VTUDATA_MEMBER; - val |= AR8216_VTUDATA_VALID; - priv->write(priv, AR8216_REG_VTU_DATA, val); - } - op |= AR8216_VTU_ACTIVE; - priv->write(priv, AR8216_REG_VTU, op); -} - -static int -ar8216_hw_apply(struct switch_dev *dev) -{ - struct ar8216_priv *priv = to_ar8216(dev); - u8 portmask[AR8216_NUM_PORTS]; - int i, j; - - /* flush all vlan translation unit entries */ - ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); - - memset(portmask, 0, sizeof(portmask)); - if (priv->vlan) { - /* calculate the port destination masks and load vlans - * into the vlan translation unit */ - for (j = 0; j < AR8216_NUM_VLANS; j++) { - u8 vp = priv->vlan_table[j]; - - if (!vp) - continue; - - for (i = 0; i < AR8216_NUM_PORTS; i++) { - u8 mask = (1 << i); - if (vp & mask) - portmask[i] |= vp & ~mask; - } - - if (!priv->vlan_table[j]) - continue; - - ar8216_vtu_op(priv, - AR8216_VTU_OP_LOAD | - (priv->vlan_id[j] << AR8216_VTU_VID_S), - priv->vlan_table[j]); - } - } else { - /* vlan disabled: - * isolate all ports, but connect them to the cpu port */ - for (i = 0; i < AR8216_NUM_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 < AR8216_NUM_PORTS; i++) { - int egress, ingress; - int pvid; - - if (priv->vlan) { - pvid = priv->vlan_id[priv->pvid[i]]; - } else { - pvid = i; - } - - if (priv->vlan && (priv->vlan_tagged & (1 << i))) { - egress = AR8216_OUT_ADD_VLAN; - } else { - egress = AR8216_OUT_STRIP_VLAN; - } - ingress = AR8216_IN_SECURE; - - ar8216_rmw(priv, AR8216_REG_PORT_CTRL(i), - AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | - AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | - AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, - AR8216_PORT_CTRL_LEARN | - (priv->vlan && i == AR8216_PORT_CPU ? - AR8216_PORT_CTRL_HEADER : 0) | - (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | - (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); - - ar8216_rmw(priv, AR8216_REG_PORT_VLAN(i), - AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE | - AR8216_PORT_VLAN_DEFAULT_ID, - (portmask[i] << AR8216_PORT_VLAN_DEST_PORTS_S) | - (ingress << AR8216_PORT_VLAN_MODE_S) | - (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); - } - - return 0; -} - -static int -ar8216_reset_switch(struct switch_dev *dev) -{ - struct ar8216_priv *priv = to_ar8216(dev); - int i; - - memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - - offsetof(struct ar8216_priv, vlan)); - for (i = 0; i < AR8216_NUM_VLANS; i++) { - priv->vlan_id[i] = i; - } - for (i = 0; i < AR8216_NUM_PORTS; i++) { - /* Enable port learning and tx */ - priv->write(priv, AR8216_REG_PORT_CTRL(i), - AR8216_PORT_CTRL_LEARN | - (4 << AR8216_PORT_CTRL_STATE_S)); - - priv->write(priv, AR8216_REG_PORT_VLAN(i), 0); - - /* Configure all PHYs */ - if (i == AR8216_PORT_CPU) { - priv->write(priv, AR8216_REG_PORT_STATUS(i), - AR8216_PORT_STATUS_LINK_UP | - AR8216_PORT_STATUS_SPEED | - AR8216_PORT_STATUS_TXMAC | - AR8216_PORT_STATUS_RXMAC | - AR8216_PORT_STATUS_DUPLEX); - } else { - priv->write(priv, AR8216_REG_PORT_STATUS(i), - AR8216_PORT_STATUS_LINK_AUTO); - } - } - /* XXX: undocumented magic from atheros, required! */ - priv->write(priv, 0x38, 0xc000050e); - - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, - AR8216_GCTRL_MTU, 1518 + 8 + 2); - - return ar8216_hw_apply(dev); -} - -static int -ar8216_config_init(struct phy_device *pdev) -{ - struct ar8216_priv *priv; - struct net_device *dev = pdev->attached_dev; - int ret; - - printk("%s: AR8216 PHY driver attached.\n", pdev->attached_dev->name); - pdev->supported = ADVERTISED_100baseT_Full; - pdev->advertising = ADVERTISED_100baseT_Full; - - priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - - priv->phy = pdev; - priv->read = ar8216_mii_read; - priv->write = ar8216_mii_write; - memcpy(&priv->dev, &athdev, sizeof(struct switch_dev)); - pdev->priv = priv; - if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) { - kfree(priv); - goto done; - } - - ret = ar8216_reset_switch(&priv->dev); - if (ret) - goto done; - - dev->phy_ptr = priv; - pdev->pkt_align = 2; - pdev->netif_receive_skb = ar8216_netif_receive_skb; - pdev->netif_rx = ar8216_netif_rx; - - priv->ndo_old = dev->netdev_ops; - memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops)); - priv->ndo.ndo_start_xmit = ar8216_mangle_tx; - dev->netdev_ops = &priv->ndo; - -done: - return ret; -} - -static int -ar8216_read_status(struct phy_device *phydev) -{ - struct ar8216_priv *priv = phydev->priv; - - phydev->speed = SPEED_100; - phydev->duplex = DUPLEX_FULL; - phydev->state = PHY_UP; - - /* flush the address translation unit */ - if (ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0)) - return -ETIMEDOUT; - - priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); - - return 0; -} - -static int -ar8216_config_aneg(struct phy_device *phydev) -{ - return 0; -} - -static int -ar8216_probe(struct phy_device *pdev) -{ - struct ar8216_priv priv; - - u8 id, rev; - u32 val; - - priv.phy = pdev; - val = ar8216_mii_read(&priv, AR8216_REG_CTRL); - rev = val & 0xff; - id = (val >> 8) & 0xff; - if ((id != 1) || (rev != 1)) - return -ENODEV; - - return 0; -} - -static void -ar8216_remove(struct phy_device *pdev) -{ - struct ar8216_priv *priv = pdev->priv; - struct net_device *dev = pdev->attached_dev; - - if (!priv) - return; - - if (priv->ndo_old && dev) - dev->netdev_ops = priv->ndo_old; - unregister_switch(&priv->dev); - kfree(priv); -} - -/* template */ -static struct switch_dev athdev = { - .name = "Atheros AR8216", - .cpu_port = AR8216_PORT_CPU, - .ports = AR8216_NUM_PORTS, - .vlans = AR8216_NUM_VLANS, - .attr_global = { - .attr = ar8216_globals, - .n_attr = ARRAY_SIZE(ar8216_globals), - }, - .attr_port = { - .attr = ar8216_port, - .n_attr = ARRAY_SIZE(ar8216_port), - }, - .attr_vlan = { - .attr = ar8216_vlan, - .n_attr = ARRAY_SIZE(ar8216_vlan), - }, - .get_port_pvid = ar8216_get_pvid, - .set_port_pvid = ar8216_set_pvid, - .get_vlan_ports = ar8216_get_ports, - .set_vlan_ports = ar8216_set_ports, - .apply_config = ar8216_hw_apply, - .reset_switch = ar8216_reset_switch, -}; - -static struct phy_driver ar8216_driver = { - .name = "Atheros AR8216", - .features = PHY_BASIC_FEATURES, - .probe = ar8216_probe, - .remove = ar8216_remove, - .config_init = &ar8216_config_init, - .config_aneg = &ar8216_config_aneg, - .read_status = &ar8216_read_status, - .driver = { .owner = THIS_MODULE }, -}; - -int __init -ar8216_init(void) -{ - return phy_driver_register(&ar8216_driver); -} - -void __exit -ar8216_exit(void) -{ - phy_driver_unregister(&ar8216_driver); -} - -module_init(ar8216_init); -module_exit(ar8216_exit); -MODULE_LICENSE("GPL"); - diff --git a/target/linux/generic-2.6/files-2.6.30/drivers/net/phy/mvswitch.c b/target/linux/generic-2.6/files-2.6.30/drivers/net/phy/mvswitch.c deleted file mode 100644 index 77899e082..000000000 --- a/target/linux/generic-2.6/files-2.6.30/drivers/net/phy/mvswitch.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Marvell 88E6060 switch driver - * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org> - * - * 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 - */ -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/mii.h> -#include <linux/ethtool.h> -#include <linux/phy.h> -#include <linux/if_vlan.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/uaccess.h> -#include "mvswitch.h" - -/* Undefine this to use trailer mode instead. - * I don't know if header mode works with all chips */ -#define HEADER_MODE 1 - -MODULE_DESCRIPTION("Marvell 88E6060 Switch driver"); -MODULE_AUTHOR("Felix Fietkau"); -MODULE_LICENSE("GPL"); - -#define MVSWITCH_MAGIC 0x88E6060 - -struct mvswitch_priv { - const struct net_device_ops *ndo_old; - struct net_device_ops ndo; - struct vlan_group *grp; - u8 vlans[16]; -}; - -#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv) - -static inline u16 -r16(struct phy_device *phydev, int addr, int reg) -{ - return phydev->bus->read(phydev->bus, addr, reg); -} - -static inline void -w16(struct phy_device *phydev, int addr, int reg, u16 val) -{ - phydev->bus->write(phydev->bus, addr, reg, val); -} - - -static int -mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct mvswitch_priv *priv; - char *buf = NULL; - u16 vid; - - priv = dev->phy_ptr; - if (unlikely(!priv)) - goto error; - - if (unlikely(skb->len < 16)) - goto error; - -#ifdef HEADER_MODE - if (__vlan_hwaccel_get_tag(skb, &vid)) - goto error; - - if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) { - if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC)) - goto error_expand; - if (skb->len < 62) - skb->len = 62; - } - buf = skb_push(skb, MV_HEADER_SIZE); -#else - if (__vlan_get_tag(skb, &vid)) - goto error; - - if (unlikely((vid > 15 || !priv->vlans[vid]))) - goto error; - - if (skb->len <= 64) { - if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC)) - goto error_expand; - - buf = skb->data + 64; - skb->len = 64 + MV_TRAILER_SIZE; - } else { - if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) { - if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC)) - goto error_expand; - } - buf = skb_put(skb, 4); - } - - /* move the ethernet header 4 bytes forward, overwriting the vlan tag */ - memmove(skb->data + 4, skb->data, 12); - skb->data += 4; - skb->len -= 4; - skb->mac_header += 4; -#endif - - if (!buf) - goto error; - - -#ifdef HEADER_MODE - /* prepend the tag */ - *((__be16 *) buf) = cpu_to_be16( - ((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) | - ((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M) - ); -#else - /* append the tag */ - *((__be32 *) buf) = cpu_to_be32(( - (MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) | - ((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S) - )); -#endif - - return priv->ndo_old->ndo_start_xmit(skb, dev); - -error_expand: - if (net_ratelimit()) - printk("%s: failed to expand/update skb for the switch\n", dev->name); - -error: - /* any errors? drop the packet! */ - dev_kfree_skb_any(skb); - return 0; -} - -static int -mvswitch_mangle_rx(struct sk_buff *skb, int napi) -{ - struct mvswitch_priv *priv; - struct net_device *dev; - int vlan = -1; - unsigned char *buf; - int i; - - dev = skb->dev; - if (!dev) - goto error; - - priv = dev->phy_ptr; - if (!priv) - goto error; - - if (!priv->grp) - goto error; - -#ifdef HEADER_MODE - buf = skb->data; - skb_pull(skb, MV_HEADER_SIZE); -#else - buf = skb->data + skb->len - MV_TRAILER_SIZE; - if (buf[0] != 0x80) - goto error; -#endif - - /* look for the vlan matching the incoming port */ - for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) { - if ((1 << buf[1]) & priv->vlans[i]) - vlan = i; - } - - if (vlan == -1) - goto error; - - skb->protocol = eth_type_trans(skb, skb->dev); - - if (napi) - return vlan_hwaccel_receive_skb(skb, priv->grp, vlan); - else - return vlan_hwaccel_rx(skb, priv->grp, vlan); - -error: - /* no vlan? eat the packet! */ - dev_kfree_skb_any(skb); - return 0; -} - - -static int -mvswitch_netif_rx(struct sk_buff *skb) -{ - return mvswitch_mangle_rx(skb, 0); -} - -static int -mvswitch_netif_receive_skb(struct sk_buff *skb) -{ - return mvswitch_mangle_rx(skb, 1); -} - - -static void -mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) -{ - struct mvswitch_priv *priv = dev->phy_ptr; - priv->grp = grp; -} - - -static int -mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val) -{ - int i = 100; - u16 r; - - do { - r = r16(pdev, addr, reg) & mask; - if (r == val) - return 0; - } while(--i > 0); - return -ETIMEDOUT; -} - -static int -mvswitch_config_init(struct phy_device *pdev) -{ - struct mvswitch_priv *priv = to_mvsw(pdev); - struct net_device *dev = pdev->attached_dev; - u8 vlmap = 0; - int i; - - if (!dev) - return -EINVAL; - - printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name); - pdev->supported = ADVERTISED_100baseT_Full; - pdev->advertising = ADVERTISED_100baseT_Full; - dev->phy_ptr = priv; - dev->irq = PHY_POLL; -#ifdef HEADER_MODE - dev->flags |= IFF_PROMISC; -#endif - - /* initialize default vlans */ - for (i = 0; i < MV_PORTS; i++) - priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i); - - /* before entering reset, disable all ports */ - for (i = 0; i < MV_PORTS; i++) - w16(pdev, MV_PORTREG(CONTROL, i), 0x00); - - msleep(2); /* wait for the status change to settle in */ - - /* put the ATU in reset */ - w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET); - - i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0); - if (i < 0) { - printk("%s: Timeout waiting for the switch to reset.\n", dev->name); - return i; - } - - /* set the ATU flags */ - w16(pdev, MV_SWITCHREG(ATU_CTRL), - MV_ATUCTL_NO_LEARN | - MV_ATUCTL_ATU_1K | - MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */ - ); - - /* initialize the cpu port */ - w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT), -#ifdef HEADER_MODE - MV_PORTCTRL_HEADER | -#else - MV_PORTCTRL_RXTR | - MV_PORTCTRL_TXTR | -#endif - MV_PORTCTRL_ENABLED - ); - /* wait for the phy change to settle in */ - msleep(2); - for (i = 0; i < MV_PORTS; i++) { - u8 pvid = 0; - int j; - - vlmap = 0; - - /* look for the matching vlan */ - for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) { - if (priv->vlans[j] & (1 << i)) { - vlmap = priv->vlans[j]; - pvid = j; - } - } - /* leave port unconfigured if it's not part of a vlan */ - if (!vlmap) - continue; - - /* add the cpu port to the allowed destinations list */ - vlmap |= (1 << MV_CPUPORT); - - /* take port out of its own vlan destination map */ - vlmap &= ~(1 << i); - - /* apply vlan settings */ - w16(pdev, MV_PORTREG(VLANMAP, i), - MV_PORTVLAN_PORTS(vlmap) | - MV_PORTVLAN_ID(i) - ); - - /* re-enable port */ - w16(pdev, MV_PORTREG(CONTROL, i), - MV_PORTCTRL_ENABLED - ); - } - - w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT), - MV_PORTVLAN_ID(MV_CPUPORT) - ); - - /* set the port association vector */ - for (i = 0; i <= MV_PORTS; i++) { - w16(pdev, MV_PORTREG(ASSOC, i), - MV_PORTASSOC_PORTS(1 << i) - ); - } - - /* init switch control */ - w16(pdev, MV_SWITCHREG(CTRL), - MV_SWITCHCTL_MSIZE | - MV_SWITCHCTL_DROP - ); - - /* hook into the tx function */ - priv->ndo_old = dev->netdev_ops; - memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops)); - priv->ndo.ndo_start_xmit = mvswitch_mangle_tx; - priv->ndo.ndo_vlan_rx_register = mvswitch_vlan_rx_register; - dev->netdev_ops = &priv->ndo; - - pdev->pkt_align = 2; - pdev->netif_receive_skb = mvswitch_netif_receive_skb; - pdev->netif_rx = mvswitch_netif_rx; -#ifdef HEADER_MODE - dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX; -#else - dev->features |= NETIF_F_HW_VLAN_RX; -#endif - - return 0; -} - -static int -mvswitch_read_status(struct phy_device *pdev) -{ - pdev->speed = SPEED_100; - pdev->duplex = DUPLEX_FULL; - pdev->state = PHY_UP; - - /* XXX ugly workaround: we can't force the switch - * to gracefully handle hosts moving from one port to another, - * so we have to regularly clear the ATU database */ - - /* wait for the ATU to become available */ - mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0); - - /* flush the ATU */ - w16(pdev, MV_SWITCHREG(ATU_OP), - MV_ATUOP_INPROGRESS | - MV_ATUOP_FLUSH_ALL - ); - - /* wait for operation to complete */ - mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0); - - return 0; -} - -static int -mvswitch_config_aneg(struct phy_device *phydev) -{ - return 0; -} - -static void -mvswitch_remove(struct phy_device *pdev) -{ - struct mvswitch_priv *priv = to_mvsw(pdev); - struct net_device *dev = pdev->attached_dev; - - /* restore old netdev ops */ - if (priv->ndo_old && dev) - dev->netdev_ops = priv->ndo_old; - dev->vlan_rx_register = NULL; - dev->vlan_rx_kill_vid = NULL; - dev->phy_ptr = NULL; - dev->features &= ~NETIF_F_HW_VLAN_RX; - kfree(priv); -} - -static int -mvswitch_probe(struct phy_device *pdev) -{ - struct mvswitch_priv *priv; - - priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - - pdev->priv = priv; - - return 0; -} - -static int -mvswitch_fixup(struct phy_device *dev) -{ - u16 reg; - - if (dev->addr != 0x10) - return 0; - - reg = dev->bus->read(dev->bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; - if (reg != MV_IDENT_VALUE) - return 0; - - dev->phy_id = MVSWITCH_MAGIC; - return 0; -} - - -static struct phy_driver mvswitch_driver = { - .name = "Marvell 88E6060", - .phy_id = MVSWITCH_MAGIC, - .phy_id_mask = 0xffffffff, - .features = PHY_BASIC_FEATURES, - .probe = &mvswitch_probe, - .remove = &mvswitch_remove, - .config_init = &mvswitch_config_init, - .config_aneg = &mvswitch_config_aneg, - .read_status = &mvswitch_read_status, - .driver = { .owner = THIS_MODULE,}, -}; - -static int __init -mvswitch_init(void) -{ - phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup); - return phy_driver_register(&mvswitch_driver); -} - -static void __exit -mvswitch_exit(void) -{ - phy_driver_unregister(&mvswitch_driver); -} - -module_init(mvswitch_init); -module_exit(mvswitch_exit); |