[PATCH v7 08/14] net: phy: at803x: add qca8084 switch registe access

From: Luo Jie
Date: Thu Dec 14 2023 - 04:49:43 EST


For qca8084 chip, there are GCC, TLMM and security control
modules besides the PHY, these moudles are accessed with 32
bits value, which has the special MDIO sequences to read or
write this 32bit register.

There are initial configurations configured to make qca8084 PHY
probeable, and the MDIO address of qca8084 can be programmed for
the PHY device and PCS device.

Signed-off-by: Luo Jie <quic_luoj@xxxxxxxxxxx>
---
drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)

diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index c186ef8e798f..016e40e32982 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -296,6 +296,13 @@
#define QCA8084_MMD7_IPG_OP 0x901d
#define QCA8084_IPG_10_TO_11_EN BIT(0)

+/* QCA8084 includes secure control module, which supports customizing the
+ * MDIO address of PHY device and PCS device, the register of secure control
+ * is accessed by MDIO bus with the special MDIO sequences.
+ */
+#define QCA8084_HIGH_ADDR_PREFIX 0x18
+#define QCA8084_LOW_ADDR_PREFIX 0x10
+
MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
MODULE_AUTHOR("Matus Ujhelyi");
MODULE_LICENSE("GPL");
@@ -408,6 +415,85 @@ static int at803x_read_page(struct phy_device *phydev)
return AT803X_PAGE_FIBER;
}

+static void qca8084_split_addr(u32 regaddr, u16 *r1, u16 *r2,
+ u16 *page, u16 *sw_addr)
+{
+ *r1 = regaddr & 0x1c;
+
+ regaddr >>= 5;
+ *r2 = regaddr & 0x7;
+
+ regaddr >>= 3;
+ *page = regaddr & 0xffff;
+
+ regaddr >>= 16;
+ *sw_addr = regaddr & 0xff;
+}
+
+static int __qca8084_set_page(struct mii_bus *bus, u16 sw_addr, u16 page)
+{
+ return __mdiobus_write(bus, QCA8084_HIGH_ADDR_PREFIX | (sw_addr >> 5),
+ sw_addr & 0x1f, page);
+}
+
+static int __qca8084_mii_read(struct mii_bus *bus, u16 addr, u16 reg, u32 *val)
+{
+ int ret, data;
+
+ ret = __mdiobus_read(bus, addr, reg);
+ if (ret >= 0) {
+ data = ret;
+
+ ret = __mdiobus_read(bus, addr, reg | BIT(1));
+ if (ret >= 0)
+ *val = data | ret << 16;
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+static int __qca8084_mii_write(struct mii_bus *bus, u16 addr, u16 reg, u32 val)
+{
+ int ret;
+
+ ret = __mdiobus_write(bus, addr, reg, lower_16_bits(val));
+ if (!ret)
+ ret = __mdiobus_write(bus, addr, reg | BIT(1), upper_16_bits(val));
+
+ return ret;
+}
+
+static int qca8084_mii_modify(struct phy_device *phydev, u32 regaddr,
+ u32 clear, u32 set)
+{
+ struct mii_bus *bus;
+ u16 reg, addr, page, sw_addr;
+ u32 val;
+ int ret;
+
+ bus = phydev->mdio.bus;
+ mutex_lock(&bus->mdio_lock);
+
+ qca8084_split_addr(regaddr, &reg, &addr, &page, &sw_addr);
+ ret = __qca8084_set_page(bus, sw_addr, page);
+ if (ret < 0)
+ goto qca8084_mii_modify_exit;
+
+ ret = __qca8084_mii_read(bus, QCA8084_LOW_ADDR_PREFIX | addr,
+ reg, &val);
+ if (ret < 0)
+ goto qca8084_mii_modify_exit;
+
+ val &= ~clear;
+ val |= set;
+ ret = __qca8084_mii_write(bus, QCA8084_LOW_ADDR_PREFIX | addr,
+ reg, val);
+
+qca8084_mii_modify_exit:
+ mutex_unlock(&bus->mdio_lock);
+ return ret;
+};
+
static int at803x_enable_rx_delay(struct phy_device *phydev)
{
return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0,
--
2.42.0