]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
net: mv88e6xxx: add Clause 45 support
authorRobert Marko <robert.marko@sartura.hr>
Tue, 8 Aug 2023 16:05:16 +0000 (18:05 +0200)
committerTom Rini <trini@konsulko.com>
Sun, 5 Nov 2023 21:11:37 +0000 (16:11 -0500)
Marvell LinkStreet switches support Clause 45 MDIO on the internal bus.

C45 read or writes require the register address to be written first to
the SMI PHY Data register, and then a special C45 Write Address Register
OP is used on the SMI PHY Register before making a C45 Read Data Register
OP and being able to actually read the register.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
drivers/net/mv88e6xxx.c

index deb72772d19e4e207bdac3b43e49ec20c8813607..c073f81e72d0dffc1f3a94bd451cb64acce68256 100644 (file)
 #define SMI_CMD_OP_MASK                        GENMASK(11, 10)
 #define SMI_CMD_CLAUSE_22_OP_WRITE     0x1
 #define SMI_CMD_CLAUSE_22_OP_READ      0x2
+#define SMI_CMD_CLAUSE_45_OP_WRITE_ADDR        0x0
+#define SMI_CMD_CLAUSE_45_OP_WRITE     0x1
+#define SMI_CMD_CLAUSE_45_OP_READ      0x3
 
 #define SMI_CMD_ADDR_MASK              GENMASK(9, 5)
 #define SMI_CMD_REG_MASK               GENMASK(4, 0)
        (SMI_BUSY | SMI_CMD_CLAUSE_22 | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_22_OP_WRITE)) | \
        (FIELD_PREP(SMI_CMD_ADDR_MASK, addr)) | \
        (FIELD_PREP(SMI_CMD_REG_MASK, reg))
+#define SMI_CMD_SET_C45_ADDR(phyad, devad) \
+       (SMI_BUSY | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_45_OP_WRITE_ADDR)) | \
+       (FIELD_PREP(SMI_CMD_ADDR_MASK, phyad)) | \
+       (FIELD_PREP(SMI_CMD_REG_MASK, devad))
+#define SMI_CMD_READ_C45(phyad, devad) \
+       (SMI_BUSY | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_45_OP_READ)) | \
+       (FIELD_PREP(SMI_CMD_ADDR_MASK, phyad)) | \
+       (FIELD_PREP(SMI_CMD_REG_MASK, devad))
+#define SMI_CMD_WRITE_C45(phyad, devad) \
+       (SMI_BUSY | FIELD_PREP(SMI_CMD_OP_MASK, SMI_CMD_CLAUSE_45_OP_WRITE)) | \
+       (FIELD_PREP(SMI_CMD_ADDR_MASK, phyad)) | \
+       (FIELD_PREP(SMI_CMD_REG_MASK, devad))
 
 /* ID register values for different switch models */
 #define PORT_SWITCH_ID_6020            0x0200
@@ -273,12 +288,37 @@ static int mv88e6xxx_phy_wait(struct udevice *dev)
 static int mv88e6xxx_phy_read_indirect(struct udevice *dev, int phyad, int devad, int reg)
 {
        struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+       u16 smi_cmd;
        int res;
 
+       if (devad >= 0) {
+               /*
+                * For C45 we need to write the register address into the
+                * PHY Data register first and then call the Write Address
+                * Register OP in the PHY command register.
+                */
+               res = mv88e6xxx_reg_write(dev, priv->global2,
+                                         GLOBAL2_REG_PHY_DATA,
+                                         reg);
+
+               res = mv88e6xxx_reg_write(dev, priv->global2,
+                                         GLOBAL2_REG_PHY_CMD,
+                                         SMI_CMD_SET_C45_ADDR(phyad, devad));
+
+               /* Wait for busy bit to clear */
+               res = mv88e6xxx_phy_wait(dev);
+               if (res < 0)
+                       return res;
+
+               /* Set the actual C45 or C22 OP-s */
+               smi_cmd = SMI_CMD_READ_C45(phyad, devad);
+       } else
+               smi_cmd = SMI_CMD_READ(phyad, reg);
+
        /* Issue command to read */
        res = mv88e6xxx_reg_write(dev, priv->global2,
                                  GLOBAL2_REG_PHY_CMD,
-                                 SMI_CMD_READ(phyad, reg));
+                                 smi_cmd);
 
        /* Wait for data to be read */
        res = mv88e6xxx_phy_wait(dev);
@@ -294,8 +334,33 @@ static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad,
                                        int devad, int reg, u16 data)
 {
        struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+       u16 smi_cmd;
        int res;
 
+       if (devad >= 0) {
+               /*
+                * For C45 we need to write the register address into the
+                * PHY Data register first and then call the Write Address
+                * Register OP in the PHY command register.
+                */
+               res = mv88e6xxx_reg_write(dev, priv->global2,
+                                         GLOBAL2_REG_PHY_DATA,
+                                         reg);
+
+               res = mv88e6xxx_reg_write(dev, priv->global2,
+                                         GLOBAL2_REG_PHY_CMD,
+                                         SMI_CMD_SET_C45_ADDR(phyad, devad));
+
+               /* Wait for busy bit to clear */
+               res = mv88e6xxx_phy_wait(dev);
+               if (res < 0)
+                       return res;
+
+               /* Set the actual C45 or C22 OP-s */
+               smi_cmd = SMI_CMD_WRITE_C45(phyad, devad);
+       } else
+               smi_cmd = SMI_CMD_WRITE(phyad, reg);
+
        /* Set the data to write */
        res = mv88e6xxx_reg_write(dev, priv->global2,
                                  GLOBAL2_REG_PHY_DATA, data);
@@ -304,7 +369,7 @@ static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad,
        /* Issue the write command */
        res = mv88e6xxx_reg_write(dev, priv->global2,
                                  GLOBAL2_REG_PHY_CMD,
-                                 SMI_CMD_WRITE(phyad, reg));
+                                 smi_cmd);
        if (res < 0)
                return res;