[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260123120117.10883-3-ansuelsmth@gmail.com>
Date: Fri, 23 Jan 2026 13:00:30 +0100
From: Christian Marangi <ansuelsmth@...il.com>
To: Christian Marangi <ansuelsmth@...il.com>,
Andrew Lunn <andrew@...n.ch>,
Heiner Kallweit <hkallweit1@...il.com>,
Russell King <linux@...linux.org.uk>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
netdev@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [net-next PATCH v2 2/3] net: phy: as21xxx: add support for DBG command
Rework the msg send logic to support sending DBG command.
These DBG command use a special way to send and receive data and use
data in u8 size and are used to tweak advanced (and later introduced)
feature of the PHY.
Signed-off-by: Christian Marangi <ansuelsmth@...il.com>
---
drivers/net/phy/as21xxx.c | 190 +++++++++++++++++++++++++++++++-------
1 file changed, 155 insertions(+), 35 deletions(-)
diff --git a/drivers/net/phy/as21xxx.c b/drivers/net/phy/as21xxx.c
index a5344abde91a..2098fa6a2f63 100644
--- a/drivers/net/phy/as21xxx.c
+++ b/drivers/net/phy/as21xxx.c
@@ -59,6 +59,10 @@
#define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */
#define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */
#define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */
+#define IPC_CMD_DBG 0x16
+#define IPC_CMD_POLL 0x17
+#define IPC_CMD_WRITE_BUF 0x18
+#define IPC_CMD_READ_BUF 0x19
#define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */
#define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */
#define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */
@@ -115,6 +119,12 @@
/* Sub command of CMD_TEMP_MON */
#define IPC_CMD_TEMP_MON_GET 0x4
+/* Sub command of CMD_DBG */
+#define IPC_DBG_DPC 0x8b
+
+#define IPC_DATA_DBG_SEC GENMASK(15, 8)
+#define IPC_DATA_DBG_CMD GENMASK(7, 0)
+
#define AS21XXX_MDIO_AN_C22 0xffe0
#define PHY_ID_AS21XXX 0x75009410
@@ -451,18 +461,9 @@ static int aeon_ipc_send_cmd(struct phy_device *phydev,
return 0;
}
-/* If data is NULL, return 0 or negative error.
- * If data not NULL, return number of Bytes received from IPC or
- * a negative error.
- */
-static int aeon_ipc_send_msg(struct phy_device *phydev,
- u16 opcode, u16 *data, unsigned int data_len,
- u16 *ret_data)
+static int aeon_ipc_set_msg_data(struct phy_device *phydev, u16 *data,
+ unsigned int data_len)
{
- struct as21xxx_priv *priv = phydev->priv;
- unsigned int ret_size;
- u16 cmd, ret_sts;
- int ret;
int i;
/* IPC have a max of 8 register to transfer data,
@@ -475,46 +476,69 @@ static int aeon_ipc_send_msg(struct phy_device *phydev,
phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
data[i]);
- cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
- FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
-
- mutex_lock(&priv->ipc_lock);
-
- ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
- if (ret) {
- phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
- opcode, ret);
- goto out;
- }
-
- if (!data)
- goto out;
+ return 0;
+}
- if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) {
- ret = -EINVAL;
- goto out;
- }
+static int
+aeon_ipc_get_msg_ret_data(struct phy_device *phydev, u16 ret_sts,
+ u16 *ret_data) __must_hold(&priv->ipc_lock)
+{
+ unsigned int ret_size;
+ int ret;
+ int i;
/* Prevent IPC from stack smashing the kernel.
* We can't trust IPC to return a good value and we always
* preallocate space for 16 Bytes.
*/
ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts);
- if (ret_size > AEON_IPC_DATA_MAX) {
- ret = -EINVAL;
- goto out;
- }
+ if (ret_size > AEON_IPC_DATA_MAX)
+ return -EINVAL;
/* Read data from IPC data register for ret_size value from IPC */
for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) {
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i));
if (ret < 0)
- goto out;
+ return ret;
ret_data[i] = ret;
}
- ret = ret_size;
+ return ret_size;
+}
+
+/* If data is NULL, return 0 or negative error.
+ * If data not NULL, return number of Bytes received from IPC or
+ * a negative error.
+ */
+static int aeon_ipc_send_msg(struct phy_device *phydev,
+ u16 opcode, u16 *data, unsigned int data_len,
+ u16 *ret_data)
+{
+ struct as21xxx_priv *priv = phydev->priv;
+ u16 cmd, ret_sts;
+ int ret;
+
+ ret = aeon_ipc_set_msg_data(phydev, data, data_len);
+ if (ret)
+ return ret;
+
+ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
+ FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
+
+ mutex_lock(&priv->ipc_lock);
+
+ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
+ if (ret) {
+ phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
+ opcode, ret);
+ goto out;
+ }
+
+ if (!data)
+ goto out;
+
+ ret = aeon_ipc_get_msg_ret_data(phydev, ret_sts, ret_data);
out:
mutex_unlock(&priv->ipc_lock);
@@ -604,6 +628,102 @@ static int aeon_ipc_get_fw_version(struct phy_device *phydev)
return 0;
}
+static int aeon_ipc_poll(struct phy_device *phydev)
+{
+ struct as21xxx_priv *priv = phydev->priv;
+ u16 ret_sts;
+ u16 cmd;
+ int ret;
+
+ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) |
+ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_POLL);
+
+ mutex_lock(&priv->ipc_lock);
+
+ ret = aeon_ipc_send_cmd(phydev, phydev->priv, cmd, &ret_sts);
+ if (ret)
+ phydev_err(phydev, "Invalid IPC status on IPC poll: %x\n",
+ ret_sts);
+
+ mutex_unlock(&priv->ipc_lock);
+
+ return ret;
+}
+
+static int aeon_ipc_dbg_cmd(struct phy_device *phydev, u16 dbg_sec,
+ u16 dbg_cmd, u16 msg_size)
+{
+ u16 data[3];
+
+ data[0] = FIELD_PREP(IPC_DATA_DBG_SEC, dbg_sec) |
+ FIELD_PREP(IPC_DATA_DBG_CMD, dbg_cmd);
+ data[1] = msg_size;
+
+ return aeon_ipc_send_msg(phydev, IPC_CMD_DBG, data,
+ sizeof(data), NULL);
+}
+
+static int aeon_ipc_dbg_read_buf(struct phy_device *phydev, u16 *buf)
+{
+ struct as21xxx_priv *priv = phydev->priv;
+ u16 cmd, ret_sts;
+ int ret;
+
+ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) |
+ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_READ_BUF);
+
+ mutex_lock(&priv->ipc_lock);
+
+ ret = aeon_ipc_send_cmd(phydev, phydev->priv, cmd, &ret_sts);
+ if (ret)
+ goto out;
+
+ ret = aeon_ipc_get_msg_ret_data(phydev, ret_sts, buf);
+
+out:
+ mutex_unlock(&priv->ipc_lock);
+
+ return ret;
+}
+
+static int aeon_ipc_dbg_write_buf(struct phy_device *phydev, u8 *data,
+ u8 data_len)
+{
+ u16 msg_data[AEON_IPC_DATA_NUM_REGISTERS];
+ struct as21xxx_priv *priv = phydev->priv;
+ u16 cmd, ret_sts;
+ int ret;
+ int i;
+
+ /* Make sure we don't try to write more data than supported */
+ if (data_len * 2 > AEON_IPC_DATA_MAX)
+ return -EINVAL;
+
+ /* Pack u8 DBG data in u16 buffer */
+ for (i = 0; i < data_len; i += 2) {
+ msg_data[i] = data[i];
+ msg_data[i] |= data[i + 1] << 8;
+ }
+
+ ret = aeon_ipc_set_msg_data(phydev, msg_data, data_len * 2);
+ if (ret)
+ return ret;
+
+ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
+ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_WRITE_BUF);
+
+ mutex_lock(&priv->ipc_lock);
+
+ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
+ if (ret)
+ phydev_err(phydev, "failed to send IPC msg for %x: %d\n",
+ IPC_CMD_WRITE_BUF, ret);
+
+ mutex_unlock(&priv->ipc_lock);
+
+ return ret;
+}
+
static int aeon_dpc_ra_enable(struct phy_device *phydev)
{
u16 data[2];
--
2.51.0
Powered by blists - more mailing lists