lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ