mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2026-02-16 16:06:19 -05:00
kernel: net: pse-pd: backport PSE v6.13-v6.19
Backport the PSE-PD (Power Sourcing Equipment - Powered Device) framework updates from Linux 6.13 through 6.19. This brings modern PoE (Power over Ethernet) controller support to OpenWrt, enabling userspace control of PSE hardware via ethtool. Key features: - Enhanced ethtool integration for PSE status and configuration - Power domain support with budget evaluation strategies - PSE event reporting via netlink - Port priority management for power budget allocation - New Si3474 PSE controller driver Backported commits: v6.13 core framework and TPS23881 improvements: - 6e56a6d47a7f net: pse-pd: Add power limit check - 0b567519d115 net: pse-pd: tps23881: Simplify function returns - 4c2bab507eb7 net: pse-pd: tps23881: Use helpers to calculate bit offset - f3cb3c7bea0c net: pse-pd: tps23881: Add missing configuration register - 3e9dbfec4998 net: pse-pd: Split ethtool_get_status into multiple callbacks - 4640a1f0d8f2 net: pse-pd: Remove is_enabled callback from drivers - 7f076ce3f173 net: pse-pd: tps23881: Add power limit and measurement features - 10276f3e1c7e net: pse-pd: Fix missing PI of_node description - 5385f1e1923c net: pse-pd: Clean ethtool header of PSE structures v6.17 power domains and event support: - fa2f0454174c net: pse-pd: Introduce attached_phydev to pse control - fc0e6db30941 net: pse-pd: Add support for reporting events - f5e7aecaa4ef net: pse-pd: tps23881: Add support for PSE events - 50f8b341d268 net: pse-pd: Add support for PSE power domains - 1176978ed851 net: ethtool: Add support for power domains index - c394e757dedd net: pse-pd: Add helper to report hw enable status - ffef61d6d273 net: pse-pd: Add support for budget evaluation strategies - 359754013e6a net: pse-pd: pd692x0: Add PSE PI priority feature - 24a4e3a05dd0 net: pse-pd: pd692x0: Add controller and manager power - 56cfc97635e9 net: pse-pd: tps23881: Add static port priority feature - d12b3dc10609 net: pse-pd: pd692x0: reduce stack usage v6.18 Si3474 driver and fixes: - 1c67f9c54cdc net: pse-pd: pd692x0: Fix power budget leak - 7ef353879f71 net: pse-pd: pd692x0: Skip power budget when undefined - a2317231df4b net: pse-pd: Add Si3474 PSE controller driver v6.19 maintenance and TPS23881B support: - 2c95a756e0cf net: pse-pd: tps23881: Fix current measurement scaling - f197902cd21a net: pse-pd: pd692x0: Replace __free macro - 6fa1f8b64a47 net: pse-pd: pd692x0: Separate configuration parsing - 8f3d044b34fe net: pse-pd: pd692x0: Preserve PSE configuration - 4d07797faaa1 net: pse-pd: tps23881: Add support for TPS23881B Signed-off-by: Carlo Szelinsky <github@szelinsky.de> Link: https://github.com/openwrt/openwrt/pull/21810 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
committed by
Hauke Mehrtens
parent
658e4adca4
commit
528c9259a7
@@ -0,0 +1,41 @@
|
||||
From 6e56a6d47a7fad705a1a1d088237b0858c01a770 Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:22 +0100
|
||||
Subject: [PATCH] net: pse-pd: Add power limit check
|
||||
|
||||
Checking only the current limit is not sufficient. According to the
|
||||
standard, voltage can reach up to 57V and current up to 1.92A, which
|
||||
exceeds the power limit described in the standard (99.9W). Add a power
|
||||
limit check to prevent this.
|
||||
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pse_core.c | 3 +++
|
||||
include/linux/pse-pd/pse.h | 2 ++
|
||||
2 files changed, 5 insertions(+)
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -868,6 +868,9 @@ int pse_ethtool_set_pw_limit(struct pse_
|
||||
int uV, uA, ret;
|
||||
s64 tmp_64;
|
||||
|
||||
+ if (pw_limit > MAX_PI_PW)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
ret = regulator_get_voltage(psec->ps);
|
||||
if (!ret) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
|
||||
#define MAX_PI_CURRENT 1920000
|
||||
+/* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */
|
||||
+#define MAX_PI_PW 99900
|
||||
|
||||
struct phy_device;
|
||||
struct pse_controller_dev;
|
||||
@@ -0,0 +1,112 @@
|
||||
From 0b567519d1152de52b29b2da2c47aa0f39a46266 Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:23 +0100
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Simplify function returns by removing
|
||||
redundant checks
|
||||
|
||||
Cleaned up several functions in tps23881 by removing redundant checks on
|
||||
return values at the end of functions. These check has been removed, and
|
||||
the return statement now directly returns the function result, reducing
|
||||
the code's complexity and making it more concise.
|
||||
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Reviewed-by: Kyle Swenson <kyle.swenson@est.tech>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 34 ++++++----------------------------
|
||||
1 file changed, 6 insertions(+), 28 deletions(-)
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -59,7 +59,6 @@ static int tps23881_pi_enable(struct pse
|
||||
struct i2c_client *client = priv->client;
|
||||
u8 chan;
|
||||
u16 val;
|
||||
- int ret;
|
||||
|
||||
if (id >= TPS23881_MAX_CHANS)
|
||||
return -ERANGE;
|
||||
@@ -78,11 +77,7 @@ static int tps23881_pi_enable(struct pse
|
||||
val |= BIT(chan + 4);
|
||||
}
|
||||
|
||||
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return 0;
|
||||
+ return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
}
|
||||
|
||||
static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
|
||||
@@ -91,7 +86,6 @@ static int tps23881_pi_disable(struct ps
|
||||
struct i2c_client *client = priv->client;
|
||||
u8 chan;
|
||||
u16 val;
|
||||
- int ret;
|
||||
|
||||
if (id >= TPS23881_MAX_CHANS)
|
||||
return -ERANGE;
|
||||
@@ -110,11 +104,7 @@ static int tps23881_pi_disable(struct ps
|
||||
val |= BIT(chan + 8);
|
||||
}
|
||||
|
||||
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return 0;
|
||||
+ return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
}
|
||||
|
||||
static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
|
||||
@@ -480,7 +470,7 @@ tps23881_write_port_matrix(struct tps238
|
||||
struct i2c_client *client = priv->client;
|
||||
u8 pi_id, lgcl_chan, hw_chan;
|
||||
u16 val = 0;
|
||||
- int i, ret;
|
||||
+ int i;
|
||||
|
||||
for (i = 0; i < port_cnt; i++) {
|
||||
pi_id = port_matrix[i].pi_id;
|
||||
@@ -511,11 +501,7 @@ tps23881_write_port_matrix(struct tps238
|
||||
}
|
||||
|
||||
/* Write hardware ports matrix */
|
||||
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return 0;
|
||||
+ return i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -564,11 +550,7 @@ tps23881_set_ports_conf(struct tps23881_
|
||||
val |= BIT(port_matrix[i].lgcl_chan[1]) |
|
||||
BIT(port_matrix[i].lgcl_chan[1] + 4);
|
||||
}
|
||||
- ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return 0;
|
||||
+ return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -594,11 +576,7 @@ tps23881_set_ports_matrix(struct tps2388
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = tps23881_set_ports_conf(priv, port_matrix);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- return 0;
|
||||
+ return tps23881_set_ports_conf(priv, port_matrix);
|
||||
}
|
||||
|
||||
static int tps23881_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
@@ -0,0 +1,190 @@
|
||||
From 4c2bab507eb7edc8e497e91b9b7f05d76d7e32bb Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:24 +0100
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Use helpers to calculate bit offset
|
||||
for a channel
|
||||
|
||||
This driver frequently follows a pattern where two registers are read or
|
||||
written in a single operation, followed by calculating the bit offset for
|
||||
a specific channel.
|
||||
|
||||
Introduce helpers to streamline this process and reduce code redundancy,
|
||||
making the codebase cleaner and more maintainable.
|
||||
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 107 ++++++++++++++++++++++------------
|
||||
1 file changed, 69 insertions(+), 38 deletions(-)
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -53,6 +53,55 @@ static struct tps23881_priv *to_tps23881
|
||||
return container_of(pcdev, struct tps23881_priv, pcdev);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Helper to extract a value from a u16 register value, which is made of two
|
||||
+ * u8 registers. The function calculates the bit offset based on the channel
|
||||
+ * and extracts the relevant bits using a provided field mask.
|
||||
+ *
|
||||
+ * @param reg_val: The u16 register value (composed of two u8 registers).
|
||||
+ * @param chan: The channel number (0-7).
|
||||
+ * @param field_offset: The base bit offset to apply (e.g., 0 or 4).
|
||||
+ * @param field_mask: The mask to apply to extract the required bits.
|
||||
+ * @return: The extracted value for the specific channel.
|
||||
+ */
|
||||
+static u16 tps23881_calc_val(u16 reg_val, u8 chan, u8 field_offset,
|
||||
+ u16 field_mask)
|
||||
+{
|
||||
+ if (chan >= 4)
|
||||
+ reg_val >>= 8;
|
||||
+
|
||||
+ return (reg_val >> field_offset) & field_mask;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Helper to combine individual channel values into a u16 register value.
|
||||
+ * The function sets the value for a specific channel in the appropriate
|
||||
+ * position.
|
||||
+ *
|
||||
+ * @param reg_val: The current u16 register value.
|
||||
+ * @param chan: The channel number (0-7).
|
||||
+ * @param field_offset: The base bit offset to apply (e.g., 0 or 4).
|
||||
+ * @param field_mask: The mask to apply for the field (e.g., 0x0F).
|
||||
+ * @param field_val: The value to set for the specific channel (masked by
|
||||
+ * field_mask).
|
||||
+ * @return: The updated u16 register value with the channel value set.
|
||||
+ */
|
||||
+static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset,
|
||||
+ u16 field_mask, u16 field_val)
|
||||
+{
|
||||
+ field_val &= field_mask;
|
||||
+
|
||||
+ if (chan < 4) {
|
||||
+ reg_val &= ~(field_mask << field_offset);
|
||||
+ reg_val |= (field_val << field_offset);
|
||||
+ } else {
|
||||
+ reg_val &= ~(field_mask << (field_offset + 8));
|
||||
+ reg_val |= (field_val << (field_offset + 8));
|
||||
+ }
|
||||
+
|
||||
+ return reg_val;
|
||||
+}
|
||||
+
|
||||
static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
|
||||
{
|
||||
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
@@ -64,17 +113,12 @@ static int tps23881_pi_enable(struct pse
|
||||
return -ERANGE;
|
||||
|
||||
chan = priv->port[id].chan[0];
|
||||
- if (chan < 4)
|
||||
- val = BIT(chan);
|
||||
- else
|
||||
- val = BIT(chan + 4);
|
||||
+ val = tps23881_set_val(0, chan, 0, BIT(chan % 4), BIT(chan % 4));
|
||||
|
||||
if (priv->port[id].is_4p) {
|
||||
chan = priv->port[id].chan[1];
|
||||
- if (chan < 4)
|
||||
- val |= BIT(chan);
|
||||
- else
|
||||
- val |= BIT(chan + 4);
|
||||
+ val = tps23881_set_val(val, chan, 0, BIT(chan % 4),
|
||||
+ BIT(chan % 4));
|
||||
}
|
||||
|
||||
return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
@@ -91,17 +135,12 @@ static int tps23881_pi_disable(struct ps
|
||||
return -ERANGE;
|
||||
|
||||
chan = priv->port[id].chan[0];
|
||||
- if (chan < 4)
|
||||
- val = BIT(chan + 4);
|
||||
- else
|
||||
- val = BIT(chan + 8);
|
||||
+ val = tps23881_set_val(0, chan, 4, BIT(chan % 4), BIT(chan % 4));
|
||||
|
||||
if (priv->port[id].is_4p) {
|
||||
chan = priv->port[id].chan[1];
|
||||
- if (chan < 4)
|
||||
- val |= BIT(chan + 4);
|
||||
- else
|
||||
- val |= BIT(chan + 8);
|
||||
+ val = tps23881_set_val(val, chan, 4, BIT(chan % 4),
|
||||
+ BIT(chan % 4));
|
||||
}
|
||||
|
||||
return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
@@ -113,6 +152,7 @@ static int tps23881_pi_is_enabled(struct
|
||||
struct i2c_client *client = priv->client;
|
||||
bool enabled;
|
||||
u8 chan;
|
||||
+ u16 val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
|
||||
@@ -120,17 +160,13 @@ static int tps23881_pi_is_enabled(struct
|
||||
return ret;
|
||||
|
||||
chan = priv->port[id].chan[0];
|
||||
- if (chan < 4)
|
||||
- enabled = ret & BIT(chan);
|
||||
- else
|
||||
- enabled = ret & BIT(chan + 4);
|
||||
+ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
+ enabled = !!(val);
|
||||
|
||||
if (priv->port[id].is_4p) {
|
||||
chan = priv->port[id].chan[1];
|
||||
- if (chan < 4)
|
||||
- enabled &= !!(ret & BIT(chan));
|
||||
- else
|
||||
- enabled &= !!(ret & BIT(chan + 4));
|
||||
+ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
+ enabled &= !!(val);
|
||||
}
|
||||
|
||||
/* Return enabled status only if both channel are on this state */
|
||||
@@ -146,6 +182,7 @@ static int tps23881_ethtool_get_status(s
|
||||
struct i2c_client *client = priv->client;
|
||||
bool enabled, delivering;
|
||||
u8 chan;
|
||||
+ u16 val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
|
||||
@@ -153,23 +190,17 @@ static int tps23881_ethtool_get_status(s
|
||||
return ret;
|
||||
|
||||
chan = priv->port[id].chan[0];
|
||||
- if (chan < 4) {
|
||||
- enabled = ret & BIT(chan);
|
||||
- delivering = ret & BIT(chan + 4);
|
||||
- } else {
|
||||
- enabled = ret & BIT(chan + 4);
|
||||
- delivering = ret & BIT(chan + 8);
|
||||
- }
|
||||
+ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
+ enabled = !!(val);
|
||||
+ val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4));
|
||||
+ delivering = !!(val);
|
||||
|
||||
if (priv->port[id].is_4p) {
|
||||
chan = priv->port[id].chan[1];
|
||||
- if (chan < 4) {
|
||||
- enabled &= !!(ret & BIT(chan));
|
||||
- delivering &= !!(ret & BIT(chan + 4));
|
||||
- } else {
|
||||
- enabled &= !!(ret & BIT(chan + 4));
|
||||
- delivering &= !!(ret & BIT(chan + 8));
|
||||
- }
|
||||
+ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
+ enabled &= !!(val);
|
||||
+ val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4));
|
||||
+ delivering &= !!(val);
|
||||
}
|
||||
|
||||
/* Return delivering status only if both channel are on this state */
|
||||
@@ -0,0 +1,63 @@
|
||||
From f3cb3c7bea0c08e821d8e9dfd2f96acd1db7c24e Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:25 +0100
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Add missing configuration register
|
||||
after disable
|
||||
|
||||
When setting the PWOFF register, the controller resets multiple
|
||||
configuration registers. This patch ensures these registers are
|
||||
reconfigured as needed following a disable operation.
|
||||
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 30 +++++++++++++++++++++++++++++-
|
||||
1 file changed, 29 insertions(+), 1 deletion(-)
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -130,6 +130,7 @@ static int tps23881_pi_disable(struct ps
|
||||
struct i2c_client *client = priv->client;
|
||||
u8 chan;
|
||||
u16 val;
|
||||
+ int ret;
|
||||
|
||||
if (id >= TPS23881_MAX_CHANS)
|
||||
return -ERANGE;
|
||||
@@ -143,7 +144,34 @@ static int tps23881_pi_disable(struct ps
|
||||
BIT(chan % 4));
|
||||
}
|
||||
|
||||
- return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* PWOFF command resets lots of register which need to be
|
||||
+ * configured again. According to the datasheet "It may take upwards
|
||||
+ * of 5ms after PWOFFn command for all register values to be updated"
|
||||
+ */
|
||||
+ mdelay(5);
|
||||
+
|
||||
+ /* Enable detection and classification */
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_CLA_EN);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4));
|
||||
+ val = tps23881_set_val(val, chan, 4, BIT(chan % 4), BIT(chan % 4));
|
||||
+
|
||||
+ if (priv->port[id].is_4p) {
|
||||
+ chan = priv->port[id].chan[1];
|
||||
+ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4),
|
||||
+ BIT(chan % 4));
|
||||
+ val = tps23881_set_val(val, chan, 4, BIT(chan % 4),
|
||||
+ BIT(chan % 4));
|
||||
+ }
|
||||
+
|
||||
+ return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
|
||||
}
|
||||
|
||||
static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
|
||||
@@ -0,0 +1,714 @@
|
||||
From 3e9dbfec499807767d03592ebdf19d9c15fd495b Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:27 +0100
|
||||
Subject: [PATCH] net: pse-pd: Split ethtool_get_status into multiple callbacks
|
||||
|
||||
The ethtool_get_status callback currently handles all status and PSE
|
||||
information within a single function. This approach has two key
|
||||
drawbacks:
|
||||
|
||||
1. If the core requires some information for purposes other than
|
||||
ethtool_get_status, redundant code will be needed to fetch the same
|
||||
data from the driver (like is_enabled).
|
||||
|
||||
2. Drivers currently have access to all information passed to ethtool.
|
||||
New variables will soon be added to ethtool status, such as PSE ID,
|
||||
power domain IDs, and budget evaluation strategies, which are meant
|
||||
to be managed solely by the core. Drivers should not have the ability
|
||||
to modify these variables.
|
||||
|
||||
To resolve these issues, ethtool_get_status has been split into multiple
|
||||
callbacks, with each handling a specific piece of information required
|
||||
by ethtool or the core.
|
||||
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 153 ++++++++++++++++++++---------
|
||||
drivers/net/pse-pd/pse_core.c | 83 ++++++++++++++--
|
||||
drivers/net/pse-pd/pse_regulator.c | 26 +++--
|
||||
drivers/net/pse-pd/tps23881.c | 60 ++++++++---
|
||||
include/linux/pse-pd/pse.h | 87 +++++++++++++---
|
||||
net/ethtool/pse-pd.c | 8 +-
|
||||
6 files changed, 323 insertions(+), 94 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -517,21 +517,38 @@ pd692x0_pse_ext_state_map[] = {
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
-static void
|
||||
-pd692x0_get_ext_state(struct ethtool_c33_pse_ext_state_info *c33_ext_state_info,
|
||||
- u32 status_code)
|
||||
+static int
|
||||
+pd692x0_pi_get_ext_state(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_ext_state_info *ext_state_info)
|
||||
{
|
||||
+ struct ethtool_c33_pse_ext_state_info *c33_ext_state_info;
|
||||
const struct pd692x0_pse_ext_state_mapping *ext_state_map;
|
||||
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
+ struct pd692x0_msg msg, buf = {0};
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pd692x0_fw_unavailable(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS];
|
||||
+ msg.sub[2] = id;
|
||||
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ c33_ext_state_info = &ext_state_info->c33_ext_state_info;
|
||||
ext_state_map = pd692x0_pse_ext_state_map;
|
||||
while (ext_state_map->status_code) {
|
||||
- if (ext_state_map->status_code == status_code) {
|
||||
+ if (ext_state_map->status_code == buf.sub[0]) {
|
||||
c33_ext_state_info->c33_pse_ext_state = ext_state_map->pse_ext_state;
|
||||
c33_ext_state_info->__c33_pse_ext_substate = ext_state_map->pse_ext_substate;
|
||||
- return;
|
||||
+ return 0;
|
||||
}
|
||||
ext_state_map++;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
struct pd692x0_class_pw {
|
||||
@@ -613,35 +630,36 @@ static int pd692x0_pi_set_pw_from_table(
|
||||
}
|
||||
|
||||
static int
|
||||
-pd692x0_pi_get_pw_ranges(struct pse_control_status *st)
|
||||
+pd692x0_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_limit_ranges *pw_limit_ranges)
|
||||
{
|
||||
+ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
|
||||
const struct pd692x0_class_pw *pw_table;
|
||||
int i;
|
||||
|
||||
pw_table = pd692x0_class_pw_table;
|
||||
- st->c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE,
|
||||
- sizeof(struct ethtool_c33_pse_pw_limit_range),
|
||||
- GFP_KERNEL);
|
||||
- if (!st->c33_pw_limit_ranges)
|
||||
+ c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE,
|
||||
+ sizeof(*c33_pw_limit_ranges),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!c33_pw_limit_ranges)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < PD692X0_CLASS_PW_TABLE_SIZE; i++, pw_table++) {
|
||||
- st->c33_pw_limit_ranges[i].min = pw_table->class_pw;
|
||||
- st->c33_pw_limit_ranges[i].max = pw_table->class_pw + pw_table->max_added_class_pw;
|
||||
+ c33_pw_limit_ranges[i].min = pw_table->class_pw;
|
||||
+ c33_pw_limit_ranges[i].max = pw_table->class_pw +
|
||||
+ pw_table->max_added_class_pw;
|
||||
}
|
||||
|
||||
- st->c33_pw_limit_nb_ranges = i;
|
||||
- return 0;
|
||||
+ pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges;
|
||||
+ return i;
|
||||
}
|
||||
|
||||
-static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev,
|
||||
- unsigned long id,
|
||||
- struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status)
|
||||
+static int
|
||||
+pd692x0_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_admin_state *admin_state)
|
||||
{
|
||||
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
struct pd692x0_msg msg, buf = {0};
|
||||
- u32 class;
|
||||
int ret;
|
||||
|
||||
ret = pd692x0_fw_unavailable(priv);
|
||||
@@ -654,39 +672,65 @@ static int pd692x0_ethtool_get_status(st
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- /* Compare Port Status (Communication Protocol Document par. 7.1) */
|
||||
- if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90)
|
||||
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
|
||||
- else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22)
|
||||
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING;
|
||||
- else if (buf.sub[0] == 0x12)
|
||||
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_FAULT;
|
||||
- else
|
||||
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
|
||||
-
|
||||
if (buf.sub[1])
|
||||
- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
|
||||
else
|
||||
- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
|
||||
|
||||
- priv->admin_state[id] = status->c33_admin_state;
|
||||
+ priv->admin_state[id] = admin_state->c33_admin_state;
|
||||
|
||||
- pd692x0_get_ext_state(&status->c33_ext_state_info, buf.sub[0]);
|
||||
- status->c33_actual_pw = (buf.data[0] << 4 | buf.data[1]) * 100;
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
- msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM];
|
||||
+static int
|
||||
+pd692x0_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_status *pw_status)
|
||||
+{
|
||||
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
+ struct pd692x0_msg msg, buf = {0};
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pd692x0_fw_unavailable(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS];
|
||||
msg.sub[2] = id;
|
||||
- memset(&buf, 0, sizeof(buf));
|
||||
ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- ret = pd692x0_pi_get_pw_from_table(buf.data[0], buf.data[1]);
|
||||
- if (ret < 0)
|
||||
+ /* Compare Port Status (Communication Protocol Document par. 7.1) */
|
||||
+ if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90)
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
|
||||
+ else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22)
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING;
|
||||
+ else if (buf.sub[0] == 0x12)
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_FAULT;
|
||||
+ else
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pd692x0_pi_get_pw_class(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
+ struct pd692x0_msg msg, buf = {0};
|
||||
+ u32 class;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pd692x0_fw_unavailable(priv);
|
||||
+ if (ret)
|
||||
return ret;
|
||||
- status->c33_avail_pw_limit = ret;
|
||||
|
||||
- memset(&buf, 0, sizeof(buf));
|
||||
msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_CLASS];
|
||||
msg.sub[2] = id;
|
||||
ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
@@ -695,13 +739,29 @@ static int pd692x0_ethtool_get_status(st
|
||||
|
||||
class = buf.data[3] >> 4;
|
||||
if (class <= 8)
|
||||
- status->c33_pw_class = class;
|
||||
+ return class;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pd692x0_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
+ struct pd692x0_msg msg, buf = {0};
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pd692x0_fw_unavailable(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- ret = pd692x0_pi_get_pw_ranges(status);
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS];
|
||||
+ msg.sub[2] = id;
|
||||
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- return 0;
|
||||
+ return (buf.data[0] << 4 | buf.data[1]) * 100;
|
||||
}
|
||||
|
||||
static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv)
|
||||
@@ -1038,13 +1098,18 @@ static int pd692x0_pi_set_pw_limit(struc
|
||||
|
||||
static const struct pse_controller_ops pd692x0_ops = {
|
||||
.setup_pi_matrix = pd692x0_setup_pi_matrix,
|
||||
- .ethtool_get_status = pd692x0_ethtool_get_status,
|
||||
+ .pi_get_admin_state = pd692x0_pi_get_admin_state,
|
||||
+ .pi_get_pw_status = pd692x0_pi_get_pw_status,
|
||||
+ .pi_get_ext_state = pd692x0_pi_get_ext_state,
|
||||
+ .pi_get_pw_class = pd692x0_pi_get_pw_class,
|
||||
+ .pi_get_actual_pw = pd692x0_pi_get_actual_pw,
|
||||
.pi_enable = pd692x0_pi_enable,
|
||||
.pi_disable = pd692x0_pi_disable,
|
||||
.pi_is_enabled = pd692x0_pi_is_enabled,
|
||||
.pi_get_voltage = pd692x0_pi_get_voltage,
|
||||
.pi_get_pw_limit = pd692x0_pi_get_pw_limit,
|
||||
.pi_set_pw_limit = pd692x0_pi_set_pw_limit,
|
||||
+ .pi_get_pw_limit_ranges = pd692x0_pi_get_pw_limit_ranges,
|
||||
};
|
||||
|
||||
#define PD692X0_FW_LINE_MAX_SZ 0xff
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -443,6 +443,13 @@ int pse_controller_register(struct pse_c
|
||||
if (!pcdev->nr_lines)
|
||||
pcdev->nr_lines = 1;
|
||||
|
||||
+ if (!pcdev->ops->pi_get_admin_state ||
|
||||
+ !pcdev->ops->pi_get_pw_status) {
|
||||
+ dev_err(pcdev->dev,
|
||||
+ "Mandatory status report callbacks are missing");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
ret = of_load_pse_pis(pcdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -745,25 +752,81 @@ EXPORT_SYMBOL_GPL(of_pse_control_get);
|
||||
*/
|
||||
int pse_ethtool_get_status(struct pse_control *psec,
|
||||
struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status)
|
||||
+ struct ethtool_pse_control_status *status)
|
||||
{
|
||||
+ struct pse_admin_state admin_state = {0};
|
||||
+ struct pse_pw_status pw_status = {0};
|
||||
const struct pse_controller_ops *ops;
|
||||
struct pse_controller_dev *pcdev;
|
||||
- int err;
|
||||
+ int ret;
|
||||
|
||||
pcdev = psec->pcdev;
|
||||
ops = pcdev->ops;
|
||||
- if (!ops->ethtool_get_status) {
|
||||
- NL_SET_ERR_MSG(extack,
|
||||
- "PSE driver does not support status report");
|
||||
- return -EOPNOTSUPP;
|
||||
+ mutex_lock(&pcdev->lock);
|
||||
+ ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+ status->podl_admin_state = admin_state.podl_admin_state;
|
||||
+ status->c33_admin_state = admin_state.c33_admin_state;
|
||||
+
|
||||
+ ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+ status->podl_pw_status = pw_status.podl_pw_status;
|
||||
+ status->c33_pw_status = pw_status.c33_pw_status;
|
||||
+
|
||||
+ if (ops->pi_get_ext_state) {
|
||||
+ struct pse_ext_state_info ext_state_info = {0};
|
||||
+
|
||||
+ ret = ops->pi_get_ext_state(pcdev, psec->id,
|
||||
+ &ext_state_info);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ memcpy(&status->c33_ext_state_info,
|
||||
+ &ext_state_info.c33_ext_state_info,
|
||||
+ sizeof(status->c33_ext_state_info));
|
||||
}
|
||||
|
||||
- mutex_lock(&pcdev->lock);
|
||||
- err = ops->ethtool_get_status(pcdev, psec->id, extack, status);
|
||||
- mutex_unlock(&pcdev->lock);
|
||||
+ if (ops->pi_get_pw_class) {
|
||||
+ ret = ops->pi_get_pw_class(pcdev, psec->id);
|
||||
+ if (ret < 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ status->c33_pw_class = ret;
|
||||
+ }
|
||||
+
|
||||
+ if (ops->pi_get_actual_pw) {
|
||||
+ ret = ops->pi_get_actual_pw(pcdev, psec->id);
|
||||
+ if (ret < 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ status->c33_actual_pw = ret;
|
||||
+ }
|
||||
+
|
||||
+ if (ops->pi_get_pw_limit) {
|
||||
+ ret = ops->pi_get_pw_limit(pcdev, psec->id);
|
||||
+ if (ret < 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ status->c33_avail_pw_limit = ret;
|
||||
+ }
|
||||
+
|
||||
+ if (ops->pi_get_pw_limit_ranges) {
|
||||
+ struct pse_pw_limit_ranges pw_limit_ranges = {0};
|
||||
|
||||
- return err;
|
||||
+ ret = ops->pi_get_pw_limit_ranges(pcdev, psec->id,
|
||||
+ &pw_limit_ranges);
|
||||
+ if (ret < 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ status->c33_pw_limit_ranges =
|
||||
+ pw_limit_ranges.c33_pw_limit_ranges;
|
||||
+ status->c33_pw_limit_nb_ranges = ret;
|
||||
+ }
|
||||
+out:
|
||||
+ mutex_unlock(&psec->pcdev->lock);
|
||||
+ return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pse_ethtool_get_status);
|
||||
|
||||
--- a/drivers/net/pse-pd/pse_regulator.c
|
||||
+++ b/drivers/net/pse-pd/pse_regulator.c
|
||||
@@ -60,9 +60,19 @@ pse_reg_pi_is_enabled(struct pse_control
|
||||
}
|
||||
|
||||
static int
|
||||
-pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id,
|
||||
- struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status)
|
||||
+pse_reg_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_admin_state *admin_state)
|
||||
+{
|
||||
+ struct pse_reg_priv *priv = to_pse_reg(pcdev);
|
||||
+
|
||||
+ admin_state->podl_admin_state = priv->admin_state;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pse_reg_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_status *pw_status)
|
||||
{
|
||||
struct pse_reg_priv *priv = to_pse_reg(pcdev);
|
||||
int ret;
|
||||
@@ -72,18 +82,18 @@ pse_reg_ethtool_get_status(struct pse_co
|
||||
return ret;
|
||||
|
||||
if (!ret)
|
||||
- status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED;
|
||||
+ pw_status->podl_pw_status =
|
||||
+ ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED;
|
||||
else
|
||||
- status->podl_pw_status =
|
||||
+ pw_status->podl_pw_status =
|
||||
ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING;
|
||||
|
||||
- status->podl_admin_state = priv->admin_state;
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pse_controller_ops pse_reg_ops = {
|
||||
- .ethtool_get_status = pse_reg_ethtool_get_status,
|
||||
+ .pi_get_admin_state = pse_reg_pi_get_admin_state,
|
||||
+ .pi_get_pw_status = pse_reg_pi_get_pw_status,
|
||||
.pi_enable = pse_reg_pi_enable,
|
||||
.pi_is_enabled = pse_reg_pi_is_enabled,
|
||||
.pi_disable = pse_reg_pi_disable,
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -201,14 +201,13 @@ static int tps23881_pi_is_enabled(struct
|
||||
return enabled;
|
||||
}
|
||||
|
||||
-static int tps23881_ethtool_get_status(struct pse_controller_dev *pcdev,
|
||||
- unsigned long id,
|
||||
- struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status)
|
||||
+static int
|
||||
+tps23881_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_admin_state *admin_state)
|
||||
{
|
||||
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
struct i2c_client *client = priv->client;
|
||||
- bool enabled, delivering;
|
||||
+ bool enabled;
|
||||
u8 chan;
|
||||
u16 val;
|
||||
int ret;
|
||||
@@ -220,28 +219,56 @@ static int tps23881_ethtool_get_status(s
|
||||
chan = priv->port[id].chan[0];
|
||||
val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
enabled = !!(val);
|
||||
- val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4));
|
||||
- delivering = !!(val);
|
||||
|
||||
if (priv->port[id].is_4p) {
|
||||
chan = priv->port[id].chan[1];
|
||||
val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
enabled &= !!(val);
|
||||
+ }
|
||||
+
|
||||
+ /* Return enabled status only if both channel are on this state */
|
||||
+ if (enabled)
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
|
||||
+ else
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tps23881_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_status *pw_status)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ bool delivering;
|
||||
+ u8 chan;
|
||||
+ u16 val;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4));
|
||||
+ delivering = !!(val);
|
||||
+
|
||||
+ if (priv->port[id].is_4p) {
|
||||
+ chan = priv->port[id].chan[1];
|
||||
val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4));
|
||||
delivering &= !!(val);
|
||||
}
|
||||
|
||||
/* Return delivering status only if both channel are on this state */
|
||||
if (delivering)
|
||||
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
|
||||
- else
|
||||
- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
|
||||
-
|
||||
- /* Return enabled status only if both channel are on this state */
|
||||
- if (enabled)
|
||||
- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
|
||||
else
|
||||
- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -664,7 +691,8 @@ static const struct pse_controller_ops t
|
||||
.pi_enable = tps23881_pi_enable,
|
||||
.pi_disable = tps23881_pi_disable,
|
||||
.pi_is_enabled = tps23881_pi_is_enabled,
|
||||
- .ethtool_get_status = tps23881_ethtool_get_status,
|
||||
+ .pi_get_admin_state = tps23881_pi_get_admin_state,
|
||||
+ .pi_get_pw_status = tps23881_pi_get_pw_status,
|
||||
};
|
||||
|
||||
static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -31,7 +31,52 @@ struct pse_control_config {
|
||||
};
|
||||
|
||||
/**
|
||||
- * struct pse_control_status - PSE control/channel status.
|
||||
+ * struct pse_admin_state - PSE operational state
|
||||
+ *
|
||||
+ * @podl_admin_state: operational state of the PoDL PSE
|
||||
+ * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
|
||||
+ * @c33_admin_state: operational state of the PSE
|
||||
+ * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState
|
||||
+ */
|
||||
+struct pse_admin_state {
|
||||
+ enum ethtool_podl_pse_admin_state podl_admin_state;
|
||||
+ enum ethtool_c33_pse_admin_state c33_admin_state;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct pse_pw_status - PSE power detection status
|
||||
+ *
|
||||
+ * @podl_pw_status: power detection status of the PoDL PSE.
|
||||
+ * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus:
|
||||
+ * @c33_pw_status: power detection status of the PSE.
|
||||
+ * IEEE 802.3-2022 30.9.1.1.5 aPSEPowerDetectionStatus:
|
||||
+ */
|
||||
+struct pse_pw_status {
|
||||
+ enum ethtool_podl_pse_pw_d_status podl_pw_status;
|
||||
+ enum ethtool_c33_pse_pw_d_status c33_pw_status;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct pse_ext_state_info - PSE extended state information
|
||||
+ *
|
||||
+ * @c33_ext_state_info: extended state information of the PSE
|
||||
+ */
|
||||
+struct pse_ext_state_info {
|
||||
+ struct ethtool_c33_pse_ext_state_info c33_ext_state_info;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct pse_pw_limit_ranges - PSE power limit configuration range
|
||||
+ *
|
||||
+ * @c33_pw_limit_ranges: supported power limit configuration range. The driver
|
||||
+ * is in charge of the memory allocation.
|
||||
+ */
|
||||
+struct pse_pw_limit_ranges {
|
||||
+ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct ethtool_pse_control_status - PSE control/channel status.
|
||||
*
|
||||
* @podl_admin_state: operational state of the PoDL PSE
|
||||
* functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
|
||||
@@ -49,11 +94,11 @@ struct pse_control_config {
|
||||
* @c33_avail_pw_limit: available power limit of the PSE in mW
|
||||
* IEEE 802.3-2022 145.2.5.4 pse_avail_pwr
|
||||
* @c33_pw_limit_ranges: supported power limit configuration range. The driver
|
||||
- * is in charge of the memory allocation.
|
||||
+ * is in charge of the memory allocation
|
||||
* @c33_pw_limit_nb_ranges: number of supported power limit configuration
|
||||
* ranges
|
||||
*/
|
||||
-struct pse_control_status {
|
||||
+struct ethtool_pse_control_status {
|
||||
enum ethtool_podl_pse_admin_state podl_admin_state;
|
||||
enum ethtool_podl_pse_pw_d_status podl_pw_status;
|
||||
enum ethtool_c33_pse_admin_state c33_admin_state;
|
||||
@@ -69,22 +114,37 @@ struct pse_control_status {
|
||||
/**
|
||||
* struct pse_controller_ops - PSE controller driver callbacks
|
||||
*
|
||||
- * @ethtool_get_status: get PSE control status for ethtool interface
|
||||
* @setup_pi_matrix: setup PI matrix of the PSE controller
|
||||
+ * @pi_get_admin_state: Get the operational state of the PSE PI. This ops
|
||||
+ * is mandatory.
|
||||
+ * @pi_get_pw_status: Get the power detection status of the PSE PI. This
|
||||
+ * ops is mandatory.
|
||||
+ * @pi_get_ext_state: Get the extended state of the PSE PI.
|
||||
+ * @pi_get_pw_class: Get the power class of the PSE PI.
|
||||
+ * @pi_get_actual_pw: Get actual power of the PSE PI in mW.
|
||||
* @pi_is_enabled: Return 1 if the PSE PI is enabled, 0 if not.
|
||||
* May also return negative errno.
|
||||
* @pi_enable: Configure the PSE PI as enabled.
|
||||
* @pi_disable: Configure the PSE PI as disabled.
|
||||
* @pi_get_voltage: Return voltage similarly to get_voltage regulator
|
||||
- * callback.
|
||||
- * @pi_get_pw_limit: Get the configured power limit of the PSE PI.
|
||||
- * @pi_set_pw_limit: Configure the power limit of the PSE PI.
|
||||
+ * callback in uV.
|
||||
+ * @pi_get_pw_limit: Get the configured power limit of the PSE PI in mW.
|
||||
+ * @pi_set_pw_limit: Configure the power limit of the PSE PI in mW.
|
||||
+ * @pi_get_pw_limit_ranges: Get the supported power limit configuration
|
||||
+ * range. The driver is in charge of the memory
|
||||
+ * allocation and should return the number of
|
||||
+ * ranges.
|
||||
*/
|
||||
struct pse_controller_ops {
|
||||
- int (*ethtool_get_status)(struct pse_controller_dev *pcdev,
|
||||
- unsigned long id, struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status);
|
||||
int (*setup_pi_matrix)(struct pse_controller_dev *pcdev);
|
||||
+ int (*pi_get_admin_state)(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_admin_state *admin_state);
|
||||
+ int (*pi_get_pw_status)(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_status *pw_status);
|
||||
+ int (*pi_get_ext_state)(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_ext_state_info *ext_state_info);
|
||||
+ int (*pi_get_pw_class)(struct pse_controller_dev *pcdev, int id);
|
||||
+ int (*pi_get_actual_pw)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_enable)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_disable)(struct pse_controller_dev *pcdev, int id);
|
||||
@@ -93,12 +153,15 @@ struct pse_controller_ops {
|
||||
int id);
|
||||
int (*pi_set_pw_limit)(struct pse_controller_dev *pcdev,
|
||||
int id, int max_mW);
|
||||
+ int (*pi_get_pw_limit_ranges)(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_limit_ranges *pw_limit_ranges);
|
||||
};
|
||||
|
||||
struct module;
|
||||
struct device_node;
|
||||
struct of_phandle_args;
|
||||
struct pse_control;
|
||||
+struct ethtool_pse_control_status;
|
||||
|
||||
/* PSE PI pairset pinout can either be Alternative A or Alternative B */
|
||||
enum pse_pi_pairset_pinout {
|
||||
@@ -175,7 +238,7 @@ void pse_control_put(struct pse_control
|
||||
|
||||
int pse_ethtool_get_status(struct pse_control *psec,
|
||||
struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status);
|
||||
+ struct ethtool_pse_control_status *status);
|
||||
int pse_ethtool_set_config(struct pse_control *psec,
|
||||
struct netlink_ext_ack *extack,
|
||||
const struct pse_control_config *config);
|
||||
@@ -201,7 +264,7 @@ static inline void pse_control_put(struc
|
||||
|
||||
static inline int pse_ethtool_get_status(struct pse_control *psec,
|
||||
struct netlink_ext_ack *extack,
|
||||
- struct pse_control_status *status)
|
||||
+ struct ethtool_pse_control_status *status)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
--- a/net/ethtool/pse-pd.c
|
||||
+++ b/net/ethtool/pse-pd.c
|
||||
@@ -19,7 +19,7 @@ struct pse_req_info {
|
||||
|
||||
struct pse_reply_data {
|
||||
struct ethnl_reply_data base;
|
||||
- struct pse_control_status status;
|
||||
+ struct ethtool_pse_control_status status;
|
||||
};
|
||||
|
||||
#define PSE_REPDATA(__reply_base) \
|
||||
@@ -80,7 +80,7 @@ static int pse_reply_size(const struct e
|
||||
const struct ethnl_reply_data *reply_base)
|
||||
{
|
||||
const struct pse_reply_data *data = PSE_REPDATA(reply_base);
|
||||
- const struct pse_control_status *st = &data->status;
|
||||
+ const struct ethtool_pse_control_status *st = &data->status;
|
||||
int len = 0;
|
||||
|
||||
if (st->podl_admin_state > 0)
|
||||
@@ -114,7 +114,7 @@ static int pse_reply_size(const struct e
|
||||
}
|
||||
|
||||
static int pse_put_pw_limit_ranges(struct sk_buff *skb,
|
||||
- const struct pse_control_status *st)
|
||||
+ const struct ethtool_pse_control_status *st)
|
||||
{
|
||||
const struct ethtool_c33_pse_pw_limit_range *pw_limit_ranges;
|
||||
int i;
|
||||
@@ -146,7 +146,7 @@ static int pse_fill_reply(struct sk_buff
|
||||
const struct ethnl_reply_data *reply_base)
|
||||
{
|
||||
const struct pse_reply_data *data = PSE_REPDATA(reply_base);
|
||||
- const struct pse_control_status *st = &data->status;
|
||||
+ const struct ethtool_pse_control_status *st = &data->status;
|
||||
|
||||
if (st->podl_admin_state > 0 &&
|
||||
nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE,
|
||||
@@ -0,0 +1,184 @@
|
||||
From 4640a1f0d8f2246f34d6e74330d7e7d2cf75605b Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:28 +0100
|
||||
Subject: [PATCH] net: pse-pd: Remove is_enabled callback from drivers
|
||||
|
||||
The is_enabled callback is now redundant as the admin_state can be obtained
|
||||
directly from the driver and provides the same information.
|
||||
|
||||
To simplify functionality, the core will handle this internally, making
|
||||
the is_enabled callback unnecessary at the driver level. Remove the
|
||||
callback from all drivers.
|
||||
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 26 --------------------------
|
||||
drivers/net/pse-pd/pse_core.c | 13 +++++++++++--
|
||||
drivers/net/pse-pd/pse_regulator.c | 9 ---------
|
||||
drivers/net/pse-pd/tps23881.c | 28 ----------------------------
|
||||
include/linux/pse-pd/pse.h | 3 ---
|
||||
5 files changed, 11 insertions(+), 68 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -431,31 +431,6 @@ static int pd692x0_pi_disable(struct pse
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int pd692x0_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
|
||||
-{
|
||||
- struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
- struct pd692x0_msg msg, buf = {0};
|
||||
- int ret;
|
||||
-
|
||||
- ret = pd692x0_fw_unavailable(priv);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS];
|
||||
- msg.sub[2] = id;
|
||||
- ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- if (buf.sub[1]) {
|
||||
- priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
|
||||
- return 1;
|
||||
- } else {
|
||||
- priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
|
||||
- return 0;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
struct pd692x0_pse_ext_state_mapping {
|
||||
u32 status_code;
|
||||
enum ethtool_c33_pse_ext_state pse_ext_state;
|
||||
@@ -1105,7 +1080,6 @@ static const struct pse_controller_ops p
|
||||
.pi_get_actual_pw = pd692x0_pi_get_actual_pw,
|
||||
.pi_enable = pd692x0_pi_enable,
|
||||
.pi_disable = pd692x0_pi_disable,
|
||||
- .pi_is_enabled = pd692x0_pi_is_enabled,
|
||||
.pi_get_voltage = pd692x0_pi_get_voltage,
|
||||
.pi_get_pw_limit = pd692x0_pi_get_pw_limit,
|
||||
.pi_set_pw_limit = pd692x0_pi_set_pw_limit,
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -210,16 +210,25 @@ out:
|
||||
static int pse_pi_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
|
||||
+ struct pse_admin_state admin_state = {0};
|
||||
const struct pse_controller_ops *ops;
|
||||
int id, ret;
|
||||
|
||||
ops = pcdev->ops;
|
||||
- if (!ops->pi_is_enabled)
|
||||
+ if (!ops->pi_get_admin_state)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
id = rdev_get_id(rdev);
|
||||
mutex_lock(&pcdev->lock);
|
||||
- ret = ops->pi_is_enabled(pcdev, id);
|
||||
+ ret = ops->pi_get_admin_state(pcdev, id, &admin_state);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED ||
|
||||
+ admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED)
|
||||
+ ret = 1;
|
||||
+
|
||||
+out:
|
||||
mutex_unlock(&pcdev->lock);
|
||||
|
||||
return ret;
|
||||
--- a/drivers/net/pse-pd/pse_regulator.c
|
||||
+++ b/drivers/net/pse-pd/pse_regulator.c
|
||||
@@ -52,14 +52,6 @@ pse_reg_pi_disable(struct pse_controller
|
||||
}
|
||||
|
||||
static int
|
||||
-pse_reg_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
|
||||
-{
|
||||
- struct pse_reg_priv *priv = to_pse_reg(pcdev);
|
||||
-
|
||||
- return regulator_is_enabled(priv->ps);
|
||||
-}
|
||||
-
|
||||
-static int
|
||||
pse_reg_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
|
||||
struct pse_admin_state *admin_state)
|
||||
{
|
||||
@@ -95,7 +87,6 @@ static const struct pse_controller_ops p
|
||||
.pi_get_admin_state = pse_reg_pi_get_admin_state,
|
||||
.pi_get_pw_status = pse_reg_pi_get_pw_status,
|
||||
.pi_enable = pse_reg_pi_enable,
|
||||
- .pi_is_enabled = pse_reg_pi_is_enabled,
|
||||
.pi_disable = pse_reg_pi_disable,
|
||||
};
|
||||
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -174,33 +174,6 @@ static int tps23881_pi_disable(struct ps
|
||||
return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
|
||||
}
|
||||
|
||||
-static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
|
||||
-{
|
||||
- struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
- struct i2c_client *client = priv->client;
|
||||
- bool enabled;
|
||||
- u8 chan;
|
||||
- u16 val;
|
||||
- int ret;
|
||||
-
|
||||
- ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- chan = priv->port[id].chan[0];
|
||||
- val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
- enabled = !!(val);
|
||||
-
|
||||
- if (priv->port[id].is_4p) {
|
||||
- chan = priv->port[id].chan[1];
|
||||
- val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4));
|
||||
- enabled &= !!(val);
|
||||
- }
|
||||
-
|
||||
- /* Return enabled status only if both channel are on this state */
|
||||
- return enabled;
|
||||
-}
|
||||
-
|
||||
static int
|
||||
tps23881_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
|
||||
struct pse_admin_state *admin_state)
|
||||
@@ -690,7 +663,6 @@ static const struct pse_controller_ops t
|
||||
.setup_pi_matrix = tps23881_setup_pi_matrix,
|
||||
.pi_enable = tps23881_pi_enable,
|
||||
.pi_disable = tps23881_pi_disable,
|
||||
- .pi_is_enabled = tps23881_pi_is_enabled,
|
||||
.pi_get_admin_state = tps23881_pi_get_admin_state,
|
||||
.pi_get_pw_status = tps23881_pi_get_pw_status,
|
||||
};
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -122,8 +122,6 @@ struct ethtool_pse_control_status {
|
||||
* @pi_get_ext_state: Get the extended state of the PSE PI.
|
||||
* @pi_get_pw_class: Get the power class of the PSE PI.
|
||||
* @pi_get_actual_pw: Get actual power of the PSE PI in mW.
|
||||
- * @pi_is_enabled: Return 1 if the PSE PI is enabled, 0 if not.
|
||||
- * May also return negative errno.
|
||||
* @pi_enable: Configure the PSE PI as enabled.
|
||||
* @pi_disable: Configure the PSE PI as disabled.
|
||||
* @pi_get_voltage: Return voltage similarly to get_voltage regulator
|
||||
@@ -145,7 +143,6 @@ struct pse_controller_ops {
|
||||
struct pse_ext_state_info *ext_state_info);
|
||||
int (*pi_get_pw_class)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_get_actual_pw)(struct pse_controller_dev *pcdev, int id);
|
||||
- int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_enable)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_disable)(struct pse_controller_dev *pcdev, int id);
|
||||
int (*pi_get_voltage)(struct pse_controller_dev *pcdev, int id);
|
||||
@@ -0,0 +1,337 @@
|
||||
From 7f076ce3f17334964590c2cce49a02c0851c099a Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:29 +0100
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Add support for power limit and
|
||||
measurement features
|
||||
|
||||
Expand PSE callbacks to support the newly introduced
|
||||
pi_get/set_pw_limit() and pi_get_voltage() functions. These callbacks
|
||||
allow for power limit configuration in the TPS23881 controller.
|
||||
|
||||
Additionally, the patch includes the pi_get_pw_class() the
|
||||
pi_get_actual_pw(), and the pi_get_pw_limit_ranges') callbacks providing
|
||||
more comprehensive PoE status reporting.
|
||||
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 258 +++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 256 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -25,20 +25,32 @@
|
||||
#define TPS23881_REG_GEN_MASK 0x17
|
||||
#define TPS23881_REG_NBITACC BIT(5)
|
||||
#define TPS23881_REG_PW_EN 0x19
|
||||
+#define TPS23881_REG_2PAIR_POL1 0x1e
|
||||
#define TPS23881_REG_PORT_MAP 0x26
|
||||
#define TPS23881_REG_PORT_POWER 0x29
|
||||
-#define TPS23881_REG_POEPLUS 0x40
|
||||
+#define TPS23881_REG_4PAIR_POL1 0x2a
|
||||
+#define TPS23881_REG_INPUT_V 0x2e
|
||||
+#define TPS23881_REG_CHAN1_A 0x30
|
||||
+#define TPS23881_REG_CHAN1_V 0x32
|
||||
+#define TPS23881_REG_FOLDBACK 0x40
|
||||
#define TPS23881_REG_TPON BIT(0)
|
||||
#define TPS23881_REG_FWREV 0x41
|
||||
#define TPS23881_REG_DEVID 0x43
|
||||
#define TPS23881_REG_DEVID_MASK 0xF0
|
||||
#define TPS23881_DEVICE_ID 0x02
|
||||
+#define TPS23881_REG_CHAN1_CLASS 0x4c
|
||||
#define TPS23881_REG_SRAM_CTRL 0x60
|
||||
#define TPS23881_REG_SRAM_DATA 0x61
|
||||
|
||||
+#define TPS23881_UV_STEP 3662
|
||||
+#define TPS23881_NA_STEP 70190
|
||||
+#define TPS23881_MW_STEP 500
|
||||
+#define TPS23881_MIN_PI_PW_LIMIT_MW 2000
|
||||
+
|
||||
struct tps23881_port_desc {
|
||||
u8 chan[2];
|
||||
bool is_4p;
|
||||
+ int pw_pol;
|
||||
};
|
||||
|
||||
struct tps23881_priv {
|
||||
@@ -102,6 +114,54 @@ static u16 tps23881_set_val(u16 reg_val,
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
+static int
|
||||
+tps23881_pi_set_pw_pol_limit(struct tps23881_priv *priv, int id, u8 pw_pol,
|
||||
+ bool is_4p)
|
||||
+{
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret, reg;
|
||||
+ u16 val;
|
||||
+ u8 chan;
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ if (!is_4p) {
|
||||
+ reg = TPS23881_REG_2PAIR_POL1 + (chan % 4);
|
||||
+ } else {
|
||||
+ /* One chan is enough to configure the 4p PI power limit */
|
||||
+ if ((chan % 4) < 2)
|
||||
+ reg = TPS23881_REG_4PAIR_POL1;
|
||||
+ else
|
||||
+ reg = TPS23881_REG_4PAIR_POL1 + 1;
|
||||
+ }
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_set_val(ret, chan, 0, 0xff, pw_pol);
|
||||
+ return i2c_smbus_write_word_data(client, reg, val);
|
||||
+}
|
||||
+
|
||||
+static int tps23881_pi_enable_manual_pol(struct tps23881_priv *priv, int id)
|
||||
+{
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret;
|
||||
+ u8 chan;
|
||||
+ u16 val;
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FOLDBACK);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* No need to test if the chan is PoE4 as setting either bit for a
|
||||
+ * 4P configured port disables the automatic configuration on both
|
||||
+ * channels.
|
||||
+ */
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4));
|
||||
+ return i2c_smbus_write_byte_data(client, TPS23881_REG_FOLDBACK, val);
|
||||
+}
|
||||
+
|
||||
static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
|
||||
{
|
||||
struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
@@ -171,7 +231,21 @@ static int tps23881_pi_disable(struct ps
|
||||
BIT(chan % 4));
|
||||
}
|
||||
|
||||
- return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* No power policy */
|
||||
+ if (priv->port[id].pw_pol < 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = tps23881_pi_enable_manual_pol(priv, id);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Set power policy */
|
||||
+ return tps23881_pi_set_pw_pol_limit(priv, id, priv->port[id].pw_pol,
|
||||
+ priv->port[id].is_4p);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -246,6 +320,177 @@ tps23881_pi_get_pw_status(struct pse_con
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int tps23881_pi_get_voltage(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret;
|
||||
+ u64 uV;
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_INPUT_V);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ uV = ret & 0x3fff;
|
||||
+ uV *= TPS23881_UV_STEP;
|
||||
+
|
||||
+ return (int)uV;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tps23881_pi_get_chan_current(struct tps23881_priv *priv, u8 chan)
|
||||
+{
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int reg, ret;
|
||||
+ u64 tmp_64;
|
||||
+
|
||||
+ /* Registers 0x30 to 0x3d */
|
||||
+ reg = TPS23881_REG_CHAN1_A + (chan % 4) * 4 + (chan >= 4);
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ tmp_64 = ret & 0x3fff;
|
||||
+ tmp_64 *= TPS23881_NA_STEP;
|
||||
+ /* uA = nA / 1000 */
|
||||
+ tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000);
|
||||
+ return (int)tmp_64;
|
||||
+}
|
||||
+
|
||||
+static int tps23881_pi_get_pw_class(struct pse_controller_dev *pcdev,
|
||||
+ int id)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret, reg;
|
||||
+ u8 chan;
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ reg = TPS23881_REG_CHAN1_CLASS + (chan % 4);
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return tps23881_calc_val(ret, chan, 4, 0x0f);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tps23881_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ int ret, uV, uA;
|
||||
+ u64 tmp_64;
|
||||
+ u8 chan;
|
||||
+
|
||||
+ ret = tps23881_pi_get_voltage(&priv->pcdev, id);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ uV = ret;
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ ret = tps23881_pi_get_chan_current(priv, chan);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ uA = ret;
|
||||
+
|
||||
+ if (priv->port[id].is_4p) {
|
||||
+ chan = priv->port[id].chan[1];
|
||||
+ ret = tps23881_pi_get_chan_current(priv, chan);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ uA += ret;
|
||||
+ }
|
||||
+
|
||||
+ tmp_64 = uV;
|
||||
+ tmp_64 *= uA;
|
||||
+ /* mW = uV * uA / 1000000000 */
|
||||
+ return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tps23881_pi_get_pw_limit_chan(struct tps23881_priv *priv, u8 chan)
|
||||
+{
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret, reg;
|
||||
+ u16 val;
|
||||
+
|
||||
+ reg = TPS23881_REG_2PAIR_POL1 + (chan % 4);
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_calc_val(ret, chan, 0, 0xff);
|
||||
+ return val * TPS23881_MW_STEP;
|
||||
+}
|
||||
+
|
||||
+static int tps23881_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ int ret, mW;
|
||||
+ u8 chan;
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ ret = tps23881_pi_get_pw_limit_chan(priv, chan);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ mW = ret;
|
||||
+ if (priv->port[id].is_4p) {
|
||||
+ chan = priv->port[id].chan[1];
|
||||
+ ret = tps23881_pi_get_pw_limit_chan(priv, chan);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ mW += ret;
|
||||
+ }
|
||||
+
|
||||
+ return mW;
|
||||
+}
|
||||
+
|
||||
+static int tps23881_pi_set_pw_limit(struct pse_controller_dev *pcdev,
|
||||
+ int id, int max_mW)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ u8 pw_pol;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (max_mW < TPS23881_MIN_PI_PW_LIMIT_MW || MAX_PI_PW < max_mW) {
|
||||
+ dev_err(&priv->client->dev,
|
||||
+ "power limit %d out of ranges [%d,%d]",
|
||||
+ max_mW, TPS23881_MIN_PI_PW_LIMIT_MW, MAX_PI_PW);
|
||||
+ return -ERANGE;
|
||||
+ }
|
||||
+
|
||||
+ ret = tps23881_pi_enable_manual_pol(priv, id);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ pw_pol = DIV_ROUND_CLOSEST_ULL(max_mW, TPS23881_MW_STEP);
|
||||
+
|
||||
+ /* Save power policy to reconfigure it after a disabled call */
|
||||
+ priv->port[id].pw_pol = pw_pol;
|
||||
+ return tps23881_pi_set_pw_pol_limit(priv, id, pw_pol,
|
||||
+ priv->port[id].is_4p);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tps23881_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_limit_ranges *pw_limit_ranges)
|
||||
+{
|
||||
+ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
|
||||
+
|
||||
+ c33_pw_limit_ranges = kzalloc(sizeof(*c33_pw_limit_ranges),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!c33_pw_limit_ranges)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ c33_pw_limit_ranges->min = TPS23881_MIN_PI_PW_LIMIT_MW;
|
||||
+ c33_pw_limit_ranges->max = MAX_PI_PW;
|
||||
+ pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges;
|
||||
+
|
||||
+ /* Return the number of ranges */
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
/* Parse managers subnode into a array of device node */
|
||||
static int
|
||||
tps23881_get_of_channels(struct tps23881_priv *priv,
|
||||
@@ -540,6 +785,9 @@ tps23881_write_port_matrix(struct tps238
|
||||
if (port_matrix[i].exist)
|
||||
priv->port[pi_id].chan[0] = lgcl_chan;
|
||||
|
||||
+ /* Initialize power policy internal value */
|
||||
+ priv->port[pi_id].pw_pol = -1;
|
||||
+
|
||||
/* Set hardware port matrix for all ports */
|
||||
val |= hw_chan << (lgcl_chan * 2);
|
||||
|
||||
@@ -665,6 +913,12 @@ static const struct pse_controller_ops t
|
||||
.pi_disable = tps23881_pi_disable,
|
||||
.pi_get_admin_state = tps23881_pi_get_admin_state,
|
||||
.pi_get_pw_status = tps23881_pi_get_pw_status,
|
||||
+ .pi_get_pw_class = tps23881_pi_get_pw_class,
|
||||
+ .pi_get_actual_pw = tps23881_pi_get_actual_pw,
|
||||
+ .pi_get_voltage = tps23881_pi_get_voltage,
|
||||
+ .pi_get_pw_limit = tps23881_pi_get_pw_limit,
|
||||
+ .pi_set_pw_limit = tps23881_pi_set_pw_limit,
|
||||
+ .pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges,
|
||||
};
|
||||
|
||||
static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
|
||||
@@ -0,0 +1,28 @@
|
||||
From 10276f3e1c7e7f5de9f0bba58f8a849cb195253d Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:30 +0100
|
||||
Subject: [PATCH] net: pse-pd: Fix missing PI of_node description
|
||||
|
||||
The PI of_node was not assigned in the regulator_config structure, leading
|
||||
to failures in resolving the correct supply when different power supplies
|
||||
are assigned to multiple PIs of a PSE controller. This fix ensures that the
|
||||
of_node is properly set in the regulator_config, allowing accurate supply
|
||||
resolution for each PI.
|
||||
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pse_core.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -422,6 +422,7 @@ devm_pse_pi_regulator_register(struct ps
|
||||
rconfig.dev = pcdev->dev;
|
||||
rconfig.driver_data = pcdev;
|
||||
rconfig.init_data = rinit_data;
|
||||
+ rconfig.of_node = pcdev->pi[id].np;
|
||||
|
||||
rdev = devm_regulator_register(pcdev->dev, rdesc, &rconfig);
|
||||
if (IS_ERR(rdev)) {
|
||||
@@ -0,0 +1,92 @@
|
||||
From 5385f1e1923ca8131eb143567d509b101a344e06 Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Fri, 10 Jan 2025 10:40:31 +0100
|
||||
Subject: [PATCH] net: pse-pd: Clean ethtool header of PSE structures
|
||||
|
||||
Remove PSE-specific structures from the ethtool header to improve code
|
||||
modularity, maintain independent headers, and reduce incremental build
|
||||
time.
|
||||
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pse_core.c | 1 +
|
||||
include/linux/ethtool.h | 20 --------------------
|
||||
include/linux/pse-pd/pse.h | 22 +++++++++++++++++++++-
|
||||
3 files changed, 22 insertions(+), 21 deletions(-)
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
#include <linux/device.h>
|
||||
+#include <linux/ethtool.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pse-pd/pse.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
--- a/include/linux/ethtool.h
|
||||
+++ b/include/linux/ethtool.h
|
||||
@@ -1322,24 +1322,4 @@ struct ethtool_forced_speed_map {
|
||||
|
||||
void
|
||||
ethtool_forced_speed_maps_init(struct ethtool_forced_speed_map *maps, u32 size);
|
||||
-
|
||||
-/* C33 PSE extended state and substate. */
|
||||
-struct ethtool_c33_pse_ext_state_info {
|
||||
- enum ethtool_c33_pse_ext_state c33_pse_ext_state;
|
||||
- union {
|
||||
- enum ethtool_c33_pse_ext_substate_error_condition error_condition;
|
||||
- enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable;
|
||||
- enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted;
|
||||
- enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim;
|
||||
- enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected;
|
||||
- enum ethtool_c33_pse_ext_substate_power_not_available power_not_available;
|
||||
- enum ethtool_c33_pse_ext_substate_short_detected short_detected;
|
||||
- u32 __c33_pse_ext_substate;
|
||||
- };
|
||||
-};
|
||||
-
|
||||
-struct ethtool_c33_pse_pw_limit_range {
|
||||
- u32 min;
|
||||
- u32 max;
|
||||
-};
|
||||
#endif /* _LINUX_ETHTOOL_H */
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -5,7 +5,6 @@
|
||||
#ifndef _LINUX_PSE_CONTROLLER_H
|
||||
#define _LINUX_PSE_CONTROLLER_H
|
||||
|
||||
-#include <linux/ethtool.h>
|
||||
#include <linux/list.h>
|
||||
#include <uapi/linux/ethtool.h>
|
||||
|
||||
@@ -16,6 +15,27 @@
|
||||
|
||||
struct phy_device;
|
||||
struct pse_controller_dev;
|
||||
+struct netlink_ext_ack;
|
||||
+
|
||||
+/* C33 PSE extended state and substate. */
|
||||
+struct ethtool_c33_pse_ext_state_info {
|
||||
+ enum ethtool_c33_pse_ext_state c33_pse_ext_state;
|
||||
+ union {
|
||||
+ enum ethtool_c33_pse_ext_substate_error_condition error_condition;
|
||||
+ enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable;
|
||||
+ enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted;
|
||||
+ enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim;
|
||||
+ enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected;
|
||||
+ enum ethtool_c33_pse_ext_substate_power_not_available power_not_available;
|
||||
+ enum ethtool_c33_pse_ext_substate_short_detected short_detected;
|
||||
+ u32 __c33_pse_ext_substate;
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
+struct ethtool_c33_pse_pw_limit_range {
|
||||
+ u32 min;
|
||||
+ u32 max;
|
||||
+};
|
||||
|
||||
/**
|
||||
* struct pse_control_config - PSE control/channel configuration.
|
||||
@@ -0,0 +1,171 @@
|
||||
From fa2f0454174c2f33005f5a6e6f70c7160a15b2a1 Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:00 +0200
|
||||
Subject: [PATCH] net: pse-pd: Introduce attached_phydev to pse control
|
||||
|
||||
In preparation for reporting PSE events via ethtool notifications,
|
||||
introduce an attached_phydev field in the pse_control structure.
|
||||
This field stores the phy_device associated with the PSE PI,
|
||||
ensuring that notifications are sent to the correct network
|
||||
interface.
|
||||
|
||||
The attached_phydev pointer is directly tied to the PHY lifecycle. It
|
||||
is set when the PHY is registered and cleared when the PHY is removed.
|
||||
There is no need to use a refcount, as doing so could interfere with
|
||||
the PHY removal process.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-1-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/mdio/fwnode_mdio.c | 26 ++++++++++++++------------
|
||||
drivers/net/pse-pd/pse_core.c | 11 ++++++++---
|
||||
include/linux/pse-pd/pse.h | 6 ++++--
|
||||
3 files changed, 26 insertions(+), 17 deletions(-)
|
||||
--- a/drivers/net/mdio/fwnode_mdio.c
|
||||
+++ b/drivers/net/mdio/fwnode_mdio.c
|
||||
@@ -18,7 +18,8 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors");
|
||||
|
||||
static struct pse_control *
|
||||
-fwnode_find_pse_control(struct fwnode_handle *fwnode)
|
||||
+fwnode_find_pse_control(struct fwnode_handle *fwnode,
|
||||
+ struct phy_device *phydev)
|
||||
{
|
||||
struct pse_control *psec;
|
||||
struct device_node *np;
|
||||
@@ -30,7 +31,7 @@ fwnode_find_pse_control(struct fwnode_ha
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
- psec = of_pse_control_get(np);
|
||||
+ psec = of_pse_control_get(np, phydev);
|
||||
if (PTR_ERR(psec) == -ENOENT)
|
||||
return NULL;
|
||||
|
||||
@@ -128,15 +129,9 @@ int fwnode_mdiobus_register_phy(struct m
|
||||
u32 phy_id;
|
||||
int rc;
|
||||
|
||||
- psec = fwnode_find_pse_control(child);
|
||||
- if (IS_ERR(psec))
|
||||
- return PTR_ERR(psec);
|
||||
-
|
||||
mii_ts = fwnode_find_mii_timestamper(child);
|
||||
- if (IS_ERR(mii_ts)) {
|
||||
- rc = PTR_ERR(mii_ts);
|
||||
- goto clean_pse;
|
||||
- }
|
||||
+ if (IS_ERR(mii_ts))
|
||||
+ return PTR_ERR(mii_ts);
|
||||
|
||||
is_c45 = fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45");
|
||||
if (is_c45 || fwnode_get_phy_id(child, &phy_id))
|
||||
@@ -169,6 +164,12 @@ int fwnode_mdiobus_register_phy(struct m
|
||||
goto clean_phy;
|
||||
}
|
||||
|
||||
+ psec = fwnode_find_pse_control(child, phy);
|
||||
+ if (IS_ERR(psec)) {
|
||||
+ rc = PTR_ERR(psec);
|
||||
+ goto unregister_phy;
|
||||
+ }
|
||||
+
|
||||
phy->psec = psec;
|
||||
|
||||
/* phy->mii_ts may already be defined by the PHY driver. A
|
||||
@@ -180,12 +181,13 @@ int fwnode_mdiobus_register_phy(struct m
|
||||
|
||||
return 0;
|
||||
|
||||
+unregister_phy:
|
||||
+ if (is_acpi_node(child) || is_of_node(child))
|
||||
+ phy_device_remove(phy);
|
||||
clean_phy:
|
||||
phy_device_free(phy);
|
||||
clean_mii_ts:
|
||||
unregister_mii_timestamper(mii_ts);
|
||||
-clean_pse:
|
||||
- pse_control_put(psec);
|
||||
|
||||
return rc;
|
||||
}
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -23,6 +23,7 @@ static LIST_HEAD(pse_controller_list);
|
||||
* @list: list entry for the pcdev's PSE controller list
|
||||
* @id: ID of the PSE line in the PSE controller device
|
||||
* @refcnt: Number of gets of this pse_control
|
||||
+ * @attached_phydev: PHY device pointer attached by the PSE control
|
||||
*/
|
||||
struct pse_control {
|
||||
struct pse_controller_dev *pcdev;
|
||||
@@ -30,6 +31,7 @@ struct pse_control {
|
||||
struct list_head list;
|
||||
unsigned int id;
|
||||
struct kref refcnt;
|
||||
+ struct phy_device *attached_phydev;
|
||||
};
|
||||
|
||||
static int of_load_single_pse_pi_pairset(struct device_node *node,
|
||||
@@ -599,7 +601,8 @@ void pse_control_put(struct pse_control
|
||||
EXPORT_SYMBOL_GPL(pse_control_put);
|
||||
|
||||
static struct pse_control *
|
||||
-pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index)
|
||||
+pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,
|
||||
+ struct phy_device *phydev)
|
||||
{
|
||||
struct pse_control *psec;
|
||||
int ret;
|
||||
@@ -638,6 +641,7 @@ pse_control_get_internal(struct pse_cont
|
||||
psec->pcdev = pcdev;
|
||||
list_add(&psec->list, &pcdev->pse_control_head);
|
||||
psec->id = index;
|
||||
+ psec->attached_phydev = phydev;
|
||||
kref_init(&psec->refcnt);
|
||||
|
||||
return psec;
|
||||
@@ -693,7 +697,8 @@ static int psec_id_xlate(struct pse_cont
|
||||
return pse_spec->args[0];
|
||||
}
|
||||
|
||||
-struct pse_control *of_pse_control_get(struct device_node *node)
|
||||
+struct pse_control *of_pse_control_get(struct device_node *node,
|
||||
+ struct phy_device *phydev)
|
||||
{
|
||||
struct pse_controller_dev *r, *pcdev;
|
||||
struct of_phandle_args args;
|
||||
@@ -743,7 +748,7 @@ struct pse_control *of_pse_control_get(s
|
||||
}
|
||||
|
||||
/* pse_list_mutex also protects the pcdev's pse_control list */
|
||||
- psec = pse_control_get_internal(pcdev, psec_id);
|
||||
+ psec = pse_control_get_internal(pcdev, psec_id, phydev);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pse_list_mutex);
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -250,7 +250,8 @@ struct device;
|
||||
int devm_pse_controller_register(struct device *dev,
|
||||
struct pse_controller_dev *pcdev);
|
||||
|
||||
-struct pse_control *of_pse_control_get(struct device_node *node);
|
||||
+struct pse_control *of_pse_control_get(struct device_node *node,
|
||||
+ struct phy_device *phydev);
|
||||
void pse_control_put(struct pse_control *psec);
|
||||
|
||||
int pse_ethtool_get_status(struct pse_control *psec,
|
||||
@@ -270,7 +271,8 @@ bool pse_has_c33(struct pse_control *pse
|
||||
|
||||
#else
|
||||
|
||||
-static inline struct pse_control *of_pse_control_get(struct device_node *node)
|
||||
+static inline struct pse_control *of_pse_control_get(struct device_node *node,
|
||||
+ struct phy_device *phydev)
|
||||
{
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
# ADAPTED FOR OPENWRT 6.12.67 - Documentation changes removed
|
||||
# Original commit: fc0e6db30941
|
||||
From fc0e6db30941a66e284b8516b82356f97f31061d Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:01 +0200
|
||||
Subject: [PATCH] net: pse-pd: Add support for reporting events
|
||||
|
||||
Add support for devm_pse_irq_helper() to register PSE interrupts and report
|
||||
events such as over-current or over-temperature conditions. This follows a
|
||||
similar approach to the regulator API but also sends notifications using a
|
||||
dedicated PSE ethtool netlink socket.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-2-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
Documentation/netlink/specs/ethtool.yaml | 34 ++++
|
||||
Documentation/networking/ethtool-netlink.rst | 19 ++
|
||||
drivers/net/pse-pd/pse_core.c | 179 ++++++++++++++++++
|
||||
include/linux/ethtool_netlink.h | 7 +
|
||||
include/linux/pse-pd/pse.h | 20 ++
|
||||
.../uapi/linux/ethtool_netlink_generated.h | 19 ++
|
||||
net/ethtool/pse-pd.c | 39 ++++
|
||||
7 files changed, 317 insertions(+)
|
||||
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -7,10 +7,14 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/ethtool.h>
|
||||
+#include <linux/ethtool_netlink.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/phy.h>
|
||||
#include <linux/pse-pd/pse.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
+#include <linux/rtnetlink.h>
|
||||
+#include <net/net_trackers.h>
|
||||
|
||||
static DEFINE_MUTEX(pse_list_mutex);
|
||||
static LIST_HEAD(pse_controller_list);
|
||||
@@ -210,6 +214,48 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * pse_control_find_net_by_id - Find net attached to the pse control id
|
||||
+ * @pcdev: a pointer to the PSE
|
||||
+ * @id: index of the PSE control
|
||||
+ *
|
||||
+ * Return: pse_control pointer or NULL. The device returned has had a
|
||||
+ * reference added and the pointer is safe until the user calls
|
||||
+ * pse_control_put() to indicate they have finished with it.
|
||||
+ */
|
||||
+static struct pse_control *
|
||||
+pse_control_find_by_id(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct pse_control *psec;
|
||||
+
|
||||
+ mutex_lock(&pse_list_mutex);
|
||||
+ list_for_each_entry(psec, &pcdev->pse_control_head, list) {
|
||||
+ if (psec->id == id) {
|
||||
+ kref_get(&psec->refcnt);
|
||||
+ mutex_unlock(&pse_list_mutex);
|
||||
+ return psec;
|
||||
+ }
|
||||
+ }
|
||||
+ mutex_unlock(&pse_list_mutex);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pse_control_get_netdev - Return netdev associated to a PSE control
|
||||
+ * @psec: PSE control pointer
|
||||
+ *
|
||||
+ * Return: netdev pointer or NULL
|
||||
+ */
|
||||
+static struct net_device *pse_control_get_netdev(struct pse_control *psec)
|
||||
+{
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
+ if (!psec || !psec->attached_phydev)
|
||||
+ return NULL;
|
||||
+
|
||||
+ return psec->attached_phydev->attached_dev;
|
||||
+}
|
||||
+
|
||||
static int pse_pi_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
|
||||
@@ -559,6 +605,139 @@ int devm_pse_controller_register(struct
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_pse_controller_register);
|
||||
|
||||
+struct pse_irq {
|
||||
+ struct pse_controller_dev *pcdev;
|
||||
+ struct pse_irq_desc desc;
|
||||
+ unsigned long *notifs;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * pse_to_regulator_notifs - Convert PSE notifications to Regulator
|
||||
+ * notifications
|
||||
+ * @notifs: PSE notifications
|
||||
+ *
|
||||
+ * Return: Regulator notifications
|
||||
+ */
|
||||
+static unsigned long pse_to_regulator_notifs(unsigned long notifs)
|
||||
+{
|
||||
+ unsigned long rnotifs = 0;
|
||||
+
|
||||
+ if (notifs & ETHTOOL_PSE_EVENT_OVER_CURRENT)
|
||||
+ rnotifs |= REGULATOR_EVENT_OVER_CURRENT;
|
||||
+ if (notifs & ETHTOOL_PSE_EVENT_OVER_TEMP)
|
||||
+ rnotifs |= REGULATOR_EVENT_OVER_TEMP;
|
||||
+
|
||||
+ return rnotifs;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pse_isr - IRQ handler for PSE
|
||||
+ * @irq: irq number
|
||||
+ * @data: pointer to user interrupt structure
|
||||
+ *
|
||||
+ * Return: irqreturn_t - status of IRQ
|
||||
+ */
|
||||
+static irqreturn_t pse_isr(int irq, void *data)
|
||||
+{
|
||||
+ struct pse_controller_dev *pcdev;
|
||||
+ unsigned long notifs_mask = 0;
|
||||
+ struct pse_irq_desc *desc;
|
||||
+ struct pse_irq *h = data;
|
||||
+ int ret, i;
|
||||
+
|
||||
+ desc = &h->desc;
|
||||
+ pcdev = h->pcdev;
|
||||
+
|
||||
+ /* Clear notifs mask */
|
||||
+ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
|
||||
+ mutex_lock(&pcdev->lock);
|
||||
+ ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask);
|
||||
+ mutex_unlock(&pcdev->lock);
|
||||
+ if (ret || !notifs_mask)
|
||||
+ return IRQ_NONE;
|
||||
+
|
||||
+ for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) {
|
||||
+ unsigned long notifs, rnotifs;
|
||||
+ struct net_device *netdev;
|
||||
+ struct pse_control *psec;
|
||||
+
|
||||
+ /* Do nothing PI not described */
|
||||
+ if (!pcdev->pi[i].rdev)
|
||||
+ continue;
|
||||
+
|
||||
+ notifs = h->notifs[i];
|
||||
+ dev_dbg(h->pcdev->dev,
|
||||
+ "Sending PSE notification EVT 0x%lx\n", notifs);
|
||||
+
|
||||
+ psec = pse_control_find_by_id(pcdev, i);
|
||||
+ rtnl_lock();
|
||||
+ netdev = pse_control_get_netdev(psec);
|
||||
+ if (netdev)
|
||||
+ ethnl_pse_send_ntf(netdev, notifs);
|
||||
+ rtnl_unlock();
|
||||
+ pse_control_put(psec);
|
||||
+
|
||||
+ rnotifs = pse_to_regulator_notifs(notifs);
|
||||
+ regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs,
|
||||
+ NULL);
|
||||
+ }
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * devm_pse_irq_helper - Register IRQ based PSE event notifier
|
||||
+ * @pcdev: a pointer to the PSE
|
||||
+ * @irq: the irq value to be passed to request_irq
|
||||
+ * @irq_flags: the flags to be passed to request_irq
|
||||
+ * @d: PSE interrupt description
|
||||
+ *
|
||||
+ * Return: 0 on success and errno on failure
|
||||
+ */
|
||||
+int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
|
||||
+ int irq_flags, const struct pse_irq_desc *d)
|
||||
+{
|
||||
+ struct device *dev = pcdev->dev;
|
||||
+ size_t irq_name_len;
|
||||
+ struct pse_irq *h;
|
||||
+ char *irq_name;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!d || !d->map_event || !d->name)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
|
||||
+ if (!h)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ h->pcdev = pcdev;
|
||||
+ h->desc = *d;
|
||||
+
|
||||
+ /* IRQ name len is pcdev dev name + 5 char + irq desc name + 1 */
|
||||
+ irq_name_len = strlen(dev_name(pcdev->dev)) + 5 + strlen(d->name) + 1;
|
||||
+ irq_name = devm_kzalloc(dev, irq_name_len, GFP_KERNEL);
|
||||
+ if (!irq_name)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ snprintf(irq_name, irq_name_len, "pse-%s:%s", dev_name(pcdev->dev),
|
||||
+ d->name);
|
||||
+
|
||||
+ h->notifs = devm_kcalloc(dev, pcdev->nr_lines,
|
||||
+ sizeof(*h->notifs), GFP_KERNEL);
|
||||
+ if (!h->notifs)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr,
|
||||
+ IRQF_ONESHOT | irq_flags,
|
||||
+ irq_name, h);
|
||||
+ if (ret)
|
||||
+ dev_err(pcdev->dev, "Failed to request IRQ %d\n", irq);
|
||||
+
|
||||
+ pcdev->irq = irq;
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(devm_pse_irq_helper);
|
||||
+
|
||||
/* PSE control section */
|
||||
|
||||
static void __pse_control_release(struct kref *kref)
|
||||
--- a/include/linux/ethtool_netlink.h
|
||||
+++ b/include/linux/ethtool_netlink.h
|
||||
@@ -43,6 +43,8 @@ void ethtool_aggregate_rmon_stats(struct
|
||||
struct ethtool_rmon_stats *rmon_stats);
|
||||
bool ethtool_dev_mm_supported(struct net_device *dev);
|
||||
|
||||
+void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notif);
|
||||
+
|
||||
#else
|
||||
static inline int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
|
||||
{
|
||||
@@ -120,6 +122,11 @@ static inline bool ethtool_dev_mm_suppor
|
||||
return false;
|
||||
}
|
||||
|
||||
+static inline void ethnl_pse_send_ntf(struct phy_device *phydev,
|
||||
+ unsigned long notif)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
#endif /* IS_ENABLED(CONFIG_ETHTOOL_NETLINK) */
|
||||
|
||||
static inline int ethnl_cable_test_result(struct phy_device *phydev, u8 pair,
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -7,12 +7,15 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <uapi/linux/ethtool.h>
|
||||
+#include <uapi/linux/ethtool_netlink_generated.h>
|
||||
+#include <linux/regulator/driver.h>
|
||||
|
||||
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
|
||||
#define MAX_PI_CURRENT 1920000
|
||||
/* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */
|
||||
#define MAX_PI_PW 99900
|
||||
|
||||
+struct net_device;
|
||||
struct phy_device;
|
||||
struct pse_controller_dev;
|
||||
struct netlink_ext_ack;
|
||||
@@ -38,6 +41,19 @@ struct ethtool_c33_pse_pw_limit_range {
|
||||
};
|
||||
|
||||
/**
|
||||
+ * struct pse_irq_desc - notification sender description for IRQ based events.
|
||||
+ *
|
||||
+ * @name: the visible name for the IRQ
|
||||
+ * @map_event: driver callback to map IRQ status into PSE devices with events.
|
||||
+ */
|
||||
+struct pse_irq_desc {
|
||||
+ const char *name;
|
||||
+ int (*map_event)(int irq, struct pse_controller_dev *pcdev,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask);
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
* struct pse_control_config - PSE control/channel configuration.
|
||||
*
|
||||
* @podl_admin_control: set PoDL PSE admin control as described in
|
||||
@@ -228,6 +244,7 @@ struct pse_pi {
|
||||
* @types: types of the PSE controller
|
||||
* @pi: table of PSE PIs described in this controller device
|
||||
* @no_of_pse_pi: flag set if the pse_pis devicetree node is not used
|
||||
+ * @irq: PSE interrupt
|
||||
*/
|
||||
struct pse_controller_dev {
|
||||
const struct pse_controller_ops *ops;
|
||||
@@ -241,6 +258,7 @@ struct pse_controller_dev {
|
||||
enum ethtool_pse_types types;
|
||||
struct pse_pi *pi;
|
||||
bool no_of_pse_pi;
|
||||
+ int irq;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_PSE_CONTROLLER)
|
||||
@@ -249,6 +267,8 @@ void pse_controller_unregister(struct ps
|
||||
struct device;
|
||||
int devm_pse_controller_register(struct device *dev,
|
||||
struct pse_controller_dev *pcdev);
|
||||
+int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
|
||||
+ int irq_flags, const struct pse_irq_desc *d);
|
||||
|
||||
struct pse_control *of_pse_control_get(struct device_node *node,
|
||||
struct phy_device *phydev);
|
||||
--- a/net/ethtool/pse-pd.c
|
||||
+++ b/net/ethtool/pse-pd.c
|
||||
@@ -315,3 +315,42 @@ const struct ethnl_request_ops ethnl_pse
|
||||
.set = ethnl_set_pse,
|
||||
/* PSE has no notification */
|
||||
};
|
||||
+
|
||||
+void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notifs)
|
||||
+{
|
||||
+ void *reply_payload;
|
||||
+ struct sk_buff *skb;
|
||||
+ int reply_len;
|
||||
+ int ret;
|
||||
+
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
+ if (!netdev || !notifs)
|
||||
+ return;
|
||||
+
|
||||
+ reply_len = ethnl_reply_header_size() +
|
||||
+ nla_total_size(sizeof(u32)); /* _PSE_NTF_EVENTS */
|
||||
+
|
||||
+ skb = genlmsg_new(reply_len, GFP_KERNEL);
|
||||
+ if (!skb)
|
||||
+ return;
|
||||
+
|
||||
+ reply_payload = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_PSE_NTF);
|
||||
+ if (!reply_payload)
|
||||
+ goto err_skb;
|
||||
+
|
||||
+ ret = ethnl_fill_reply_header(skb, netdev, ETHTOOL_A_PSE_NTF_HEADER);
|
||||
+ if (ret < 0)
|
||||
+ goto err_skb;
|
||||
+
|
||||
+ if (nla_put_uint(skb, ETHTOOL_A_PSE_NTF_EVENTS, notifs))
|
||||
+ goto err_skb;
|
||||
+
|
||||
+ genlmsg_end(skb, reply_payload);
|
||||
+ ethnl_multicast(skb, netdev);
|
||||
+ return;
|
||||
+
|
||||
+err_skb:
|
||||
+ nlmsg_free(skb);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(ethnl_pse_send_ntf);
|
||||
@@ -0,0 +1,252 @@
|
||||
From f5e7aecaa4efcd4c85477b6a62f94fea668031db Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:02 +0200
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Add support for PSE events and
|
||||
interrupts
|
||||
|
||||
Add support for PSE event reporting through interrupts. Set up the newly
|
||||
introduced devm_pse_irq_helper helper to register the interrupt. Events are
|
||||
reported for over-current and over-temperature conditions.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-3-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 189 +++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 187 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -16,7 +16,15 @@
|
||||
#include <linux/pse-pd/pse.h>
|
||||
|
||||
#define TPS23881_MAX_CHANS 8
|
||||
+#define TPS23881_MAX_IRQ_RETRIES 10
|
||||
|
||||
+#define TPS23881_REG_IT 0x0
|
||||
+#define TPS23881_REG_IT_MASK 0x1
|
||||
+#define TPS23881_REG_IT_IFAULT BIT(5)
|
||||
+#define TPS23881_REG_IT_SUPF BIT(7)
|
||||
+#define TPS23881_REG_FAULT 0x7
|
||||
+#define TPS23881_REG_SUPF_EVENT 0xb
|
||||
+#define TPS23881_REG_TSD BIT(7)
|
||||
#define TPS23881_REG_PW_STATUS 0x10
|
||||
#define TPS23881_REG_OP_MODE 0x12
|
||||
#define TPS23881_OP_MODE_SEMIAUTO 0xaaaa
|
||||
@@ -24,6 +32,7 @@
|
||||
#define TPS23881_REG_DET_CLA_EN 0x14
|
||||
#define TPS23881_REG_GEN_MASK 0x17
|
||||
#define TPS23881_REG_NBITACC BIT(5)
|
||||
+#define TPS23881_REG_INTEN BIT(7)
|
||||
#define TPS23881_REG_PW_EN 0x19
|
||||
#define TPS23881_REG_2PAIR_POL1 0x1e
|
||||
#define TPS23881_REG_PORT_MAP 0x26
|
||||
@@ -51,6 +60,7 @@ struct tps23881_port_desc {
|
||||
u8 chan[2];
|
||||
bool is_4p;
|
||||
int pw_pol;
|
||||
+ bool exist;
|
||||
};
|
||||
|
||||
struct tps23881_priv {
|
||||
@@ -782,8 +792,10 @@ tps23881_write_port_matrix(struct tps238
|
||||
hw_chan = port_matrix[i].hw_chan[0] % 4;
|
||||
|
||||
/* Set software port matrix for existing ports */
|
||||
- if (port_matrix[i].exist)
|
||||
+ if (port_matrix[i].exist) {
|
||||
priv->port[pi_id].chan[0] = lgcl_chan;
|
||||
+ priv->port[pi_id].exist = true;
|
||||
+ }
|
||||
|
||||
/* Initialize power policy internal value */
|
||||
priv->port[pi_id].pw_pol = -1;
|
||||
@@ -1017,6 +1029,173 @@ static int tps23881_flash_sram_fw(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/* Convert interrupt events to 0xff to be aligned with the chan
|
||||
+ * number.
|
||||
+ */
|
||||
+static u8 tps23881_irq_export_chans_helper(u16 reg_val, u8 field_offset)
|
||||
+{
|
||||
+ u8 val;
|
||||
+
|
||||
+ val = (reg_val >> (4 + field_offset) & 0xf0) |
|
||||
+ (reg_val >> field_offset & 0x0f);
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
+/* Convert chan number to port number */
|
||||
+static void tps23881_set_notifs_helper(struct tps23881_priv *priv,
|
||||
+ u8 chans,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask,
|
||||
+ enum ethtool_pse_event event)
|
||||
+{
|
||||
+ u8 chan;
|
||||
+ int i;
|
||||
+
|
||||
+ if (!chans)
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
|
||||
+ if (!priv->port[i].exist)
|
||||
+ continue;
|
||||
+ /* No need to look at the 2nd channel in case of PoE4 as
|
||||
+ * both registers are set.
|
||||
+ */
|
||||
+ chan = priv->port[i].chan[0];
|
||||
+
|
||||
+ if (BIT(chan) & chans) {
|
||||
+ *notifs_mask |= BIT(i);
|
||||
+ notifs[i] |= event;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void tps23881_irq_event_over_temp(struct tps23881_priv *priv,
|
||||
+ u16 reg_val,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (reg_val & TPS23881_REG_TSD) {
|
||||
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
|
||||
+ if (!priv->port[i].exist)
|
||||
+ continue;
|
||||
+
|
||||
+ *notifs_mask |= BIT(i);
|
||||
+ notifs[i] |= ETHTOOL_PSE_EVENT_OVER_TEMP;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void tps23881_irq_event_over_current(struct tps23881_priv *priv,
|
||||
+ u16 reg_val,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ u8 chans;
|
||||
+
|
||||
+ chans = tps23881_irq_export_chans_helper(reg_val, 0);
|
||||
+ if (chans)
|
||||
+ tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask,
|
||||
+ ETHTOOL_PSE_EVENT_OVER_CURRENT);
|
||||
+}
|
||||
+
|
||||
+static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* The Supply event bit is repeated twice so we only need to read
|
||||
+ * the one from the first byte.
|
||||
+ */
|
||||
+ if (reg & TPS23881_REG_IT_SUPF) {
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_SUPF_EVENT);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ tps23881_irq_event_over_temp(priv, ret, notifs, notifs_mask);
|
||||
+ }
|
||||
+
|
||||
+ if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8)) {
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_FAULT);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ tps23881_irq_event_over_current(priv, ret, notifs, notifs_mask);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int tps23881_irq_handler(int irq, struct pse_controller_dev *pcdev,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ int ret, it_mask, retry;
|
||||
+
|
||||
+ /* Get interruption mask */
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_IT_MASK);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ it_mask = ret;
|
||||
+
|
||||
+ /* Read interrupt register until it frees the interruption pin. */
|
||||
+ retry = 0;
|
||||
+ while (true) {
|
||||
+ if (retry > TPS23881_MAX_IRQ_RETRIES) {
|
||||
+ dev_err(&client->dev, "interrupt never freed");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_IT);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* No more relevant interruption */
|
||||
+ if (!(ret & it_mask))
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = tps23881_irq_event_handler(priv, (u16)ret, notifs,
|
||||
+ notifs_mask);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ retry++;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int tps23881_setup_irq(struct tps23881_priv *priv, int irq)
|
||||
+{
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ struct pse_irq_desc irq_desc = {
|
||||
+ .name = "tps23881-irq",
|
||||
+ .map_event = tps23881_irq_handler,
|
||||
+ };
|
||||
+ int ret;
|
||||
+ u16 val;
|
||||
+
|
||||
+ val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF;
|
||||
+ val |= val << 8;
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_IT_MASK, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_GEN_MASK);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = (u16)(ret | TPS23881_REG_INTEN | TPS23881_REG_INTEN << 8);
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_GEN_MASK, val);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return devm_pse_irq_helper(&priv->pcdev, irq, 0, &irq_desc);
|
||||
+}
|
||||
+
|
||||
static int tps23881_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
@@ -1097,6 +1276,12 @@ static int tps23881_i2c_probe(struct i2c
|
||||
"failed to register PSE controller\n");
|
||||
}
|
||||
|
||||
+ if (client->irq) {
|
||||
+ ret = tps23881_setup_irq(priv, client->irq);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
From 50f8b341d26826aa5fdccb8f497fbff2500934b3 Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:03 +0200
|
||||
Subject: [PATCH] net: pse-pd: Add support for PSE power domains
|
||||
|
||||
Introduce PSE power domain support as groundwork for upcoming port
|
||||
priority features. Multiple PSE PIs can now be grouped under a single
|
||||
PSE power domain, enabling future enhancements like defining available
|
||||
power budgets, port priority modes, and disconnection policies. This
|
||||
setup will allow the system to assess whether activating a port would
|
||||
exceed the available power budget, preventing over-budget states
|
||||
proactively.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-4-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pse_core.c | 140 ++++++++++++++++++++++++++++++++++
|
||||
include/linux/pse-pd/pse.h | 2 +
|
||||
2 files changed, 142 insertions(+)
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -16,8 +16,12 @@
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/net_trackers.h>
|
||||
|
||||
+#define PSE_PW_D_LIMIT INT_MAX
|
||||
+
|
||||
static DEFINE_MUTEX(pse_list_mutex);
|
||||
static LIST_HEAD(pse_controller_list);
|
||||
+static DEFINE_XARRAY_ALLOC(pse_pw_d_map);
|
||||
+static DEFINE_MUTEX(pse_pw_d_mutex);
|
||||
|
||||
/**
|
||||
* struct pse_control - a PSE control
|
||||
@@ -38,6 +42,18 @@ struct pse_control {
|
||||
struct phy_device *attached_phydev;
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * struct pse_power_domain - a PSE power domain
|
||||
+ * @id: ID of the power domain
|
||||
+ * @supply: Power supply the Power Domain
|
||||
+ * @refcnt: Number of gets of this pse_power_domain
|
||||
+ */
|
||||
+struct pse_power_domain {
|
||||
+ int id;
|
||||
+ struct regulator *supply;
|
||||
+ struct kref refcnt;
|
||||
+};
|
||||
+
|
||||
static int of_load_single_pse_pi_pairset(struct device_node *node,
|
||||
struct pse_pi *pi,
|
||||
int pairset_num)
|
||||
@@ -485,6 +501,125 @@ devm_pse_pi_regulator_register(struct ps
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void __pse_pw_d_release(struct kref *kref)
|
||||
+{
|
||||
+ struct pse_power_domain *pw_d = container_of(kref,
|
||||
+ struct pse_power_domain,
|
||||
+ refcnt);
|
||||
+
|
||||
+ regulator_put(pw_d->supply);
|
||||
+ xa_erase(&pse_pw_d_map, pw_d->id);
|
||||
+ mutex_unlock(&pse_pw_d_mutex);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pse_flush_pw_ds - flush all PSE power domains of a PSE
|
||||
+ * @pcdev: a pointer to the initialized PSE controller device
|
||||
+ */
|
||||
+static void pse_flush_pw_ds(struct pse_controller_dev *pcdev)
|
||||
+{
|
||||
+ struct pse_power_domain *pw_d;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < pcdev->nr_lines; i++) {
|
||||
+ if (!pcdev->pi[i].pw_d)
|
||||
+ continue;
|
||||
+
|
||||
+ pw_d = xa_load(&pse_pw_d_map, pcdev->pi[i].pw_d->id);
|
||||
+ if (!pw_d)
|
||||
+ continue;
|
||||
+
|
||||
+ kref_put_mutex(&pw_d->refcnt, __pse_pw_d_release,
|
||||
+ &pse_pw_d_mutex);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * devm_pse_alloc_pw_d - allocate a new PSE power domain for a device
|
||||
+ * @dev: device that is registering this PSE power domain
|
||||
+ *
|
||||
+ * Return: Pointer to the newly allocated PSE power domain or error pointers
|
||||
+ */
|
||||
+static struct pse_power_domain *devm_pse_alloc_pw_d(struct device *dev)
|
||||
+{
|
||||
+ struct pse_power_domain *pw_d;
|
||||
+ int index, ret;
|
||||
+
|
||||
+ pw_d = devm_kzalloc(dev, sizeof(*pw_d), GFP_KERNEL);
|
||||
+ if (!pw_d)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ ret = xa_alloc(&pse_pw_d_map, &index, pw_d, XA_LIMIT(1, PSE_PW_D_LIMIT),
|
||||
+ GFP_KERNEL);
|
||||
+ if (ret)
|
||||
+ return ERR_PTR(ret);
|
||||
+
|
||||
+ kref_init(&pw_d->refcnt);
|
||||
+ pw_d->id = index;
|
||||
+ return pw_d;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pse_register_pw_ds - register the PSE power domains for a PSE
|
||||
+ * @pcdev: a pointer to the PSE controller device
|
||||
+ *
|
||||
+ * Return: 0 on success and failure value on error
|
||||
+ */
|
||||
+static int pse_register_pw_ds(struct pse_controller_dev *pcdev)
|
||||
+{
|
||||
+ int i, ret = 0;
|
||||
+
|
||||
+ mutex_lock(&pse_pw_d_mutex);
|
||||
+ for (i = 0; i < pcdev->nr_lines; i++) {
|
||||
+ struct regulator_dev *rdev = pcdev->pi[i].rdev;
|
||||
+ struct pse_power_domain *pw_d;
|
||||
+ struct regulator *supply;
|
||||
+ bool present = false;
|
||||
+ unsigned long index;
|
||||
+
|
||||
+ /* No regulator or regulator parent supply registered.
|
||||
+ * We need a regulator parent to register a PSE power domain
|
||||
+ */
|
||||
+ if (!rdev || !rdev->supply)
|
||||
+ continue;
|
||||
+
|
||||
+ xa_for_each(&pse_pw_d_map, index, pw_d) {
|
||||
+ /* Power supply already registered as a PSE power
|
||||
+ * domain.
|
||||
+ */
|
||||
+ if (regulator_is_equal(pw_d->supply, rdev->supply)) {
|
||||
+ present = true;
|
||||
+ pcdev->pi[i].pw_d = pw_d;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (present) {
|
||||
+ kref_get(&pw_d->refcnt);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ pw_d = devm_pse_alloc_pw_d(pcdev->dev);
|
||||
+ if (IS_ERR(pw_d)) {
|
||||
+ ret = PTR_ERR(pw_d);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ supply = regulator_get(&rdev->dev, rdev->supply_name);
|
||||
+ if (IS_ERR(supply)) {
|
||||
+ xa_erase(&pse_pw_d_map, pw_d->id);
|
||||
+ ret = PTR_ERR(supply);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ pw_d->supply = supply;
|
||||
+ pcdev->pi[i].pw_d = pw_d;
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ mutex_unlock(&pse_pw_d_mutex);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* pse_controller_register - register a PSE controller device
|
||||
* @pcdev: a pointer to the initialized PSE controller device
|
||||
@@ -544,6 +679,10 @@ int pse_controller_register(struct pse_c
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ ret = pse_register_pw_ds(pcdev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
mutex_lock(&pse_list_mutex);
|
||||
list_add(&pcdev->list, &pse_controller_list);
|
||||
mutex_unlock(&pse_list_mutex);
|
||||
@@ -558,6 +697,7 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
|
||||
*/
|
||||
void pse_controller_unregister(struct pse_controller_dev *pcdev)
|
||||
{
|
||||
+ pse_flush_pw_ds(pcdev);
|
||||
pse_release_pis(pcdev);
|
||||
mutex_lock(&pse_list_mutex);
|
||||
list_del(&pcdev->list);
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -222,12 +222,14 @@ struct pse_pi_pairset {
|
||||
* @np: device node pointer of the PSE PI node
|
||||
* @rdev: regulator represented by the PSE PI
|
||||
* @admin_state_enabled: PI enabled state
|
||||
+ * @pw_d: Power domain of the PSE PI
|
||||
*/
|
||||
struct pse_pi {
|
||||
struct pse_pi_pairset pairset[2];
|
||||
struct device_node *np;
|
||||
struct regulator_dev *rdev;
|
||||
bool admin_state_enabled;
|
||||
+ struct pse_power_domain *pw_d;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,78 @@
|
||||
# ADAPTED FOR OPENWRT 6.12.67 - Documentation changes removed
|
||||
# Original commit: 1176978ed851
|
||||
From 1176978ed851952652ddea3685e2f71a0e5d61ff Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:04 +0200
|
||||
Subject: [PATCH] net: ethtool: Add support for new power domains index
|
||||
description
|
||||
|
||||
Report the index of the newly introduced PSE power domain to the user,
|
||||
enabling improved management of the power budget for PSE devices.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-5-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
Documentation/netlink/specs/ethtool.yaml | 5 +++++
|
||||
Documentation/networking/ethtool-netlink.rst | 4 ++++
|
||||
drivers/net/pse-pd/pse_core.c | 3 +++
|
||||
include/linux/pse-pd/pse.h | 2 ++
|
||||
include/uapi/linux/ethtool_netlink_generated.h | 1 +
|
||||
net/ethtool/pse-pd.c | 7 +++++++
|
||||
6 files changed, 22 insertions(+)
|
||||
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -1098,6 +1098,9 @@ int pse_ethtool_get_status(struct pse_co
|
||||
pcdev = psec->pcdev;
|
||||
ops = pcdev->ops;
|
||||
mutex_lock(&pcdev->lock);
|
||||
+ if (pcdev->pi[psec->id].pw_d)
|
||||
+ status->pw_d_id = pcdev->pi[psec->id].pw_d->id;
|
||||
+
|
||||
ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state);
|
||||
if (ret)
|
||||
goto out;
|
||||
--- a/include/linux/pse-pd/pse.h
|
||||
+++ b/include/linux/pse-pd/pse.h
|
||||
@@ -114,6 +114,7 @@ struct pse_pw_limit_ranges {
|
||||
/**
|
||||
* struct ethtool_pse_control_status - PSE control/channel status.
|
||||
*
|
||||
+ * @pw_d_id: PSE power domain index.
|
||||
* @podl_admin_state: operational state of the PoDL PSE
|
||||
* functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
|
||||
* @podl_pw_status: power detection status of the PoDL PSE.
|
||||
@@ -135,6 +136,7 @@ struct pse_pw_limit_ranges {
|
||||
* ranges
|
||||
*/
|
||||
struct ethtool_pse_control_status {
|
||||
+ u32 pw_d_id;
|
||||
enum ethtool_podl_pse_admin_state podl_admin_state;
|
||||
enum ethtool_podl_pse_pw_d_status podl_pw_status;
|
||||
enum ethtool_c33_pse_admin_state c33_admin_state;
|
||||
--- a/net/ethtool/pse-pd.c
|
||||
+++ b/net/ethtool/pse-pd.c
|
||||
@@ -83,6 +83,8 @@ static int pse_reply_size(const struct e
|
||||
const struct ethtool_pse_control_status *st = &data->status;
|
||||
int len = 0;
|
||||
|
||||
+ if (st->pw_d_id)
|
||||
+ len += nla_total_size(sizeof(u32)); /* _PSE_PW_D_ID */
|
||||
if (st->podl_admin_state > 0)
|
||||
len += nla_total_size(sizeof(u32)); /* _PODL_PSE_ADMIN_STATE */
|
||||
if (st->podl_pw_status > 0)
|
||||
@@ -148,6 +150,11 @@ static int pse_fill_reply(struct sk_buff
|
||||
const struct pse_reply_data *data = PSE_REPDATA(reply_base);
|
||||
const struct ethtool_pse_control_status *st = &data->status;
|
||||
|
||||
+ if (st->pw_d_id &&
|
||||
+ nla_put_u32(skb, ETHTOOL_A_PSE_PW_D_ID,
|
||||
+ st->pw_d_id))
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
if (st->podl_admin_state > 0 &&
|
||||
nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE,
|
||||
st->podl_admin_state))
|
||||
@@ -0,0 +1,74 @@
|
||||
From c394e757dedd9cf947f9ac470d615d28fd2b07d1 Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:05 +0200
|
||||
Subject: [PATCH] net: pse-pd: Add helper to report hardware enable status of
|
||||
the PI
|
||||
|
||||
Refactor code by introducing a helper function to retrieve the hardware
|
||||
enabled state of the PI, avoiding redundant implementations in the
|
||||
future.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-6-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pse_core.c | 36 +++++++++++++++++++++++++----------
|
||||
1 file changed, 26 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pse_core.c
|
||||
+++ b/drivers/net/pse-pd/pse_core.c
|
||||
@@ -272,10 +272,34 @@ static struct net_device *pse_control_ge
|
||||
return psec->attached_phydev->attached_dev;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * pse_pi_is_hw_enabled - Is PI enabled at the hardware level
|
||||
+ * @pcdev: a pointer to the PSE controller device
|
||||
+ * @id: Index of the PI
|
||||
+ *
|
||||
+ * Return: 1 if the PI is enabled at the hardware level, 0 if not, and
|
||||
+ * a failure value on error
|
||||
+ */
|
||||
+static int pse_pi_is_hw_enabled(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct pse_admin_state admin_state = {0};
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pcdev->ops->pi_get_admin_state(pcdev, id, &admin_state);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* PI is well enabled at the hardware level */
|
||||
+ if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED ||
|
||||
+ admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED)
|
||||
+ return 1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int pse_pi_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
|
||||
- struct pse_admin_state admin_state = {0};
|
||||
const struct pse_controller_ops *ops;
|
||||
int id, ret;
|
||||
|
||||
@@ -285,15 +309,7 @@ static int pse_pi_is_enabled(struct regu
|
||||
|
||||
id = rdev_get_id(rdev);
|
||||
mutex_lock(&pcdev->lock);
|
||||
- ret = ops->pi_get_admin_state(pcdev, id, &admin_state);
|
||||
- if (ret)
|
||||
- goto out;
|
||||
-
|
||||
- if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED ||
|
||||
- admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED)
|
||||
- ret = 1;
|
||||
-
|
||||
-out:
|
||||
+ ret = pse_pi_is_hw_enabled(pcdev, id);
|
||||
mutex_unlock(&pcdev->lock);
|
||||
|
||||
return ret;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,326 @@
|
||||
From 359754013e6a7fc81af6735ebbfedd4a01999f68 Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:08 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Add support for PSE PI priority feature
|
||||
|
||||
This patch extends the PSE callbacks by adding support for the newly
|
||||
introduced pi_set_prio() callback, enabling the configuration of PSE PI
|
||||
priorities. The current port priority is now also included in the status
|
||||
information returned to users.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-9-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 205 +++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 205 insertions(+)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pse-pd/pse.h>
|
||||
+#include <linux/regulator/driver.h>
|
||||
+#include <linux/regulator/machine.h>
|
||||
|
||||
#define PD692X0_PSE_NAME "pd692x0_pse"
|
||||
|
||||
@@ -76,6 +78,8 @@ enum {
|
||||
PD692X0_MSG_GET_PORT_CLASS,
|
||||
PD692X0_MSG_GET_PORT_MEAS,
|
||||
PD692X0_MSG_GET_PORT_PARAM,
|
||||
+ PD692X0_MSG_GET_POWER_BANK,
|
||||
+ PD692X0_MSG_SET_POWER_BANK,
|
||||
|
||||
/* add new message above here */
|
||||
PD692X0_MSG_CNT
|
||||
@@ -95,6 +99,8 @@ struct pd692x0_priv {
|
||||
unsigned long last_cmd_key_time;
|
||||
|
||||
enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS];
|
||||
+ struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS];
|
||||
+ int manager_pw_budget[PD692X0_MAX_MANAGERS];
|
||||
};
|
||||
|
||||
/* Template list of communication messages. The non-null bytes defined here
|
||||
@@ -170,6 +176,16 @@ static const struct pd692x0_msg pd692x0_
|
||||
.data = {0x4e, 0x4e, 0x4e, 0x4e,
|
||||
0x4e, 0x4e, 0x4e, 0x4e},
|
||||
},
|
||||
+ [PD692X0_MSG_GET_POWER_BANK] = {
|
||||
+ .key = PD692X0_KEY_REQ,
|
||||
+ .sub = {0x07, 0x0b, 0x57},
|
||||
+ .data = { 0, 0x4e, 0x4e, 0x4e,
|
||||
+ 0x4e, 0x4e, 0x4e, 0x4e},
|
||||
+ },
|
||||
+ [PD692X0_MSG_SET_POWER_BANK] = {
|
||||
+ .key = PD692X0_KEY_CMD,
|
||||
+ .sub = {0x07, 0x0b, 0x57},
|
||||
+ },
|
||||
};
|
||||
|
||||
static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo)
|
||||
@@ -739,6 +755,29 @@ pd692x0_pi_get_actual_pw(struct pse_cont
|
||||
return (buf.data[0] << 4 | buf.data[1]) * 100;
|
||||
}
|
||||
|
||||
+static int
|
||||
+pd692x0_pi_get_prio(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
+ struct pd692x0_msg msg, buf = {0};
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pd692x0_fw_unavailable(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM];
|
||||
+ msg.sub[2] = id;
|
||||
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ if (!buf.data[2] || buf.data[2] > pcdev->pis_prio_max + 1)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ /* PSE core priority start at 0 */
|
||||
+ return buf.data[2] - 1;
|
||||
+}
|
||||
+
|
||||
static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv)
|
||||
{
|
||||
struct device *dev = &priv->client->dev;
|
||||
@@ -766,6 +805,7 @@ static struct pd692x0_msg_ver pd692x0_ge
|
||||
|
||||
struct pd692x0_manager {
|
||||
struct device_node *port_node[PD692X0_MAX_MANAGER_PORTS];
|
||||
+ struct device_node *node;
|
||||
int nports;
|
||||
};
|
||||
|
||||
@@ -857,6 +897,8 @@ pd692x0_of_get_managers(struct pd692x0_p
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
+ of_node_get(node);
|
||||
+ manager[manager_id].node = node;
|
||||
nmanagers++;
|
||||
}
|
||||
|
||||
@@ -869,6 +911,8 @@ out:
|
||||
of_node_put(manager[i].port_node[j]);
|
||||
manager[i].port_node[j] = NULL;
|
||||
}
|
||||
+ of_node_put(manager[i].node);
|
||||
+ manager[i].node = NULL;
|
||||
}
|
||||
|
||||
of_node_put(node);
|
||||
@@ -876,6 +920,130 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static const struct regulator_ops dummy_ops;
|
||||
+
|
||||
+static struct regulator_dev *
|
||||
+pd692x0_register_manager_regulator(struct device *dev, char *reg_name,
|
||||
+ struct device_node *node)
|
||||
+{
|
||||
+ struct regulator_init_data *rinit_data;
|
||||
+ struct regulator_config rconfig = {0};
|
||||
+ struct regulator_desc *rdesc;
|
||||
+ struct regulator_dev *rdev;
|
||||
+
|
||||
+ rinit_data = devm_kzalloc(dev, sizeof(*rinit_data),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!rinit_data)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL);
|
||||
+ if (!rdesc)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ rdesc->name = reg_name;
|
||||
+ rdesc->type = REGULATOR_VOLTAGE;
|
||||
+ rdesc->ops = &dummy_ops;
|
||||
+ rdesc->owner = THIS_MODULE;
|
||||
+
|
||||
+ rinit_data->supply_regulator = "vmain";
|
||||
+
|
||||
+ rconfig.dev = dev;
|
||||
+ rconfig.init_data = rinit_data;
|
||||
+ rconfig.of_node = node;
|
||||
+
|
||||
+ rdev = devm_regulator_register(dev, rdesc, &rconfig);
|
||||
+ if (IS_ERR(rdev)) {
|
||||
+ dev_err_probe(dev, PTR_ERR(rdev),
|
||||
+ "Failed to register regulator\n");
|
||||
+ return rdev;
|
||||
+ }
|
||||
+
|
||||
+ return rdev;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pd692x0_register_managers_regulator(struct pd692x0_priv *priv,
|
||||
+ const struct pd692x0_manager *manager,
|
||||
+ int nmanagers)
|
||||
+{
|
||||
+ struct device *dev = &priv->client->dev;
|
||||
+ size_t reg_name_len;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Each regulator name len is dev name + 12 char +
|
||||
+ * int max digit number (10) + 1
|
||||
+ */
|
||||
+ reg_name_len = strlen(dev_name(dev)) + 23;
|
||||
+
|
||||
+ for (i = 0; i < nmanagers; i++) {
|
||||
+ struct regulator_dev *rdev;
|
||||
+ char *reg_name;
|
||||
+
|
||||
+ reg_name = devm_kzalloc(dev, reg_name_len, GFP_KERNEL);
|
||||
+ if (!reg_name)
|
||||
+ return -ENOMEM;
|
||||
+ snprintf(reg_name, 26, "pse-%s-manager%d", dev_name(dev), i);
|
||||
+ rdev = pd692x0_register_manager_regulator(dev, reg_name,
|
||||
+ manager[i].node);
|
||||
+ if (IS_ERR(rdev))
|
||||
+ return PTR_ERR(rdev);
|
||||
+
|
||||
+ priv->manager_reg[i] = rdev;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id, int pw)
|
||||
+{
|
||||
+ struct pd692x0_msg msg, buf;
|
||||
+ int ret, pw_mW = pw / 1000;
|
||||
+
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_POWER_BANK];
|
||||
+ msg.data[0] = id;
|
||||
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_POWER_BANK];
|
||||
+ msg.data[0] = id;
|
||||
+ msg.data[1] = pw_mW >> 8;
|
||||
+ msg.data[2] = pw_mW & 0xff;
|
||||
+ msg.data[3] = buf.sub[2];
|
||||
+ msg.data[4] = buf.data[0];
|
||||
+ msg.data[5] = buf.data[1];
|
||||
+ msg.data[6] = buf.data[2];
|
||||
+ msg.data[7] = buf.data[3];
|
||||
+ return pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pd692x0_configure_managers(struct pd692x0_priv *priv, int nmanagers)
|
||||
+{
|
||||
+ int i, ret;
|
||||
+
|
||||
+ for (i = 0; i < nmanagers; i++) {
|
||||
+ struct regulator *supply = priv->manager_reg[i]->supply;
|
||||
+ int pw_budget;
|
||||
+
|
||||
+ pw_budget = regulator_get_unclaimed_power_budget(supply);
|
||||
+ /* Max power budget per manager */
|
||||
+ if (pw_budget > 6000000)
|
||||
+ pw_budget = 6000000;
|
||||
+ ret = regulator_request_power_budget(supply, pw_budget);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ priv->manager_pw_budget[i] = pw_budget;
|
||||
+ ret = pd692x0_conf_manager_power_budget(priv, i, pw_budget);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
pd692x0_set_port_matrix(const struct pse_pi_pairset *pairset,
|
||||
const struct pd692x0_manager *manager,
|
||||
@@ -998,6 +1166,14 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
return ret;
|
||||
|
||||
nmanagers = ret;
|
||||
+ ret = pd692x0_register_managers_regulator(priv, manager, nmanagers);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ ret = pd692x0_configure_managers(priv, nmanagers);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix);
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -1008,8 +1184,14 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
|
||||
out:
|
||||
for (i = 0; i < nmanagers; i++) {
|
||||
+ struct regulator *supply = priv->manager_reg[i]->supply;
|
||||
+
|
||||
+ regulator_free_power_budget(supply,
|
||||
+ priv->manager_pw_budget[i]);
|
||||
+
|
||||
for (j = 0; j < manager[i].nports; j++)
|
||||
of_node_put(manager[i].port_node[j]);
|
||||
+ of_node_put(manager[i].node);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1071,6 +1253,25 @@ static int pd692x0_pi_set_pw_limit(struc
|
||||
return pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
}
|
||||
|
||||
+static int pd692x0_pi_set_prio(struct pse_controller_dev *pcdev, int id,
|
||||
+ unsigned int prio)
|
||||
+{
|
||||
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
+ struct pd692x0_msg msg, buf = {0};
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pd692x0_fw_unavailable(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM];
|
||||
+ msg.sub[2] = id;
|
||||
+ /* Controller priority from 1 to 3 */
|
||||
+ msg.data[4] = prio + 1;
|
||||
+
|
||||
+ return pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
+}
|
||||
+
|
||||
static const struct pse_controller_ops pd692x0_ops = {
|
||||
.setup_pi_matrix = pd692x0_setup_pi_matrix,
|
||||
.pi_get_admin_state = pd692x0_pi_get_admin_state,
|
||||
@@ -1084,6 +1285,8 @@ static const struct pse_controller_ops p
|
||||
.pi_get_pw_limit = pd692x0_pi_get_pw_limit,
|
||||
.pi_set_pw_limit = pd692x0_pi_set_pw_limit,
|
||||
.pi_get_pw_limit_ranges = pd692x0_pi_get_pw_limit_ranges,
|
||||
+ .pi_get_prio = pd692x0_pi_get_prio,
|
||||
+ .pi_set_prio = pd692x0_pi_set_prio,
|
||||
};
|
||||
|
||||
#define PD692X0_FW_LINE_MAX_SZ 0xff
|
||||
@@ -1500,6 +1703,8 @@ static int pd692x0_i2c_probe(struct i2c_
|
||||
priv->pcdev.ops = &pd692x0_ops;
|
||||
priv->pcdev.dev = dev;
|
||||
priv->pcdev.types = ETHTOOL_PSE_C33;
|
||||
+ priv->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_DYNAMIC;
|
||||
+ priv->pcdev.pis_prio_max = 2;
|
||||
ret = devm_pse_controller_register(dev, &priv->pcdev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
@@ -0,0 +1,71 @@
|
||||
From 24a4e3a05dd0eadd0c9585c411880e5dcb6be97f Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:09 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Add support for controller and manager
|
||||
power supplies
|
||||
|
||||
Add support for managing the VDD and VDDA power supplies for the PD692x0
|
||||
PSE controller, as well as the VAUX5 and VAUX3P3 power supplies for the
|
||||
PD6920x PSE managers.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-10-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 20 ++++++++++++++++++++
|
||||
1 file changed, 20 insertions(+)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -976,8 +976,10 @@ pd692x0_register_managers_regulator(stru
|
||||
reg_name_len = strlen(dev_name(dev)) + 23;
|
||||
|
||||
for (i = 0; i < nmanagers; i++) {
|
||||
+ static const char * const regulators[] = { "vaux5", "vaux3p3" };
|
||||
struct regulator_dev *rdev;
|
||||
char *reg_name;
|
||||
+ int ret;
|
||||
|
||||
reg_name = devm_kzalloc(dev, reg_name_len, GFP_KERNEL);
|
||||
if (!reg_name)
|
||||
@@ -988,6 +990,17 @@ pd692x0_register_managers_regulator(stru
|
||||
if (IS_ERR(rdev))
|
||||
return PTR_ERR(rdev);
|
||||
|
||||
+ /* VMAIN is described as main supply for the manager.
|
||||
+ * Add other VAUX power supplies and link them to the
|
||||
+ * virtual device rdev->dev.
|
||||
+ */
|
||||
+ ret = devm_regulator_bulk_get_enable(&rdev->dev,
|
||||
+ ARRAY_SIZE(regulators),
|
||||
+ regulators);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(&rdev->dev, ret,
|
||||
+ "Failed to enable regulators\n");
|
||||
+
|
||||
priv->manager_reg[i] = rdev;
|
||||
}
|
||||
|
||||
@@ -1640,6 +1653,7 @@ static const struct fw_upload_ops pd692x
|
||||
|
||||
static int pd692x0_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
+ static const char * const regulators[] = { "vdd", "vdda" };
|
||||
struct pd692x0_msg msg, buf = {0}, zero = {0};
|
||||
struct device *dev = &client->dev;
|
||||
struct pd692x0_msg_ver ver;
|
||||
@@ -1647,6 +1661,12 @@ static int pd692x0_i2c_probe(struct i2c_
|
||||
struct fw_upload *fwl;
|
||||
int ret;
|
||||
|
||||
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators),
|
||||
+ regulators);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret,
|
||||
+ "Failed to enable regulators\n");
|
||||
+
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(dev, "i2c check functionality failed\n");
|
||||
return -ENXIO;
|
||||
@@ -0,0 +1,392 @@
|
||||
From 56cfc97635e9164395c9242f72746454347155ab Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Tue, 17 Jun 2025 14:12:11 +0200
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Add support for static port priority
|
||||
feature
|
||||
|
||||
This patch enhances PSE callbacks by introducing support for the static
|
||||
port priority feature. It extends interrupt management to handle and report
|
||||
detection, classification, and disconnection events. Additionally, it
|
||||
introduces the pi_get_pw_req() callback, which provides information about
|
||||
the power requested by the Powered Devices.
|
||||
|
||||
Interrupt support is essential for the proper functioning of the TPS23881
|
||||
controller. Without it, after a power-on (PWON), the controller will
|
||||
no longer perform detection and classification. This could lead to
|
||||
potential hazards, such as connecting a non-PoE device after a PoE device,
|
||||
which might result in magic smoke.
|
||||
|
||||
Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-12-78a1a645e2ee@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 244 +++++++++++++++++++++++++++++++---
|
||||
1 file changed, 228 insertions(+), 16 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -20,20 +20,30 @@
|
||||
|
||||
#define TPS23881_REG_IT 0x0
|
||||
#define TPS23881_REG_IT_MASK 0x1
|
||||
+#define TPS23881_REG_IT_DISF BIT(2)
|
||||
+#define TPS23881_REG_IT_DETC BIT(3)
|
||||
+#define TPS23881_REG_IT_CLASC BIT(4)
|
||||
#define TPS23881_REG_IT_IFAULT BIT(5)
|
||||
#define TPS23881_REG_IT_SUPF BIT(7)
|
||||
+#define TPS23881_REG_DET_EVENT 0x5
|
||||
#define TPS23881_REG_FAULT 0x7
|
||||
#define TPS23881_REG_SUPF_EVENT 0xb
|
||||
#define TPS23881_REG_TSD BIT(7)
|
||||
+#define TPS23881_REG_DISC 0xc
|
||||
#define TPS23881_REG_PW_STATUS 0x10
|
||||
#define TPS23881_REG_OP_MODE 0x12
|
||||
+#define TPS23881_REG_DISC_EN 0x13
|
||||
#define TPS23881_OP_MODE_SEMIAUTO 0xaaaa
|
||||
#define TPS23881_REG_DIS_EN 0x13
|
||||
#define TPS23881_REG_DET_CLA_EN 0x14
|
||||
#define TPS23881_REG_GEN_MASK 0x17
|
||||
+#define TPS23881_REG_CLCHE BIT(2)
|
||||
+#define TPS23881_REG_DECHE BIT(3)
|
||||
#define TPS23881_REG_NBITACC BIT(5)
|
||||
#define TPS23881_REG_INTEN BIT(7)
|
||||
#define TPS23881_REG_PW_EN 0x19
|
||||
+#define TPS23881_REG_RESET 0x1a
|
||||
+#define TPS23881_REG_CLRAIN BIT(7)
|
||||
#define TPS23881_REG_2PAIR_POL1 0x1e
|
||||
#define TPS23881_REG_PORT_MAP 0x26
|
||||
#define TPS23881_REG_PORT_POWER 0x29
|
||||
@@ -178,6 +188,7 @@ static int tps23881_pi_enable(struct pse
|
||||
struct i2c_client *client = priv->client;
|
||||
u8 chan;
|
||||
u16 val;
|
||||
+ int ret;
|
||||
|
||||
if (id >= TPS23881_MAX_CHANS)
|
||||
return -ERANGE;
|
||||
@@ -191,7 +202,22 @@ static int tps23881_pi_enable(struct pse
|
||||
BIT(chan % 4));
|
||||
}
|
||||
|
||||
- return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Enable DC disconnect*/
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DISC_EN);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4));
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DISC_EN, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
|
||||
@@ -224,6 +250,17 @@ static int tps23881_pi_disable(struct ps
|
||||
*/
|
||||
mdelay(5);
|
||||
|
||||
+ /* Disable DC disconnect*/
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DISC_EN);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_set_val(ret, chan, 0, 0, BIT(chan % 4));
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DISC_EN, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* Enable detection and classification */
|
||||
ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_CLA_EN);
|
||||
if (ret < 0)
|
||||
@@ -919,6 +956,47 @@ static int tps23881_setup_pi_matrix(stru
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int tps23881_power_class_table[] = {
|
||||
+ -ERANGE,
|
||||
+ 4000,
|
||||
+ 7000,
|
||||
+ 15500,
|
||||
+ 30000,
|
||||
+ 15500,
|
||||
+ 15500,
|
||||
+ -ERANGE,
|
||||
+ 45000,
|
||||
+ 60000,
|
||||
+ 75000,
|
||||
+ 90000,
|
||||
+ 15500,
|
||||
+ 45000,
|
||||
+ -ERANGE,
|
||||
+ -ERANGE,
|
||||
+};
|
||||
+
|
||||
+static int tps23881_pi_get_pw_req(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
|
||||
+ struct i2c_client *client = priv->client;
|
||||
+ u8 reg, chan;
|
||||
+ int ret;
|
||||
+ u16 val;
|
||||
+
|
||||
+ /* For a 4-pair the classification need 5ms to be completed */
|
||||
+ if (priv->port[id].is_4p)
|
||||
+ mdelay(5);
|
||||
+
|
||||
+ chan = priv->port[id].chan[0];
|
||||
+ reg = TPS23881_REG_DISC + (chan % 4);
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_calc_val(ret, chan, 4, 0xf);
|
||||
+ return tps23881_power_class_table[val];
|
||||
+}
|
||||
+
|
||||
static const struct pse_controller_ops tps23881_ops = {
|
||||
.setup_pi_matrix = tps23881_setup_pi_matrix,
|
||||
.pi_enable = tps23881_pi_enable,
|
||||
@@ -931,6 +1009,7 @@ static const struct pse_controller_ops t
|
||||
.pi_get_pw_limit = tps23881_pi_get_pw_limit,
|
||||
.pi_set_pw_limit = tps23881_pi_set_pw_limit,
|
||||
.pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges,
|
||||
+ .pi_get_pw_req = tps23881_pi_get_pw_req,
|
||||
};
|
||||
|
||||
static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
|
||||
@@ -1088,17 +1167,113 @@ static void tps23881_irq_event_over_temp
|
||||
}
|
||||
}
|
||||
|
||||
-static void tps23881_irq_event_over_current(struct tps23881_priv *priv,
|
||||
- u16 reg_val,
|
||||
- unsigned long *notifs,
|
||||
- unsigned long *notifs_mask)
|
||||
+static int tps23881_irq_event_over_current(struct tps23881_priv *priv,
|
||||
+ u16 reg_val,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
{
|
||||
+ int i, ret;
|
||||
u8 chans;
|
||||
|
||||
chans = tps23881_irq_export_chans_helper(reg_val, 0);
|
||||
+ if (!chans)
|
||||
+ return 0;
|
||||
+
|
||||
+ tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask,
|
||||
+ ETHTOOL_PSE_EVENT_OVER_CURRENT |
|
||||
+ ETHTOOL_C33_PSE_EVENT_DISCONNECTION);
|
||||
+
|
||||
+ /* Over Current event resets the power limit registers so we need
|
||||
+ * to configured it again.
|
||||
+ */
|
||||
+ for_each_set_bit(i, notifs_mask, priv->pcdev.nr_lines) {
|
||||
+ if (priv->port[i].pw_pol < 0)
|
||||
+ continue;
|
||||
+
|
||||
+ ret = tps23881_pi_enable_manual_pol(priv, i);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Set power policy */
|
||||
+ ret = tps23881_pi_set_pw_pol_limit(priv, i,
|
||||
+ priv->port[i].pw_pol,
|
||||
+ priv->port[i].is_4p);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void tps23881_irq_event_disconnection(struct tps23881_priv *priv,
|
||||
+ u16 reg_val,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ u8 chans;
|
||||
+
|
||||
+ chans = tps23881_irq_export_chans_helper(reg_val, 4);
|
||||
if (chans)
|
||||
tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask,
|
||||
- ETHTOOL_PSE_EVENT_OVER_CURRENT);
|
||||
+ ETHTOOL_C33_PSE_EVENT_DISCONNECTION);
|
||||
+}
|
||||
+
|
||||
+static int tps23881_irq_event_detection(struct tps23881_priv *priv,
|
||||
+ u16 reg_val,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ enum ethtool_pse_event event;
|
||||
+ int reg, ret, i, val;
|
||||
+ unsigned long chans;
|
||||
+
|
||||
+ chans = tps23881_irq_export_chans_helper(reg_val, 0);
|
||||
+ for_each_set_bit(i, &chans, TPS23881_MAX_CHANS) {
|
||||
+ reg = TPS23881_REG_DISC + (i % 4);
|
||||
+ ret = i2c_smbus_read_word_data(priv->client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_calc_val(ret, i, 0, 0xf);
|
||||
+ /* If detection valid */
|
||||
+ if (val == 0x4)
|
||||
+ event = ETHTOOL_C33_PSE_EVENT_DETECTION;
|
||||
+ else
|
||||
+ event = ETHTOOL_C33_PSE_EVENT_DISCONNECTION;
|
||||
+
|
||||
+ tps23881_set_notifs_helper(priv, BIT(i), notifs,
|
||||
+ notifs_mask, event);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int tps23881_irq_event_classification(struct tps23881_priv *priv,
|
||||
+ u16 reg_val,
|
||||
+ unsigned long *notifs,
|
||||
+ unsigned long *notifs_mask)
|
||||
+{
|
||||
+ int reg, ret, val, i;
|
||||
+ unsigned long chans;
|
||||
+
|
||||
+ chans = tps23881_irq_export_chans_helper(reg_val, 4);
|
||||
+ for_each_set_bit(i, &chans, TPS23881_MAX_CHANS) {
|
||||
+ reg = TPS23881_REG_DISC + (i % 4);
|
||||
+ ret = i2c_smbus_read_word_data(priv->client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = tps23881_calc_val(ret, i, 4, 0xf);
|
||||
+ /* Do not report classification event for unknown class */
|
||||
+ if (!val || val == 0x8 || val == 0xf)
|
||||
+ continue;
|
||||
+
|
||||
+ tps23881_set_notifs_helper(priv, BIT(i), notifs,
|
||||
+ notifs_mask,
|
||||
+ ETHTOOL_C33_PSE_EVENT_CLASSIFICATION);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg,
|
||||
@@ -1106,7 +1281,7 @@ static int tps23881_irq_event_handler(st
|
||||
unsigned long *notifs_mask)
|
||||
{
|
||||
struct i2c_client *client = priv->client;
|
||||
- int ret;
|
||||
+ int ret, val;
|
||||
|
||||
/* The Supply event bit is repeated twice so we only need to read
|
||||
* the one from the first byte.
|
||||
@@ -1118,13 +1293,36 @@ static int tps23881_irq_event_handler(st
|
||||
tps23881_irq_event_over_temp(priv, ret, notifs, notifs_mask);
|
||||
}
|
||||
|
||||
- if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8)) {
|
||||
+ if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8 |
|
||||
+ TPS23881_REG_IT_DISF | TPS23881_REG_IT_DISF << 8)) {
|
||||
ret = i2c_smbus_read_word_data(client, TPS23881_REG_FAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
- tps23881_irq_event_over_current(priv, ret, notifs, notifs_mask);
|
||||
+ ret = tps23881_irq_event_over_current(priv, ret, notifs,
|
||||
+ notifs_mask);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ tps23881_irq_event_disconnection(priv, ret, notifs, notifs_mask);
|
||||
}
|
||||
|
||||
+ if (reg & (TPS23881_REG_IT_DETC | TPS23881_REG_IT_DETC << 8 |
|
||||
+ TPS23881_REG_IT_CLASC | TPS23881_REG_IT_CLASC << 8)) {
|
||||
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_EVENT);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = ret;
|
||||
+ ret = tps23881_irq_event_detection(priv, val, notifs,
|
||||
+ notifs_mask);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = tps23881_irq_event_classification(priv, val, notifs,
|
||||
+ notifs_mask);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1178,7 +1376,14 @@ static int tps23881_setup_irq(struct tps
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
- val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF;
|
||||
+ if (!irq) {
|
||||
+ dev_err(&client->dev, "interrupt is missing");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF |
|
||||
+ TPS23881_REG_IT_DETC | TPS23881_REG_IT_CLASC |
|
||||
+ TPS23881_REG_IT_DISF;
|
||||
val |= val << 8;
|
||||
ret = i2c_smbus_write_word_data(client, TPS23881_REG_IT_MASK, val);
|
||||
if (ret)
|
||||
@@ -1188,11 +1393,19 @@ static int tps23881_setup_irq(struct tps
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- val = (u16)(ret | TPS23881_REG_INTEN | TPS23881_REG_INTEN << 8);
|
||||
+ val = TPS23881_REG_INTEN | TPS23881_REG_CLCHE | TPS23881_REG_DECHE;
|
||||
+ val |= val << 8;
|
||||
+ val |= (u16)ret;
|
||||
ret = i2c_smbus_write_word_data(client, TPS23881_REG_GEN_MASK, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
+ /* Reset interrupts registers */
|
||||
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_RESET,
|
||||
+ TPS23881_REG_CLRAIN);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
return devm_pse_irq_helper(&priv->pcdev, irq, 0, &irq_desc);
|
||||
}
|
||||
|
||||
@@ -1270,17 +1483,16 @@ static int tps23881_i2c_probe(struct i2c
|
||||
priv->pcdev.dev = dev;
|
||||
priv->pcdev.types = ETHTOOL_PSE_C33;
|
||||
priv->pcdev.nr_lines = TPS23881_MAX_CHANS;
|
||||
+ priv->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_STATIC;
|
||||
ret = devm_pse_controller_register(dev, &priv->pcdev);
|
||||
if (ret) {
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register PSE controller\n");
|
||||
}
|
||||
|
||||
- if (client->irq) {
|
||||
- ret = tps23881_setup_irq(priv, client->irq);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- }
|
||||
+ ret = tps23881_setup_irq(priv, client->irq);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
From d12b3dc106090b358fb67b7c0c717a0884327ddf Mon Sep 17 00:00:00 2001
|
||||
From: Arnd Bergmann <arnd@arndb.de>
|
||||
Date: Wed, 9 Jul 2025 17:32:04 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: reduce stack usage in
|
||||
pd692x0_setup_pi_matrix
|
||||
|
||||
The pd692x0_manager array in this function is really too big to fit on the
|
||||
stack, though this never triggered a warning until a recent patch made
|
||||
it slightly bigger:
|
||||
|
||||
drivers/net/pse-pd/pd692x0.c: In function 'pd692x0_setup_pi_matrix':
|
||||
drivers/net/pse-pd/pd692x0.c:1210:1: error: the frame size of 1584 bytes is larger than 1536 bytes [-Werror=frame-larger-than=]
|
||||
|
||||
Change the function to dynamically allocate the array here.
|
||||
|
||||
Fixes: 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature")
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Link: https://patch.msgid.link/20250709153210.1920125-1-arnd@kernel.org
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -860,7 +860,7 @@ out:
|
||||
|
||||
static int
|
||||
pd692x0_of_get_managers(struct pd692x0_priv *priv,
|
||||
- struct pd692x0_manager manager[PD692X0_MAX_MANAGERS])
|
||||
+ struct pd692x0_manager *manager)
|
||||
{
|
||||
struct device_node *managers_node, *node;
|
||||
int ret, nmanagers, i, j;
|
||||
@@ -1164,7 +1164,7 @@ pd692x0_write_ports_matrix(struct pd692x
|
||||
|
||||
static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
{
|
||||
- struct pd692x0_manager manager[PD692X0_MAX_MANAGERS] = {0};
|
||||
+ struct pd692x0_manager *manager __free(kfree) = NULL;
|
||||
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
|
||||
int ret, i, j, nmanagers;
|
||||
@@ -1174,6 +1174,10 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
priv->fw_state != PD692X0_FW_COMPLETE)
|
||||
return 0;
|
||||
|
||||
+ manager = kcalloc(PD692X0_MAX_MANAGERS, sizeof(*manager), GFP_KERNEL);
|
||||
+ if (!manager)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
ret = pd692x0_of_get_managers(priv, manager);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -0,0 +1,121 @@
|
||||
From 1c67f9c54cdc70627e3f6472b89cd3d895df974c Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Wed, 20 Aug 2025 15:27:07 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Fix power budget leak in manager setup
|
||||
error path
|
||||
|
||||
Fix a resource leak where manager power budgets were freed on both
|
||||
success and error paths during manager setup. Power budgets should
|
||||
only be freed on error paths after regulator registration or during
|
||||
driver removal.
|
||||
|
||||
Refactor cleanup logic by extracting OF node cleanup and power budget
|
||||
freeing into separate helper functions for better maintainability.
|
||||
|
||||
Fixes: 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature")
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Link: https://patch.msgid.link/20250820132708.837255-1-kory.maincent@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 59 +++++++++++++++++++++++++++---------
|
||||
1 file changed, 44 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -1162,12 +1162,44 @@ pd692x0_write_ports_matrix(struct pd692x
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void pd692x0_of_put_managers(struct pd692x0_priv *priv,
|
||||
+ struct pd692x0_manager *manager,
|
||||
+ int nmanagers)
|
||||
+{
|
||||
+ int i, j;
|
||||
+
|
||||
+ for (i = 0; i < nmanagers; i++) {
|
||||
+ for (j = 0; j < manager[i].nports; j++)
|
||||
+ of_node_put(manager[i].port_node[j]);
|
||||
+ of_node_put(manager[i].node);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void pd692x0_managers_free_pw_budget(struct pd692x0_priv *priv)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < PD692X0_MAX_MANAGERS; i++) {
|
||||
+ struct regulator *supply;
|
||||
+
|
||||
+ if (!priv->manager_reg[i] || !priv->manager_pw_budget[i])
|
||||
+ continue;
|
||||
+
|
||||
+ supply = priv->manager_reg[i]->supply;
|
||||
+ if (!supply)
|
||||
+ continue;
|
||||
+
|
||||
+ regulator_free_power_budget(supply,
|
||||
+ priv->manager_pw_budget[i]);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
{
|
||||
struct pd692x0_manager *manager __free(kfree) = NULL;
|
||||
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
|
||||
- int ret, i, j, nmanagers;
|
||||
+ int ret, nmanagers;
|
||||
|
||||
/* Should we flash the port matrix */
|
||||
if (priv->fw_state != PD692X0_FW_OK &&
|
||||
@@ -1185,31 +1217,27 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
nmanagers = ret;
|
||||
ret = pd692x0_register_managers_regulator(priv, manager, nmanagers);
|
||||
if (ret)
|
||||
- goto out;
|
||||
+ goto err_of_managers;
|
||||
|
||||
ret = pd692x0_configure_managers(priv, nmanagers);
|
||||
if (ret)
|
||||
- goto out;
|
||||
+ goto err_of_managers;
|
||||
|
||||
ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix);
|
||||
if (ret)
|
||||
- goto out;
|
||||
+ goto err_managers_req_pw;
|
||||
|
||||
ret = pd692x0_write_ports_matrix(priv, port_matrix);
|
||||
if (ret)
|
||||
- goto out;
|
||||
-
|
||||
-out:
|
||||
- for (i = 0; i < nmanagers; i++) {
|
||||
- struct regulator *supply = priv->manager_reg[i]->supply;
|
||||
+ goto err_managers_req_pw;
|
||||
|
||||
- regulator_free_power_budget(supply,
|
||||
- priv->manager_pw_budget[i]);
|
||||
+ pd692x0_of_put_managers(priv, manager, nmanagers);
|
||||
+ return 0;
|
||||
|
||||
- for (j = 0; j < manager[i].nports; j++)
|
||||
- of_node_put(manager[i].port_node[j]);
|
||||
- of_node_put(manager[i].node);
|
||||
- }
|
||||
+err_managers_req_pw:
|
||||
+ pd692x0_managers_free_pw_budget(priv);
|
||||
+err_of_managers:
|
||||
+ pd692x0_of_put_managers(priv, manager, nmanagers);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1748,6 +1776,7 @@ static void pd692x0_i2c_remove(struct i2
|
||||
{
|
||||
struct pd692x0_priv *priv = i2c_get_clientdata(client);
|
||||
|
||||
+ pd692x0_managers_free_pw_budget(priv);
|
||||
firmware_upload_unregister(priv->fwl);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
From 7ef353879f714602b43f98662069f4fb86536761 Mon Sep 17 00:00:00 2001
|
||||
From: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Date: Wed, 20 Aug 2025 15:33:21 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Skip power budget configuration when
|
||||
undefined
|
||||
|
||||
If the power supply's power budget is not defined in the device tree,
|
||||
the current code still requests power and configures the PSE manager
|
||||
with a 0W power limit, which is undesirable behavior.
|
||||
|
||||
Skip power budget configuration entirely when the budget is zero,
|
||||
avoiding unnecessary power requests and preventing invalid 0W limits
|
||||
from being set on the PSE manager.
|
||||
|
||||
Fixes: 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature")
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20250820133321.841054-1-kory.maincent@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -1041,6 +1041,10 @@ pd692x0_configure_managers(struct pd692x
|
||||
int pw_budget;
|
||||
|
||||
pw_budget = regulator_get_unclaimed_power_budget(supply);
|
||||
+ if (!pw_budget)
|
||||
+ /* Do nothing if no power budget */
|
||||
+ continue;
|
||||
+
|
||||
/* Max power budget per manager */
|
||||
if (pw_budget > 6000000)
|
||||
pw_budget = 6000000;
|
||||
@@ -0,0 +1,636 @@
|
||||
From a2317231df4b22e6634fe3d8645e7cef848acf49 Mon Sep 17 00:00:00 2001
|
||||
From: Piotr Kubik <piotr.kubik@adtran.com>
|
||||
Date: Tue, 26 Aug 2025 14:41:58 +0000
|
||||
Subject: [PATCH] net: pse-pd: Add Si3474 PSE controller driver
|
||||
|
||||
Add a driver for the Skyworks Si3474 I2C Power Sourcing Equipment
|
||||
controller.
|
||||
|
||||
Driver supports basic features of Si3474 IC:
|
||||
- get port status,
|
||||
- get port power,
|
||||
- get port voltage,
|
||||
- enable/disable port power.
|
||||
|
||||
Only 4p configurations are supported at this moment.
|
||||
|
||||
Signed-off-by: Piotr Kubik <piotr.kubik@adtran.com>
|
||||
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Link: https://patch.msgid.link/9b72c8cd-c8d3-4053-9c80-671b9481d166@adtran.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/Kconfig | 11 +
|
||||
drivers/net/pse-pd/Makefile | 1 +
|
||||
drivers/net/pse-pd/si3474.c | 578 ++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 590 insertions(+)
|
||||
create mode 100644 drivers/net/pse-pd/si3474.c
|
||||
--- a/drivers/net/pse-pd/Kconfig
|
||||
+++ b/drivers/net/pse-pd/Kconfig
|
||||
@@ -32,6 +32,17 @@ config PSE_PD692X0
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pd692x0.
|
||||
|
||||
+config PSE_SI3474
|
||||
+ tristate "Si3474 PSE controller"
|
||||
+ depends on I2C
|
||||
+ help
|
||||
+ This module provides support for Si3474 regulator based Ethernet
|
||||
+ Power Sourcing Equipment.
|
||||
+ Only 4-pair PSE configurations are supported.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the
|
||||
+ module will be called si3474.
|
||||
+
|
||||
config PSE_TPS23881
|
||||
tristate "TPS23881 PSE controller"
|
||||
depends on I2C
|
||||
--- a/drivers/net/pse-pd/Makefile
|
||||
+++ b/drivers/net/pse-pd/Makefile
|
||||
@@ -5,4 +5,5 @@ obj-$(CONFIG_PSE_CONTROLLER) += pse_core
|
||||
|
||||
obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o
|
||||
obj-$(CONFIG_PSE_PD692X0) += pd692x0.o
|
||||
+obj-$(CONFIG_PSE_SI3474) += si3474.o
|
||||
obj-$(CONFIG_PSE_TPS23881) += tps23881.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/pse-pd/si3474.c
|
||||
@@ -0,0 +1,578 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * Driver for the Skyworks Si3474 PoE PSE Controller
|
||||
+ *
|
||||
+ * Chip Architecture & Terminology:
|
||||
+ *
|
||||
+ * The Si3474 is a single-chip PoE PSE controller managing 8 physical power
|
||||
+ * delivery channels. Internally, it's structured into two logical "Quads".
|
||||
+ *
|
||||
+ * Quad 0: Manages physical channels ('ports' in datasheet) 0, 1, 2, 3
|
||||
+ * Quad 1: Manages physical channels ('ports' in datasheet) 4, 5, 6, 7
|
||||
+ *
|
||||
+ * Each Quad is accessed via a separate I2C address. The base address range is
|
||||
+ * set by hardware pins A1-A4, and the specific address selects Quad 0 (usually
|
||||
+ * the lower/even address) or Quad 1 (usually the higher/odd address).
|
||||
+ * See datasheet Table 2.2 for the address mapping.
|
||||
+ *
|
||||
+ * While the Quads manage channel-specific operations, the Si3474 package has
|
||||
+ * several resources shared across the entire chip:
|
||||
+ * - Single RESETb input pin.
|
||||
+ * - Single INTb output pin (signals interrupts from *either* Quad).
|
||||
+ * - Single OSS input pin (Emergency Shutdown).
|
||||
+ * - Global I2C Address (0x7F) used for firmware updates.
|
||||
+ * - Global status monitoring (Temperature, VDD/VPWR Undervoltage Lockout).
|
||||
+ *
|
||||
+ * Driver Architecture:
|
||||
+ *
|
||||
+ * To handle the mix of per-Quad access and shared resources correctly, this
|
||||
+ * driver treats the entire Si3474 package as one logical device. The driver
|
||||
+ * instance associated with the primary I2C address (Quad 0) takes ownership.
|
||||
+ * It discovers and manages the I2C client for the secondary address (Quad 1).
|
||||
+ * This primary instance handles shared resources like IRQ management and
|
||||
+ * registers a single PSE controller device representing all logical PIs.
|
||||
+ * Internal functions route I2C commands to the appropriate Quad's i2c_client
|
||||
+ * based on the target channel or PI.
|
||||
+ *
|
||||
+ * Terminology Mapping:
|
||||
+ *
|
||||
+ * - "PI" (Power Interface): Refers to the logical PSE port as defined by
|
||||
+ * IEEE 802.3 (typically corresponds to an RJ45 connector). This is the
|
||||
+ * `id` (0-7) used in the pse_controller_ops.
|
||||
+ * - "Channel": Refers to one of the 8 physical power control paths within
|
||||
+ * the Si3474 chip itself (hardware channels 0-7). This terminology is
|
||||
+ * used internally within the driver to avoid confusion with 'ports'.
|
||||
+ * - "Quad": One of the two internal 4-channel management units within the
|
||||
+ * Si3474, each accessed via its own I2C address.
|
||||
+ *
|
||||
+ * Relationship:
|
||||
+ * - A 2-Pair PoE PI uses 1 Channel.
|
||||
+ * - A 4-Pair PoE PI uses 2 Channels.
|
||||
+ *
|
||||
+ * ASCII Schematic:
|
||||
+ *
|
||||
+ * +-----------------------------------------------------+
|
||||
+ * | Si3474 Chip |
|
||||
+ * | |
|
||||
+ * | +---------------------+ +---------------------+ |
|
||||
+ * | | Quad 0 | | Quad 1 | |
|
||||
+ * | | Channels 0, 1, 2, 3 | | Channels 4, 5, 6, 7 | |
|
||||
+ * | +----------^----------+ +-------^-------------+ |
|
||||
+ * | I2C Addr 0 | | I2C Addr 1 |
|
||||
+ * | +------------------------+ |
|
||||
+ * | (Primary Driver Instance) (Managed by Primary) |
|
||||
+ * | |
|
||||
+ * | Shared Resources (affect whole chip): |
|
||||
+ * | - Single INTb Output -> Handled by Primary |
|
||||
+ * | - Single RESETb Input |
|
||||
+ * | - Single OSS Input -> Handled by Primary |
|
||||
+ * | - Global I2C Addr (0x7F) for Firmware Update |
|
||||
+ * | - Global Status (Temp, VDD/VPWR UVLO) |
|
||||
+ * +-----------------------------------------------------+
|
||||
+ * | | | | | | | |
|
||||
+ * Ch0 Ch1 Ch2 Ch3 Ch4 Ch5 Ch6 Ch7 (Physical Channels)
|
||||
+ *
|
||||
+ * Example Mapping (Logical PI to Physical Channel(s)):
|
||||
+ * * 2-Pair Mode (8 PIs):
|
||||
+ * PI 0 -> Ch 0
|
||||
+ * PI 1 -> Ch 1
|
||||
+ * ...
|
||||
+ * PI 7 -> Ch 7
|
||||
+ * * 4-Pair Mode (4 PIs):
|
||||
+ * PI 0 -> Ch 0 + Ch 1 (Managed via Quad 0 Addr)
|
||||
+ * PI 1 -> Ch 2 + Ch 3 (Managed via Quad 0 Addr)
|
||||
+ * PI 2 -> Ch 4 + Ch 5 (Managed via Quad 1 Addr)
|
||||
+ * PI 3 -> Ch 6 + Ch 7 (Managed via Quad 1 Addr)
|
||||
+ * (Note: Actual mapping depends on Device Tree and PORT_REMAP config)
|
||||
+ */
|
||||
+
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pse-pd/pse.h>
|
||||
+
|
||||
+#define SI3474_MAX_CHANS 8
|
||||
+
|
||||
+#define MANUFACTURER_ID 0x08
|
||||
+#define IC_ID 0x05
|
||||
+#define SI3474_DEVICE_ID (MANUFACTURER_ID << 3 | IC_ID)
|
||||
+
|
||||
+/* Misc registers */
|
||||
+#define VENDOR_IC_ID_REG 0x1B
|
||||
+#define TEMPERATURE_REG 0x2C
|
||||
+#define FIRMWARE_REVISION_REG 0x41
|
||||
+#define CHIP_REVISION_REG 0x43
|
||||
+
|
||||
+/* Main status registers */
|
||||
+#define POWER_STATUS_REG 0x10
|
||||
+#define PORT_MODE_REG 0x12
|
||||
+#define DETECT_CLASS_ENABLE_REG 0x14
|
||||
+
|
||||
+/* PORTn Current */
|
||||
+#define PORT1_CURRENT_LSB_REG 0x30
|
||||
+
|
||||
+/* PORTn Current [mA], return in [nA] */
|
||||
+/* 1000 * ((PORTn_CURRENT_MSB << 8) + PORTn_CURRENT_LSB) / 16384 */
|
||||
+#define SI3474_NA_STEP (1000 * 1000 * 1000 / 16384)
|
||||
+
|
||||
+/* VPWR Voltage */
|
||||
+#define VPWR_LSB_REG 0x2E
|
||||
+#define VPWR_MSB_REG 0x2F
|
||||
+
|
||||
+/* PORTn Voltage */
|
||||
+#define PORT1_VOLTAGE_LSB_REG 0x32
|
||||
+
|
||||
+/* VPWR Voltage [V], return in [uV] */
|
||||
+/* 60 * (( VPWR_MSB << 8) + VPWR_LSB) / 16384 */
|
||||
+#define SI3474_UV_STEP (1000 * 1000 * 60 / 16384)
|
||||
+
|
||||
+/* Helper macros */
|
||||
+#define CHAN_IDX(chan) ((chan) % 4)
|
||||
+#define CHAN_BIT(chan) BIT(CHAN_IDX(chan))
|
||||
+#define CHAN_UPPER_BIT(chan) BIT(CHAN_IDX(chan) + 4)
|
||||
+
|
||||
+#define CHAN_MASK(chan) (0x03U << (2 * CHAN_IDX(chan)))
|
||||
+#define CHAN_REG(base, chan) ((base) + (CHAN_IDX(chan) * 4))
|
||||
+
|
||||
+struct si3474_pi_desc {
|
||||
+ u8 chan[2];
|
||||
+ bool is_4p;
|
||||
+};
|
||||
+
|
||||
+struct si3474_priv {
|
||||
+ struct i2c_client *client[2];
|
||||
+ struct pse_controller_dev pcdev;
|
||||
+ struct device_node *np;
|
||||
+ struct si3474_pi_desc pi[SI3474_MAX_CHANS];
|
||||
+};
|
||||
+
|
||||
+static struct si3474_priv *to_si3474_priv(struct pse_controller_dev *pcdev)
|
||||
+{
|
||||
+ return container_of(pcdev, struct si3474_priv, pcdev);
|
||||
+}
|
||||
+
|
||||
+static void si3474_get_channels(struct si3474_priv *priv, int id,
|
||||
+ u8 *chan0, u8 *chan1)
|
||||
+{
|
||||
+ *chan0 = priv->pi[id].chan[0];
|
||||
+ *chan1 = priv->pi[id].chan[1];
|
||||
+}
|
||||
+
|
||||
+static struct i2c_client *si3474_get_chan_client(struct si3474_priv *priv,
|
||||
+ u8 chan)
|
||||
+{
|
||||
+ return (chan < 4) ? priv->client[0] : priv->client[1];
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_admin_state *admin_state)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ struct i2c_client *client;
|
||||
+ bool is_enabled;
|
||||
+ u8 chan0, chan1;
|
||||
+ s32 ret;
|
||||
+
|
||||
+ si3474_get_channels(priv, id, &chan0, &chan1);
|
||||
+ client = si3474_get_chan_client(priv, chan0);
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(client, PORT_MODE_REG);
|
||||
+ if (ret < 0) {
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ is_enabled = ret & (CHAN_MASK(chan0) | CHAN_MASK(chan1));
|
||||
+
|
||||
+ if (is_enabled)
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
|
||||
+ else
|
||||
+ admin_state->c33_admin_state =
|
||||
+ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
|
||||
+ struct pse_pw_status *pw_status)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ struct i2c_client *client;
|
||||
+ bool delivering;
|
||||
+ u8 chan0, chan1;
|
||||
+ s32 ret;
|
||||
+
|
||||
+ si3474_get_channels(priv, id, &chan0, &chan1);
|
||||
+ client = si3474_get_chan_client(priv, chan0);
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(client, POWER_STATUS_REG);
|
||||
+ if (ret < 0) {
|
||||
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ delivering = ret & (CHAN_UPPER_BIT(chan0) | CHAN_UPPER_BIT(chan1));
|
||||
+
|
||||
+ if (delivering)
|
||||
+ pw_status->c33_pw_status =
|
||||
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
|
||||
+ else
|
||||
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int si3474_get_of_channels(struct si3474_priv *priv)
|
||||
+{
|
||||
+ struct pse_pi *pi;
|
||||
+ u32 chan_id;
|
||||
+ u8 pi_no;
|
||||
+ s32 ret;
|
||||
+
|
||||
+ for (pi_no = 0; pi_no < SI3474_MAX_CHANS; pi_no++) {
|
||||
+ pi = &priv->pcdev.pi[pi_no];
|
||||
+ bool pairset_found = false;
|
||||
+ u8 pairset_no;
|
||||
+
|
||||
+ for (pairset_no = 0; pairset_no < 2; pairset_no++) {
|
||||
+ if (!pi->pairset[pairset_no].np)
|
||||
+ continue;
|
||||
+
|
||||
+ pairset_found = true;
|
||||
+
|
||||
+ ret = of_property_read_u32(pi->pairset[pairset_no].np,
|
||||
+ "reg", &chan_id);
|
||||
+ if (ret) {
|
||||
+ dev_err(&priv->client[0]->dev,
|
||||
+ "Failed to read channel reg property\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ if (chan_id > SI3474_MAX_CHANS) {
|
||||
+ dev_err(&priv->client[0]->dev,
|
||||
+ "Incorrect channel number: %d\n", chan_id);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ priv->pi[pi_no].chan[pairset_no] = chan_id;
|
||||
+ /* Mark as 4-pair if second pairset is present */
|
||||
+ priv->pi[pi_no].is_4p = (pairset_no == 1);
|
||||
+ }
|
||||
+
|
||||
+ if (pairset_found && !priv->pi[pi_no].is_4p) {
|
||||
+ dev_err(&priv->client[0]->dev,
|
||||
+ "Second pairset is missing for PI %pOF, only 4p configs are supported\n",
|
||||
+ pi->np);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int si3474_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ s32 ret;
|
||||
+
|
||||
+ ret = si3474_get_of_channels(priv);
|
||||
+ if (ret < 0)
|
||||
+ dev_warn(&priv->client[0]->dev,
|
||||
+ "Unable to parse DT PSE power interface matrix\n");
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_enable(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ struct i2c_client *client;
|
||||
+ u8 chan0, chan1;
|
||||
+ s32 ret;
|
||||
+ u8 val;
|
||||
+
|
||||
+ si3474_get_channels(priv, id, &chan0, &chan1);
|
||||
+ client = si3474_get_chan_client(priv, chan0);
|
||||
+
|
||||
+ /* Release PI from shutdown */
|
||||
+ ret = i2c_smbus_read_byte_data(client, PORT_MODE_REG);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = (u8)ret;
|
||||
+ val |= CHAN_MASK(chan0);
|
||||
+ val |= CHAN_MASK(chan1);
|
||||
+
|
||||
+ ret = i2c_smbus_write_byte_data(client, PORT_MODE_REG, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* DETECT_CLASS_ENABLE must be set when using AUTO mode,
|
||||
+ * otherwise PI does not power up - datasheet section 2.10.2
|
||||
+ */
|
||||
+ val = CHAN_BIT(chan0) | CHAN_UPPER_BIT(chan0) |
|
||||
+ CHAN_BIT(chan1) | CHAN_UPPER_BIT(chan1);
|
||||
+
|
||||
+ ret = i2c_smbus_write_byte_data(client, DETECT_CLASS_ENABLE_REG, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_disable(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ struct i2c_client *client;
|
||||
+ u8 chan0, chan1;
|
||||
+ s32 ret;
|
||||
+ u8 val;
|
||||
+
|
||||
+ si3474_get_channels(priv, id, &chan0, &chan1);
|
||||
+ client = si3474_get_chan_client(priv, chan0);
|
||||
+
|
||||
+ /* Set PI in shutdown mode */
|
||||
+ ret = i2c_smbus_read_byte_data(client, PORT_MODE_REG);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = (u8)ret;
|
||||
+ val &= ~CHAN_MASK(chan0);
|
||||
+ val &= ~CHAN_MASK(chan1);
|
||||
+
|
||||
+ ret = i2c_smbus_write_byte_data(client, PORT_MODE_REG, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_get_chan_current(struct si3474_priv *priv, u8 chan)
|
||||
+{
|
||||
+ struct i2c_client *client;
|
||||
+ u64 tmp_64;
|
||||
+ s32 ret;
|
||||
+ u8 reg;
|
||||
+
|
||||
+ client = si3474_get_chan_client(priv, chan);
|
||||
+
|
||||
+ /* Registers 0x30 to 0x3d */
|
||||
+ reg = CHAN_REG(PORT1_CURRENT_LSB_REG, chan);
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ tmp_64 = ret * SI3474_NA_STEP;
|
||||
+
|
||||
+ /* uA = nA / 1000 */
|
||||
+ tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000);
|
||||
+ return (int)tmp_64;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_get_chan_voltage(struct si3474_priv *priv, u8 chan)
|
||||
+{
|
||||
+ struct i2c_client *client;
|
||||
+ s32 ret;
|
||||
+ u32 val;
|
||||
+ u8 reg;
|
||||
+
|
||||
+ client = si3474_get_chan_client(priv, chan);
|
||||
+
|
||||
+ /* Registers 0x32 to 0x3f */
|
||||
+ reg = CHAN_REG(PORT1_VOLTAGE_LSB_REG, chan);
|
||||
+
|
||||
+ ret = i2c_smbus_read_word_data(client, reg);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = ret * SI3474_UV_STEP;
|
||||
+
|
||||
+ return (int)val;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_get_voltage(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ struct i2c_client *client;
|
||||
+ u8 chan0, chan1;
|
||||
+ s32 ret;
|
||||
+
|
||||
+ si3474_get_channels(priv, id, &chan0, &chan1);
|
||||
+ client = si3474_get_chan_client(priv, chan0);
|
||||
+
|
||||
+ /* Check which channels are enabled*/
|
||||
+ ret = i2c_smbus_read_byte_data(client, POWER_STATUS_REG);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Take voltage from the first enabled channel */
|
||||
+ if (ret & CHAN_BIT(chan0))
|
||||
+ ret = si3474_pi_get_chan_voltage(priv, chan0);
|
||||
+ else if (ret & CHAN_BIT(chan1))
|
||||
+ ret = si3474_pi_get_chan_voltage(priv, chan1);
|
||||
+ else
|
||||
+ /* 'should' be no voltage in this case */
|
||||
+ return 0;
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int si3474_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)
|
||||
+{
|
||||
+ struct si3474_priv *priv = to_si3474_priv(pcdev);
|
||||
+ u8 chan0, chan1;
|
||||
+ u32 uV, uA;
|
||||
+ u64 tmp_64;
|
||||
+ s32 ret;
|
||||
+
|
||||
+ ret = si3474_pi_get_voltage(&priv->pcdev, id);
|
||||
+
|
||||
+ /* Do not read currents if voltage is 0 */
|
||||
+ if (ret <= 0)
|
||||
+ return ret;
|
||||
+ uV = ret;
|
||||
+
|
||||
+ si3474_get_channels(priv, id, &chan0, &chan1);
|
||||
+
|
||||
+ ret = si3474_pi_get_chan_current(priv, chan0);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ uA = ret;
|
||||
+
|
||||
+ ret = si3474_pi_get_chan_current(priv, chan1);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ uA += ret;
|
||||
+
|
||||
+ tmp_64 = uV;
|
||||
+ tmp_64 *= uA;
|
||||
+ /* mW = uV * uA / 1000000000 */
|
||||
+ return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000);
|
||||
+}
|
||||
+
|
||||
+static const struct pse_controller_ops si3474_ops = {
|
||||
+ .setup_pi_matrix = si3474_setup_pi_matrix,
|
||||
+ .pi_enable = si3474_pi_enable,
|
||||
+ .pi_disable = si3474_pi_disable,
|
||||
+ .pi_get_actual_pw = si3474_pi_get_actual_pw,
|
||||
+ .pi_get_voltage = si3474_pi_get_voltage,
|
||||
+ .pi_get_admin_state = si3474_pi_get_admin_state,
|
||||
+ .pi_get_pw_status = si3474_pi_get_pw_status,
|
||||
+};
|
||||
+
|
||||
+static void si3474_ancillary_i2c_remove(void *data)
|
||||
+{
|
||||
+ struct i2c_client *client = data;
|
||||
+
|
||||
+ i2c_unregister_device(client);
|
||||
+}
|
||||
+
|
||||
+static int si3474_i2c_probe(struct i2c_client *client)
|
||||
+{
|
||||
+ struct device *dev = &client->dev;
|
||||
+ struct si3474_priv *priv;
|
||||
+ u8 fw_version;
|
||||
+ s32 ret;
|
||||
+
|
||||
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
+ dev_err(dev, "i2c check functionality failed\n");
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(client, VENDOR_IC_ID_REG);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (ret != SI3474_DEVICE_ID) {
|
||||
+ dev_err(dev, "Wrong device ID: 0x%x\n", ret);
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(client, FIRMWARE_REVISION_REG);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ fw_version = ret;
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(client, CHIP_REVISION_REG);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ dev_dbg(dev, "Chip revision: 0x%x, firmware version: 0x%x\n",
|
||||
+ ret, fw_version);
|
||||
+
|
||||
+ priv->client[0] = client;
|
||||
+ i2c_set_clientdata(client, priv);
|
||||
+
|
||||
+ priv->client[1] = i2c_new_ancillary_device(priv->client[0], "secondary",
|
||||
+ priv->client[0]->addr + 1);
|
||||
+ if (IS_ERR(priv->client[1]))
|
||||
+ return PTR_ERR(priv->client[1]);
|
||||
+
|
||||
+ ret = devm_add_action_or_reset(dev, si3474_ancillary_i2c_remove, priv->client[1]);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&priv->client[1]->dev, "Cannot register remove callback\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = i2c_smbus_read_byte_data(priv->client[1], VENDOR_IC_ID_REG);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&priv->client[1]->dev, "Cannot access secondary PSE controller\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (ret != SI3474_DEVICE_ID) {
|
||||
+ dev_err(&priv->client[1]->dev,
|
||||
+ "Wrong device ID for secondary PSE controller: 0x%x\n", ret);
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+
|
||||
+ priv->np = dev->of_node;
|
||||
+ priv->pcdev.owner = THIS_MODULE;
|
||||
+ priv->pcdev.ops = &si3474_ops;
|
||||
+ priv->pcdev.dev = dev;
|
||||
+ priv->pcdev.types = ETHTOOL_PSE_C33;
|
||||
+ priv->pcdev.nr_lines = SI3474_MAX_CHANS;
|
||||
+
|
||||
+ ret = devm_pse_controller_register(dev, &priv->pcdev);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "Failed to register PSE controller: 0x%x\n", ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct i2c_device_id si3474_id[] = {
|
||||
+ { "si3474" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(i2c, si3474_id);
|
||||
+
|
||||
+static const struct of_device_id si3474_of_match[] = {
|
||||
+ {
|
||||
+ .compatible = "skyworks,si3474",
|
||||
+ },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, si3474_of_match);
|
||||
+
|
||||
+static struct i2c_driver si3474_driver = {
|
||||
+ .probe = si3474_i2c_probe,
|
||||
+ .id_table = si3474_id,
|
||||
+ .driver = {
|
||||
+ .name = "si3474",
|
||||
+ .of_match_table = si3474_of_match,
|
||||
+ },
|
||||
+};
|
||||
+module_i2c_driver(si3474_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Piotr Kubik <piotr.kubik@adtran.com>");
|
||||
+MODULE_DESCRIPTION("Skyworks Si3474 PoE PSE Controller driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,31 @@
|
||||
From 2c95a756e0cfc19af6d0b32b0c6cf3bada334998 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Wismer <thomas.wismer@scs.ch>
|
||||
Date: Mon, 6 Oct 2025 22:40:29 +0200
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Fix current measurement scaling
|
||||
|
||||
The TPS23881 improves on the TPS23880 with current sense resistors reduced
|
||||
from 255 mOhm to 200 mOhm. This has a direct impact on the scaling of the
|
||||
current measurement. However, the latest TPS23881 data sheet from May 2023
|
||||
still shows the scaling of the TPS23880 model.
|
||||
|
||||
Fixes: 7f076ce3f1733 ("net: pse-pd: tps23881: Add support for power limit and measurement features")
|
||||
Signed-off-by: Thomas Wismer <thomas.wismer@scs.ch>
|
||||
Acked-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Link: https://patch.msgid.link/20251006204029.7169-2-thomas@wismer.xyz
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -62,7 +62,7 @@
|
||||
#define TPS23881_REG_SRAM_DATA 0x61
|
||||
|
||||
#define TPS23881_UV_STEP 3662
|
||||
-#define TPS23881_NA_STEP 70190
|
||||
+#define TPS23881_NA_STEP 89500
|
||||
#define TPS23881_MW_STEP 500
|
||||
#define TPS23881_MIN_PI_PW_LIMIT_MW 2000
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
From f197902cd21ae833850679b216bb62c0d056bbb3 Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Mon, 13 Oct 2025 16:05:31 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Replace __free macro with explicit
|
||||
kfree calls
|
||||
|
||||
Replace __free(kfree) with explicit kfree() calls to follow the net
|
||||
subsystem policy of avoiding automatic cleanup macros as described in
|
||||
the documentation.
|
||||
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Link: https://patch.msgid.link/20251013-feature_pd692x0_reboot_keep_conf-v2-1-68ab082a93dd@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 7 +++++--
|
||||
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -1200,9 +1200,9 @@ static void pd692x0_managers_free_pw_bud
|
||||
|
||||
static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
{
|
||||
- struct pd692x0_manager *manager __free(kfree) = NULL;
|
||||
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
|
||||
+ struct pd692x0_manager *manager;
|
||||
int ret, nmanagers;
|
||||
|
||||
/* Should we flash the port matrix */
|
||||
@@ -1216,7 +1216,7 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
|
||||
ret = pd692x0_of_get_managers(priv, manager);
|
||||
if (ret < 0)
|
||||
- return ret;
|
||||
+ goto err_free_manager;
|
||||
|
||||
nmanagers = ret;
|
||||
ret = pd692x0_register_managers_regulator(priv, manager, nmanagers);
|
||||
@@ -1236,12 +1236,15 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
goto err_managers_req_pw;
|
||||
|
||||
pd692x0_of_put_managers(priv, manager, nmanagers);
|
||||
+ kfree(manager);
|
||||
return 0;
|
||||
|
||||
err_managers_req_pw:
|
||||
pd692x0_managers_free_pw_budget(priv);
|
||||
err_of_managers:
|
||||
pd692x0_of_put_managers(priv, manager, nmanagers);
|
||||
+err_free_manager:
|
||||
+ kfree(manager);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
From 6fa1f8b64a47edd7d8420d8fd1008507aee2853e Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Mon, 13 Oct 2025 16:05:32 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Separate configuration parsing from
|
||||
hardware setup
|
||||
|
||||
Cache the port matrix configuration in driver private data to enable
|
||||
PSE controller reconfiguration. This refactoring separates device tree
|
||||
parsing from hardware configuration application, allowing settings to be
|
||||
reapplied without reparsing the device tree.
|
||||
|
||||
This refactoring is a prerequisite for preserving PSE configuration
|
||||
across reboots to prevent power disruption to connected devices.
|
||||
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Link: https://patch.msgid.link/20251013-feature_pd692x0_reboot_keep_conf-v2-2-68ab082a93dd@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 115 +++++++++++++++++++++++------------
|
||||
1 file changed, 76 insertions(+), 39 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -85,6 +85,11 @@ enum {
|
||||
PD692X0_MSG_CNT
|
||||
};
|
||||
|
||||
+struct pd692x0_matrix {
|
||||
+ u8 hw_port_a;
|
||||
+ u8 hw_port_b;
|
||||
+};
|
||||
+
|
||||
struct pd692x0_priv {
|
||||
struct i2c_client *client;
|
||||
struct pse_controller_dev pcdev;
|
||||
@@ -101,6 +106,8 @@ struct pd692x0_priv {
|
||||
enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS];
|
||||
struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS];
|
||||
int manager_pw_budget[PD692X0_MAX_MANAGERS];
|
||||
+ int nmanagers;
|
||||
+ struct pd692x0_matrix *port_matrix;
|
||||
};
|
||||
|
||||
/* Template list of communication messages. The non-null bytes defined here
|
||||
@@ -809,11 +816,6 @@ struct pd692x0_manager {
|
||||
int nports;
|
||||
};
|
||||
|
||||
-struct pd692x0_matrix {
|
||||
- u8 hw_port_a;
|
||||
- u8 hw_port_b;
|
||||
-};
|
||||
-
|
||||
static int
|
||||
pd692x0_of_get_ports_manager(struct pd692x0_priv *priv,
|
||||
struct pd692x0_manager *manager,
|
||||
@@ -903,7 +905,8 @@ pd692x0_of_get_managers(struct pd692x0_p
|
||||
}
|
||||
|
||||
of_node_put(managers_node);
|
||||
- return nmanagers;
|
||||
+ priv->nmanagers = nmanagers;
|
||||
+ return 0;
|
||||
|
||||
out:
|
||||
for (i = 0; i < nmanagers; i++) {
|
||||
@@ -963,8 +966,7 @@ pd692x0_register_manager_regulator(struc
|
||||
|
||||
static int
|
||||
pd692x0_register_managers_regulator(struct pd692x0_priv *priv,
|
||||
- const struct pd692x0_manager *manager,
|
||||
- int nmanagers)
|
||||
+ const struct pd692x0_manager *manager)
|
||||
{
|
||||
struct device *dev = &priv->client->dev;
|
||||
size_t reg_name_len;
|
||||
@@ -975,7 +977,7 @@ pd692x0_register_managers_regulator(stru
|
||||
*/
|
||||
reg_name_len = strlen(dev_name(dev)) + 23;
|
||||
|
||||
- for (i = 0; i < nmanagers; i++) {
|
||||
+ for (i = 0; i < priv->nmanagers; i++) {
|
||||
static const char * const regulators[] = { "vaux5", "vaux3p3" };
|
||||
struct regulator_dev *rdev;
|
||||
char *reg_name;
|
||||
@@ -1008,10 +1010,14 @@ pd692x0_register_managers_regulator(stru
|
||||
}
|
||||
|
||||
static int
|
||||
-pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id, int pw)
|
||||
+pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id)
|
||||
{
|
||||
struct pd692x0_msg msg, buf;
|
||||
- int ret, pw_mW = pw / 1000;
|
||||
+ int ret, pw_mW;
|
||||
+
|
||||
+ pw_mW = priv->manager_pw_budget[id] / 1000;
|
||||
+ if (!pw_mW)
|
||||
+ return 0;
|
||||
|
||||
msg = pd692x0_msg_template_list[PD692X0_MSG_GET_POWER_BANK];
|
||||
msg.data[0] = id;
|
||||
@@ -1032,11 +1038,11 @@ pd692x0_conf_manager_power_budget(struct
|
||||
}
|
||||
|
||||
static int
|
||||
-pd692x0_configure_managers(struct pd692x0_priv *priv, int nmanagers)
|
||||
+pd692x0_req_managers_pw_budget(struct pd692x0_priv *priv)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
- for (i = 0; i < nmanagers; i++) {
|
||||
+ for (i = 0; i < priv->nmanagers; i++) {
|
||||
struct regulator *supply = priv->manager_reg[i]->supply;
|
||||
int pw_budget;
|
||||
|
||||
@@ -1053,7 +1059,18 @@ pd692x0_configure_managers(struct pd692x
|
||||
return ret;
|
||||
|
||||
priv->manager_pw_budget[i] = pw_budget;
|
||||
- ret = pd692x0_conf_manager_power_budget(priv, i, pw_budget);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pd692x0_configure_managers(struct pd692x0_priv *priv)
|
||||
+{
|
||||
+ int i, ret;
|
||||
+
|
||||
+ for (i = 0; i < priv->nmanagers; i++) {
|
||||
+ ret = pd692x0_conf_manager_power_budget(priv, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
@@ -1101,10 +1118,9 @@ pd692x0_set_port_matrix(const struct pse
|
||||
|
||||
static int
|
||||
pd692x0_set_ports_matrix(struct pd692x0_priv *priv,
|
||||
- const struct pd692x0_manager *manager,
|
||||
- int nmanagers,
|
||||
- struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
|
||||
+ const struct pd692x0_manager *manager)
|
||||
{
|
||||
+ struct pd692x0_matrix *port_matrix = priv->port_matrix;
|
||||
struct pse_controller_dev *pcdev = &priv->pcdev;
|
||||
int i, ret;
|
||||
|
||||
@@ -1117,7 +1133,7 @@ pd692x0_set_ports_matrix(struct pd692x0_
|
||||
/* Update with values for every PSE PIs */
|
||||
for (i = 0; i < pcdev->nr_lines; i++) {
|
||||
ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[0],
|
||||
- manager, nmanagers,
|
||||
+ manager, priv->nmanagers,
|
||||
&port_matrix[i]);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev,
|
||||
@@ -1126,7 +1142,7 @@ pd692x0_set_ports_matrix(struct pd692x0_
|
||||
}
|
||||
|
||||
ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[1],
|
||||
- manager, nmanagers,
|
||||
+ manager, priv->nmanagers,
|
||||
&port_matrix[i]);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev,
|
||||
@@ -1139,9 +1155,9 @@ pd692x0_set_ports_matrix(struct pd692x0_
|
||||
}
|
||||
|
||||
static int
|
||||
-pd692x0_write_ports_matrix(struct pd692x0_priv *priv,
|
||||
- const struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
|
||||
+pd692x0_write_ports_matrix(struct pd692x0_priv *priv)
|
||||
{
|
||||
+ struct pd692x0_matrix *port_matrix = priv->port_matrix;
|
||||
struct pd692x0_msg msg, buf;
|
||||
int ret, i;
|
||||
|
||||
@@ -1166,13 +1182,32 @@ pd692x0_write_ports_matrix(struct pd692x
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int pd692x0_hw_conf_init(struct pd692x0_priv *priv)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Is PD692x0 ready to be configured? */
|
||||
+ if (priv->fw_state != PD692X0_FW_OK &&
|
||||
+ priv->fw_state != PD692X0_FW_COMPLETE)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = pd692x0_configure_managers(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = pd692x0_write_ports_matrix(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static void pd692x0_of_put_managers(struct pd692x0_priv *priv,
|
||||
- struct pd692x0_manager *manager,
|
||||
- int nmanagers)
|
||||
+ struct pd692x0_manager *manager)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
- for (i = 0; i < nmanagers; i++) {
|
||||
+ for (i = 0; i < priv->nmanagers; i++) {
|
||||
for (j = 0; j < manager[i].nports; j++)
|
||||
of_node_put(manager[i].port_node[j]);
|
||||
of_node_put(manager[i].node);
|
||||
@@ -1201,48 +1236,50 @@ static void pd692x0_managers_free_pw_bud
|
||||
static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
{
|
||||
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
- struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
|
||||
+ struct pd692x0_matrix *port_matrix;
|
||||
struct pd692x0_manager *manager;
|
||||
- int ret, nmanagers;
|
||||
-
|
||||
- /* Should we flash the port matrix */
|
||||
- if (priv->fw_state != PD692X0_FW_OK &&
|
||||
- priv->fw_state != PD692X0_FW_COMPLETE)
|
||||
- return 0;
|
||||
+ int ret;
|
||||
|
||||
manager = kcalloc(PD692X0_MAX_MANAGERS, sizeof(*manager), GFP_KERNEL);
|
||||
if (!manager)
|
||||
return -ENOMEM;
|
||||
|
||||
+ port_matrix = devm_kcalloc(&priv->client->dev, PD692X0_MAX_PIS,
|
||||
+ sizeof(*port_matrix), GFP_KERNEL);
|
||||
+ if (!port_matrix) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_free_manager;
|
||||
+ }
|
||||
+ priv->port_matrix = port_matrix;
|
||||
+
|
||||
ret = pd692x0_of_get_managers(priv, manager);
|
||||
if (ret < 0)
|
||||
goto err_free_manager;
|
||||
|
||||
- nmanagers = ret;
|
||||
- ret = pd692x0_register_managers_regulator(priv, manager, nmanagers);
|
||||
+ ret = pd692x0_register_managers_regulator(priv, manager);
|
||||
if (ret)
|
||||
goto err_of_managers;
|
||||
|
||||
- ret = pd692x0_configure_managers(priv, nmanagers);
|
||||
+ ret = pd692x0_req_managers_pw_budget(priv);
|
||||
if (ret)
|
||||
goto err_of_managers;
|
||||
|
||||
- ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix);
|
||||
+ ret = pd692x0_set_ports_matrix(priv, manager);
|
||||
if (ret)
|
||||
goto err_managers_req_pw;
|
||||
|
||||
- ret = pd692x0_write_ports_matrix(priv, port_matrix);
|
||||
+ ret = pd692x0_hw_conf_init(priv);
|
||||
if (ret)
|
||||
goto err_managers_req_pw;
|
||||
|
||||
- pd692x0_of_put_managers(priv, manager, nmanagers);
|
||||
+ pd692x0_of_put_managers(priv, manager);
|
||||
kfree(manager);
|
||||
return 0;
|
||||
|
||||
err_managers_req_pw:
|
||||
pd692x0_managers_free_pw_budget(priv);
|
||||
err_of_managers:
|
||||
- pd692x0_of_put_managers(priv, manager, nmanagers);
|
||||
+ pd692x0_of_put_managers(priv, manager);
|
||||
err_free_manager:
|
||||
kfree(manager);
|
||||
return ret;
|
||||
@@ -1647,7 +1684,7 @@ static enum fw_upload_err pd692x0_fw_pol
|
||||
return FW_UPLOAD_ERR_FW_INVALID;
|
||||
}
|
||||
|
||||
- ret = pd692x0_setup_pi_matrix(&priv->pcdev);
|
||||
+ ret = pd692x0_hw_conf_init(priv);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error configuring ports matrix (%pe)\n",
|
||||
ERR_PTR(ret));
|
||||
@@ -0,0 +1,111 @@
|
||||
From 8f3d044b34fe99b894046edb84605456195cabc0 Mon Sep 17 00:00:00 2001
|
||||
From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
|
||||
Date: Mon, 13 Oct 2025 16:05:33 +0200
|
||||
Subject: [PATCH] net: pse-pd: pd692x0: Preserve PSE configuration across
|
||||
reboots
|
||||
|
||||
Detect when PSE hardware is already configured (user byte == 42) and
|
||||
skip hardware initialization to prevent power interruption to connected
|
||||
devices during system reboots.
|
||||
|
||||
Previously, the driver would always reconfigure the PSE hardware on
|
||||
probe, causing a port matrix reflash that resulted in temporary power
|
||||
loss to all connected devices. This change maintains power continuity
|
||||
by preserving existing configuration when the PSE has been previously
|
||||
initialized.
|
||||
|
||||
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Link: https://patch.msgid.link/20251013-feature_pd692x0_reboot_keep_conf-v2-3-68ab082a93dd@bootlin.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/pd692x0.c | 35 ++++++++++++++++++++++++++++++++---
|
||||
1 file changed, 32 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/pd692x0.c
|
||||
+++ b/drivers/net/pse-pd/pd692x0.c
|
||||
@@ -30,6 +30,8 @@
|
||||
#define PD692X0_FW_MIN_VER 5
|
||||
#define PD692X0_FW_PATCH_VER 5
|
||||
|
||||
+#define PD692X0_USER_BYTE 42
|
||||
+
|
||||
enum pd692x0_fw_state {
|
||||
PD692X0_FW_UNKNOWN,
|
||||
PD692X0_FW_OK,
|
||||
@@ -80,6 +82,7 @@ enum {
|
||||
PD692X0_MSG_GET_PORT_PARAM,
|
||||
PD692X0_MSG_GET_POWER_BANK,
|
||||
PD692X0_MSG_SET_POWER_BANK,
|
||||
+ PD692X0_MSG_SET_USER_BYTE,
|
||||
|
||||
/* add new message above here */
|
||||
PD692X0_MSG_CNT
|
||||
@@ -103,6 +106,7 @@ struct pd692x0_priv {
|
||||
bool last_cmd_key;
|
||||
unsigned long last_cmd_key_time;
|
||||
|
||||
+ bool cfg_saved;
|
||||
enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS];
|
||||
struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS];
|
||||
int manager_pw_budget[PD692X0_MAX_MANAGERS];
|
||||
@@ -193,6 +197,12 @@ static const struct pd692x0_msg pd692x0_
|
||||
.key = PD692X0_KEY_CMD,
|
||||
.sub = {0x07, 0x0b, 0x57},
|
||||
},
|
||||
+ [PD692X0_MSG_SET_USER_BYTE] = {
|
||||
+ .key = PD692X0_KEY_PRG,
|
||||
+ .sub = {0x41, PD692X0_USER_BYTE},
|
||||
+ .data = {0x4e, 0x4e, 0x4e, 0x4e,
|
||||
+ 0x4e, 0x4e, 0x4e, 0x4e},
|
||||
+ },
|
||||
};
|
||||
|
||||
static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo)
|
||||
@@ -1233,6 +1243,15 @@ static void pd692x0_managers_free_pw_bud
|
||||
}
|
||||
}
|
||||
|
||||
+static int
|
||||
+pd692x0_save_user_byte(struct pd692x0_priv *priv)
|
||||
+{
|
||||
+ struct pd692x0_msg msg, buf;
|
||||
+
|
||||
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_USER_BYTE];
|
||||
+ return pd692x0_sendrecv_msg(priv, &msg, &buf);
|
||||
+}
|
||||
+
|
||||
static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
|
||||
{
|
||||
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
|
||||
@@ -1268,9 +1287,16 @@ static int pd692x0_setup_pi_matrix(struc
|
||||
if (ret)
|
||||
goto err_managers_req_pw;
|
||||
|
||||
- ret = pd692x0_hw_conf_init(priv);
|
||||
- if (ret)
|
||||
- goto err_managers_req_pw;
|
||||
+ /* Do not init the conf if it is already saved */
|
||||
+ if (!priv->cfg_saved) {
|
||||
+ ret = pd692x0_hw_conf_init(priv);
|
||||
+ if (ret)
|
||||
+ goto err_managers_req_pw;
|
||||
+
|
||||
+ ret = pd692x0_save_user_byte(priv);
|
||||
+ if (ret)
|
||||
+ goto err_managers_req_pw;
|
||||
+ }
|
||||
|
||||
pd692x0_of_put_managers(priv, manager);
|
||||
kfree(manager);
|
||||
@@ -1793,6 +1819,9 @@ static int pd692x0_i2c_probe(struct i2c_
|
||||
}
|
||||
}
|
||||
|
||||
+ if (buf.data[2] == PD692X0_USER_BYTE)
|
||||
+ priv->cfg_saved = true;
|
||||
+
|
||||
priv->np = dev->of_node;
|
||||
priv->pcdev.nr_lines = PD692X0_MAX_PIS;
|
||||
priv->pcdev.owner = THIS_MODULE;
|
||||
@@ -0,0 +1,170 @@
|
||||
From 4d07797faaa19aa8e80e10a04ca1a72c643ef5cf Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Wismer <thomas.wismer@scs.ch>
|
||||
Date: Wed, 29 Oct 2025 22:23:09 +0100
|
||||
Subject: [PATCH] net: pse-pd: tps23881: Add support for TPS23881B
|
||||
|
||||
The TPS23881B uses different firmware than the TPS23881. Trying to load the
|
||||
TPS23881 firmware on a TPS23881B device fails and must be omitted.
|
||||
|
||||
The TPS23881B ships with a more recent ROM firmware. Moreover, no updated
|
||||
firmware has been released yet and so the firmware loading step must be
|
||||
skipped. As of today, the TPS23881B is intended to use its ROM firmware.
|
||||
|
||||
Signed-off-by: Thomas Wismer <thomas.wismer@scs.ch>
|
||||
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
|
||||
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
Link: https://patch.msgid.link/20251029212312.108749-2-thomas@wismer.xyz
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
drivers/net/pse-pd/tps23881.c | 69 +++++++++++++++++++++++++++--------
|
||||
1 file changed, 54 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/drivers/net/pse-pd/tps23881.c
|
||||
+++ b/drivers/net/pse-pd/tps23881.c
|
||||
@@ -55,8 +55,6 @@
|
||||
#define TPS23881_REG_TPON BIT(0)
|
||||
#define TPS23881_REG_FWREV 0x41
|
||||
#define TPS23881_REG_DEVID 0x43
|
||||
-#define TPS23881_REG_DEVID_MASK 0xF0
|
||||
-#define TPS23881_DEVICE_ID 0x02
|
||||
#define TPS23881_REG_CHAN1_CLASS 0x4c
|
||||
#define TPS23881_REG_SRAM_CTRL 0x60
|
||||
#define TPS23881_REG_SRAM_DATA 0x61
|
||||
@@ -1012,8 +1010,28 @@ static const struct pse_controller_ops t
|
||||
.pi_get_pw_req = tps23881_pi_get_pw_req,
|
||||
};
|
||||
|
||||
-static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
|
||||
-static const char fw_sram_name[] = "ti/tps23881/tps23881-sram-14.bin";
|
||||
+struct tps23881_info {
|
||||
+ u8 dev_id; /* device ID and silicon revision */
|
||||
+ const char *fw_parity_name; /* parity code firmware file name */
|
||||
+ const char *fw_sram_name; /* SRAM code firmware file name */
|
||||
+};
|
||||
+
|
||||
+enum tps23881_model {
|
||||
+ TPS23881,
|
||||
+ TPS23881B,
|
||||
+};
|
||||
+
|
||||
+static const struct tps23881_info tps23881_info[] = {
|
||||
+ [TPS23881] = {
|
||||
+ .dev_id = 0x22,
|
||||
+ .fw_parity_name = "ti/tps23881/tps23881-parity-14.bin",
|
||||
+ .fw_sram_name = "ti/tps23881/tps23881-sram-14.bin",
|
||||
+ },
|
||||
+ [TPS23881B] = {
|
||||
+ .dev_id = 0x24,
|
||||
+ /* skip SRAM load, ROM provides Clause 145 hardware-level support */
|
||||
+ },
|
||||
+};
|
||||
|
||||
struct tps23881_fw_conf {
|
||||
u8 reg;
|
||||
@@ -1085,16 +1103,17 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int tps23881_flash_sram_fw(struct i2c_client *client)
|
||||
+static int tps23881_flash_sram_fw(struct i2c_client *client,
|
||||
+ const struct tps23881_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
- ret = tps23881_flash_sram_fw_part(client, fw_parity_name,
|
||||
+ ret = tps23881_flash_sram_fw_part(client, info->fw_parity_name,
|
||||
tps23881_fw_parity_conf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = tps23881_flash_sram_fw_part(client, fw_sram_name,
|
||||
+ ret = tps23881_flash_sram_fw_part(client, info->fw_sram_name,
|
||||
tps23881_fw_sram_conf);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1412,6 +1431,7 @@ static int tps23881_setup_irq(struct tps
|
||||
static int tps23881_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
+ const struct tps23881_info *info;
|
||||
struct tps23881_priv *priv;
|
||||
struct gpio_desc *reset;
|
||||
int ret;
|
||||
@@ -1422,6 +1442,10 @@ static int tps23881_i2c_probe(struct i2c
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
+ info = i2c_get_match_data(client);
|
||||
+ if (!info)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
@@ -1440,7 +1464,7 @@ static int tps23881_i2c_probe(struct i2c
|
||||
* to Load TPS2388x SRAM and Parity Code over I2C" (Rev E))
|
||||
* indicates we should delay that programming by at least 50ms. So
|
||||
* we'll wait the entire 50ms here to ensure we're safe to go to the
|
||||
- * SRAM loading proceedure.
|
||||
+ * SRAM loading procedure.
|
||||
*/
|
||||
msleep(50);
|
||||
}
|
||||
@@ -1449,20 +1473,27 @@ static int tps23881_i2c_probe(struct i2c
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- if (FIELD_GET(TPS23881_REG_DEVID_MASK, ret) != TPS23881_DEVICE_ID) {
|
||||
+ if (ret != info->dev_id) {
|
||||
dev_err(dev, "Wrong device ID\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
- ret = tps23881_flash_sram_fw(client);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ if (info->fw_sram_name) {
|
||||
+ ret = tps23881_flash_sram_fw(client, info);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FWREV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- dev_info(&client->dev, "Firmware revision 0x%x\n", ret);
|
||||
+ if (ret == 0xFF) {
|
||||
+ dev_err(&client->dev, "Device entered safe mode\n");
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+ dev_info(&client->dev, "Firmware revision 0x%x%s\n", ret,
|
||||
+ ret == 0x00 ? " (ROM firmware)" : "");
|
||||
|
||||
/* Set configuration B, 16 bit access on a single device address */
|
||||
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_GEN_MASK);
|
||||
@@ -1498,13 +1529,21 @@ static int tps23881_i2c_probe(struct i2c
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tps23881_id[] = {
|
||||
- { "tps23881" },
|
||||
+ { "tps23881", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881] },
|
||||
+ { "tps23881b", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881B] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tps23881_id);
|
||||
|
||||
static const struct of_device_id tps23881_of_match[] = {
|
||||
- { .compatible = "ti,tps23881", },
|
||||
+ {
|
||||
+ .compatible = "ti,tps23881",
|
||||
+ .data = &tps23881_info[TPS23881]
|
||||
+ },
|
||||
+ {
|
||||
+ .compatible = "ti,tps23881b",
|
||||
+ .data = &tps23881_info[TPS23881B]
|
||||
+ },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tps23881_of_match);
|
||||
@@ -0,0 +1,80 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: OpenWrt PSE-PD Backport <openwrt@openwrt.org>
|
||||
Date: Fri, 31 Jan 2025 00:00:00 +0000
|
||||
Subject: [PATCH] net: pse-pd: Add ethtool netlink definitions for PSE events
|
||||
|
||||
GENERATED PATCH - OpenWrt PSE-PD Backport
|
||||
|
||||
This patch:
|
||||
1. Creates include/uapi/linux/ethtool_netlink_generated.h with PSE event
|
||||
definitions (in mainline 6.17+, this file is auto-generated)
|
||||
2. Adds ETHTOOL_MSG_PSE_NTF to the kernel message types enum
|
||||
|
||||
These definitions are required by PSE event reporting patches.
|
||||
|
||||
Signed-off-by: OpenWrt PSE-PD Backport <openwrt@openwrt.org>
|
||||
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
|
||||
---
|
||||
include/uapi/linux/ethtool_netlink.h | 1 +
|
||||
include/uapi/linux/ethtool_netlink_generated.h | 45 ++++++++++++++++++
|
||||
2 files changed, 46 insertions(+)
|
||||
create mode 100644 include/uapi/linux/ethtool_netlink_generated.h
|
||||
|
||||
--- a/include/uapi/linux/ethtool_netlink.h
|
||||
+++ b/include/uapi/linux/ethtool_netlink.h
|
||||
@@ -115,6 +115,7 @@ enum {
|
||||
ETHTOOL_MSG_PHY_GET_REPLY,
|
||||
ETHTOOL_MSG_PHY_NTF,
|
||||
|
||||
+ ETHTOOL_MSG_PSE_NTF,
|
||||
/* add new constants above here */
|
||||
__ETHTOOL_MSG_KERNEL_CNT,
|
||||
ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1
|
||||
--- /dev/null
|
||||
+++ b/include/uapi/linux/ethtool_netlink_generated.h
|
||||
@@ -0,0 +1,45 @@
|
||||
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
+/*
|
||||
+ * Minimal PSE ethtool netlink definitions - OpenWrt backport for 6.12.67
|
||||
+ * In mainline 6.17+, this file is auto-generated from:
|
||||
+ * Documentation/netlink/specs/ethtool.yaml
|
||||
+ *
|
||||
+ * GENERATED PATCH - OpenWrt PSE-PD Backport
|
||||
+ */
|
||||
+
|
||||
+#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H
|
||||
+#define _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H
|
||||
+
|
||||
+/**
|
||||
+ * enum ethtool_pse_event - PSE event list for the PSE controller
|
||||
+ * @ETHTOOL_PSE_EVENT_OVER_CURRENT: PSE output current is too high
|
||||
+ * @ETHTOOL_PSE_EVENT_OVER_TEMP: PSE in over temperature state
|
||||
+ * @ETHTOOL_C33_PSE_EVENT_DETECTION: detection process occur on the PSE
|
||||
+ * @ETHTOOL_C33_PSE_EVENT_CLASSIFICATION: classification process occur
|
||||
+ * @ETHTOOL_C33_PSE_EVENT_DISCONNECTION: PD has been disconnected
|
||||
+ * @ETHTOOL_PSE_EVENT_OVER_BUDGET: PSE turned off due to over budget
|
||||
+ * @ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR: PSE power control error
|
||||
+ */
|
||||
+enum ethtool_pse_event {
|
||||
+ ETHTOOL_PSE_EVENT_OVER_CURRENT = 1,
|
||||
+ ETHTOOL_PSE_EVENT_OVER_TEMP = 2,
|
||||
+ ETHTOOL_C33_PSE_EVENT_DETECTION = 4,
|
||||
+ ETHTOOL_C33_PSE_EVENT_CLASSIFICATION = 8,
|
||||
+ ETHTOOL_C33_PSE_EVENT_DISCONNECTION = 16,
|
||||
+ ETHTOOL_PSE_EVENT_OVER_BUDGET = 32,
|
||||
+ ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR = 64,
|
||||
+};
|
||||
+
|
||||
+/* PSE notification attributes */
|
||||
+enum {
|
||||
+ ETHTOOL_A_PSE_NTF_HEADER = 1,
|
||||
+ ETHTOOL_A_PSE_NTF_EVENTS,
|
||||
+
|
||||
+ __ETHTOOL_A_PSE_NTF_CNT,
|
||||
+ ETHTOOL_A_PSE_NTF_MAX = (__ETHTOOL_A_PSE_NTF_CNT - 1)
|
||||
+};
|
||||
+
|
||||
+/* PSE power domain ID attribute - value 14 in the ETHTOOL_A_PSE_* enum */
|
||||
+#define ETHTOOL_A_PSE_PW_D_ID 14
|
||||
+
|
||||
+#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */
|
||||
@@ -24,7 +24,7 @@ Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
|
||||
--- a/drivers/net/mdio/fwnode_mdio.c
|
||||
+++ b/drivers/net/mdio/fwnode_mdio.c
|
||||
@@ -89,7 +89,7 @@ int fwnode_mdiobus_phy_device_register(s
|
||||
@@ -90,7 +90,7 @@ int fwnode_mdiobus_phy_device_register(s
|
||||
}
|
||||
|
||||
if (fwnode_property_read_bool(child, "broken-turn-around"))
|
||||
|
||||
Reference in New Issue
Block a user