RFC: ag71xx_ar7240 add new switch func export_netdevs

export_netdevs will export a net device for every port. These netdev represent a port
with out traffic.
When such a device is broght down via ifconfig the port is shutdown and
vice versa. Carrier sense is working too and ethtool can be used to
control advertise, autoneg, ...
test-seperate-ports
Alexander Couzens 9 years ago
parent af54c0148b
commit 21f5cd2f8a

@ -197,6 +197,17 @@
#define AR7240_PHY_ID1 0x004d
#define AR7240_PHY_ID2 0xd041
#define MII_ATH_REG_FUNCTION_CTRL 0x10
#define MII_ATH_FUNCTION_CTRL_MDIX BITS(5, 2)
#define MII_ATH_FUNCTION_CTRL_MDIX_OFFSET 5
#define MII_ATH_FUNCTION_CTRL_MDIX_MDI 0x0
#define MII_ATH_FUNCTION_CTRL_MDIX_MDIX 0x1
#define MII_ATH_FUNCTION_CTRL_MDIX_AUTO 0x3
#define MII_ATH_REG_PHY_SPECIFIC_STATUS 0x11
#define MII_ATH_PHY_SPECIFIC_STATUS_MDIX BIT(6)
#define MII_ATH_PHY_SPECIFIC_STATUS_MDIX_MDI 0x0
#define MII_ATH_PHY_SPECIFIC_STATUS_MDIX_MDIX 0x1
#define AR934X_PHY_ID1 0x004d
#define AR934X_PHY_ID2 0xd042
@ -303,6 +314,8 @@ struct ar7240sw {
rwlock_t stats_lock;
struct ar7240sw_port_stat port_stats[AR7240_NUM_PORTS];
bool export_netdev;
struct net_device *port_netdev[AR7240_NUM_PORTS];
};
struct ar7240sw_hw_stat {
@ -835,6 +848,260 @@ ar7240_set_ports(struct switch_dev *dev, struct switch_val *val)
return 0;
}
struct priv_netdev_port {
uint8_t port;
struct net_device *netdev;
};
int port_init(struct net_device *dev) {
return 0;
}
void port_dummy_cb(struct net_device *dev) {
}
int port_open(struct net_device *dev) {
phy_start(dev->phydev);
netif_start_queue(dev);
phy_start_aneg(dev->phydev);
return 0;
}
int port_stop(struct net_device *dev) {
netif_stop_queue(dev);
phy_stop(dev->phydev);
return 0;
}
static int port_start_xmit(struct sk_buff *skb, struct net_device *dev) {
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static int port_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mii_bus *bus;
struct phy_device *phydev;
int reg;
if (!dev->phydev || !dev->phydev->bus)
return -EINVAL;
phydev = dev->phydev;
bus = dev->phydev->bus;
/* get mdix ctrl */
mutex_lock(&bus->mdio_lock);
reg = bus->read(bus, phydev->addr, MII_ATH_REG_FUNCTION_CTRL);
mutex_unlock(&bus->mdio_lock);
reg &= MII_ATH_FUNCTION_CTRL_MDIX;
reg = reg >> MII_ATH_FUNCTION_CTRL_MDIX_OFFSET;
switch (reg) {
case MII_ATH_FUNCTION_CTRL_MDIX_MDI:
cmd->eth_tp_mdix_ctrl = ETH_TP_MDI;
break;
case MII_ATH_FUNCTION_CTRL_MDIX_MDIX:
cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X;
break;
case MII_ATH_FUNCTION_CTRL_MDIX_AUTO:
cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
break;
default:
printk(KERN_ERR "%s:%d Unknown state %x\n", __FILE__, __LINE__, reg);
}
/* get mdix status */
mutex_lock(&bus->mdio_lock);
reg = bus->read(bus, phydev->addr, MII_ATH_REG_PHY_SPECIFIC_STATUS);
mutex_unlock(&bus->mdio_lock);
cmd->eth_tp_mdix = reg & MII_ATH_PHY_SPECIFIC_STATUS_MDIX ? ETH_TP_MDI_X : ETH_TP_MDI;
/* phy_ethtool_gset sets everything correct beside the port */
phy_ethtool_gset(dev->phydev, cmd);
cmd->port = PORT_TP;
return 0;
}
static int port_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
int reg = 0;
int ret = 0;
struct mii_bus *bus;
struct phy_device *phydev;
if (!dev->phydev || !dev->phydev->bus)
return -EINVAL;
phydev = dev->phydev;
bus = dev->phydev->bus;
/* mdix is vendor specific */
if (cmd->eth_tp_mdix_ctrl) {
int mdi;
switch (cmd->eth_tp_mdix_ctrl) {
case ETH_TP_MDI:
mdi = MII_ATH_FUNCTION_CTRL_MDIX_MDI;
break;
case ETH_TP_MDI_X:
mdi = MII_ATH_FUNCTION_CTRL_MDIX_MDIX;
break;
case ETH_TP_MDI_AUTO:
mdi = MII_ATH_FUNCTION_CTRL_MDIX_AUTO;
break;
default:
return -EINVAL;
}
mutex_lock(&bus->mdio_lock);
reg = bus->read(bus, phydev->addr, MII_ATH_REG_FUNCTION_CTRL);
reg &= ~MII_ATH_FUNCTION_CTRL_MDIX;
reg |= mdi << MII_ATH_FUNCTION_CTRL_MDIX_OFFSET;
bus->write(bus, phydev->addr, MII_ATH_REG_FUNCTION_CTRL, reg);
mutex_unlock(&bus->mdio_lock);
ret = genphy_soft_reset(phydev);
if (ret)
return ret;
}
return phy_ethtool_sset(dev->phydev, cmd);
}
static int port_nway_reset(struct net_device *dev)
{
if (dev->phydev)
return phy_start_aneg(dev->phydev);
return -EINVAL;
}
int port_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
if (!dev->phydev)
return -EINVAL;
return phy_mii_ioctl(dev->phydev, rq, cmd);
}
static const struct net_device_ops port_netdev_ops = {
.ndo_init = port_init,
.ndo_uninit = port_dummy_cb,
.ndo_open = port_open,
.ndo_stop = port_stop,
.ndo_start_xmit = port_start_xmit,
.ndo_tx_timeout = port_dummy_cb,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = port_ioctl,
};
static const struct ethtool_ops port_ethtool_ops = {
.get_settings = port_get_settings,
.set_settings = port_set_settings,
.get_link = ethtool_op_get_link,
.nway_reset = port_nway_reset,
};
void port_dev_setup(struct net_device *dev) {
}
static int
ar7240_set_export_netdevs(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct ar7240sw *as = sw_to_ar7240(dev);
bool create = !!val->value.i;
int i;
int err = 0;
if (as->export_netdev == create)
return 0;
if (create == as->export_netdev)
return 0;
if (create) {
for (i = 0; i < AR7240_NUM_PORTS; i++) {
struct net_device *netdev;
struct phy_device *phy;
struct priv_netdev_port *priv;
phy = as->mii_bus->phy_map[i];
if (!phy)
continue;
netdev = alloc_netdev_mqs(sizeof(struct priv_netdev_port), "phy%d", ether_setup, 1, 1);
if (!netdev) {
printk(KERN_ERR "can not allow netdev\n");
/* TODO: free and set export = 0 */
return -1;
}
SET_NETDEV_DEV(netdev, &dev->netdev->dev);
netdev->netdev_ops = &port_netdev_ops;
SET_ETHTOOL_OPS(netdev, &port_ethtool_ops);
eth_hw_addr_random(netdev);
priv = netdev_priv(netdev);
priv->netdev = netdev;
priv->port = i;
err = phy_connect_direct(netdev, phy, &port_dummy_cb, PHY_INTERFACE_MODE_MII);
if (err) {
printk(KERN_ERR "phy attach failed %d with err %d\n", i, err);
continue;
}
phy->supported = (PHY_BASIC_FEATURES & ~SUPPORTED_MII);
phy->advertising = phy->supported;
err = register_netdev(netdev);
if (err) {
dev_err(&dev->netdev->dev, "register netdevice failed %d\n", err);
/* TODO: exit */
return -1;
}
as->port_netdev[i] = netdev;
/* after creating the port phys we put them up */
rtnl_lock();
dev_open(netdev);
rtnl_unlock();
}
as->export_netdev = true;
} else {
/* disable ports */
struct net_device *netdev;
for (i = 0; i < AR7240_NUM_PORTS; i++) {
netdev = as->port_netdev[i];
if (!netdev)
continue;
unregister_netdev(netdev);
as->port_netdev[i] = NULL;
}
as->export_netdev = false;
}
return 0;
}
static int
ar7240_get_export_netdevs(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct ar7240sw *as = sw_to_ar7240(dev);
val->value.i = as->export_netdev;
return 0;
}
static int
ar7240_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
@ -995,6 +1262,14 @@ static struct switch_attr ar7240_globals[] = {
.get = ar7240_get_vlan,
.max = 1
},
{
.type = SWITCH_TYPE_INT,
.name = "export_netdevs",
.description = "Export ports a netdev (no traffic!)",
.set = ar7240_set_export_netdevs,
.get = ar7240_get_export_netdevs,
.max = 1
}
};
static struct switch_attr ar7240_port[] = {

Loading…
Cancel
Save