[OpenWrt-Devel,3/3,RFC] ag71xx_ar7240 add new switch func export_netdevs
diff mbox

Message ID 1418152881-7354-3-git-send-email-lynxis@fe80.eu
State Rejected
Headers show

Commit Message

Alexander Couzens Dec. 9, 2014, 7:21 p.m. UTC
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(+)

Comments

Florian Fainelli Dec. 10, 2014, 7:21 a.m. UTC | #1
Le 9 déc. 2014 11:22, "Alexander Couzens" <lynxis@fe80.eu> a écrit :
>
> 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, ...

And you just duplicated DSA, which now supports not using a particular
tagging protocol, just network devices for the ethtool control path. Can
you take a look at using DSA and coming up with an ar7240 driver if this is
the direction we want to take here?
John Crispin Dec. 10, 2014, 8:04 a.m. UTC | #2
On 10/12/2014 08:21, Florian Fainelli wrote:
> Le 9 déc. 2014 11:22, "Alexander Couzens" <lynxis@fe80.eu 
> <mailto:lynxis@fe80.eu>> a écrit :
>> 
>> 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, ...
> 
> And you just duplicated DSA, which now supports not using a
> particular tagging protocol, just network devices for the ethtool
> control path. Can you take a look at using DSA and coming up with
> an ar7240 driver if this is the direction we want to take here?

if at all we want to use DSA. to me the patch looks like an ugly hack
that implements stuff in the driver that should be generic.

i agree with florian that this is a NAK

	John

> 
> 



> 
> _______________________________________________ openwrt-devel
> mailing list openwrt-devel@lists.openwrt.org 
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>
Alexander Couzens Dec. 10, 2014, 11:02 a.m. UTC | #3
Hi John,

can you or florian give me a small overview what it's missing for atheros DSA support?

IMHO The switchpart ag71xx_ar7240 should be merged into ar8216.c first
or is there a reason, why it's has an own driver?

Best,
lynxis
John Crispin Dec. 10, 2014, 11:05 a.m. UTC | #4
On 10/12/2014 12:02, Alexander Couzens wrote:
> Hi John,
> 
> can you or florian give me a small overview what it's missing for atheros DSA support?
> 
> IMHO The switchpart ag71xx_ar7240 should be merged into ar8216.c first
> or is there a reason, why it's has an own driver?
> 
> Best,
> lynxis
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
> 

i think felix needs to comment on this. the whole swconfig vs dsa
situation was a topic at the plumbers conference

	John

Patch
diff mbox

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[] = {