Driver for the MII/GMII/RGMII media converter blocks found in the
Renesas RZ/N1 SoC. These converters sit in the Ethernet data path,
and need to be configured with speed/duplex as detected by the PHY.

Signed-off-by: Ralph Siemsen <[email protected]>
---
 drivers/net/Kconfig     |   6 ++
 drivers/net/Makefile    |   1 +
 drivers/net/rzn1_miic.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 200 insertions(+)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index edc4cb29598..659e9fd82d0 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -886,6 +886,12 @@ config RZN1_A5PSW
          Support the Advanced 5-Port ethernet switch (A5PSW) found in
          the Renesas RZ/N1 SoC.
 
+config RZN1_MIIC
+       bool "Renesas RZ/N1 MIIC ethernet accessory"
+       help
+         Support the MIIC ethernet accessory found in Renesas RZ/N1 SoC.
+         These are media converters for MII/GMII/RGMII.
+
 config MPC8XX_FEC
        bool "Fast Ethernet Controller on MPC8XX"
        depends on MPC8xx
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b8f270ae233..049212b1fd2 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
 obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch.o
 obj-$(CONFIG_RENESAS_RAVB) += ravb.o
 obj-$(CONFIG_RZN1_A5PSW) += rzn1_a5psw.o
+obj-$(CONFIG_RZN1_MIIC) += rzn1_miic.o
 obj-$(CONFIG_RTL8139) += rtl8139.o
 obj-$(CONFIG_RTL8169) += rtl8169.o
 obj-$(CONFIG_SH_ETHER) += sh_eth.o
diff --git a/drivers/net/rzn1_miic.c b/drivers/net/rzn1_miic.c
new file mode 100644
index 00000000000..491923d8511
--- /dev/null
+++ b/drivers/net/rzn1_miic.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MII/GMII/RGMII converter support
+ *
+ * Based loosely on Linux drivers/net/pcs/pcs-rzn1-miic.c
+ * and the older driver in Renesas u-boot.
+ */
+
+#include <clk.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/of_extra.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <miiphy.h>
+#include <net/dsa.h>
+#include <regmap.h>
+
+struct rzn1_miic_plat {
+       struct clk_bulk bulk;
+       struct regmap *regmap;
+};
+
+/* RIN Ether Accessory (Switch Control) regs */
+#define PRCMD                  0x0             /* Ethernet Protect */
+#define IDCODE                 0x4             /* EtherSwitch IDCODE */
+#define MODCTRL                        0x8             /* Mode Control */
+#define PTP_MODE_CTRL          0xc             /* PTP Mode Control */
+#define PHY_LINK_MODE          0x14            /* Ethernet PHY Link Mode */
+
+/* RIN RGMII/RMII Converter regs */
+#define CONVCTRL(x)            (0x100 + ((x)) * 4) /* RGMII/RMII Converter */
+#define  CONVCTRL_10_MBPS              0
+#define  CONVCTRL_100_MBPS             BIT(0)
+#define  CONVCTRL_1000_MBPS            BIT(1)
+#define  CONVCTRL_MII                  0
+#define  CONVCTRL_RMII                 BIT(2)
+#define  CONVCTRL_RGMII                        BIT(3)
+#define  CONVCTRL_REF_CLK_OUT          BIT(4)
+#define  CONVCTRL_HALF_DUPLEX          0
+#define  CONVCTRL_FULL_DUPLEX          BIT(8)
+#define CONVRST                        0x114           /* RGMII/RMII Converter 
RST */
+#define  PHYIF_RST(x)                  BIT(x)
+
+static void rzn1_rin_prot_writel(struct udevice *dev, u32 reg, u32 val)
+{
+       struct rzn1_miic_plat *plat = dev_get_plat(dev);
+       struct regmap *regmap = plat->regmap;
+
+       /* RIN: Unprotect register writes */
+       regmap_write(regmap, PRCMD, 0x00a5);
+       regmap_write(regmap, PRCMD, 0x0001);
+       regmap_write(regmap, PRCMD, 0xfffe);
+       regmap_write(regmap, PRCMD, 0x0001);
+
+       /* Do the write */
+       regmap_write(regmap, reg, val);
+
+       /* Re-enable protection */
+       regmap_write(regmap, PRCMD, 0x0000);
+}
+
+/*
+ * RIN RGMII/RMII Converter set speed
+ * RGMII/RMII Converter number: 0..4
+ * speed: 10, 100, 1000
+ */
+void rzn1_rgmii_rmii_conv_speed(struct udevice *dev, int phy,
+                               int full_duplex, int speed)
+{
+       struct rzn1_miic_plat *plat = dev_get_plat(dev);
+       struct regmap *regmap = plat->regmap;
+       u32 val = 0;
+
+       regmap_read(regmap, CONVCTRL(phy), &val);
+
+       val &= CONVCTRL_MII | CONVCTRL_RMII | CONVCTRL_RGMII |
+               CONVCTRL_REF_CLK_OUT;
+
+       /* The interface type and speed bits are somewhat intertwined */
+       if (val != CONVCTRL_MII) {
+               if (speed == SPEED_1000)
+                       val |= CONVCTRL_1000_MBPS;
+               else if (speed == SPEED_100)
+                       val |= CONVCTRL_100_MBPS;
+               else if (speed == SPEED_10)
+                       val |= CONVCTRL_10_MBPS;
+       }
+
+       if (full_duplex)
+               val |= CONVCTRL_FULL_DUPLEX;
+
+       regmap_write(regmap, CONVCTRL(phy), val);
+}
+
+/*
+ * RIN RGMII/RMII Converter setup one
+ * RGMII/RMII Converter number: 0..4
+ * if_type: Type of PHY interface, see phy_interface_t
+ */
+void rzn1_rgmii_rmii_conv_setup(struct udevice *dev, int phy,
+                               u32 if_type, int rmii_ref_clk_out)
+{
+       struct rzn1_miic_plat *plat = dev_get_plat(dev);
+       struct regmap *regmap = plat->regmap;
+       u32 val = 0;
+       int speed = SPEED_100;
+
+       switch (if_type) {
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               val |= CONVCTRL_RGMII;
+               speed = SPEED_1000;
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               val |= CONVCTRL_RMII;
+               break;
+       case PHY_INTERFACE_MODE_MII:
+               val |= CONVCTRL_MII;
+               break;
+       };
+
+       if (if_type == PHY_INTERFACE_MODE_RMII && rmii_ref_clk_out)
+               val |= CONVCTRL_REF_CLK_OUT;
+
+       regmap_write(regmap, CONVCTRL(phy), val);
+
+       rzn1_rgmii_rmii_conv_speed(dev, phy, DUPLEX_FULL, speed);
+
+       /* reset */
+       regmap_read(regmap, CONVRST, &val);
+       val &= ~PHYIF_RST(phy);
+       rzn1_rin_prot_writel(dev, CONVRST, val);
+       udelay(1000);
+       val |= PHYIF_RST(phy);
+       rzn1_rin_prot_writel(dev, CONVRST, val);
+}
+
+static int rzn1_miic_probe(struct udevice *dev)
+{
+       struct rzn1_miic_plat *plat = dev_get_plat(dev);
+       int ret;
+
+       dev_warn(dev, "%s:%d\n", __func__, __LINE__);
+
+       /* Enable clocks */
+       ret = clk_get_bulk(dev, &plat->bulk);
+       if (ret) {
+               dev_err(dev, "Failed to get clocks\n");
+               return ret;
+       }
+       ret = clk_enable_bulk(&plat->bulk);
+       if (ret) {
+               dev_err(dev, "Failed to enable clocks\n");
+               return ret;
+       }
+
+       /* Access the registers */
+       ret = regmap_init_mem(dev_ofnode(dev), &plat->regmap);
+       if (ret) {
+               dev_err(dev, "Failed to get regmap\n");
+               return ret;
+       }
+
+       /* Set PTP to use 125MHz PLL clock */
+       rzn1_rin_prot_writel(dev, PTP_MODE_CTRL, 0x2);
+
+       /* Setup RGMII/RMII Converters */
+       rzn1_rgmii_rmii_conv_setup(dev, 3, PHY_INTERFACE_MODE_RGMII_ID, 0);
+       rzn1_rgmii_rmii_conv_setup(dev, 4, PHY_INTERFACE_MODE_RGMII_ID, 0);
+
+       /* RIN: Mode Control - GMAC1 on all Switch ports */
+       rzn1_rin_prot_writel(dev, MODCTRL, 0x13);
+
+       return 0;
+}
+
+static const struct udevice_id rzn1_miic_ids[] = {
+       { .compatible = "renesas,rzn1-miic" },
+       { }
+};
+
+U_BOOT_DRIVER(rzn1_miic) = {
+       .name           = "rzn1_miic",
+       .id             = UCLASS_MISC,
+       .of_match       = rzn1_miic_ids,
+       .probe          = rzn1_miic_probe,
+       .plat_auto      = sizeof(struct rzn1_miic_plat),
+};

-- 
2.55.0

Reply via email to