Hi Marek,

On 7/12/23 5:15 PM, Marek Vasut wrote:
[You don't often get email from marek.va...@mailbox.org. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]

On 7/12/23 22:48, Nate Drude wrote:
The MxL86110 is a low power Ethernet PHY transceiver integrated
circuit following the IEEE 802.3 [1] standard. It offers a
cost-optimized solution that is well-suited for routers,
switches, and home gateways. It performs data transmission on
an Ethernet twisted pair copper cable of category CAT5e or higher.
The MxL86110 supports 1000, 100, and 10 Mbit/s data rates.

The current driver implementation supports:
- configuring rgmii from the device tree
- configuring the LEDs from the device tree
- reading extended registers using the mdio command, e.g.:
     => mdio rx ethernet@428a0000 0xa00c
     Reading from bus ethernet@428a0000
     PHY at address 0:
     40972 - 0x2600

Signed-off-by: Nate Drude <nat...@variscite.com>
---
  drivers/net/phy/Kconfig     |   5 +
  drivers/net/phy/Makefile    |   1 +
  drivers/net/phy/mxl-8611x.c | 271 ++++++++++++++++++++++++++++++++++++
  3 files changed, 277 insertions(+)
  create mode 100644 drivers/net/phy/mxl-8611x.c

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 24158776f52..0fc0fb2c5bf 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -220,6 +220,11 @@ config PHY_MICREL_KSZ8XXX

  endif # PHY_MICREL

+config PHY_MXL8611X
+    bool "MaxLinear MXL8611X Ethernet PHYs"
+    help
+        Add support for configuring MaxLinear MXL8611X Ethernet PHYs.

Probably better just "Support for MaxLinear MXL8611X Ethernet PHYs." in
the help text.

  config PHY_MSCC
      bool "Microsemi Corp Ethernet PHYs support"

diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 85d17f109cd..b3f4d75936c 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_PHY_MARVELL_10G) += marvell10g.o
  obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o
  obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o
  obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o
+obj-$(CONFIG_PHY_MXL8611X) += mxl-8611x.o
  obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
  obj-$(CONFIG_PHY_NXP_C45_TJA11XX) += nxp-c45-tja11xx.o
  obj-$(CONFIG_PHY_NXP_TJA11XX) += nxp-tja11xx.o
diff --git a/drivers/net/phy/mxl-8611x.c b/drivers/net/phy/mxl-8611x.c
new file mode 100644
index 00000000000..467edc5bb5f
--- /dev/null
+++ b/drivers/net/phy/mxl-8611x.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ *  Driver for MaxLinear MXL861100 Ethernet PHY

611x ... you have either 1 or 0 duplicated above.

+ * Copyright 2023 Variscite Ltd.
+ * Copyright 2023 MaxLinear Inc.
+ * Author: Nate Drude <nat...@variscite.com>
+ */
+#include <common.h>
+#include <phy.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+/* PHY IDs */
+#define PHY_ID_MXL86110        0xC1335580
+#define PHY_ID_MXL86111        0xC1335588
+
+/* required to access extended registers */
+#define MXL8611X_EXTD_REG_ADDR_OFFSET                0x1E
+#define MXL8611X_EXTD_REG_ADDR_DATA                0x1F
+
+/* RGMII register */
+#define MXL8611X_EXT_RGMII_CFG1_REG                0xA003
+#define MXL8611X_EXT_RGMII_CFG1_NO_DELAY            0
+
+#define MXL8611X_EXT_RGMII_CFG1_RX_DELAY_MASK            GENMASK(13, 10)
+#define MXL8611X_EXT_RGMII_CFG1_TX_1G_DELAY_MASK        GENMASK(3, 0)
+#define MXL8611X_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK GENMASK(7, 4)
+
+/* LED registers and defines */
+#define MXL8611X_LED0_CFG_REG                    0xA00C
+#define MXL8611X_LED1_CFG_REG                    0xA00D
+#define MXL8611X_LED2_CFG_REG                    0xA00E
+
+/**
+ * struct mxl8611x_cfg_reg_map - map a config value to aregister value
+ * @cfg        value in device configuration
+ * @reg        value in the register
+ */
+struct mxl8611x_cfg_reg_map {
+    int cfg;
+    int reg;
+};
+
+static const struct mxl8611x_cfg_reg_map mxl8611x_rgmii_delays[] = {
+    { 0, 0 },
+    { 150, 1 },

Is this like n * 150 expanded into a table here ?
Please drop.

+    { 300, 2 },
+    { 450, 3 },
+    { 600, 4 },
+    { 750, 5 },
+    { 900, 6 },
+    { 1050, 7 },
+    { 1200, 8 },
+    { 1350, 9 },
+    { 1500, 10 },
+    { 1650, 11 },
+    { 1800, 12 },
+    { 1950, 13 },
+    { 2100, 14 },
+    { 2250, 15 },
+    { 0, 0 } // Marks the end of the array
+};
+
+static int mxl8611x_lookup_reg_value(const struct mxl8611x_cfg_reg_map
*tbl,
+                     const int cfg, int *reg)
+{
+    size_t i;
+
+    for (i = 0; i == 0 || tbl[i].cfg; i++) {
+        if (tbl[i].cfg == cfg) {
+            *reg = tbl[i].reg;
+            return 0;
+        }
+    }

value / 150 instead of this ?

+    return -EINVAL;
+}
+
+static u16 mxl8611x_ext_read(struct phy_device *phydev, const u32 regnum)
+{
+    u16 val;
+
+    phy_write(phydev, MDIO_DEVAD_NONE, MXL8611X_EXTD_REG_ADDR_OFFSET,
regnum);
+    val = phy_read(phydev, MDIO_DEVAD_NONE, MXL8611X_EXTD_REG_ADDR_DATA);

Is this some phy_read_mmd() reimplementation here ?

+    debug("%s: MXL86110@0x%x 0x%x=0x%x\n", __func__, phydev->addr,
regnum, val);
+
+    return val;
+}
+
+static int mxl8611x_ext_write(struct phy_device *phydev, const u32
regnum, const u16 val)
+{
+    debug("%s: MXL86110@0x%x 0x%x=0x%x\n", __func__, phydev->addr,
regnum, val);
+
+    phy_write(phydev, MDIO_DEVAD_NONE, MXL8611X_EXTD_REG_ADDR_OFFSET,
regnum);
+
+    return phy_write(phydev, MDIO_DEVAD_NONE,
MXL8611X_EXTD_REG_ADDR_DATA, val);
+}
+
+static int mxl8611x_extread(struct phy_device *phydev, int addr, int
devaddr,
+                   int regnum)
+{
+    return mxl8611x_ext_read(phydev, regnum);
+}
+
+static int mxl8611x_extwrite(struct phy_device *phydev, int addr,
+                int devaddr, int regnum, u16 val)
+{
+    return mxl8611x_ext_write(phydev, regnum, val);
+}
+
+static int mxl8611x_led_cfg(struct phy_device *phydev)
+{
+    int ret = 0;

No need to init ret to 0 here, it is inited in snprintf below.

+    int i;
+    char propname[25];
+    u32 val;
+
+    ofnode node = phy_get_ofnode(phydev);
+
+    if (!ofnode_valid(node)) {
+        printf("%s: failed to get node\n", __func__);

Try 'dev_err(phydev->dev,' instead of the printf()s

+        return -EINVAL;
+    }
+
+    /* Loop through three the LED registers */
+    for (i = 0; i < 3; i++) {
+        /* Read property from device tree */
+        ret = snprintf(propname, 25, "mxl-8611x,led%d_cfg", i);

Instead of hardcoding 25, use 'sizeof(propname)' .

+        if (ofnode_read_u32(node, propname, &val))
+            continue;

It might be simpler to call this thrice instead of a loop here:

ofnode_read_u32_default();
...ext_write();

And just initialize the registers with default values of the prop is
missing in DT. And you avoid the snprintf() too.

+        /* Update PHY LED register */
+        mxl8611x_ext_write(phydev, MXL8611X_LED0_CFG_REG + i, val);
+    }
+
+    return 0;
+}
+
+static int mxl8611x_rgmii_cfg_of_delay(struct phy_device *phydev, const
char *property, int *val)
+{
+    u32 of_val;
+    int ret;
+
+    ofnode node = phy_get_ofnode(phydev);
+
+    if (!ofnode_valid(node)) {
+        printf("%s: failed to get node\n", __func__);
+        return -EINVAL;
+    }
+
+    /* Get property from dts */
+    ret = ofnode_read_u32(node, property, &of_val);
+    if (ret)
+        return ret;
+
+    /* Convert delay in ps to register value */
+    ret = mxl8611x_lookup_reg_value(mxl8611x_rgmii_delays, of_val, val);
+    if (ret)
+        printf("%s: Error: %s = %d is invalid, using default value\n",
+               __func__, property, of_val);
+
+    return ret;
+}
+
+static int mxl8611x_rgmii_cfg(struct phy_device *phydev)
+{
+    u32 val = 0;
+    int rxdelay, txdelay_100m, txdelay_1g;
+
+    /* Get rgmii register value */
+    val = mxl8611x_ext_read(phydev, MXL8611X_EXT_RGMII_CFG1_REG);
+
+    /* Get RGMII Rx Delay Selection from device tree or rgmii register */
+    if (mxl8611x_rgmii_cfg_of_delay(phydev,
"mxl-8611x,rx-internal-delay-ps", &rxdelay))
+        rxdelay = FIELD_GET(MXL8611X_EXT_RGMII_CFG1_RX_DELAY_MASK, val);
+
+    /* Get Fast Ethernet RGMII Tx Clock Delay Selection from device
tree or rgmii register */
+    if (mxl8611x_rgmii_cfg_of_delay(phydev,

Use this form please, fix globally:

ret = operation();
if (ret)
   handle_error();

or

ret = operation();
if (ret)
   goto err_handler;

"mxl-8611x,tx-internal-delay-ps-100m",
+                    &txdelay_100m))
+        txdelay_100m =
FIELD_GET(MXL8611X_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK, val);
+
+    /* Get Gigabit Ethernet RGMII Tx Clock Delay Selection from device
tree or rgmii register */
+    if (mxl8611x_rgmii_cfg_of_delay(phydev,
"mxl-8611x,tx-internal-delay-ps-1g", &txdelay_1g))
+        txdelay_1g =
FIELD_GET(MXL8611X_EXT_RGMII_CFG1_TX_1G_DELAY_MASK, val);
+
+    switch (phydev->interface) {
+    case PHY_INTERFACE_MODE_RGMII:
+        val = MXL8611X_EXT_RGMII_CFG1_NO_DELAY;
+        break;
+    case PHY_INTERFACE_MODE_RGMII_RXID:
+        val = (val & ~MXL8611X_EXT_RGMII_CFG1_RX_DELAY_MASK) |
+            FIELD_PREP(MXL8611X_EXT_RGMII_CFG1_RX_DELAY_MASK, rxdelay);
+        break;
+    case PHY_INTERFACE_MODE_RGMII_TXID:
+        val = (val & ~MXL8611X_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK) |
+
FIELD_PREP(MXL8611X_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK, txdelay_100m);
+        val = (val & ~MXL8611X_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) |
+            FIELD_PREP(MXL8611X_EXT_RGMII_CFG1_TX_1G_DELAY_MASK,
txdelay_1g);
+        break;
+    case PHY_INTERFACE_MODE_RGMII_ID:
+        val = (val & ~MXL8611X_EXT_RGMII_CFG1_RX_DELAY_MASK) |
+            FIELD_PREP(MXL8611X_EXT_RGMII_CFG1_RX_DELAY_MASK, rxdelay);
+        val = (val & ~MXL8611X_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK) |
+
FIELD_PREP(MXL8611X_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK, txdelay_100m);
+        val = (val & ~MXL8611X_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) |
+            FIELD_PREP(MXL8611X_EXT_RGMII_CFG1_TX_1G_DELAY_MASK,
txdelay_1g);
+        break;
+    default:
+        printf("%s: Error: Unsupported rgmii mode %d\n", __func__,

dev_err()

phydev->interface);
+        return -EINVAL;
+    }
+
+    return mxl8611x_ext_write(phydev, MXL8611X_EXT_RGMII_CFG1_REG, val);
+}
+
+static int mxl8611x_config(struct phy_device *phydev)
+{
+    int ret;
+
+    /* Configure rgmii */
+    ret = mxl8611x_rgmii_cfg(phydev);
+

Drop newline

+    if (ret < 0)
+        return ret;
+
+    /* Configure LEDs */
+    ret = mxl8611x_led_cfg(phydev);
+

Drop newline

+    if (ret < 0)
+        return ret;
+
+    return genphy_config(phydev);
+}
+
+static int mxl86110_config(struct phy_device *phydev)
+{
+    printf("MXL86110 PHY detected at addr %d\n", phydev->addr);

Drop the printf

+    return mxl8611x_config(phydev);
+}
+
+static int mxl86111_config(struct phy_device *phydev)
+{
+    printf("MXL86111 PHY detected at addr %d\n", phydev->addr);

Drop the printf

+    return mxl8611x_config(phydev);
+}
+
+U_BOOT_PHY_DRIVER(mxl86110) = {
+    .name = "MXL86110",
+    .uid = PHY_ID_MXL86110,
+    .mask = 0xffffffff,
+    .features = PHY_GBIT_FEATURES,
+    .config = mxl86110_config,

This can be unified into single PHY driver , see printf above, just
tweak the .mask accordingly.

+    .startup = genphy_startup,
+    .shutdown = genphy_shutdown,
+    .readext = mxl8611x_extread,
+    .writeext = mxl8611x_extwrite,
+};
+
+U_BOOT_PHY_DRIVER(mxl86111) = {
+    .name = "MXL86111",
+    .uid = PHY_ID_MXL86111,
+    .mask = 0xffffffff,
+    .features = PHY_GBIT_FEATURES,
+    .config = mxl86111_config,
+    .startup = genphy_startup,
+    .shutdown = genphy_shutdown,
+    .readext = mxl8611x_extread,
+    .writeext = mxl8611x_extwrite,
+};

--
Best regards,
Marek Vasut


Thanks for your review. I will make these changes and send a v2 of the patches.

Sincerely,
Nate

Reply via email to