From patchwork Tue Dec 9 19:21:21 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander 'lynxis' Couzens X-Patchwork-Id: 419229 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from arrakis.dune.hu (arrakis.dune.hu [78.24.191.176]) (using TLSv1.1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 2A2B01400B7 for ; Wed, 10 Dec 2014 06:22:54 +1100 (AEDT) Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id C653928C1A6; Tue, 9 Dec 2014 20:20:13 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on arrakis.dune.hu X-Spam-Level: X-Spam-Status: No, score=-1.5 required=5.0 tests=BAYES_00 autolearn=unavailable version=3.3.2 Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id AFD2328C07F for ; Tue, 9 Dec 2014 20:19:54 +0100 (CET) X-policyd-weight: using cached result; rate: -7.6 Received: from mail.base45.de (mail.base45.de [80.241.61.77]) by arrakis.dune.hu (Postfix) with ESMTPS for ; Tue, 9 Dec 2014 20:19:54 +0100 (CET) Received: from ip5b422265.dynamic.kabel-deutschland.de ([91.66.34.101] helo=lazus.lan) by mail.base45.de with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA256:128) (Exim 4.82) (envelope-from ) id 1XyQLo-000163-HG; Tue, 09 Dec 2014 20:21:36 +0100 From: Alexander Couzens To: openwrt-devel@lists.openwrt.org Date: Tue, 9 Dec 2014 20:21:21 +0100 Message-Id: <1418152881-7354-3-git-send-email-lynxis@fe80.eu> X-Mailer: git-send-email 2.1.3 In-Reply-To: <1418152881-7354-1-git-send-email-lynxis@fe80.eu> References: <1418152881-7354-1-git-send-email-lynxis@fe80.eu> Subject: [OpenWrt-Devel] [PATCH 3/3][RFC] ag71xx_ar7240 add new switch func export_netdevs X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openwrt-devel-bounces@lists.openwrt.org Sender: "openwrt-devel" 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, ... --- .../net/ethernet/atheros/ag71xx/ag71xx_ar7240.c | 275 +++++++++++++++++++++ 1 file changed, 275 insertions(+) diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c index a94837a..9dc913f 100644 --- a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c @@ -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 @@ -304,6 +315,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 { @@ -836,6 +849,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) @@ -996,6 +1263,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[] = {