On 09/07/19 3:52 AM, Joe Hershberger wrote:
On Thu, Jun 6, 2019 at 7:14 AM Keerthy <j-keer...@ti.com> wrote:

From: Grygorii Strashko <grygorii.stras...@ti.com>

Add new driver for the TI AM65x SoC Gigabit Ethernet Switch subsystem (CPSW
NUSS). It has two ports and provides Ethernet packet communication for the
device and can be configured as an Ethernet switch. CPSW NUSS features: the
Reduced Gigabit Media Independent Interface (RGMII), Reduced Media
Independent Interface (RMII), and the Management Data Input/Output (MDIO)
interface for physical layer device (PHY) management. The TI AM65x SoC has
integrated two-port Gigabit Ethernet Switch subsystem into device MCU
domain named MCU_CPSW0. One Ethernet port (port 1) with selectable RGMII
and RMII interfaces and an internal Communications Port Programming
Interface (CPPI) port (Host port 0).

Host Port 0 CPPI Packet Streaming Interface interface supports 8 TX
channels and on RX channels operating by TI am654 NAVSS Unified DMA
Peripheral Root Complex (UDMA-P) controller.

Signed-off-by: Grygorii Strashko <grygorii.stras...@ti.com>
Signed-off-by: Keerthy <j-keer...@ti.com>
---
  drivers/net/ti/Kconfig          |   8 +
  drivers/net/ti/Makefile         |   1 +
  drivers/net/ti/am65-cpsw-nuss.c | 794 ++++++++++++++++++++++++++++++++
  3 files changed, 803 insertions(+)
  create mode 100644 drivers/net/ti/am65-cpsw-nuss.c

diff --git a/drivers/net/ti/Kconfig b/drivers/net/ti/Kconfig
index 82bc9f5d03..ecf642de10 100644
--- a/drivers/net/ti/Kconfig
+++ b/drivers/net/ti/Kconfig
@@ -18,3 +18,11 @@ config DRIVER_TI_KEYSTONE_NET
         bool "TI Keystone 2 Ethernet"
         help
            This driver supports the TI Keystone 2 Ethernet subsystem
+
+config TI_AM65_CPSW_NUSS
+       bool "TI K3 AM65x MCU CPSW Nuss Ethernet controller driver"
+       depends on ARCH_K3
+       select PHYLIB
+       help
+         This driver supports TI K3 MCU CPSW Nuss Ethernet controller
+         in Texas Instruments K3 AM65x SoCs.
diff --git a/drivers/net/ti/Makefile b/drivers/net/ti/Makefile
index ee3e4eb5d6..8d3808bb4b 100644
--- a/drivers/net/ti/Makefile
+++ b/drivers/net/ti/Makefile
@@ -5,3 +5,4 @@
  obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o cpsw-common.o cpsw_mdio.o
  obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
  obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o cpsw_mdio.o
+obj-$(CONFIG_TI_AM65_CPSW_NUSS) += am65-cpsw-nuss.o cpsw_mdio.o
diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c
new file mode 100644
index 0000000000..658cc34033
--- /dev/null
+++ b/drivers/net/ti/am65-cpsw-nuss.c

[ ... ]

+static const struct eth_ops am65_cpsw_ops = {
+       .start          = am65_cpsw_start,
+       .send           = am65_cpsw_send,
+       .recv           = am65_cpsw_recv,
+       .free_pkt       = am65_cpsw_free_pkt,
+       .stop           = am65_cpsw_stop,
+       .read_rom_hwaddr = am65_cpsw_read_rom_hwaddr,

I'm surprised that write_hwaddr is not included.

This driver is pretty much based on cpsw.c under drivers/net/ti.
I believe there is no mandate to write the hw mac address to specific IP register.


+};
+
+static int am65_cpsw_mdio_init(struct udevice *dev)
+{
+       struct am65_cpsw_priv *priv = dev_get_priv(dev);
+       struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
+
+       if (!priv->has_phy || cpsw_common->bus)
+               return 0;
+
+       cpsw_common->bus = cpsw_mdio_init(dev->name,
+                                         cpsw_common->mdio_base,
+                                         cpsw_common->bus_freq,
+                                         clk_get_rate(&cpsw_common->fclk));
+       if (!cpsw_common->bus)
+               return -EFAULT;
+
+       return 0;
+}
+
+static int am65_cpsw_phy_init(struct udevice *dev)
+{
+       struct am65_cpsw_priv *priv = dev_get_priv(dev);
+       struct am65_cpsw_common *cpsw_common = priv->cpsw_common;
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct phy_device *phydev;
+       u32 supported = PHY_GBIT_FEATURES;
+       int ret;
+
+       phydev = phy_connect(cpsw_common->bus,
+                            priv->phy_addr,
+                            priv->dev,
+                            pdata->phy_interface);
+
+       if (!phydev) {
+               dev_err(dev, "phy_connect() failed\n");
+               return -ENODEV;
+       }
+
+       phydev->supported &= supported;
+       if (pdata->max_speed) {
+               ret = phy_set_supported(phydev, pdata->max_speed);
+               if (ret)
+                       return ret;
+       }
+       phydev->advertising = phydev->supported;
+
+#ifdef CONFIG_DM_ETH

Why is this needed? I would expect this to already be assumed.

Yes. This is redundant.


+       if (ofnode_valid(priv->phy_node))
+               phydev->node = priv->phy_node;
+#endif
+
+       priv->phydev = phydev;
+       ret = phy_config(phydev);
+       if (ret < 0)
+               pr_err("phy_config() failed: %d", ret);
+
+       return ret;
+}
+
+static int am65_cpsw_ofdata_parse_phy(struct udevice *dev, ofnode port_np)
+{
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct am65_cpsw_priv *priv = dev_get_priv(dev);
+       struct ofnode_phandle_args out_args;
+       const char *phy_mode;
+       int ret = 0;
+
+       phy_mode = ofnode_read_string(port_np, "phy-mode");
+       if (phy_mode) {
+               pdata->phy_interface =
+                               phy_get_interface_by_name(phy_mode);
+               if (pdata->phy_interface == -1) {
+                       dev_err(dev, "Invalid PHY mode '%s', port %u\n",
+                               phy_mode, priv->port_id);
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ofnode_read_u32(port_np, "max-speed", (u32 *)&pdata->max_speed);
+       if (pdata->max_speed)
+               dev_err(dev, "Port %u speed froced to %uMbit\n",
+                       priv->port_id, pdata->max_speed);
+
+       priv->has_phy  = true;
+       ret = ofnode_parse_phandle_with_args(port_np, "phy-handle",
+                                            NULL, 0, 0, &out_args);
+       if (ret) {
+               dev_err(dev, "can't parse phy-handle port %u (%d)\n",
+                       priv->port_id, ret);
+               priv->has_phy  = false;
+               ret = 0;
+       }
+
+       priv->phy_node = out_args.node;
+       if (priv->has_phy) {
+               ret = ofnode_read_u32(priv->phy_node, "reg", &priv->phy_addr);
+               if (ret) {
+                       dev_err(dev, "failed to get phy_addr port %u (%d)\n",
+                               priv->port_id, ret);
+                       goto out;
+               }
+       }
+
+out:
+       return ret;
+}
+
+static int am65_cpsw_probe_cpsw(struct udevice *dev)
+{
+       struct am65_cpsw_priv *priv = dev_get_priv(dev);
+       struct eth_pdata *pdata = dev_get_platdata(dev);
+       struct am65_cpsw_common *cpsw_common;
+       ofnode ports_np, node;
+       int ret, i;
+
+       priv->dev = dev;
+
+       cpsw_common = calloc(1, sizeof(*priv->cpsw_common));
+       if (!cpsw_common)
+               return -ENOMEM;
+       priv->cpsw_common = cpsw_common;
+
+       cpsw_common->dev = dev;
+       cpsw_common->ss_base = dev_read_addr(dev);
+       if (cpsw_common->ss_base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+       cpsw_common->mac_efuse = devfdt_get_addr_name(dev, "mac_efuse");
+       /* no err check - optional */
+
+       ret = power_domain_get_by_index(dev, &cpsw_common->pwrdmn, 0);
+       if (ret) {
+               dev_err(dev, "failed to get pwrdmn: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_get_by_name(dev, "fck", &cpsw_common->fclk);
+       if (ret) {
+               power_domain_free(&cpsw_common->pwrdmn);
+               dev_err(dev, "failed to get clock %d\n", ret);
+               return ret;
+       }
+
+       cpsw_common->cpsw_base = cpsw_common->ss_base + AM65_CPSW_CPSW_NU_BASE;
+       cpsw_common->ale_base = cpsw_common->cpsw_base +
+                               AM65_CPSW_CPSW_NU_ALE_BASE;
+       cpsw_common->mdio_base = cpsw_common->ss_base + AM65_CPSW_MDIO_BASE;
+
+       cpsw_common->rflow_id_base = 0;
+       cpsw_common->rflow_id_base =
+                       dev_read_u32_default(dev, "ti,rx-flow-id-base",
+                                            cpsw_common->rflow_id_base);
+
+       ports_np = dev_read_subnode(dev, "ports");
+       if (!ofnode_valid(ports_np)) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       ofnode_for_each_subnode(node, ports_np) {
+               const char *node_name;
+               u32 port_id;
+               bool disabled;
+
+               node_name = ofnode_get_name(node);
+
+               disabled = !ofnode_is_available(node);
+
+               ret = ofnode_read_u32(node, "reg", &port_id);
+               if (ret) {
+                       dev_err(dev, "%s: failed to get port_id (%d)\n",
+                               node_name, ret);
+                       goto out;
+               }
+
+               if (port_id >= AM65_CPSW_CPSWNU_MAX_PORTS) {
+                       dev_err(dev, "%s: invalid port_id (%d)\n",
+                               node_name, port_id);
+                       ret = -EINVAL;
+                       goto out;
+               }
+               cpsw_common->port_num++;
+
+               if (!port_id)
+                       continue;
+
+               priv->port_id = port_id;
+               cpsw_common->ports[port_id].disabled = disabled;
+               if (disabled)
+                       continue;
+
+               ret = am65_cpsw_ofdata_parse_phy(dev, node);
+               if (ret)
+                       goto out;
+       }
+
+       for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) {
+               struct am65_cpsw_port *port = &cpsw_common->ports[i];
+
+               port->port_base = cpsw_common->cpsw_base +
+                                 AM65_CPSW_CPSW_NU_PORTS_OFFSET +
+                                 (i * AM65_CPSW_CPSW_NU_PORTS_OFFSET);
+               port->macsl_base = port->port_base +
+                                  AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET;
+       }
+
+       node = dev_read_subnode(dev, "cpsw-phy-sel");
+       if (!ofnode_valid(node)) {
+               dev_err(dev, "can't find cpsw-phy-se\n");

cpsw-phy-se -> cpsw-phy-sel

okay. Will correct this.


+               ret = -ENOENT;
+               goto out;
+       }
+
+       cpsw_common->gmii_sel = ofnode_get_addr(node);
+       if (cpsw_common->gmii_sel == FDT_ADDR_T_NONE) {
+               dev_err(dev, "failed to get gmii_sel base\n");
+               goto out;
+       }
+
+       node = dev_read_subnode(dev, "mdio");
+       if (!ofnode_valid(node)) {
+               dev_err(dev, "can't find mdio\n");
+               ret = -ENOENT;
+               goto out;
+       }
+
+       cpsw_common->bus_freq =
+                       dev_read_u32_default(dev, "bus_freq",
+                                            AM65_CPSW_MDIO_BUS_FREQ_DEF);
+
+       am65_cpsw_gmii_sel_k3(priv, pdata->phy_interface, priv->port_id);
+
+       ret = am65_cpsw_mdio_init(dev);
+       if (ret)
+               goto out;
+
+       ret = am65_cpsw_phy_init(dev);
+       if (ret)
+               goto out;
+
+       dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X 
Ports:%u rflow_id_base:%u mdio_freq:%u\n",
+                readl(cpsw_common->ss_base),
+                readl(cpsw_common->cpsw_base),
+                readl(cpsw_common->ale_base),
+                cpsw_common->port_num,
+                cpsw_common->rflow_id_base,
+                cpsw_common->bus_freq);
+
+out:
+       clk_free(&cpsw_common->fclk);
+       power_domain_free(&cpsw_common->pwrdmn);
+       return ret;
+}
+
+static const struct udevice_id am65_cpsw_nuss_ids[] = {
+       { .compatible = "ti,am654-cpsw-nuss" },
+       { }
+};
+
+U_BOOT_DRIVER(am65_cpsw_nuss_slave) = {
+       .name   = "am65_cpsw_nuss_slave",
+       .id     = UCLASS_ETH,
+       .of_match = am65_cpsw_nuss_ids,
+       .probe  = am65_cpsw_probe_cpsw,
+       .ops    = &am65_cpsw_ops,
+       .priv_auto_alloc_size = sizeof(struct am65_cpsw_priv),
+       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+       .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
--
2.17.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot
_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to