2021-11-05 12:24:09

by Wells Lu 呂芳騰

[permalink] [raw]
Subject: RE: [PATCH 2/2] net: ethernet: Add driver for Sunplus SP7021

Hi,

Thanks a lot for your review.

> > +config NET_VENDOR_SUNPLUS
> > + tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
>
> The "with L2 Switch" is causing lots of warning bells to ring for me.
>
> I don't see any references to switchdev or DSA in this driver. How is the
> switch managed? There have been a few examples in the past of similar two
> port switches being first supported in Dual MAC mode. Later trying to
> actually use the switch in the Linux was always ran into problems, and
> basically needed a new driver. So i want to make sure you don't have this
> problem.
>
> In the Linux world, Ethernet switches default to having there
> ports/interfaces separated. This effectively gives you your dual MAC mode by
> default. You then create a Linux bridge, and add the ports/interfaces to the
> bridge. switchdev is used to offload the bridge, telling the hardware to
> enable the L2 switch between the ports.
>
> So you don't need the mode parameter in DT. switchdev tells you this.
> Switchdev gives user space access to the address table etc.

The L2 switch of Ethernet of SP7021 is not used to forward packets
between two network interfaces.

Sunplus Dual Ethernet devices consists of one CPU port, two LAN
ports, and a L2 switch. L2 switch is a circuitry which receives packets
from CPU or LAN ports and then forwards them other ports. Rules of
forwarding packets are set by driver.

Ethernet driver of SP7021 of Sunplus supports 3 operation modes:
- Dual NIC mode
- An NIC with two LAN ports mode (daisy-chain mode)
- An NIC with two LAN ports mode 2

Dual NIC mode
Ethernet driver creates two net-device interfaces (eg: eth0 and eth1).
Each has its dedicated LAN port. For example, LAN port 0 is for
net-device interface eth0. LAN port 1 is for net-device interface
eth1. Packets from LAN port 0 will be always forwarded to eth0 and
vice versa by L2 switch. Similarly, packets from LAN port 1 will be
always forwarded to eth1 and vice versa by L2 switch. Packets will
never be forwarded between two LAN ports, or between eth0 and
LAN port 1, or between eth1 and LAN port 0. The two network
devices work independently.

An NIC with two LAN ports mode (daisy-chain mode)
Ethernet driver creates one net-device interface (eg: eth0), but the
net-device interface has two LAN ports. In this mode, a packet from
one LAN port will be either forwarded to net-device interface (eht0)
if its destination address matches MAC address of net-device
interface (eth0), or forwarded to other LAN port. A packet from
net-device interface (eth0) will be forwarded to a LAN port if its
destination address is learnt by L2 switch, or forwarded to both
LAN ports if its destination has not been learnt yet.

An NIC with two LAN ports mode 2
This mode is similar to ??An NIC with two LAN ports mode??. The
difference is that a packet from net-device interface (eth0) will be
always forwarded to both LAN ports. Learning function of L2 switch
is turned off in this mode. This means L2 switch will never learn the
source address of a packet. So, it always forward packets to both
LAN ports. This mode works like you have 2-port Ethernet hub.

>
> > +obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sp_l2sw.o
> ...
> > +struct l2sw_common {
>
> Please change your prefix. l2sw is a common prefix, there are other silicon
> vendors using l2sw. I would suggest sp_l2sw or spl2sw.

Ok, I'll modify two struct names in next patch as shown below:
l2sw_common --> sp_common
l2sw_mac --> sp_mac

Should I also modify prefix of file name?

> > +static int ethernet_do_ioctl(struct net_device *net_dev, struct ifreq
> > +*ifr, int cmd) {
> > + struct l2sw_mac *mac = netdev_priv(net_dev);
> > + struct l2sw_common *comm = mac->comm;
> > + struct mii_ioctl_data *data = if_mii(ifr);
> > + unsigned long flags;
> > +
> > + pr_debug(" if = %s, cmd = %04x\n", ifr->ifr_ifrn.ifrn_name, cmd);
> > + pr_debug(" phy_id = %d, reg_num = %d, val_in = %04x\n",
> data->phy_id,
> > + data->reg_num, data->val_in);
>
> You should not be using any of the pr_ functions. You have a net_dev, so
> netdev_dbg().

Ok, I will replace pr_xxx() functions with netdev_xxx() functions
where 'net_dev' is available in next patch.


> > +
> > + // Check parameters' range.
> > + if ((cmd == SIOCGMIIREG) || (cmd == SIOCSMIIREG)) {
> > + if (data->reg_num > 31) {
> > + pr_err(" reg_num (= %d) excesses range!\n",
> (int)data->reg_num);
>
> Don't spam the kernel log for things like this.

Ok, I'll remove the pr_err() line in next patch.


> > + return -EINVAL;
> > + }
> > + }
> > +
> > + switch (cmd) {
> > + case SIOCGMIIPHY:
> > + if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1") ==
> > +0))
>
> You cannot rely on the name, systemd has probably renamed it. If you have
> using phylib correctly, net_dev->phydev is what you want.

Ok, I'll use name of the second net device to do compare,
instead of using fixed string "eth1", in next patch.


>
>
> > + return comm->phy2_addr;
> > + else
> > + return comm->phy1_addr;
> > +
> > + case SIOCGMIIREG:
> > + spin_lock_irqsave(&comm->ioctl_lock, flags);
> > + data->val_out = mdio_read(data->phy_id, data->reg_num);
> > + spin_unlock_irqrestore(&comm->ioctl_lock, flags);
> > + pr_debug(" val_out = %04x\n", data->val_out);
> > + break;
> > +
> > + case SIOCSMIIREG:
> > + spin_lock_irqsave(&comm->ioctl_lock, flags);
> > + mdio_write(data->phy_id, data->reg_num, data->val_in);
> > + spin_unlock_irqrestore(&comm->ioctl_lock, flags);
> > + break;
> > +
>
> You should be using phylink_mii_ioctl() or phy_mii_ioctl().
>
> You locking is also suspect.

Ok, I'll use phy_mii_ioctl() for SIOCSMIIREG and SIOCGMIIREG commands
and remove spin_lock in next patch.


> > +static int ethernet_change_mtu(struct net_device *ndev, int new_mtu)
> > +{
> > + if (netif_running(ndev))
> > + return -EBUSY;
> > +
> > + if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
> > + return -EINVAL;
>
> The core will do this for you, if you set the values in the ndev correct at
> probe time.

Ok, I'll remove the whole function ethernet_change_mtu () in next patch
as the core will do everything for changing mtu.


> > +
> > + ndev->mtu = new_mtu;
> > +
> > + return 0;
> > +}
> > +
>
>
> > +static int mdio_access(u8 op_cd, u8 dev_reg_addr, u8 phy_addr, u32
> > +wdata) {
> > + u32 value, time = 0;
> > +
> > + HWREG_W(phy_cntl_reg0, (wdata << 16) | (op_cd << 13) |
> (dev_reg_addr << 8) | phy_addr);
> > + wmb(); // make sure settings are effective.
>
> That suggests you are using the wrong macros to access the registers.

Ok, I'll modify HWREG_W() and HWREG_R() and remove wmb().


> > + do {
> > + if (++time > MDIO_RW_TIMEOUT_RETRY_NUMBERS) {
> > + pr_err(" mdio failed to operate!\n");
> > + time = 0;
> > + }
> > +
> > + value = HWREG_R(phy_cntl_reg1);
> > + } while ((value & 0x3) == 0);
>
> include/linux/iopoll.h.
>
>
> > + if (time == 0)
> > + return -1;
>
> -ETIMDEOUT. One of the advantages of iopoll.h is that reusing code avoids
> issues like this.

Ok, I'll replace the do-while loop with read_poll_timeout().


> > +u32 mdio_read(u32 phy_id, u16 regnum) {
> > + int ret;
>
> Please check for C45 and return -EOPNOTSUPP.

Ok, I'll modify mdio_read() to return -EOPNOTSUPP
when error (time-out) occurs.

> > +
> > + ret = mdio_access(MDIO_READ_CMD, regnum, phy_id, 0);
> > + if (ret < 0)
> > + return -EIO;
> > +
> > + return ret;
> > +}
> > +
> > +u32 mdio_write(u32 phy_id, u32 regnum, u16 val) {
> > + int ret;
> > +
>
> Please check for C45 and return -EOPNOTSUPP.

Ok, I'll modify mdio_read() to return -EOPNOTSUPP
when error (time-out) occurs.


> > +
> > +inline void tx_trigger(void)
>
> No inline functions in C code. Let the compiler decide.

Ok, I'll remove 'inline' for all functions in next patch.


> > +int phy_cfg(struct l2sw_mac *mac)
> > +{
> > + // Bug workaround:
> > + // Flow-control of phy should be enabled. L2SW IP flow-control will refer
> > + // to the bit to decide to enable or disable flow-control.
> > + mdio_write(mac->comm->phy1_addr, 4,
> mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));
> > + mdio_write(mac->comm->phy2_addr, 4,
> mdio_read(mac->comm->phy2_addr,
> > +4) | (1 << 10));
>
> This should be in the PHY driver. The MAC driver should never need to touch
> PHY registers.

Sunplus Ethernet MAC integrates MDIO controller.
So Ethernet driver has MDIO- and PHY-related code.
To work-around a circuitry bug, we need to enable
bit 10 of register 4 of PHY.
Where should we place the code?


> > +++ b/drivers/net/ethernet/sunplus/l2sw_mdio.c
> > @@ -0,0 +1,118 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright Sunplus Technology Co., Ltd.
> > + * All rights reserved.
> > + */
> > +
> > +#include "l2sw_mdio.h"
> > +
> > +static int mii_read(struct mii_bus *bus, int phy_id, int regnum) {
> > + return mdio_read(phy_id, regnum);
> > +}
> > +
> > +static int mii_write(struct mii_bus *bus, int phy_id, int regnum, u16
> > +val) {
> > + return mdio_write(phy_id, regnum, val); }
> > +
> > +u32 mdio_init(struct platform_device *pdev, struct net_device
> > +*net_dev) {
> > + struct l2sw_mac *mac = netdev_priv(net_dev);
> > + struct mii_bus *mii_bus;
> > + struct device_node *mdio_node;
> > + u32 ret;
> > +
> > + mii_bus = mdiobus_alloc();
> > + if (!mii_bus) {
> > + pr_err(" Failed to allocate mdio_bus memory!\n");
> > + return -ENOMEM;
> > + }
> > +
> > + mii_bus->name = "sunplus_mii_bus";
> > + mii_bus->parent = &pdev->dev;
> > + mii_bus->priv = mac;
> > + mii_bus->read = mii_read;
> > + mii_bus->write = mii_write;
> > + snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-mii",
> > +dev_name(&pdev->dev));
> > +
> > + mdio_node = of_get_parent(mac->comm->phy1_node);
> > + ret = of_mdiobus_register(mii_bus, mdio_node);
> > + if (ret) {
> > + pr_err(" Failed to register mii bus (ret = %d)!\n", ret);
> > + mdiobus_free(mii_bus);
> > + return ret;
> > + }
> > +
> > + mac->comm->mii_bus = mii_bus;
> > + return ret;
> > +}
> > +
> > +void mdio_remove(struct net_device *net_dev) {
> > + struct l2sw_mac *mac = netdev_priv(net_dev);
> > +
> > + if (mac->comm->mii_bus) {
> > + mdiobus_unregister(mac->comm->mii_bus);
> > + mdiobus_free(mac->comm->mii_bus);
> > + mac->comm->mii_bus = NULL;
> > + }
> > +}
>
> You MDIO code is pretty scattered around. Please bring it all together in one
> file.

Ok, I'll move mdio_read() and mdio_write() to 'l2sw_mdio.c',
but keep mdio_access() in 'l2sw_hal.c' because it is a function
which accesses hardware registers actually.


> > +static void mii_linkchange(struct net_device *netdev) { }
>
> Nothing to do? Seems very odd. Don't you need to tell the MAC it should do
> 10Mbps or 100Mbps? What about pause?

No, hardware does it automatically.
Sunplus MAC integrates MDIO controller.
It reads PHY status and set MAC automatically.


> > +
> > +int mac_phy_probe(struct net_device *netdev) {
> > + struct l2sw_mac *mac = netdev_priv(netdev);
> > + struct phy_device *phydev;
> > + int i;
> > +
> > + phydev = of_phy_connect(mac->net_dev, mac->comm->phy1_node,
> mii_linkchange,
> > + 0, PHY_INTERFACE_MODE_RGMII_ID);
>
> You should not hard code PHY_INTERFACE_MODE_RGMII_ID. Use the DT
> property "phy-mode"

Ok, I'll do it in next patch.
I create a new file 'l2sw_phy.c' and move phy-related functions
from 'l2sw_hal.c' to it.


> > + if (!phydev) {
> > + pr_err(" \"%s\" has no phy found\n", netdev->name);
> > + return -1;
>
> -ENODEV;
>
> Never use -1, pick an error code.

Ok, I'll modify it in next patch.

>
> > + }
> > +
> > + if (mac->comm->phy2_node) {
> > + of_phy_connect(mac->net_dev, mac->comm->phy2_node,
> mii_linkchange,
> > + 0, PHY_INTERFACE_MODE_RGMII_ID);
> > + }
> > +
> > + linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
> phydev->supported);
> > + linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
> > +phydev->supported);
>
> So the MAC does not support pause? I'm then confused about phy_cfg().

Yes, MAC supports pause. MAC (hardware) takes care of pause
automatically.

Should I remove the two lines?


>
> Andrew


2021-11-05 13:39:59

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH 2/2] net: ethernet: Add driver for Sunplus SP7021

> > > +config NET_VENDOR_SUNPLUS
> > > + tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
> >
> > The "with L2 Switch" is causing lots of warning bells to ring for me.
> >
> > I don't see any references to switchdev or DSA in this driver. How is the
> > switch managed? There have been a few examples in the past of similar two
> > port switches being first supported in Dual MAC mode. Later trying to
> > actually use the switch in the Linux was always ran into problems, and
> > basically needed a new driver. So i want to make sure you don't have this
> > problem.
> >
> > In the Linux world, Ethernet switches default to having there
> > ports/interfaces separated. This effectively gives you your dual MAC mode by
> > default. You then create a Linux bridge, and add the ports/interfaces to the
> > bridge. switchdev is used to offload the bridge, telling the hardware to
> > enable the L2 switch between the ports.
> >
> > So you don't need the mode parameter in DT. switchdev tells you this.
> > Switchdev gives user space access to the address table etc.
>
> The L2 switch of Ethernet of SP7021 is not used to forward packets
> between two network interfaces.
>
> Sunplus Dual Ethernet devices consists of one CPU port, two LAN
> ports, and a L2 switch. L2 switch is a circuitry which receives packets
> from CPU or LAN ports and then forwards them other ports. Rules of
> forwarding packets are set by driver.
>
> Ethernet driver of SP7021 of Sunplus supports 3 operation modes:
> - Dual NIC mode
> - An NIC with two LAN ports mode (daisy-chain mode)
> - An NIC with two LAN ports mode 2
>
> Dual NIC mode
> Ethernet driver creates two net-device interfaces (eg: eth0 and eth1).
> Each has its dedicated LAN port. For example, LAN port 0 is for
> net-device interface eth0. LAN port 1 is for net-device interface
> eth1. Packets from LAN port 0 will be always forwarded to eth0 and
> vice versa by L2 switch. Similarly, packets from LAN port 1 will be
> always forwarded to eth1 and vice versa by L2 switch. Packets will
> never be forwarded between two LAN ports, or between eth0 and
> LAN port 1, or between eth1 and LAN port 0. The two network
> devices work independently.
>
> An NIC with two LAN ports mode (daisy-chain mode)
> Ethernet driver creates one net-device interface (eg: eth0), but the
> net-device interface has two LAN ports. In this mode, a packet from
> one LAN port will be either forwarded to net-device interface (eht0)
> if its destination address matches MAC address of net-device
> interface (eth0), or forwarded to other LAN port. A packet from
> net-device interface (eth0) will be forwarded to a LAN port if its
> destination address is learnt by L2 switch, or forwarded to both
> LAN ports if its destination has not been learnt yet.
>
> An NIC with two LAN ports mode 2
> This mode is similar to “An NIC with two LAN ports mode”. The
> difference is that a packet from net-device interface (eth0) will be
> always forwarded to both LAN ports. Learning function of L2 switch
> is turned off in this mode. This means L2 switch will never learn the
> source address of a packet. So, it always forward packets to both
> LAN ports. This mode works like you have 2-port Ethernet hub.

So here you describe how the hardware can be used. Dual is two
interfaces. Daisy-chain is what you get by taking those two interfaces
and adding them to a bridge. The bridge then forwards frames between
the interfaces and the CPU as needed, based on learning. And your
third mode is the bridge always performs flooding.

A linux driver must follow the linux networking model. You cannot make
up your own model. In the linux world, you model the external
ports. The hardware always has two external ports, so you need to
always have two netdev interfaces. To bridge packets between those two
interfaces, you create a bridge and you add the interfaces to the
bridge. That is the model you need to follow. switchdev gives you the
API calls you need to implement this.

> > > +struct l2sw_common {
> >
> > Please change your prefix. l2sw is a common prefix, there are other silicon
> > vendors using l2sw. I would suggest sp_l2sw or spl2sw.
>
> Ok, I'll modify two struct names in next patch as shown below:
> l2sw_common --> sp_common
> l2sw_mac --> sp_mac
>
> Should I also modify prefix of file name?

You need to modify the prefix everywhere you use it. Function names,
variable names, all symbols. Search and replace throughout the whole
code.

> > > + return -EINVAL;
> > > + }
> > > + }
> > > +
> > > + switch (cmd) {
> > > + case SIOCGMIIPHY:
> > > + if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1") ==
> > > +0))
> >
> > You cannot rely on the name, systemd has probably renamed it. If you have
> > using phylib correctly, net_dev->phydev is what you want.
>
> Ok, I'll use name of the second net device to do compare,
> instead of using fixed string "eth1", in next patch.

No. There are always two interfaces. You always have two netdev
structures. Each netdev structure has a phydev. So use netdev->phydev.

This is another advantage of the Linux model. In your daisy chain
mode, how do i control the two PHYs? How do i see one is up and one is
down? How do i configure one to 10Half and the other 100Full?

> > > +int phy_cfg(struct l2sw_mac *mac)
> > > +{
> > > + // Bug workaround:
> > > + // Flow-control of phy should be enabled. L2SW IP flow-control will refer
> > > + // to the bit to decide to enable or disable flow-control.
> > > + mdio_write(mac->comm->phy1_addr, 4,
> > mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));
> > > + mdio_write(mac->comm->phy2_addr, 4,
> > mdio_read(mac->comm->phy2_addr,
> > > +4) | (1 << 10));
> >
> > This should be in the PHY driver. The MAC driver should never need to touch
> > PHY registers.
>
> Sunplus Ethernet MAC integrates MDIO controller.
> So Ethernet driver has MDIO- and PHY-related code.
> To work-around a circuitry bug, we need to enable
> bit 10 of register 4 of PHY.
> Where should we place the code?

The silicon is integrated, but it is still a collection of standard
blocks. Linux models those blocks independently. There is a subsystem
for the MAC, a subsystem for the MDIO bus master and a subsystem for
the PHY. You register a driver with each of these subsystems. PHY
drivers live in drivers/net/phy. Put a PHY driver in there, which
includes this workaround.

> > > +static void mii_linkchange(struct net_device *netdev) { }
> >
> > Nothing to do? Seems very odd. Don't you need to tell the MAC it should do
> > 10Mbps or 100Mbps? What about pause?
>
> No, hardware does it automatically.
> Sunplus MAC integrates MDIO controller.
> It reads PHY status and set MAC automatically.

The PHY is external? So you have no idea what PHY that is? It could be
a Marvell PHY, a microchip PHY, an Atheros PHY. Often PHYs have
pages. In order to read the temperature sensor you change the page,
read a register, and then hopefully change the page back again. If the
PHY supports Fibre as well as copper, it can put the fibre registers
in a second page. The PHY driver knows about this, it will flip the
pages as needed. The phylib core has a mutex, so that only one
operation happens at a time. So a page flip does not happen
unexpectedly.

Your MAC hardware does not take this mutex. It has no idea what page
is selected when it reads registers. Instead of getting the basic mode
register, it could get the LED control register...

The MAC should never directly access the PHY. Please disable this
hardware, and use the mii_linkchange callback to configure the MAC.

> > So the MAC does not support pause? I'm then confused about phy_cfg().

> Yes, MAC supports pause. MAC (hardware) takes care of pause
> automatically.
>
> Should I remove the two lines?

Yes.

And you need to configure the MAC based on the results of the
auto-neg.

Andrew

2021-11-08 09:37:34

by Wells Lu 呂芳騰

[permalink] [raw]
Subject: RE: [PATCH 2/2] net: ethernet: Add driver for Sunplus SP7021

> > > > +config NET_VENDOR_SUNPLUS
> > > > + tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
> > >
> > > The "with L2 Switch" is causing lots of warning bells to ring for me.
> > >
> > > I don't see any references to switchdev or DSA in this driver. How
> > > is the switch managed? There have been a few examples in the past of
> > > similar two port switches being first supported in Dual MAC mode.
> > > Later trying to actually use the switch in the Linux was always ran
> > > into problems, and basically needed a new driver. So i want to make
> > > sure you don't have this problem.
> > >
> > > In the Linux world, Ethernet switches default to having there
> > > ports/interfaces separated. This effectively gives you your dual MAC
> > > mode by default. You then create a Linux bridge, and add the
> > > ports/interfaces to the bridge. switchdev is used to offload the
> > > bridge, telling the hardware to enable the L2 switch between the ports.
> > >
> > > So you don't need the mode parameter in DT. switchdev tells you this.
> > > Switchdev gives user space access to the address table etc.
> >
> > The L2 switch of Ethernet of SP7021 is not used to forward packets
> > between two network interfaces.
> >
> > Sunplus Dual Ethernet devices consists of one CPU port, two LAN ports,
> > and a L2 switch. L2 switch is a circuitry which receives packets from
> > CPU or LAN ports and then forwards them other ports. Rules of
> > forwarding packets are set by driver.
> >
> > Ethernet driver of SP7021 of Sunplus supports 3 operation modes:
> > - Dual NIC mode
> > - An NIC with two LAN ports mode (daisy-chain mode)
> > - An NIC with two LAN ports mode 2
> >
> > Dual NIC mode
> > Ethernet driver creates two net-device interfaces (eg: eth0 and eth1).
> > Each has its dedicated LAN port. For example, LAN port 0 is for
> > net-device interface eth0. LAN port 1 is for net-device interface
> > eth1. Packets from LAN port 0 will be always forwarded to eth0 and
> > vice versa by L2 switch. Similarly, packets from LAN port 1 will be
> > always forwarded to eth1 and vice versa by L2 switch. Packets will
> > never be forwarded between two LAN ports, or between eth0 and LAN port
> > 1, or between eth1 and LAN port 0. The two network devices work
> > independently.
> >
> > An NIC with two LAN ports mode (daisy-chain mode) Ethernet driver
> > creates one net-device interface (eg: eth0), but the net-device
> > interface has two LAN ports. In this mode, a packet from one LAN port
> > will be either forwarded to net-device interface (eht0) if its
> > destination address matches MAC address of net-device interface
> > (eth0), or forwarded to other LAN port. A packet from net-device
> > interface (eth0) will be forwarded to a LAN port if its destination
> > address is learnt by L2 switch, or forwarded to both LAN ports if its
> > destination has not been learnt yet.
> >
> > An NIC with two LAN ports mode 2
> > This mode is similar to “An NIC with two LAN ports mode”. The
> > difference is that a packet from net-device interface (eth0) will be
> > always forwarded to both LAN ports. Learning function of L2 switch is
> > turned off in this mode. This means L2 switch will never learn the
> > source address of a packet. So, it always forward packets to both LAN
> > ports. This mode works like you have 2-port Ethernet hub.
>
> So here you describe how the hardware can be used. Dual is two interfaces.
> Daisy-chain is what you get by taking those two interfaces and adding them to
> a bridge. The bridge then forwards frames between the interfaces and the CPU
> as needed, based on learning. And your third mode is the bridge always
> performs flooding.
>
> A linux driver must follow the linux networking model. You cannot make up
> your own model. In the linux world, you model the external ports. The
> hardware always has two external ports, so you need to always have two
> netdev interfaces. To bridge packets between those two interfaces, you create
> a bridge and you add the interfaces to the bridge. That is the model you need
> to follow. switchdev gives you the API calls you need to implement this.

Thank you very much for your explanation.

I realize that we need to follow the Linux networking model.
I'll remove all descriptions about L2 switch or daisy-chain mode.

I'd like to modify Sunplus Ethernet driver to fulfill Linux networking model.
Here is my proposal:

SP7021 Ethernet supports 3 operation modes:
- Dual Ethernet mode
In this mode, driver creates two net-device interfaces. Each connects
to PHY. There are two LAN ports totally.
I am sorry that EMAC of SP7021 cannot support L2 switch functions
of Linux switch-device model because it only has partial function of
switch.

- One Ethernet mode
In this mode, driver creates one net-device interface. It connects to
to a PHY (There is only one LAN port).
The LAN port is then connected to a 3-port Ethernet hub.
The 3-port Ethernet hub is a hardware circuitry. All operations
(packet forwarding) are done by hardware. No software
intervention is needed. Actually, even just power-on, no software
running, two LAN ports of SP7021 work well as 2-port hub.

- One Ethernet mode 2
This is mode is similar to previous mode, but a bit different settings
to the hub.

Please kindly comment if my proposal is feasible or not


> > > > +struct l2sw_common {
> > >
> > > Please change your prefix. l2sw is a common prefix, there are other
> > > silicon vendors using l2sw. I would suggest sp_l2sw or spl2sw.
> >
> > Ok, I'll modify two struct names in next patch as shown below:
> > l2sw_common --> sp_common
> > l2sw_mac --> sp_mac
> >
> > Should I also modify prefix of file name?
>
> You need to modify the prefix everywhere you use it. Function names,
> variable names, all symbols. Search and replace throughout the whole code.

Yes, I'll do in next patch.


> > > > + return -EINVAL;
> > > > + }
> > > > + }
> > > > +
> > > > + switch (cmd) {
> > > > + case SIOCGMIIPHY:
> > > > + if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1")
> > > > +==
> > > > +0))
> > >
> > > You cannot rely on the name, systemd has probably renamed it. If you
> > > have using phylib correctly, net_dev->phydev is what you want.
> >
> > Ok, I'll use name of the second net device to do compare, instead of
> > using fixed string "eth1", in next patch.
>
> No. There are always two interfaces. You always have two netdev structures.
> Each netdev structure has a phydev. So use netdev->phydev.

Yes, I'll modify driver to use 'netdev->phydev'.


> This is another advantage of the Linux model. In your daisy chain mode, how
> do i control the two PHYs? How do i see one is up and one is down? How do i
> configure one to 10Half and the other 100Full?

No software intervention is needed.
Hardware circuitry of EMAC of Sunplus SP7021 does it well.
EMAC will communicate with PHY chips (via MDIO bus) automatically.
Actually, just giving power to SP7021, the two LAN ports act as 2-port
Ethernet hub, forwarding packets between ports.


> > > > +int phy_cfg(struct l2sw_mac *mac) {
> > > > + // Bug workaround:
> > > > + // Flow-control of phy should be enabled. L2SW IP flow-control will
> refer
> > > > + // to the bit to decide to enable or disable flow-control.
> > > > + mdio_write(mac->comm->phy1_addr, 4,
> > > mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));
> > > > + mdio_write(mac->comm->phy2_addr, 4,
> > > mdio_read(mac->comm->phy2_addr,
> > > > +4) | (1 << 10));
> > >
> > > This should be in the PHY driver. The MAC driver should never need
> > > to touch PHY registers.
> >
> > Sunplus Ethernet MAC integrates MDIO controller.
> > So Ethernet driver has MDIO- and PHY-related code.
> > To work-around a circuitry bug, we need to enable bit 10 of register 4
> > of PHY.
> > Where should we place the code?
>
> The silicon is integrated, but it is still a collection of standard blocks. Linux
> models those blocks independently. There is a subsystem for the MAC, a
> subsystem for the MDIO bus master and a subsystem for the PHY. You register
> a driver with each of these subsystems. PHY drivers live in drivers/net/phy. Put
> a PHY driver in there, which includes this workaround.
>
> > > > +static void mii_linkchange(struct net_device *netdev) { }
> > >
> > > Nothing to do? Seems very odd. Don't you need to tell the MAC it
> > > should do 10Mbps or 100Mbps? What about pause?
> >
> > No, hardware does it automatically.
> > Sunplus MAC integrates MDIO controller.
> > It reads PHY status and set MAC automatically.
>
> The PHY is external? So you have no idea what PHY that is? It could be a
> Marvell PHY, a microchip PHY, an Atheros PHY. Often PHYs have pages. In order
> to read the temperature sensor you change the page, read a register, and then
> hopefully change the page back again. If the PHY supports Fibre as well as
> copper, it can put the fibre registers in a second page. The PHY driver knows
> about this, it will flip the pages as needed. The phylib core has a mutex, so that
> only one operation happens at a time. So a page flip does not happen
> unexpectedly.
>
> Your MAC hardware does not take this mutex. It has no idea what page is
> selected when it reads registers. Instead of getting the basic mode register, it
> could get the LED control register...
>
> The MAC should never directly access the PHY. Please disable this hardware,
> and use the mii_linkchange callback to configure the MAC.

Yes, the PHYs are external. SP7021 are connected to two ICPlus IP101 PHY.
EMAC of SP7021 communicates with PHY via MDIO bus automatically after
It is setup and enabled. Sorry that the function cannot be disabled.

The mentioned bug is not a bug of PHY, but a hardware bug of EMAC.
SP7021 EMAC will read bit 10 (pause bit) of register 4 of PHY and then set
pause mode of EMAC itself. At initial, bit 10 of register 4 of PHY is 0. This
results in pause mode of EMAC be turned off. Due to timing issue, setting
bit 10 on PHY driver is not feasible, we need to set it on EMAC driver right
just after MAC is enabled.


> > > So the MAC does not support pause? I'm then confused about phy_cfg().
>
> > Yes, MAC supports pause. MAC (hardware) takes care of pause
> > automatically.
> >
> > Should I remove the two lines?
>
> Yes.

Yes, I'll remove them in next patch.


> And you need to configure the MAC based on the results of the auto-neg.
>
> Andrew