2024-02-08 13:12:04

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 00/17] net: Add support for Power over Ethernet (PoE)

This patch series aims at adding support for PoE (Power over Ethernet),
based on the already existing support for PoDL (Power over Data Line)
implementation. In addition, it adds support for two specific PoE
controller, the Microchip PD692x0 and the TI TPS23881.

In detail:
- Patch 1 to 13 prepare net to support PoE devices.
- Patch 14 and 15 add PD692x0 PoE PSE controller driver and its binding.
- Patch 16 and 17 add TI TPS23881 PSE controller driver and its binding.

Changes in v2:
- Extract "firmware_loader: Expand Firmware upload error codes patches" to
send it alone and get it merge in an immutable branch.
- Add "c33" prefix for PoE variables and enums.
- Enhance few comments.
- Add PSE Documentation.
- Make several changes in pd692x0 driver, mainly for readibility.
- Link to v1: https://lore.kernel.org/r/[email protected]

Changes in v3:
- Add patches to add Oleksij and myself to PSE MAINTAINERS.
- Add patches to add pse devlink.
- Add TI TPS23881 PSE controller driver with its binding.
- Replace pse_get_types helper by pse_has_podl and pse_has_c33
- Changed the PSE core bindings.
- Add a setup_pi_matrix callback.
- Register regulator for each PSE PI (Power Interface).
- Changed the PD692x0 bindings.
- Updated PD692x0 drivers to new bindings and PSE PI description.
- Updated PD692x0 drivers according to the reviews and made fixes.
- Link to v2: https://lore.kernel.org/r/[email protected]

Signed-off-by: Kory Maincent <[email protected]>
---
Kory Maincent (17):
MAINTAINERS: net: Add Oleksij to pse-pd maintainers
of: property: Add fw_devlink support for pse parent
net: pse-pd: Rectify and adapt the naming of admin_cotrol member of struct pse_control_config
ethtool: Expand Ethernet Power Equipment with c33 (PoE) alongside PoDL
net: pse-pd: Introduce PSE types enumeration
net: ethtool: pse-pd: Expand pse commands with the PSE PoE interface
netlink: specs: Modify pse attribute prefix
netlink: specs: Expand the pse netlink command with PoE interface
MAINTAINERS: Add myself to pse networking maintainer
dt-bindings: net: pse-pd: Add another way of describing several PSE PIs
net: pse-pd: Add support for PSE PIs
net: pse-pd: Add support for setup_pi_matrix callback
net: pse-pd: Use regulator framework within PSE framework
dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller
net: pse-pd: Add PD692x0 PSE controller driver
dt-bindings: net: pse-pd: Add bindings for TPS23881 PSE controller
net: pse-pd: Add TI TPS23881 PSE controller driver

.../bindings/net/pse-pd/microchip,pd692x0.yaml | 157 +++
.../bindings/net/pse-pd/pse-controller.yaml | 101 +-
.../bindings/net/pse-pd/ti,tps2388x.yaml | 112 ++
Documentation/netlink/specs/ethtool.yaml | 33 +-
Documentation/networking/ethtool-netlink.rst | 20 +
Documentation/networking/index.rst | 1 +
Documentation/networking/pse-pd/index.rst | 9 +
Documentation/networking/pse-pd/introduction.rst | 73 ++
MAINTAINERS | 8 +
drivers/net/mdio/fwnode_mdio.c | 29 +-
drivers/net/pse-pd/Kconfig | 20 +
drivers/net/pse-pd/Makefile | 2 +
drivers/net/pse-pd/pd692x0.c | 1223 ++++++++++++++++++++
drivers/net/pse-pd/pse_core.c | 491 +++++++-
drivers/net/pse-pd/pse_regulator.c | 49 +-
drivers/net/pse-pd/tps23881.c | 818 +++++++++++++
drivers/of/property.c | 2 +
include/linux/pse-pd/pse.h | 89 +-
include/uapi/linux/ethtool.h | 43 +
include/uapi/linux/ethtool_netlink.h | 3 +
include/uapi/linux/pse.h | 23 +
net/ethtool/pse-pd.c | 60 +-
22 files changed, 3246 insertions(+), 120 deletions(-)
---
base-commit: 05f5c48cfac2335df9bb76fb6d49703670ce8b00
change-id: 20231024-feature_poe-139490e73403

Best regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com



2024-02-08 13:12:06

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 07/17] netlink: specs: Modify pse attribute prefix

Remove podl from the attribute prefix to prepare the support of PoE pse
netlink spec.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v2:
- Add the ethtool auto generated code.

Changes in v3:
- Remove the ethtool auto generated code.
---
Documentation/netlink/specs/ethtool.yaml | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 197208f419dc..33e81fa189ca 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -878,17 +878,17 @@ attribute-sets:
type: nest
nested-attributes: header
-
- name: admin-state
+ name: podl-pse-admin-state
type: u32
- name-prefix: ethtool-a-podl-pse-
+ name-prefix: ethtool-a-
-
- name: admin-control
+ name: podl-pse-admin-control
type: u32
- name-prefix: ethtool-a-podl-pse-
+ name-prefix: ethtool-a-
-
- name: pw-d-status
+ name: podl-pse-pw-d-status
type: u32
- name-prefix: ethtool-a-podl-pse-
+ name-prefix: ethtool-a-
-
name: rss
attributes:
@@ -1571,9 +1571,9 @@ operations:
reply:
attributes: &pse
- header
- - admin-state
- - admin-control
- - pw-d-status
+ - podl-pse-admin-state
+ - podl-pse-admin-control
+ - podl-pse-pw-d-status
dump: *pse-get-op
-
name: pse-set

--
2.25.1


2024-02-08 13:12:10

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 03/17] net: pse-pd: Rectify and adapt the naming of admin_cotrol member of struct pse_control_config

In commit 18ff0bcda6d1 ("ethtool: add interface to interact with Ethernet
Power Equipment"), the 'pse_control_config' structure was introduced,
housing a single member labeled 'admin_cotrol' responsible for maintaining
the operational state of the PoDL PSE functions.

A noticeable typographical error exists in the naming of this field
('cotrol' should be corrected to 'control'), which this commit aims to
rectify.

Furthermore, with upcoming extensions of this structure to encompass PoE
functionalities, the field is being renamed to 'podl_admin_state' to
distinctly indicate that this state is tailored specifically for PoDL."

Sponsored-by: Dent Project <[email protected]>
Reviewed-by: Oleksij Rempel <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---
drivers/net/pse-pd/pse_regulator.c | 8 ++++----
include/linux/pse-pd/pse.h | 4 ++--
net/ethtool/pse-pd.c | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/net/pse-pd/pse_regulator.c b/drivers/net/pse-pd/pse_regulator.c
index e2bf8306ca90..1dedf4de296e 100644
--- a/drivers/net/pse-pd/pse_regulator.c
+++ b/drivers/net/pse-pd/pse_regulator.c
@@ -31,10 +31,10 @@ pse_reg_ethtool_set_config(struct pse_controller_dev *pcdev, unsigned long id,
struct pse_reg_priv *priv = to_pse_reg(pcdev);
int ret;

- if (priv->admin_state == config->admin_cotrol)
+ if (priv->admin_state == config->podl_admin_control)
return 0;

- switch (config->admin_cotrol) {
+ switch (config->podl_admin_control) {
case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED:
ret = regulator_enable(priv->ps);
break;
@@ -43,14 +43,14 @@ pse_reg_ethtool_set_config(struct pse_controller_dev *pcdev, unsigned long id,
break;
default:
dev_err(pcdev->dev, "Unknown admin state %i\n",
- config->admin_cotrol);
+ config->podl_admin_control);
ret = -ENOTSUPP;
}

if (ret)
return ret;

- priv->admin_state = config->admin_cotrol;
+ priv->admin_state = config->podl_admin_control;

return 0;
}
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index fb724c65c77b..199cf4ae3cf2 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -15,11 +15,11 @@ struct pse_controller_dev;
/**
* struct pse_control_config - PSE control/channel configuration.
*
- * @admin_cotrol: set PoDL PSE admin control as described in
+ * @podl_admin_control: set PoDL PSE admin control as described in
* IEEE 802.3-2018 30.15.1.2.1 acPoDLPSEAdminControl
*/
struct pse_control_config {
- enum ethtool_podl_pse_admin_state admin_cotrol;
+ enum ethtool_podl_pse_admin_state podl_admin_control;
};

/**
diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
index cc478af77111..aef57a058f0d 100644
--- a/net/ethtool/pse-pd.c
+++ b/net/ethtool/pse-pd.c
@@ -130,7 +130,7 @@ ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
struct phy_device *phydev;

/* this values are already validated by the ethnl_pse_set_policy */
- config.admin_cotrol = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
+ config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);

phydev = dev->phydev;
if (!phydev) {

--
2.25.1


2024-02-08 13:12:30

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 01/17] MAINTAINERS: net: Add Oleksij to pse-pd maintainers

Oleksij was the first to add support for pse-pd net subsystem.
Add himself to the maintainers seems logical.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3f465fd778b1..2dcf48948d2b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17668,6 +17668,13 @@ F: include/net/psample.h
F: include/uapi/linux/psample.h
F: net/psample

+PSE NETWORK DRIVER
+M: Oleksij Rempel <[email protected]>
+L: [email protected]
+S: Maintained
+F: Documentation/devicetree/bindings/net/pse-pd/
+F: drivers/net/pse-pd/
+
PSTORE FILESYSTEM
M: Kees Cook <[email protected]>
R: Tony Luck <[email protected]>

--
2.25.1


2024-02-08 13:12:53

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 02/17] of: property: Add fw_devlink support for pse parent

This allows fw_devlink to create device links between consumers of
a PSE and the supplier of the PSE.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
drivers/of/property.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/of/property.c b/drivers/of/property.c
index 641a40cf5cf3..efe4dde65f30 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -1269,6 +1269,7 @@ DEFINE_SIMPLE_PROP(leds, "leds", NULL)
DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
DEFINE_SIMPLE_PROP(panel, "panel", NULL)
DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells")
+DEFINE_SIMPLE_PROP(pses, "pses", "#pse-cells")
DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")

@@ -1359,6 +1360,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_backlight, },
{ .parse_prop = parse_panel, },
{ .parse_prop = parse_msi_parent, },
+ { .parse_prop = parse_pses, },
{ .parse_prop = parse_gpio_compat, },
{ .parse_prop = parse_interrupts, },
{ .parse_prop = parse_regulators, },

--
2.25.1


2024-02-08 13:13:08

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 05/17] net: pse-pd: Introduce PSE types enumeration

Introduce an enumeration to define PSE types (C33 or PoDL),
utilizing a bitfield for potential future support of both types.
Include 'pse_get_types' helper for external access to PSE type info.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v2:
- Rename PSE_POE to PSE_C33 to have naming consistency.
- Use "static inline" instead of simple static in the header

Changes in v3:
- Move the pse_type enum in uapi.
- Replace pse_get_types helper by pse_has_podl and pse_has_c33.
---
drivers/net/pse-pd/pse_core.c | 12 ++++++++++++
drivers/net/pse-pd/pse_regulator.c | 1 +
include/linux/pse-pd/pse.h | 16 ++++++++++++++++
include/uapi/linux/pse.h | 23 +++++++++++++++++++++++
4 files changed, 52 insertions(+)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 146b81f08a89..090e04c32f9e 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -312,3 +312,15 @@ int pse_ethtool_set_config(struct pse_control *psec,
return err;
}
EXPORT_SYMBOL_GPL(pse_ethtool_set_config);
+
+bool pse_has_podl(struct pse_control *psec)
+{
+ return psec->pcdev->types & PSE_PODL;
+}
+EXPORT_SYMBOL_GPL(pse_has_podl);
+
+bool pse_has_c33(struct pse_control *psec)
+{
+ return psec->pcdev->types & PSE_C33;
+}
+EXPORT_SYMBOL_GPL(pse_has_c33);
diff --git a/drivers/net/pse-pd/pse_regulator.c b/drivers/net/pse-pd/pse_regulator.c
index 1dedf4de296e..e34ab8526067 100644
--- a/drivers/net/pse-pd/pse_regulator.c
+++ b/drivers/net/pse-pd/pse_regulator.c
@@ -116,6 +116,7 @@ pse_reg_probe(struct platform_device *pdev)
priv->pcdev.owner = THIS_MODULE;
priv->pcdev.ops = &pse_reg_ops;
priv->pcdev.dev = dev;
+ priv->pcdev.types = PSE_PODL;
ret = devm_pse_controller_register(dev, &priv->pcdev);
if (ret) {
dev_err(dev, "failed to register PSE controller (%pe)\n",
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index be4e5754eb24..f006cbdf8b3b 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -8,6 +8,7 @@
#include <linux/ethtool.h>
#include <linux/list.h>
#include <uapi/linux/ethtool.h>
+#include <uapi/linux/pse.h>

struct phy_device;
struct pse_controller_dev;
@@ -77,6 +78,7 @@ struct pse_control;
* device tree to id as given to the PSE control ops
* @nr_lines: number of PSE controls in this controller device
* @lock: Mutex for serialization access to the PSE controller
+ * @types: types of the PSE controller
*/
struct pse_controller_dev {
const struct pse_controller_ops *ops;
@@ -89,6 +91,7 @@ struct pse_controller_dev {
const struct of_phandle_args *pse_spec);
unsigned int nr_lines;
struct mutex lock;
+ u32 types;
};

#if IS_ENABLED(CONFIG_PSE_CONTROLLER)
@@ -108,6 +111,9 @@ int pse_ethtool_set_config(struct pse_control *psec,
struct netlink_ext_ack *extack,
const struct pse_control_config *config);

+bool pse_has_podl(struct pse_control *psec);
+bool pse_has_c33(struct pse_control *psec);
+
#else

static inline struct pse_control *of_pse_control_get(struct device_node *node)
@@ -133,6 +139,16 @@ static inline int pse_ethtool_set_config(struct pse_control *psec,
return -ENOTSUPP;
}

+static inline bool pse_has_podl(struct pse_control *psec)
+{
+ return false;
+}
+
+static inline bool pse_has_c33(struct pse_control *psec)
+{
+ return false;
+}
+
#endif

#endif
diff --git a/include/uapi/linux/pse.h b/include/uapi/linux/pse.h
new file mode 100644
index 000000000000..ebd9b4be9d9d
--- /dev/null
+++ b/include/uapi/linux/pse.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Userspace API for Power Sourcing Equipment
+ *
+ * Copyright (c) 2023 Bootlin, Kory Maincent <[email protected]>
+ */
+#ifndef _PSE_CONTROLLER_H
+#define _PSE_CONTROLLER_H
+
+/**
+ * enum - Types of PSE controller.
+ *
+ * @PSE_UNKNOWN: Type of PSE controller is unknown
+ * @PSE_PODL: PSE controller which support PoDL
+ * @PSE_C33: PSE controller which support Clause 33 (PoE)
+ */
+enum {
+ PSE_UNKNOWN = 1 << 0,
+ PSE_PODL = 1 << 1,
+ PSE_C33 = 1 << 2,
+};
+
+#endif /* _PSE_CONTROLLER_H */

--
2.25.1


2024-02-08 13:13:10

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 06/17] net: ethtool: pse-pd: Expand pse commands with the PSE PoE interface

Add PSE PoE interface support in the ethtool pse command.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v2:
- Follow the "c33" PoE prefix naming change.

Changes in v3:
- Replace the pse_get_types() helper by pse_has_podl() and pse_has_c33().
- Replace PoE to c33 in the netlink error log.
- Fix documentation build warning.
---
Documentation/networking/ethtool-netlink.rst | 20 ++++++++++
net/ethtool/pse-pd.c | 60 +++++++++++++++++++++++-----
2 files changed, 70 insertions(+), 10 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index d583d9abf2f8..294187c3a3b0 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1717,6 +1717,10 @@ Kernel response contents:
PSE functions
``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` u32 power detection status of the
PoDL PSE.
+ ``ETHTOOL_A_C33_PSE_ADMIN_STATE`` u32 Operational state of the PoE
+ PSE functions.
+ ``ETHTOOL_A_C33_PSE_PW_D_STATUS`` u32 power detection status of the
+ PoE PSE.
====================================== ====== =============================

When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies
@@ -1728,6 +1732,12 @@ aPoDLPSEAdminState. Possible values are:
.. kernel-doc:: include/uapi/linux/ethtool.h
:identifiers: ethtool_podl_pse_admin_state

+The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_STATE`` implementing
+``IEEE 802.3-2022`` 30.9.1.1.2 aPSEAdminState.
+
+.. kernel-doc:: include/uapi/linux/ethtool.h
+ :identifiers: ethtool_c33_pse_admin_state
+
When set, the optional ``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` attribute identifies
the power detection status of the PoDL PSE. The status depend on internal PSE
state machine and automatic PD classification support. This option is
@@ -1737,6 +1747,12 @@ Possible values are:
.. kernel-doc:: include/uapi/linux/ethtool.h
:identifiers: ethtool_podl_pse_pw_d_status

+The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_PW_D_STATUS`` implementing
+``IEEE 802.3-2022`` 30.9.1.1.5 aPSEPowerDetectionStatus.
+
+.. kernel-doc:: include/uapi/linux/ethtool.h
+ :identifiers: ethtool_c33_pse_pw_d_status
+
PSE_SET
=======

@@ -1747,6 +1763,7 @@ Request contents:
====================================== ====== =============================
``ETHTOOL_A_PSE_HEADER`` nested request header
``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` u32 Control PoDL PSE Admin state
+ ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` u32 Control PSE Admin state
====================================== ====== =============================

When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used
@@ -1754,6 +1771,9 @@ to control PoDL PSE Admin functions. This option is implementing
``IEEE 802.3-2018`` 30.15.1.2.1 acPoDLPSEAdminControl. See
``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` for supported values.

+The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` implementing
+``IEEE 802.3-2022`` 30.9.1.2.1 acPSEAdminControl.
+
RSS_GET
=======

diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
index aef57a058f0d..a3bfc3d9644e 100644
--- a/net/ethtool/pse-pd.c
+++ b/net/ethtool/pse-pd.c
@@ -82,6 +82,10 @@ static int pse_reply_size(const struct ethnl_req_info *req_base,
len += nla_total_size(sizeof(u32)); /* _PODL_PSE_ADMIN_STATE */
if (st->podl_pw_status > 0)
len += nla_total_size(sizeof(u32)); /* _PODL_PSE_PW_D_STATUS */
+ if (st->c33_admin_state > 0)
+ len += nla_total_size(sizeof(u32)); /* _C33_PSE_ADMIN_STATE */
+ if (st->c33_pw_status > 0)
+ len += nla_total_size(sizeof(u32)); /* _C33_PSE_PW_D_STATUS */

return len;
}
@@ -103,6 +107,16 @@ static int pse_fill_reply(struct sk_buff *skb,
st->podl_pw_status))
return -EMSGSIZE;

+ if (st->c33_admin_state > 0 &&
+ nla_put_u32(skb, ETHTOOL_A_C33_PSE_ADMIN_STATE,
+ st->c33_admin_state))
+ return -EMSGSIZE;
+
+ if (st->c33_pw_status > 0 &&
+ nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_D_STATUS,
+ st->c33_pw_status))
+ return -EMSGSIZE;
+
return 0;
}

@@ -113,25 +127,18 @@ const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] =
NLA_POLICY_RANGE(NLA_U32, ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED,
ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED),
+ [ETHTOOL_A_C33_PSE_ADMIN_CONTROL] =
+ NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED,
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED),
};

static int
ethnl_set_pse_validate(struct ethnl_req_info *req_info, struct genl_info *info)
-{
- return !!info->attrs[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL];
-}
-
-static int
-ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
{
struct net_device *dev = req_info->dev;
- struct pse_control_config config = {};
struct nlattr **tb = info->attrs;
struct phy_device *phydev;

- /* this values are already validated by the ethnl_pse_set_policy */
- config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
-
phydev = dev->phydev;
if (!phydev) {
NL_SET_ERR_MSG(info->extack, "No PHY is attached");
@@ -143,6 +150,39 @@ ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
return -EOPNOTSUPP;
}

+ if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] &&
+ !(pse_has_podl(phydev->psec))) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL],
+ "setting PoDL PSE admin control not supported");
+ return -EOPNOTSUPP;
+ }
+ if (tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL] &&
+ !(pse_has_c33(phydev->psec))) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL],
+ "setting C33 PSE admin control not supported");
+ return -EOPNOTSUPP;
+ }
+
+ return 1;
+}
+
+static int
+ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
+{
+ struct net_device *dev = req_info->dev;
+ struct pse_control_config config = {};
+ struct nlattr **tb = info->attrs;
+ struct phy_device *phydev;
+
+ phydev = dev->phydev;
+ /* These values are already validated by the ethnl_pse_set_policy */
+ if (pse_has_podl(phydev->psec))
+ config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
+ if (pse_has_c33(phydev->psec))
+ config.c33_admin_control = nla_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]);
+
/* Return errno directly - PSE has no notification */
return pse_ethtool_set_config(phydev->psec, info->extack, &config);
}

--
2.25.1


2024-02-08 13:13:30

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 04/17] ethtool: Expand Ethernet Power Equipment with c33 (PoE) alongside PoDL

In the current PSE interface for Ethernet Power Equipment, support is
limited to PoDL. This patch extends the interface to accommodate the
objects specified in IEEE 802.3-2022 145.2 for Power sourcing
Equipment (PSE).

The following objects are now supported and considered mandatory:
- IEEE 802.3-2022 30.9.1.1.5 aPSEPowerDetectionStatus
- IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState
- IEEE 802.3-2022 30.9.1.2.1 aPSEAdminControl

To avoid confusion between "PoDL PSE" and "PoE PSE", which have similar
names but distinct values, we have followed the suggestion of Oleksij
Rempel and Andrew Lunn to maintain separate naming schemes for each,
using c33 (clause 33) prefix for "PoE PSE".
You can find more details in the discussion threads here:
https://lore.kernel.org/netdev/[email protected]/
https://lore.kernel.org/netdev/[email protected]/

Sponsored-by: Dent Project <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
Reviewed-by: Oleksij Rempel <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v2:
- Rename all the PoE variables and enum with a c33 prefix.
- Add documentation, thanks to Oleksij for having written one.

Changes in v3:
- Fix documentation build warning.
---
Documentation/networking/index.rst | 1 +
Documentation/networking/pse-pd/index.rst | 9 +++
Documentation/networking/pse-pd/introduction.rst | 73 ++++++++++++++++++++++++
include/linux/pse-pd/pse.h | 9 +++
include/uapi/linux/ethtool.h | 43 ++++++++++++++
include/uapi/linux/ethtool_netlink.h | 3 +
6 files changed, 138 insertions(+)

diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index 69f3d6dcd9fd..af7da7006ce8 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -92,6 +92,7 @@ Contents:
plip
ppp_generic
proc_net_tcp
+ pse-pd/index
radiotap-headers
rds
regulatory
diff --git a/Documentation/networking/pse-pd/index.rst b/Documentation/networking/pse-pd/index.rst
new file mode 100644
index 000000000000..18197bc7303d
--- /dev/null
+++ b/Documentation/networking/pse-pd/index.rst
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Power Sourcing Equipment (PSE) Documentation
+============================================
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction
diff --git a/Documentation/networking/pse-pd/introduction.rst b/Documentation/networking/pse-pd/introduction.rst
new file mode 100644
index 000000000000..e3d3faaef717
--- /dev/null
+++ b/Documentation/networking/pse-pd/introduction.rst
@@ -0,0 +1,73 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Power Sourcing Equipment (PSE) in IEEE 802.3 Standard
+=====================================================
+
+Overview
+--------
+
+Power Sourcing Equipment (PSE) is essential in networks for delivering power
+along with data over Ethernet cables. It usually refers to devices like
+switches and hubs that supply power to Powered Devices (PDs) such as IP
+cameras, VoIP phones, and wireless access points.
+
+PSE vs. PoDL PSE
+----------------
+
+PSE in the IEEE 802.3 standard generally refers to equipment that provides
+power alongside data over Ethernet cables, typically associated with Power over
+Ethernet (PoE).
+
+PoDL PSE, or Power over Data Lines PSE, specifically denotes PSEs operating
+with single balanced twisted-pair PHYs, as per Clause 104 of IEEE 802.3. PoDL
+is significant in contexts like automotive and industrial controls where power
+and data delivery over a single pair is advantageous.
+
+IEEE 802.3-2018 Addendums and Related Clauses
+---------------------------------------------
+
+Key addenda to the IEEE 802.3-2018 standard relevant to power delivery over
+Ethernet are as follows:
+
+- **802.3af (Approved in 2003-06-12)**: Known as PoE in the market, detailed in
+ Clause 33, delivering up to 15.4W of power.
+- **802.3at (Approved in 2009-09-11)**: Marketed as PoE+, enhancing PoE as
+ covered in Clause 33, increasing power delivery to up to 30W.
+- **802.3bt (Approved in 2018-09-27)**: Known as 4PPoE in the market, outlined
+ in Clause 33. Type 3 delivers up to 60W, and Type 4 up to 100W.
+- **802.3bu (Approved in 2016-12-07)**: Formerly referred to as PoDL, detailed
+ in Clause 104. Introduces Classes 0 - 9. Class 9 PoDL PSE delivers up to ~65W
+
+Kernel Naming Convention Recommendations
+----------------------------------------
+
+For clarity and consistency within the Linux kernel's networking subsystem, the
+following naming conventions are recommended:
+
+- For general PSE (PoE) code, use "c33_pse" key words. For example:
+ ``enum ethtool_c33_pse_admin_state c33_admin_control;``.
+ This aligns with Clause 33, encompassing various PoE forms.
+
+- For PoDL PSE - specific code, use "podl_pse". For example:
+ ``enum ethtool_podl_pse_admin_state podl_admin_control;`` to differentiate
+ PoDL PSE settings according to Clause 104.
+
+Summary of Clause 33: Data Terminal Equipment (DTE) Power via Media Dependent Interface (MDI)
+---------------------------------------------------------------------------------------------
+
+Clause 33 of the IEEE 802.3 standard defines the functional and electrical
+characteristics of Powered Device (PD) and Power Sourcing Equipment (PSE).
+These entities enable power delivery using the same generic cabling as for data
+transmission, integrating power with data communication for devices such as
+10BASE-T, 100BASE-TX, or 1000BASE-T.
+
+Summary of Clause 104: Power over Data Lines (PoDL) of Single Balanced Twisted-Pair Ethernet
+--------------------------------------------------------------------------------------------
+
+Clause 104 of the IEEE 802.3 standard delineates the functional and electrical
+characteristics of PoDL Powered Devices (PDs) and PoDL Power Sourcing Equipment
+(PSEs). These are designed for use with single balanced twisted-pair Ethernet
+Physical Layers. In this clause, 'PSE' refers specifically to PoDL PSE, and
+'PD' to PoDL PD. The key intent is to provide devices with a unified interface
+for both data and the power required to process this data over a single
+balanced twisted-pair Ethernet connection.
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 199cf4ae3cf2..be4e5754eb24 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -17,9 +17,12 @@ struct pse_controller_dev;
*
* @podl_admin_control: set PoDL PSE admin control as described in
* IEEE 802.3-2018 30.15.1.2.1 acPoDLPSEAdminControl
+ * @c33_admin_control: set PSE admin control as described in
+ * IEEE 802.3-2022 30.9.1.2.1 acPSEAdminControl
*/
struct pse_control_config {
enum ethtool_podl_pse_admin_state podl_admin_control;
+ enum ethtool_c33_pse_admin_state c33_admin_control;
};

/**
@@ -29,10 +32,16 @@ struct pse_control_config {
* functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
* @podl_pw_status: power detection status of the PoDL PSE.
* IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus:
+ * @c33_admin_state: operational state of the PSE
+ * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState
+ * @c33_pw_status: power detection status of the PSE.
+ * IEEE 802.3-2022 30.9.1.1.5 aPSEPowerDetectionStatus:
*/
struct 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;
+ enum ethtool_c33_pse_pw_d_status c33_pw_status;
};

/**
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 06ef6b78b7de..d35f044e88eb 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -752,6 +752,49 @@ enum ethtool_module_power_mode {
ETHTOOL_MODULE_POWER_MODE_HIGH,
};

+/**
+ * enum ethtool_c33_pse_admin_state - operational state of the PoDL PSE
+ * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState
+ * @ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN: state of PSE functions is unknown
+ * @ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED: PSE functions are disabled
+ * @ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED: PSE functions are enabled
+ */
+enum ethtool_c33_pse_admin_state {
+ ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN = 1,
+ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED,
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED,
+};
+
+/**
+ * enum ethtool_c33_pse_pw_d_status - power detection status of the PSE.
+ * IEEE 802.3-2022 30.9.1.1.3 aPoDLPSEPowerDetectionStatus:
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN: PSE status is unknown
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED: "The enumeration “disabled”
+ * indicates that the PSE State diagram is in the state DISABLED."
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING: "The enumeration “searching”
+ * indicates the PSE State diagram is in a state other than those
+ * listed."
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING: "The enumeration
+ * “deliveringPower” indicates that the PSE State diagram is in the
+ * state POWER_ON."
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_TEST: "The enumeration “test” indicates that
+ * the PSE State diagram is in the state TEST_MODE."
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_FAULT: "The enumeration “fault” indicates that
+ * the PSE State diagram is in the state TEST_ERROR."
+ * @ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT: "The enumeration “otherFault”
+ * indicates that the PSE State diagram is in the state IDLE due to
+ * the variable error_condition = true."
+ */
+enum ethtool_c33_pse_pw_d_status {
+ ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN = 1,
+ ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED,
+ ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING,
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING,
+ ETHTOOL_C33_PSE_PW_D_STATUS_TEST,
+ ETHTOOL_C33_PSE_PW_D_STATUS_FAULT,
+ ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT,
+};
+
/**
* enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE
* functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 3f89074aa06c..3f32d360beb1 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -895,6 +895,9 @@ enum {
ETHTOOL_A_PODL_PSE_ADMIN_STATE, /* u32 */
ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, /* u32 */
ETHTOOL_A_PODL_PSE_PW_D_STATUS, /* u32 */
+ ETHTOOL_A_C33_PSE_ADMIN_STATE, /* u32 */
+ ETHTOOL_A_C33_PSE_ADMIN_CONTROL, /* u32 */
+ ETHTOOL_A_C33_PSE_PW_D_STATUS, /* u32 */

/* add new constants above here */
__ETHTOOL_A_PSE_CNT,

--
2.25.1


2024-02-08 13:13:38

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 09/17] MAINTAINERS: Add myself to pse networking maintainer

As I add support for PoE in PSE networking subsystem it seems legitimate
to be added to the maintainers.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2dcf48948d2b..7a69f9d3de2d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17670,6 +17670,7 @@ F: net/psample

PSE NETWORK DRIVER
M: Oleksij Rempel <[email protected]>
+M: Kory Maincent <[email protected]>
L: [email protected]
S: Maintained
F: Documentation/devicetree/bindings/net/pse-pd/

--
2.25.1


2024-02-08 13:14:35

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 11/17] net: pse-pd: Add support for PSE PIs

Add support for getting the PSE controller node through PSE PI device
subnode.
This supports adds a way to get the PSE PI id from the pse_pi devicetree
subnode of a PSE controller node simply by reading the reg property.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
drivers/net/pse-pd/pse_core.c | 223 ++++++++++++++++++++++++++++++++++--------
include/linux/pse-pd/pse.h | 38 ++++++-
2 files changed, 217 insertions(+), 44 deletions(-)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 090e04c32f9e..e03d36793d18 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -27,38 +27,137 @@ struct pse_control {
struct kref refcnt;
};

-/**
- * of_pse_zero_xlate - dummy function for controllers with one only control
- * @pcdev: a pointer to the PSE controller device
- * @pse_spec: PSE line specifier as found in the device tree
- *
- * This static translation function is used by default if of_xlate in
- * :c:type:`pse_controller_dev` is not set. It is useful for all PSE
- * controllers with #pse-cells = <0>.
- */
-static int of_pse_zero_xlate(struct pse_controller_dev *pcdev,
- const struct of_phandle_args *pse_spec)
+static int of_load_pse_pi_pairsets(struct device_node *node,
+ struct pse_pi *pi,
+ int npairsets)
{
- return 0;
+ struct device_node *pairset_np;
+ const char *name;
+ int i, ret;
+
+ for (i = 0; i < npairsets; i++) {
+ ret = of_property_read_string_index(node,
+ "pairset-names",
+ i, &name);
+ if (ret)
+ break;
+
+ if (strcmp(name, "alternative-a")) {
+ pi->pairset[i].pinout = ALTERNATIVE_A;
+ } else if (strcmp(name, "alternative-b")) {
+ pi->pairset[i].pinout = ALTERNATIVE_B;
+ } else {
+ pr_err("pse: wrong pairset-names value %s\n", name);
+ ret = -EINVAL;
+ break;
+ }
+
+ pairset_np = of_parse_phandle(node, "pairsets", i);
+ if (!pairset_np) {
+ ret = -ENODEV;
+ break;
+ }
+
+ pi->pairset[i].np = pairset_np;
+ }
+
+ if (i == 2 && pi->pairset[0].pinout == pi->pairset[1].pinout) {
+ pr_err("pse: two PI pairsets can not have identical pinout");
+ ret = -EINVAL;
+ }
+
+ /* If an error appears on the second pairset load, release the first
+ * pairset device node kref
+ */
+ if (ret) {
+ of_node_put(pi->pairset[0].np);
+ pi->pairset[0].np = NULL;
+ of_node_put(pi->pairset[1].np);
+ pi->pairset[1].np = NULL;
+ }
+
+ return ret;
}

-/**
- * of_pse_simple_xlate - translate pse_spec to the PSE line number
- * @pcdev: a pointer to the PSE controller device
- * @pse_spec: PSE line specifier as found in the device tree
- *
- * This static translation function is used by default if of_xlate in
- * :c:type:`pse_controller_dev` is not set. It is useful for all PSE
- * controllers with 1:1 mapping, where PSE lines can be indexed by number
- * without gaps.
- */
-static int of_pse_simple_xlate(struct pse_controller_dev *pcdev,
- const struct of_phandle_args *pse_spec)
+static int of_load_pse_pis(struct pse_controller_dev *pcdev)
{
- if (pse_spec->args[0] >= pcdev->nr_lines)
- return -EINVAL;
+ struct device_node *np = pcdev->dev->of_node;
+ struct device_node *node, *pis;
+ int ret, i;

- return pse_spec->args[0];
+ if (!np)
+ return -ENODEV;
+
+ pcdev->pi = kcalloc(pcdev->nr_lines, sizeof(*pcdev->pi), GFP_KERNEL);
+ if (!pcdev->pi)
+ return -ENOMEM;
+
+ pis = of_get_child_by_name(np, "pse_pis");
+ if (!pis) {
+ /* Legacy OF description of PSE PIs */
+ pcdev->of_legacy = true;
+ return 0;
+ }
+
+ for_each_child_of_node(pis, node) {
+ struct pse_pi pi = {0};
+ int npairsets;
+ u32 id;
+
+ if (!of_node_name_eq(node, "pse_pi"))
+ continue;
+
+ ret = of_property_read_u32(node, "reg", &id);
+ if (ret)
+ goto out;
+
+ if (id >= pcdev->nr_lines || pcdev->pi[id].np) {
+ dev_err(pcdev->dev, "wrong id of pse pi: %u\n",
+ id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = of_property_count_strings(node, "pairset-names");
+ if (ret <= 0)
+ goto out;
+ npairsets = ret;
+
+ ret = of_count_phandle_with_args(node, "pairsets", NULL);
+ if (ret <= 0)
+ goto out;
+
+ /* npairsets is limited to value one or two */
+ if (ret != npairsets || ret > 2) {
+ dev_err(pcdev->dev,
+ "wrong number of pairsets or pairset-names for pse pi %d\n",
+ id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = of_load_pse_pi_pairsets(node, &pi, npairsets);
+ if (ret)
+ goto out;
+
+ of_node_get(node);
+ pi.np = node;
+ memcpy(&pcdev->pi[id], &pi, sizeof(pi));
+ }
+
+ of_node_put(pis);
+ return 0;
+
+out:
+ for (i = 0; i <= pcdev->nr_lines; i++) {
+ of_node_put(pcdev->pi[i].pairset[0].np);
+ of_node_put(pcdev->pi[i].pairset[1].np);
+ of_node_put(pcdev->pi[i].np);
+ }
+ of_node_put(node);
+ of_node_put(pis);
+ kfree(pcdev->pi);
+ return ret;
}

/**
@@ -67,16 +166,18 @@ static int of_pse_simple_xlate(struct pse_controller_dev *pcdev,
*/
int pse_controller_register(struct pse_controller_dev *pcdev)
{
- if (!pcdev->of_xlate) {
- if (pcdev->of_pse_n_cells == 0)
- pcdev->of_xlate = of_pse_zero_xlate;
- else if (pcdev->of_pse_n_cells == 1)
- pcdev->of_xlate = of_pse_simple_xlate;
- }
+ int ret;

mutex_init(&pcdev->lock);
INIT_LIST_HEAD(&pcdev->pse_control_head);

+ if (!pcdev->nr_lines)
+ pcdev->nr_lines = 1;
+
+ ret = of_load_pse_pis(pcdev);
+ if (ret)
+ return ret;
+
mutex_lock(&pse_list_mutex);
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
@@ -91,6 +192,14 @@ EXPORT_SYMBOL_GPL(pse_controller_register);
*/
void pse_controller_unregister(struct pse_controller_dev *pcdev)
{
+ int i;
+
+ for (i = 0; i <= pcdev->nr_lines; i++) {
+ of_node_put(pcdev->pi[i].pairset[0].np);
+ of_node_put(pcdev->pi[i].pairset[1].np);
+ of_node_put(pcdev->pi[i].np);
+ }
+ kfree(pcdev->pi);
mutex_lock(&pse_list_mutex);
list_del(&pcdev->list);
mutex_unlock(&pse_list_mutex);
@@ -203,8 +312,33 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index)
return psec;
}

-struct pse_control *
-of_pse_control_get(struct device_node *node)
+static int of_pse_match_pi(struct pse_controller_dev *pcdev,
+ struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i <= pcdev->nr_lines; i++) {
+ if (pcdev->pi[i].np == np)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int psec_id_legacy_xlate(struct pse_controller_dev *pcdev,
+ const struct of_phandle_args *pse_spec)
+{
+ if (!pcdev->of_pse_n_cells)
+ return 0;
+
+ if (pcdev->of_pse_n_cells > 1 ||
+ pse_spec->args[0] >= pcdev->nr_lines)
+ return -EINVAL;
+
+ return pse_spec->args[0];
+}
+
+struct pse_control *of_pse_control_get(struct device_node *node)
{
struct pse_controller_dev *r, *pcdev;
struct of_phandle_args args;
@@ -222,7 +356,14 @@ of_pse_control_get(struct device_node *node)
mutex_lock(&pse_list_mutex);
pcdev = NULL;
list_for_each_entry(r, &pse_controller_list, list) {
- if (args.np == r->dev->of_node) {
+ if (!r->of_legacy) {
+ ret = of_pse_match_pi(r, args.np);
+ if (ret >= 0) {
+ pcdev = r;
+ psec_id = ret;
+ break;
+ }
+ } else if (args.np == r->dev->of_node) {
pcdev = r;
break;
}
@@ -238,10 +379,12 @@ of_pse_control_get(struct device_node *node)
goto out;
}

- psec_id = pcdev->of_xlate(pcdev, &args);
- if (psec_id < 0) {
- psec = ERR_PTR(psec_id);
- goto out;
+ if (pcdev->of_legacy) {
+ psec_id = psec_id_legacy_xlate(pcdev, &args);
+ if (psec_id < 0) {
+ psec = ERR_PTR(psec_id);
+ goto out;
+ }
}

/* pse_list_mutex also protects the pcdev's pse_control list */
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index f006cbdf8b3b..6c48668cebe3 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -65,6 +65,36 @@ struct device_node;
struct of_phandle_args;
struct pse_control;

+/* PSE PI pairset pinout can either be Alternative A or Alternative B */
+enum pse_pi_pairset_pinout {
+ ALTERNATIVE_A,
+ ALTERNATIVE_B,
+};
+
+/**
+ * struct pse_pi_pairset - PSE PI pairset entity describing the pinout
+ * alternative ant its phandle
+ *
+ * @pinout: description of the pinout alternative
+ * @np: device node pointer describing the pairset phandle
+ */
+struct pse_pi_pairset {
+ enum pse_pi_pairset_pinout pinout;
+ struct device_node *np;
+};
+
+/**
+ * struct pse_pi - PSE PI (Power Interface) entity as described in
+ * IEEE 802.3-2022 145.2.4
+ *
+ * @pairset: table of the PSE PI pinout alternative for the two pairset
+ * @np: device node pointer of the PSE PI node
+ */
+struct pse_pi {
+ struct pse_pi_pairset pairset[2];
+ struct device_node *np;
+};
+
/**
* struct pse_controller_dev - PSE controller entity that might
* provide multiple PSE controls
@@ -74,11 +104,11 @@ struct pse_control;
* @pse_control_head: head of internal list of requested PSE controls
* @dev: corresponding driver model device struct
* @of_pse_n_cells: number of cells in PSE line specifiers
- * @of_xlate: translation function to translate from specifier as found in the
- * device tree to id as given to the PSE control ops
* @nr_lines: number of PSE controls in this controller device
* @lock: Mutex for serialization access to the PSE controller
* @types: types of the PSE controller
+ * @pi: table of PSE PIs described in this controller device
+ * @of_legacy: flag set if the pse_pis devicetree node is not used
*/
struct pse_controller_dev {
const struct pse_controller_ops *ops;
@@ -87,11 +117,11 @@ struct pse_controller_dev {
struct list_head pse_control_head;
struct device *dev;
int of_pse_n_cells;
- int (*of_xlate)(struct pse_controller_dev *pcdev,
- const struct of_phandle_args *pse_spec);
unsigned int nr_lines;
struct mutex lock;
u32 types;
+ struct pse_pi *pi;
+ bool of_legacy;
};

#if IS_ENABLED(CONFIG_PSE_CONTROLLER)

--
2.25.1


2024-02-08 13:14:36

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 12/17] net: pse-pd: Add support for setup_pi_matrix callback

Implement setup_pi_matrix callback to configure the PSE PI matrix. This
functionality is invoked before registering the PSE and following the core
parsing of the pse_pis devicetree subnode.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
drivers/net/pse-pd/pse_core.c | 6 ++++++
include/linux/pse-pd/pse.h | 2 ++
2 files changed, 8 insertions(+)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index e03d36793d18..59427c79858c 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -178,6 +178,12 @@ int pse_controller_register(struct pse_controller_dev *pcdev)
if (ret)
return ret;

+ if (pcdev->ops->setup_pi_matrix) {
+ ret = pcdev->ops->setup_pi_matrix(pcdev);
+ if (ret)
+ return ret;
+ }
+
mutex_lock(&pse_list_mutex);
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 6c48668cebe3..cf0eebb3b0a9 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -50,6 +50,7 @@ struct pse_control_status {
*
* @ethtool_get_status: get PSE control status for ethtool interface
* @ethtool_set_config: set PSE control configuration over ethtool interface
+ * @setup_pi_matrix: setup PI matrix of the PSE controller
*/
struct pse_controller_ops {
int (*ethtool_get_status)(struct pse_controller_dev *pcdev,
@@ -58,6 +59,7 @@ struct pse_controller_ops {
int (*ethtool_set_config)(struct pse_controller_dev *pcdev,
unsigned long id, struct netlink_ext_ack *extack,
const struct pse_control_config *config);
+ int (*setup_pi_matrix)(struct pse_controller_dev *pcdev);
};

struct module;

--
2.25.1


2024-02-08 13:17:59

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

Before hand we set "#pse-cell" to 1 to define a PSE controller with
several PIs (Power Interface). The drawback of this was that we could not
have any information on the PI except its number.
Add support for pse_pis and pse_pi node to be able to have more information
on the PI like the number of pairset used and the pairset pinout.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
.../bindings/net/pse-pd/pse-controller.yaml | 101 ++++++++++++++++++++-
1 file changed, 98 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml b/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
index 2d382faca0e6..dd5fb53e527a 100644
--- a/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
+++ b/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
@@ -13,6 +13,7 @@ description: Binding for the Power Sourcing Equipment (PSE) as defined in the

maintainers:
- Oleksij Rempel <[email protected]>
+ - Kory Maincent <[email protected]>

properties:
$nodename:
@@ -22,11 +23,105 @@ properties:
description:
Used to uniquely identify a PSE instance within an IC. Will be
0 on PSE nodes with only a single output and at least 1 on nodes
- controlling several outputs.
+ controlling several outputs which are not described in the pse_pis
+ subnode. This property is deprecated, please use pse_pis instead.
enum: [0, 1]

-required:
- - "#pse-cells"
+ pse_pis:
+ $ref: "#/$defs/pse_pis"
+
+$defs:
+ pse_pis:
+ type: object
+ description:
+ Kind of a matrix to identify the concordance between a PSE Power
+ Interface and one or two (PoE4) physical ports.
+
+ properties:
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ patternProperties:
+ "^pse_pi@[0-9]+$":
+ $ref: "#/$defs/pse_pi"
+
+ required:
+ - "#address-cells"
+ - "#size-cells"
+
+ pse_pi:
+ description:
+ PSE PI device for power delivery via pairsets, compliant with IEEE
+ 802.3-2022, Section 145.2.4. Each pairset comprises a positive and a
+ negative VPSE pair, adhering to the pinout configurations detailed in
+ the standard.
+ type: object
+ properties:
+ reg:
+ maxItems: 1
+
+ "#pse-cells":
+ const: 0
+
+ pairset-names:
+ description:
+ Names of the pairsets as per IEEE 802.3-2022, Section 145.2.4. Valid
+ values are "alternative-a" and "alternative-b". Each name should
+ correspond to a phandle in the 'pairset' property pointing to the
+ power supply for that pairset.
+ $ref: /schemas/types.yaml#/definitions/string-array
+ minItems: 1
+ maxItems: 2
+ items:
+ - enum:
+ - "alternative-a"
+ - "alternative-b"
+
+ pairsets:
+ description:
+ List of phandles, each pointing to the power supply for the
+ corresponding pairset named in 'pairset-names'. This property aligns
+ with IEEE 802.3-2022, Section 33.2.3 and 145.2.4.
+ PSE Pinout Alternatives (as per IEEE 802.3-2022 Table 145–3)
+ | Conductor | Alternative A (MDI-X) | Alternative A (MDI) | Alternative B(X) | Alternative B(S) |
+ |-----------|-----------------------|---------------------|------------------|------------------|
+ | 1 | Negative VPSE | Positive VPSE | — | — |
+ | 2 | Negative VPSE | Positive VPSE | — | — |
+ | 3 | Positive VPSE | Negative VPSE | — | — |
+ | 4 | — | — | Negative VPSE | Positive VPSE |
+ | 5 | — | — | Negative VPSE | Positive VPSE |
+ | 6 | Positive VPSE | Negative VPSE | — | — |
+ | 7 | — | — | Positive VPSE | Negative VPSE |
+ | 8 | — | — | Positive VPSE | Negative VPSE |
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ minItems: 1
+ maxItems: 2
+
+ required:
+ - reg
+ - "#pse-cells"
+ - pairset-names
+ - pairsets
+
+allOf:
+ - if:
+ required:
+ - "#pse-cells"
+ then:
+ not:
+ required:
+ - pse-pis
+
+ - if:
+ required:
+ - pse-pis
+ then:
+ not:
+ required:
+ - "#pse-cells"

additionalProperties: true


--
2.25.1


2024-02-08 13:18:57

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 16/17] dt-bindings: net: pse-pd: Add bindings for TPS23881 PSE controller

Add the TPS23881 I2C Power Sourcing Equipment controller device tree
bindings documentation.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>

---
Change in v3:
- New patch.
---
.../bindings/net/pse-pd/ti,tps2388x.yaml | 112 +++++++++++++++++++++
1 file changed, 112 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/pse-pd/ti,tps2388x.yaml b/Documentation/devicetree/bindings/net/pse-pd/ti,tps2388x.yaml
new file mode 100644
index 000000000000..ccd1a80befa5
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/pse-pd/ti,tps2388x.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/pse-pd/ti,tps2388x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI TPS2388x Power Sourcing Equipment controller
+
+maintainers:
+ - Kory Maincent <[email protected]>
+
+allOf:
+ - $ref: pse-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ti,tps23880
+ - ti,tps23881
+
+ reg:
+ maxItems: 1
+
+ '#pse-cells':
+ const: 1
+
+ channels:
+ description: each set of 8 ports can be assigned to one physical
+ channels or two for PoE4. This parameter describes the configuration
+ of the ports conversion matrix that establishes relationship between
+ the logical ports and the physical channels.
+ type: object
+
+ patternProperties:
+ '^channel@[0-7]$':
+ type: object
+ required:
+ - reg
+
+unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-pse@20 {
+ compatible = "ti,tps2388x";
+ reg = <0x20>;
+
+ channels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phys0: channel@0 {
+ reg = <0>;
+ };
+
+ phys1: channel@1 {
+ reg = <1>;
+ };
+
+ phys2: channel@2 {
+ reg = <2>;
+ };
+
+ phys3: channel@3 {
+ reg = <3>;
+ };
+
+ phys4: channel@4 {
+ reg = <4>;
+ };
+
+ phys5: channel@5 {
+ reg = <5>;
+ };
+
+ phys6: channel@6 {
+ reg = <6>;
+ };
+
+ phys7: channel@7 {
+ reg = <7>;
+ };
+ };
+
+ pse_pis {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pse_pi0: pse_pi@0 {
+ reg = <0>;
+ #pse-cells = <0>;
+ pairset-names = "alternative-a", "alternative-b";
+ pairsets = <&phys0>, <&phys1>;
+ };
+
+ pse_pi1: pse_pi@1 {
+ reg = <1>;
+ #pse-cells = <0>;
+ pairset-names = "alternative-a";
+ pairsets = <&phys2>;
+ };
+ };
+ };
+ };

--
2.25.1


2024-02-08 13:19:08

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 15/17] net: pse-pd: Add PD692x0 PSE controller driver

Add a new driver for the PD692x0 I2C Power Sourcing Equipment controller.
This driver only support i2c communication for now.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>

---
Change in v2:
- Drop of_match_ptr
- Follow the "c33" PoE prefix naming change.
- Remove unused delay_recv variable. Then, remove struct pd692x0_msg_content
which is similar to struct pd692x0_msg.
- Fix a weird sleep loop.
- Improve pd692x0_recv_msg for better readability.
- Fix a warning reported by Simon on a pd692x0_fw_write_line call.

Change in v3:
- Fix few nit.
- Change the return value of pd692x0_try_recv_msg function.
- Replace a dev_err() function to dev_err_probe().
- Replace pd692x0_update_matrix by the newly introduced setup_pi_matrix callback.
- Follow new bindings of managers subnode description.
- Update the pse ops with the newly introduced pi_enable, pi_is_enabled and
pi_disable ones.
- Replace firmware version check by a softer one (< instead of =!).
- Rearrange the probe function, which had wrong ordering on error case
between pcdcev registering and software version read.
- Fixed the probe function in the case it is call two times. The status
won't be sent automatically on the second times so ask for it.
---
drivers/net/pse-pd/Kconfig | 11 +
drivers/net/pse-pd/Makefile | 1 +
drivers/net/pse-pd/pd692x0.c | 1223 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1235 insertions(+)

diff --git a/drivers/net/pse-pd/Kconfig b/drivers/net/pse-pd/Kconfig
index 687dec49c1e1..e3a6ba669f20 100644
--- a/drivers/net/pse-pd/Kconfig
+++ b/drivers/net/pse-pd/Kconfig
@@ -20,4 +20,15 @@ config PSE_REGULATOR
Sourcing Equipment without automatic classification support. For
example for basic implementation of PoDL (802.3bu) specification.

+config PSE_PD692X0
+ tristate "PD692X0 PSE controller"
+ depends on I2C
+ select FW_UPLOAD
+ help
+ This module provides support for PD692x0 regulator based Ethernet
+ Power Sourcing Equipment.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pd692x0.
+
endif
diff --git a/drivers/net/pse-pd/Makefile b/drivers/net/pse-pd/Makefile
index 1b8aa4c70f0b..9c12c4a65730 100644
--- a/drivers/net/pse-pd/Makefile
+++ b/drivers/net/pse-pd/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o

obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o
+obj-$(CONFIG_PSE_PD692X0) += pd692x0.o
diff --git a/drivers/net/pse-pd/pd692x0.c b/drivers/net/pse-pd/pd692x0.c
new file mode 100644
index 000000000000..dc511c925ff1
--- /dev/null
+++ b/drivers/net/pse-pd/pd692x0.c
@@ -0,0 +1,1223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the Microchip PD692X0 PoE PSE Controller driver (I2C bus)
+ *
+ * Copyright (c) 2023 Bootlin, Kory Maincent <[email protected]>
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pse-pd/pse.h>
+
+#define PD692X0_PSE_NAME "pd692x0_pse"
+
+#define PD692X0_MAX_PIS 48
+#define PD692X0_MAX_MANAGERS 12
+#define PD692X0_MAX_MANAGER_PORTS 8
+#define PD692X0_MAX_HW_PORTS (PD692X0_MAX_MANAGERS * PD692X0_MAX_MANAGER_PORTS)
+
+#define PD69200_BT_PROD_VER 24
+#define PD69210_BT_PROD_VER 26
+#define PD69220_BT_PROD_VER 29
+
+#define PD692X0_FW_MAJ_VER 3
+#define PD692X0_FW_MIN_VER 5
+#define PD692X0_FW_PATCH_VER 5
+
+enum pd692x0_fw_state {
+ PD692X0_FW_UNKNOWN,
+ PD692X0_FW_OK,
+ PD692X0_FW_BROKEN,
+ PD692X0_FW_NEED_UPDATE,
+ PD692X0_FW_PREPARE,
+ PD692X0_FW_WRITE,
+ PD692X0_FW_COMPLETE,
+};
+
+struct pd692x0_msg {
+ u8 key;
+ u8 echo;
+ u8 sub[3];
+ u8 data[8];
+ __be16 chksum;
+} __packed;
+
+struct pd692x0_msg_ver {
+ u8 prod;
+ u8 maj_sw_ver;
+ u8 min_sw_ver;
+ u8 pa_sw_ver;
+ u8 param;
+ u8 build;
+};
+
+enum {
+ PD692X0_KEY_CMD,
+ PD692X0_KEY_PRG,
+ PD692X0_KEY_REQ,
+ PD692X0_KEY_TLM,
+ PD692X0_KEY_TEST,
+ PD692X0_KEY_REPORT = 0x52
+};
+
+enum {
+ PD692X0_MSG_RESET,
+ PD692X0_MSG_GET_SYS_STATUS,
+ PD692X0_MSG_GET_SW_VER,
+ PD692X0_MSG_SET_TMP_PORT_MATRIX,
+ PD692X0_MSG_PRG_PORT_MATRIX,
+ PD692X0_MSG_SET_PORT_PARAM,
+ PD692X0_MSG_GET_PORT_STATUS,
+ PD692X0_MSG_DOWNLOAD_CMD,
+
+ /* add new message above here */
+ PD692X0_MSG_CNT
+};
+
+struct pd692x0_priv {
+ struct i2c_client *client;
+ struct pse_controller_dev pcdev;
+ struct device_node *np;
+
+ enum pd692x0_fw_state fw_state;
+ struct fw_upload *fwl;
+ bool cancel_request;
+
+ u8 msg_id;
+ bool last_cmd_key;
+ unsigned long last_cmd_key_time;
+
+ enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS];
+};
+
+/* Template list of communication messages. The non-null bytes defined here
+ * constitute the fixed portion of the messages. The remaining bytes will
+ * be configured later within the functions. Refer to the "PD692x0 BT Serial
+ * Communication Protocol User Guide" for comprehensive details on messages
+ * content.
+ */
+static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = {
+ [PD692X0_MSG_RESET] = {
+ .key = PD692X0_KEY_CMD,
+ .sub = {0x07, 0x55, 0x00},
+ .data = {0x55, 0x00, 0x55, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_GET_SYS_STATUS] = {
+ .key = PD692X0_KEY_REQ,
+ .sub = {0x07, 0xd0, 0x4e},
+ .data = {0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_GET_SW_VER] = {
+ .key = PD692X0_KEY_REQ,
+ .sub = {0x07, 0x1e, 0x21},
+ .data = {0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_SET_TMP_PORT_MATRIX] = {
+ .key = PD692X0_KEY_CMD,
+ .sub = {0x05, 0x43},
+ .data = { 0, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_PRG_PORT_MATRIX] = {
+ .key = PD692X0_KEY_CMD,
+ .sub = {0x07, 0x43, 0x4e},
+ .data = {0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_SET_PORT_PARAM] = {
+ .key = PD692X0_KEY_CMD,
+ .sub = {0x05, 0xc0},
+ .data = { 0, 0xff, 0xff, 0xff,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_GET_PORT_STATUS] = {
+ .key = PD692X0_KEY_REQ,
+ .sub = {0x05, 0xc1},
+ .data = {0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+ [PD692X0_MSG_DOWNLOAD_CMD] = {
+ .key = PD692X0_KEY_PRG,
+ .sub = {0xff, 0x99, 0x15},
+ .data = {0x16, 0x16, 0x99, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
+};
+
+static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo)
+{
+ u8 *data = (u8 *)msg;
+ u16 chksum = 0;
+ int i;
+
+ msg->echo = echo++;
+ if (echo == 0xff)
+ echo = 0;
+
+ for (i = 0; i < sizeof(*msg) - sizeof(msg->chksum); i++)
+ chksum += data[i];
+
+ msg->chksum = cpu_to_be16(chksum);
+
+ return echo;
+}
+
+static int pd692x0_send_msg(struct pd692x0_priv *priv, struct pd692x0_msg *msg)
+{
+ const struct i2c_client *client = priv->client;
+ int ret;
+
+ if (msg->key == PD692X0_KEY_CMD && priv->last_cmd_key) {
+ int cmd_msleep;
+
+ cmd_msleep = 30 - jiffies_to_msecs(jiffies - priv->last_cmd_key_time);
+ if (cmd_msleep > 0)
+ msleep(cmd_msleep);
+ }
+
+ /* Add echo and checksum bytes to the message */
+ priv->msg_id = pd692x0_build_msg(msg, priv->msg_id);
+
+ ret = i2c_master_send(client, (u8 *)msg, sizeof(*msg));
+ if (ret != sizeof(*msg))
+ return -EIO;
+
+ return 0;
+}
+
+static int pd692x0_reset(struct pd692x0_priv *priv)
+{
+ const struct i2c_client *client = priv->client;
+ struct pd692x0_msg msg, buf = {0};
+ int ret;
+
+ msg = pd692x0_msg_template_list[PD692X0_MSG_RESET];
+ ret = pd692x0_send_msg(priv, &msg);
+ if (ret) {
+ dev_err(&client->dev,
+ "Failed to reset the controller (%pe)\n", ERR_PTR(ret));
+ return ret;
+ }
+
+ msleep(30);
+
+ ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
+ if (ret != sizeof(buf))
+ return ret < 0 ? ret : -EIO;
+
+ /* Is the reply a successful report message */
+ if (buf.key != PD692X0_KEY_REPORT || buf.sub[0] || buf.sub[1])
+ return -EIO;
+
+ msleep(300);
+
+ ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
+ if (ret != sizeof(buf))
+ return ret < 0 ? ret : -EIO;
+
+ /* Is the boot status without error */
+ if (buf.key != 0x03 || buf.echo != 0xff || buf.sub[0] & 0x1) {
+ dev_err(&client->dev, "PSE controller error\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static bool pd692x0_try_recv_msg(const struct i2c_client *client,
+ struct pd692x0_msg *msg,
+ struct pd692x0_msg *buf)
+{
+ /* Wait 30ms before readback as mandated by the protocol */
+ msleep(30);
+
+ memset(buf, 0, sizeof(*buf));
+ i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
+ if (buf->key)
+ return 0;
+
+ msleep(100);
+
+ memset(buf, 0, sizeof(*buf));
+ i2c_master_recv(client, (u8 *)buf, sizeof(*buf));
+ if (buf->key)
+ return 0;
+
+ return 1;
+}
+
+/* Implementation of I2C communication, specifically addressing scenarios
+ * involving communication loss. Refer to the "Synchronization During
+ * Communication Loss" section in the Communication Protocol document for
+ * further details.
+ */
+static int pd692x0_recv_msg(struct pd692x0_priv *priv,
+ struct pd692x0_msg *msg,
+ struct pd692x0_msg *buf)
+{
+ const struct i2c_client *client = priv->client;
+ int ret;
+
+ ret = pd692x0_try_recv_msg(client, msg, buf);
+ if (!ret)
+ goto out_success;
+
+ dev_warn(&client->dev,
+ "Communication lost, rtnl is locked until communication is back!");
+
+ ret = pd692x0_send_msg(priv, msg);
+ if (ret)
+ return ret;
+
+ ret = pd692x0_try_recv_msg(client, msg, buf);
+ if (!ret)
+ goto out_success2;
+
+ msleep(10000);
+
+ ret = pd692x0_send_msg(priv, msg);
+ if (ret)
+ return ret;
+
+ ret = pd692x0_try_recv_msg(client, msg, buf);
+ if (!ret)
+ goto out_success2;
+
+ return pd692x0_reset(priv);
+
+out_success2:
+ dev_warn(&client->dev, "Communication is back, rtnl is unlocked!");
+out_success:
+ if (msg->key == PD692X0_KEY_CMD) {
+ priv->last_cmd_key = true;
+ priv->last_cmd_key_time = jiffies;
+ } else {
+ priv->last_cmd_key = false;
+ }
+
+ return 0;
+}
+
+static int pd692x0_sendrecv_msg(struct pd692x0_priv *priv,
+ struct pd692x0_msg *msg,
+ struct pd692x0_msg *buf)
+{
+ struct device *dev = &priv->client->dev;
+ int ret;
+
+ ret = pd692x0_send_msg(priv, msg);
+ if (ret)
+ return ret;
+
+ ret = pd692x0_recv_msg(priv, msg, buf);
+ if (ret)
+ return ret;
+
+ if (msg->echo != buf->echo) {
+ dev_err(dev,
+ "Wrong match in message ID, expect %d received %d.\n",
+ msg->echo, buf->echo);
+ return -EIO;
+ }
+
+ /* If the reply is a report message is it successful */
+ if (buf->key == PD692X0_KEY_REPORT &&
+ (buf->sub[0] || buf->sub[1])) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct pd692x0_priv *to_pd692x0_priv(struct pse_controller_dev *pcdev)
+{
+ return container_of(pcdev, struct pd692x0_priv, pcdev);
+}
+
+static int pd692x0_fw_unavailable(struct pd692x0_priv *priv)
+{
+ switch (priv->fw_state) {
+ case PD692X0_FW_OK:
+ return 0;
+ case PD692X0_FW_PREPARE:
+ case PD692X0_FW_WRITE:
+ case PD692X0_FW_COMPLETE:
+ dev_err(&priv->client->dev, "Firmware update in progress!\n");
+ return -EBUSY;
+ case PD692X0_FW_BROKEN:
+ case PD692X0_FW_NEED_UPDATE:
+ default:
+ dev_err(&priv->client->dev,
+ "Firmware issue. Please update it!\n");
+ return -EOPNOTSUPP;
+ }
+}
+
+static int pd692x0_pi_enable(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;
+
+ if (priv->admin_state[id] == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED)
+ return 0;
+
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM];
+ msg.data[0] = 0x1;
+ msg.sub[2] = id;
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0)
+ return ret;
+
+ priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
+
+ return 0;
+}
+
+static int pd692x0_pi_disable(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;
+
+ if (priv->admin_state[id] == ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED)
+ return 0;
+
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM];
+ msg.data[0] = 0x0;
+ msg.sub[2] = id;
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0)
+ return ret;
+
+ priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
+
+ 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;
+ }
+}
+
+static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev,
+ unsigned long id,
+ struct netlink_ext_ack *extack,
+ struct pse_control_status *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;
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ 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;
+ else
+ status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
+
+ priv->admin_state[id] = status->c33_admin_state;
+
+ return 0;
+}
+
+static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct pd692x0_msg msg, buf = {0};
+ struct pd692x0_msg_ver ver = {0};
+ int ret;
+
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SW_VER];
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get PSE version (%pe)\n", ERR_PTR(ret));
+ return ver;
+ }
+
+ /* Extract version from the message */
+ ver.prod = buf.sub[2];
+ ver.maj_sw_ver = (buf.data[0] << 8 | buf.data[1]) / 100;
+ ver.min_sw_ver = ((buf.data[0] << 8 | buf.data[1]) / 10) % 10;
+ ver.pa_sw_ver = (buf.data[0] << 8 | buf.data[1]) % 10;
+ ver.param = buf.data[2];
+ ver.build = buf.data[3];
+
+ return ver;
+}
+
+struct pd692x0_manager {
+ struct device_node *port_node[PD692X0_MAX_MANAGER_PORTS];
+ 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,
+ struct device_node *np)
+{
+ struct device_node *node;
+ int ret, nports, i;
+
+ nports = 0;
+ for_each_child_of_node(np, node) {
+ u32 port;
+
+ if (!of_node_name_eq(node, "port"))
+ continue;
+
+ ret = of_property_read_u32(node, "reg", &port);
+ if (ret)
+ goto out;
+
+ if (port >= PD692X0_MAX_MANAGER_PORTS || port != nports) {
+ dev_err(&priv->client->dev,
+ "wrong number or order of manager ports (%d)\n",
+ port);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ of_node_get(node);
+ manager->port_node[port] = node;
+ nports++;
+ }
+
+ manager->nports = nports;
+ return 0;
+
+out:
+ for (i = 0; i < nports; i++) {
+ of_node_put(manager->port_node[i]);
+ manager->port_node[i] = NULL;
+ }
+ of_node_put(node);
+ return ret;
+}
+
+static int
+pd692x0_of_get_managers(struct pd692x0_priv *priv,
+ struct pd692x0_manager manager[PD692X0_MAX_MANAGERS])
+{
+ struct device_node *managers_node, *node;
+ int ret, nmanagers, i, j;
+
+ if (!priv->np)
+ return -EINVAL;
+
+ nmanagers = 0;
+ managers_node = of_get_child_by_name(priv->np, "managers");
+ if (!managers_node)
+ return -EINVAL;
+
+ for_each_child_of_node(managers_node, node) {
+ u32 manager_id;
+
+ if (!of_node_name_eq(node, "manager"))
+ continue;
+
+ ret = of_property_read_u32(node, "reg", &manager_id);
+ if (ret)
+ goto out;
+
+ if (manager_id >= PD692X0_MAX_MANAGERS ||
+ manager_id != nmanagers) {
+ dev_err(&priv->client->dev,
+ "wrong number or order of managers (%d)\n",
+ manager_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = pd692x0_of_get_ports_manager(priv, &manager[manager_id],
+ node);
+ if (ret)
+ goto out;
+
+ nmanagers++;
+ }
+
+ of_node_put(managers_node);
+ return nmanagers;
+
+out:
+ for (i = 0; i < nmanagers; i++) {
+ for (j = 0; j < manager[i].nports; j++) {
+ of_node_put(manager[i].port_node[j]);
+ manager[i].port_node[j] = NULL;
+ }
+ }
+
+ of_node_put(node);
+ of_node_put(managers_node);
+ return ret;
+}
+
+static int
+pd692x0_set_port_matrix(const struct pse_pi_pairset *pairset,
+ const struct pd692x0_manager *manager,
+ int nmanagers, struct pd692x0_matrix *port_matrix)
+{
+ int i, j, port_cnt;
+ bool found = false;
+
+ if (!pairset->np)
+ return 0;
+
+ /* Look on every managers */
+ port_cnt = 0;
+ for (i = 0; i < nmanagers; i++) {
+ /* Look on every ports of the manager */
+ for (j = 0; j < manager[i].nports; j++) {
+ if (pairset->np == manager[i].port_node[j]) {
+ found = true;
+ break;
+ }
+ }
+ port_cnt += j;
+
+ if (found)
+ break;
+ }
+
+ if (!found)
+ return -ENODEV;
+
+ if (pairset->pinout == ALTERNATIVE_A)
+ port_matrix->hw_port_a = port_cnt;
+ else if (pairset->pinout == ALTERNATIVE_B)
+ port_matrix->hw_port_b = port_cnt;
+
+ return 0;
+}
+
+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])
+{
+ struct pse_controller_dev *pcdev = &priv->pcdev;
+ int i, ret;
+
+ /* Init Matrix */
+ for (i = 0; i < PD692X0_MAX_PIS; i++) {
+ port_matrix[i].hw_port_a = 0xff;
+ port_matrix[i].hw_port_b = 0xff;
+ }
+
+ /* 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,
+ &port_matrix[i]);
+ if (ret) {
+ dev_err(&priv->client->dev,
+ "unable to configure pi %d pairset 0", i);
+ return ret;
+ }
+
+ ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[1],
+ manager, nmanagers,
+ &port_matrix[i]);
+ if (ret) {
+ dev_err(&priv->client->dev,
+ "unable to configure pi %d pairset 1", i);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int
+pd692x0_write_ports_matrix(struct pd692x0_priv *priv,
+ const struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
+{
+ struct pd692x0_msg msg, buf;
+ int ret, i;
+
+ /* Write temporary Matrix */
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_TMP_PORT_MATRIX];
+ for (i = 0; i < PD692X0_MAX_PIS; i++) {
+ msg.sub[2] = i;
+ msg.data[0] = port_matrix[i].hw_port_b;
+ msg.data[1] = port_matrix[i].hw_port_a;
+
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Program Matrix */
+ msg = pd692x0_msg_template_list[PD692X0_MSG_PRG_PORT_MATRIX];
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
+{
+ struct pd692x0_manager manager[PD692X0_MAX_MANAGERS] = {0};
+ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
+ struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
+ int ret, i, j, nmanagers;
+
+ /* Should we flash the port matrix */
+ if (priv->fw_state != PD692X0_FW_OK &&
+ priv->fw_state != PD692X0_FW_COMPLETE)
+ return 0;
+
+ ret = pd692x0_of_get_managers(priv, manager);
+ if (ret < 0)
+ return ret;
+
+ nmanagers = ret;
+ ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix);
+ if (ret)
+ goto out;
+
+ ret = pd692x0_write_ports_matrix(priv, port_matrix);
+ if (ret)
+ goto out;
+
+out:
+ for (i = 0; i < nmanagers; i++) {
+ for (j = 0; j < manager[i].nports; j++)
+ of_node_put(manager[i].port_node[j]);
+ }
+ return ret;
+}
+
+static const struct pse_controller_ops pd692x0_ops = {
+ .setup_pi_matrix = pd692x0_setup_pi_matrix,
+ .ethtool_get_status = pd692x0_ethtool_get_status,
+ .pi_enable = pd692x0_pi_enable,
+ .pi_disable = pd692x0_pi_disable,
+ .pi_is_enabled = pd692x0_pi_is_enabled,
+};
+
+#define PD692X0_FW_LINE_MAX_SZ 0xff
+static int pd692x0_fw_get_next_line(const u8 *data,
+ char *line, size_t size)
+{
+ size_t line_size;
+ int i;
+
+ line_size = min_t(size_t, size, PD692X0_FW_LINE_MAX_SZ);
+
+ memset(line, 0, PD692X0_FW_LINE_MAX_SZ);
+ for (i = 0; i < line_size - 1; i++) {
+ if (*data == '\r' && *(data + 1) == '\n') {
+ line[i] = '\r';
+ line[i + 1] = '\n';
+ return i + 2;
+ }
+ line[i] = *data;
+ data++;
+ }
+
+ return -EIO;
+}
+
+static enum fw_upload_err
+pd692x0_fw_recv_resp(const struct i2c_client *client, unsigned long ms_timeout,
+ const char *msg_ok, unsigned int msg_size)
+{
+ /* Maximum controller response size */
+ char fw_msg_buf[5] = {0};
+ unsigned long timeout;
+ int ret;
+
+ if (msg_size > sizeof(fw_msg_buf))
+ return FW_UPLOAD_ERR_RW_ERROR;
+
+ /* Read until we get something */
+ timeout = msecs_to_jiffies(ms_timeout) + jiffies;
+ while (true) {
+ if (time_is_before_jiffies(timeout))
+ return FW_UPLOAD_ERR_TIMEOUT;
+
+ ret = i2c_master_recv(client, fw_msg_buf, 1);
+ if (ret < 0 || *fw_msg_buf == 0) {
+ usleep_range(1000, 2000);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ /* Read remaining characters */
+ ret = i2c_master_recv(client, fw_msg_buf + 1, msg_size - 1);
+ if (strncmp(fw_msg_buf, msg_ok, msg_size)) {
+ dev_err(&client->dev,
+ "Wrong FW download process answer (%*pE)\n",
+ msg_size, fw_msg_buf);
+ return FW_UPLOAD_ERR_HW_ERROR;
+ }
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static int pd692x0_fw_write_line(const struct i2c_client *client,
+ const char line[PD692X0_FW_LINE_MAX_SZ],
+ const bool last_line)
+{
+ int ret;
+
+ while (*line != 0) {
+ ret = i2c_master_send(client, line, 1);
+ if (ret < 0)
+ return FW_UPLOAD_ERR_RW_ERROR;
+ line++;
+ }
+
+ if (last_line) {
+ ret = pd692x0_fw_recv_resp(client, 100, "TP\r\n",
+ sizeof("TP\r\n") - 1);
+ if (ret)
+ return ret;
+ } else {
+ ret = pd692x0_fw_recv_resp(client, 100, "T*\r\n",
+ sizeof("T*\r\n") - 1);
+ if (ret)
+ return ret;
+ }
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err pd692x0_fw_reset(const struct i2c_client *client)
+{
+ const struct pd692x0_msg zero = {0};
+ struct pd692x0_msg buf = {0};
+ unsigned long timeout;
+ char cmd[] = "RST";
+ int ret;
+
+ ret = i2c_master_send(client, cmd, strlen(cmd));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to reset the controller (%pe)\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ timeout = msecs_to_jiffies(10000) + jiffies;
+ while (true) {
+ if (time_is_before_jiffies(timeout))
+ return FW_UPLOAD_ERR_TIMEOUT;
+
+ ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
+ if (ret < 0 ||
+ !memcmp(&buf, &zero, sizeof(buf)))
+ usleep_range(1000, 2000);
+ else
+ break;
+ }
+
+ /* Is the reply a successful report message */
+ if (buf.key != PD692X0_KEY_TLM || buf.echo != 0xff ||
+ buf.sub[0] & 0x01) {
+ dev_err(&client->dev, "PSE controller error\n");
+ return FW_UPLOAD_ERR_HW_ERROR;
+ }
+
+ /* Is the firmware operational */
+ if (buf.sub[0] & 0x02) {
+ dev_err(&client->dev,
+ "PSE firmware error. Please update it.\n");
+ return FW_UPLOAD_ERR_HW_ERROR;
+ }
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err pd692x0_fw_prepare(struct fw_upload *fwl,
+ const u8 *data, u32 size)
+{
+ struct pd692x0_priv *priv = fwl->dd_handle;
+ const struct i2c_client *client = priv->client;
+ enum pd692x0_fw_state last_fw_state;
+ int ret;
+
+ priv->cancel_request = false;
+ last_fw_state = priv->fw_state;
+
+ priv->fw_state = PD692X0_FW_PREPARE;
+
+ /* Enter program mode */
+ if (last_fw_state == PD692X0_FW_BROKEN) {
+ const char *msg = "ENTR";
+ const char *c;
+
+ c = msg;
+ do {
+ ret = i2c_master_send(client, c, 1);
+ if (ret < 0)
+ return FW_UPLOAD_ERR_RW_ERROR;
+ if (*(c + 1))
+ usleep_range(10000, 20000);
+ } while (*(++c));
+ } else {
+ struct pd692x0_msg msg, buf;
+
+ msg = pd692x0_msg_template_list[PD692X0_MSG_DOWNLOAD_CMD];
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to enter programming mode (%pe)\n",
+ ERR_PTR(ret));
+ return FW_UPLOAD_ERR_RW_ERROR;
+ }
+ }
+
+ ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1);
+ if (ret)
+ goto err_out;
+
+ if (priv->cancel_request) {
+ ret = FW_UPLOAD_ERR_CANCELED;
+ goto err_out;
+ }
+
+ return FW_UPLOAD_ERR_NONE;
+
+err_out:
+ pd692x0_fw_reset(priv->client);
+ priv->fw_state = last_fw_state;
+ return ret;
+}
+
+static enum fw_upload_err pd692x0_fw_write(struct fw_upload *fwl,
+ const u8 *data, u32 offset,
+ u32 size, u32 *written)
+{
+ struct pd692x0_priv *priv = fwl->dd_handle;
+ char line[PD692X0_FW_LINE_MAX_SZ];
+ const struct i2c_client *client;
+ int ret, i;
+ char cmd;
+
+ client = priv->client;
+ priv->fw_state = PD692X0_FW_WRITE;
+
+ /* Erase */
+ cmd = 'E';
+ ret = i2c_master_send(client, &cmd, 1);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to boot programming mode (%pe)\n",
+ ERR_PTR(ret));
+ return FW_UPLOAD_ERR_RW_ERROR;
+ }
+
+ ret = pd692x0_fw_recv_resp(client, 100, "TOE\r\n", sizeof("TOE\r\n") - 1);
+ if (ret)
+ return ret;
+
+ ret = pd692x0_fw_recv_resp(client, 5000, "TE\r\n", sizeof("TE\r\n") - 1);
+ if (ret)
+ dev_warn(&client->dev,
+ "Failed to erase internal memory, however still try to write Firmware\n");
+
+ ret = pd692x0_fw_recv_resp(client, 100, "TPE\r\n", sizeof("TPE\r\n") - 1);
+ if (ret)
+ dev_warn(&client->dev,
+ "Failed to erase internal memory, however still try to write Firmware\n");
+
+ if (priv->cancel_request)
+ return FW_UPLOAD_ERR_CANCELED;
+
+ /* Program */
+ cmd = 'P';
+ ret = i2c_master_send(client, &cmd, sizeof(char));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to boot programming mode (%pe)\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = pd692x0_fw_recv_resp(client, 100, "TOP\r\n", sizeof("TOP\r\n") - 1);
+ if (ret)
+ return ret;
+
+ i = 0;
+ while (i < size) {
+ ret = pd692x0_fw_get_next_line(data, line, size - i);
+ if (ret < 0) {
+ ret = FW_UPLOAD_ERR_FW_INVALID;
+ goto err;
+ }
+
+ i += ret;
+ data += ret;
+ if (line[0] == 'S' && line[1] == '0') {
+ continue;
+ } else if (line[0] == 'S' && line[1] == '7') {
+ ret = pd692x0_fw_write_line(client, line, true);
+ if (ret)
+ goto err;
+ } else {
+ ret = pd692x0_fw_write_line(client, line, false);
+ if (ret)
+ goto err;
+ }
+
+ if (priv->cancel_request) {
+ ret = FW_UPLOAD_ERR_CANCELED;
+ goto err;
+ }
+ }
+ *written = i;
+
+ msleep(400);
+
+ return FW_UPLOAD_ERR_NONE;
+
+err:
+ strscpy_pad(line, "S7\r\n", sizeof(line));
+ pd692x0_fw_write_line(client, line, true);
+ return ret;
+}
+
+static enum fw_upload_err pd692x0_fw_poll_complete(struct fw_upload *fwl)
+{
+ struct pd692x0_priv *priv = fwl->dd_handle;
+ const struct i2c_client *client = priv->client;
+ struct pd692x0_msg_ver ver;
+ int ret;
+
+ priv->fw_state = PD692X0_FW_COMPLETE;
+
+ ret = pd692x0_fw_reset(client);
+ if (ret)
+ return ret;
+
+ ver = pd692x0_get_sw_version(priv);
+ if (ver.maj_sw_ver < PD692X0_FW_MAJ_VER) {
+ dev_err(&client->dev,
+ "Too old firmware version. Please update it\n");
+ priv->fw_state = PD692X0_FW_NEED_UPDATE;
+ return FW_UPLOAD_ERR_FW_INVALID;
+ }
+
+ ret = pd692x0_setup_pi_matrix(&priv->pcdev);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error configuring ports matrix (%pe)\n",
+ ERR_PTR(ret));
+ priv->fw_state = PD692X0_FW_NEED_UPDATE;
+ return FW_UPLOAD_ERR_HW_ERROR;
+ }
+
+ priv->fw_state = PD692X0_FW_OK;
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void pd692x0_fw_cancel(struct fw_upload *fwl)
+{
+ struct pd692x0_priv *priv = fwl->dd_handle;
+
+ priv->cancel_request = true;
+}
+
+static void pd692x0_fw_cleanup(struct fw_upload *fwl)
+{
+ struct pd692x0_priv *priv = fwl->dd_handle;
+
+ switch (priv->fw_state) {
+ case PD692X0_FW_WRITE:
+ pd692x0_fw_reset(priv->client);
+ fallthrough;
+ case PD692X0_FW_COMPLETE:
+ priv->fw_state = PD692X0_FW_BROKEN;
+ break;
+ default:
+ break;
+ }
+}
+
+static const struct fw_upload_ops pd692x0_fw_ops = {
+ .prepare = pd692x0_fw_prepare,
+ .write = pd692x0_fw_write,
+ .poll_complete = pd692x0_fw_poll_complete,
+ .cancel = pd692x0_fw_cancel,
+ .cleanup = pd692x0_fw_cleanup,
+};
+
+static int pd692x0_i2c_probe(struct i2c_client *client)
+{
+ struct pd692x0_msg msg, buf = {0}, zero = {0};
+ struct device *dev = &client->dev;
+ struct pd692x0_msg_ver ver;
+ struct pd692x0_priv *priv;
+ struct fw_upload *fwl;
+ int 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;
+
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ dev_err(dev, "Failed to get device status\n");
+ return -EIO;
+ }
+
+ /* Probe has been already run and the status dumped */
+ if (!memcmp(&buf, &zero, sizeof(buf))) {
+ /* Ask again the controller status */
+ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_SYS_STATUS];
+ ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get device status\n");
+ return ret;
+ }
+ }
+
+ if (buf.key != 0x03 || buf.sub[0] & 0x01) {
+ dev_err(dev, "PSE controller error\n");
+ return -EIO;
+ }
+ if (buf.sub[0] & 0x02) {
+ dev_err(dev, "PSE firmware error. Please update it.\n");
+ priv->fw_state = PD692X0_FW_BROKEN;
+ } else {
+ ver = pd692x0_get_sw_version(priv);
+ dev_info(&client->dev, "Software version %d.%02d.%d.%d\n",
+ ver.prod, ver.maj_sw_ver, ver.min_sw_ver,
+ ver.pa_sw_ver);
+
+ if (ver.maj_sw_ver < PD692X0_FW_MAJ_VER) {
+ dev_err(dev, "Too old firmware version. Please update it\n");
+ priv->fw_state = PD692X0_FW_NEED_UPDATE;
+ } else {
+ priv->fw_state = PD692X0_FW_OK;
+ }
+ }
+
+ priv->np = dev->of_node;
+ priv->pcdev.nr_lines = PD692X0_MAX_PIS;
+ priv->pcdev.owner = THIS_MODULE;
+ priv->pcdev.ops = &pd692x0_ops;
+ priv->pcdev.dev = dev;
+ priv->pcdev.types = PSE_C33;
+ ret = devm_pse_controller_register(dev, &priv->pcdev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register PSE controller\n");
+
+ fwl = firmware_upload_register(THIS_MODULE, dev, dev_name(dev),
+ &pd692x0_fw_ops, priv);
+ if (IS_ERR(fwl))
+ return dev_err_probe(dev, PTR_ERR(fwl),
+ "failed to register to the Firmware Upload API\n");
+ priv->fwl = fwl;
+
+ return 0;
+}
+
+static void pd692x0_i2c_remove(struct i2c_client *client)
+{
+ struct pd692x0_priv *priv = i2c_get_clientdata(client);
+
+ firmware_upload_unregister(priv->fwl);
+}
+
+static const struct i2c_device_id pd692x0_id[] = {
+ { PD692X0_PSE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, pd692x0_id);
+
+static const struct of_device_id pd692x0_of_match[] = {
+ { .compatible = "microchip,pd69200", },
+ { .compatible = "microchip,pd69210", },
+ { .compatible = "microchip,pd69220", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pd692x0_of_match);
+
+static struct i2c_driver pd692x0_driver = {
+ .probe = pd692x0_i2c_probe,
+ .remove = pd692x0_i2c_remove,
+ .id_table = pd692x0_id,
+ .driver = {
+ .name = PD692X0_PSE_NAME,
+ .of_match_table = pd692x0_of_match,
+ },
+};
+module_i2c_driver(pd692x0_driver);
+
+MODULE_AUTHOR("Kory Maincent <[email protected]>");
+MODULE_DESCRIPTION("Microchip PD692x0 PoE PSE Controller driver");
+MODULE_LICENSE("GPL");

--
2.25.1


2024-02-08 13:19:12

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 17/17] net: pse-pd: Add TI TPS23881 PSE controller driver

Add a new driver for the TI TPS23881 I2C Power Sourcing Equipment
controller.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>

---
Change in v3:
- New patch.
---
drivers/net/pse-pd/Kconfig | 9 +
drivers/net/pse-pd/Makefile | 1 +
drivers/net/pse-pd/tps23881.c | 818 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 828 insertions(+)

diff --git a/drivers/net/pse-pd/Kconfig b/drivers/net/pse-pd/Kconfig
index e3a6ba669f20..80cf373a5a0e 100644
--- a/drivers/net/pse-pd/Kconfig
+++ b/drivers/net/pse-pd/Kconfig
@@ -31,4 +31,13 @@ config PSE_PD692X0
To compile this driver as a module, choose M here: the
module will be called pd692x0.

+config PSE_TPS23881
+ tristate "TPS23881 PSE controller"
+ depends on I2C
+ help
+ This module provides support for TPS23881 regulator based Ethernet
+ Power Sourcing Equipment.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tps23881.
endif
diff --git a/drivers/net/pse-pd/Makefile b/drivers/net/pse-pd/Makefile
index 9c12c4a65730..9d2898b36737 100644
--- a/drivers/net/pse-pd/Makefile
+++ b/drivers/net/pse-pd/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o

obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o
obj-$(CONFIG_PSE_PD692X0) += pd692x0.o
+obj-$(CONFIG_PSE_TPS23881) += tps23881.o
diff --git a/drivers/net/pse-pd/tps23881.c b/drivers/net/pse-pd/tps23881.c
new file mode 100644
index 000000000000..432b1686fd9b
--- /dev/null
+++ b/drivers/net/pse-pd/tps23881.c
@@ -0,0 +1,818 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the TI TPS23881 PoE PSE Controller driver (I2C bus)
+ *
+ * Copyright (c) 2023 Bootlin, Kory Maincent <[email protected]>
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pse-pd/pse.h>
+
+#define TPS23881_MAX_CHANS 8
+
+#define TPS23881_REG_PW_STATUS 0x10
+#define TPS23881_REG_OP_MODE 0x12
+#define TPS23881_REG_DIS_EN 0x13
+#define TPS23881_REG_DET_CLA_EN 0x14
+#define TPS23881_REG_GEN_MASK 0x17
+#define TPS23881_REG_NBITACC BIT(5)
+#define TPS23881_REG_PW_EN 0x19
+#define TPS23881_REG_PORT_MAP 0x26
+#define TPS23881_REG_PORT_POWER 0x29
+#define TPS23881_REG_POEPLUS 0x40
+#define TPS23881_REG_TPON BIT(0)
+#define TPS23881_REG_FWREV 0x41
+#define TPS23881_REG_DEVID 0x43
+#define TPS23881_REG_SRAM_CTRL 0x60
+#define TPS23881_REG_SRAM_DATA 0x61
+
+struct tps23881_port_desc {
+ u8 chan[2];
+ bool is_4p;
+};
+
+struct tps23881_priv {
+ struct i2c_client *client;
+ struct pse_controller_dev pcdev;
+ struct device_node *np;
+ struct tps23881_port_desc port[TPS23881_MAX_CHANS];
+};
+
+static struct tps23881_priv *to_tps23881_priv(struct pse_controller_dev *pcdev)
+{
+ return container_of(pcdev, struct tps23881_priv, pcdev);
+}
+
+static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id)
+{
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
+ struct i2c_client *client = priv->client;
+ u8 chan;
+ u16 val;
+ int ret;
+
+ if (id >= TPS23881_MAX_CHANS)
+ return -ERANGE;
+
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
+ if (ret < 0)
+ return ret;
+
+ chan = priv->port[id].chan[0];
+ if (chan < 4)
+ val = (u16)(ret | BIT(chan));
+ else
+ val = (u16)(ret | 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);
+ }
+
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id)
+{
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
+ struct i2c_client *client = priv->client;
+ u8 chan;
+ u16 val;
+ int ret;
+
+ if (id >= TPS23881_MAX_CHANS)
+ return -ERANGE;
+
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
+ if (ret < 0)
+ return ret;
+
+ chan = priv->port[id].chan[0];
+ if (chan < 4)
+ val = (u16)(ret | BIT(chan + 4));
+ else
+ val = (u16)(ret | BIT(chan + 8));
+
+ if (priv->port[id].is_4p) {
+ chan = priv->port[id].chan[1];
+ if (chan < 4)
+ val |= BIT(chan + 4);
+ else
+ val |= BIT(chan + 8);
+ }
+
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+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;
+ int ret;
+
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
+ if (ret < 0)
+ return ret;
+
+ chan = priv->port[id].chan[0];
+ if (chan < 4)
+ enabled = ret & BIT(chan);
+ else
+ enabled = ret & BIT(chan + 4);
+
+ 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));
+ }
+
+ /* Return enabled status only if both channel are on this state */
+ 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)
+{
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
+ struct i2c_client *client = priv->client;
+ bool enabled, delivering;
+ u8 chan;
+ int ret;
+
+ ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS);
+ if (ret < 0)
+ 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);
+ }
+
+ 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));
+ }
+ }
+
+ /* 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;
+ else
+ status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
+
+ return 0;
+}
+
+/* Parse managers subnode into a array of device node */
+static int
+tps23881_get_of_channels(struct tps23881_priv *priv,
+ struct device_node *chan_node[TPS23881_MAX_CHANS])
+{
+ struct device_node *channels_node, *node;
+ int i, ret;
+
+ if (!priv->np)
+ return -EINVAL;
+
+ channels_node = of_find_node_by_name(priv->np, "channels");
+ if (!channels_node)
+ return -EINVAL;
+
+ for_each_child_of_node(channels_node, node) {
+ u32 chan_id;
+
+ if (!of_node_name_eq(node, "channel"))
+ continue;
+
+ ret = of_property_read_u32(node, "reg", &chan_id);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (chan_id >= TPS23881_MAX_CHANS || chan_node[chan_id]) {
+ dev_err(&priv->client->dev,
+ "wrong number of port (%d)\n", chan_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ of_node_get(node);
+ chan_node[chan_id] = node;
+ }
+
+ of_node_put(channels_node);
+ return 0;
+
+out:
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ of_node_put(chan_node[i]);
+ chan_node[i] = NULL;
+ }
+
+ of_node_put(node);
+ of_node_put(channels_node);
+ return ret;
+}
+
+struct tps23881_port_matrix {
+ u8 pi_id;
+ u8 lgcl_chan[2];
+ u8 hw_chan[2];
+ bool is_4p;
+ bool exist;
+};
+
+static int
+tps23881_match_channel(const struct pse_pi_pairset *pairset,
+ struct device_node *chan_node[TPS23881_MAX_CHANS])
+{
+ int i;
+
+ /* Look on every channels */
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ if (pairset->np == chan_node[i])
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static bool
+tps23881_is_chan_free(struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS],
+ int chan)
+{
+ int i;
+
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ if (port_matrix[i].exist &&
+ (port_matrix[i].hw_chan[0] == chan ||
+ port_matrix[i].hw_chan[1] == chan))
+ return false;
+ }
+
+ return true;
+}
+
+/* Fill port matrix with the matching channels */
+static int
+tps23881_match_port_matrix(struct pse_pi *pi, int pi_id,
+ struct device_node *chan_node[TPS23881_MAX_CHANS],
+ struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS])
+{
+ int ret;
+
+ if (!pi->pairset[0].np)
+ return 0;
+
+ ret = tps23881_match_channel(&pi->pairset[0], chan_node);
+ if (ret < 0)
+ return ret;
+
+ if (!tps23881_is_chan_free(port_matrix, ret)) {
+ pr_err("tps23881: channel %d already used\n", ret);
+ return -ENODEV;
+ }
+
+ port_matrix[pi_id].hw_chan[0] = ret;
+ port_matrix[pi_id].exist = true;
+
+ if (!pi->pairset[1].np)
+ return 0;
+
+ ret = tps23881_match_channel(&pi->pairset[1], chan_node);
+ if (ret < 0)
+ return ret;
+
+ if (!tps23881_is_chan_free(port_matrix, ret)) {
+ pr_err("tps23881: channel %d already used\n", ret);
+ return -ENODEV;
+ }
+
+ if (port_matrix[pi_id].hw_chan[0] / 4 != ret / 4) {
+ pr_err("tps23881: 4-pair PSE can only be set within the same 4 ports group");
+ return -ENODEV;
+ }
+
+ port_matrix[pi_id].hw_chan[1] = ret;
+ port_matrix[pi_id].is_4p = true;
+
+ return 0;
+}
+
+static int
+tps23881_get_unused_chan(struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS],
+ int port_cnt)
+{
+ bool used;
+ int i, j;
+
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ used = false;
+
+ for (j = 0; j < port_cnt; j++) {
+ if (port_matrix[j].hw_chan[0] == i) {
+ used = true;
+ break;
+ }
+
+ if (port_matrix[j].is_4p &&
+ port_matrix[j].hw_chan[1] == i) {
+ used = true;
+ break;
+ }
+ }
+
+ if (!used)
+ return i;
+ }
+
+ return -1;
+}
+
+/* Sort the port matrix to following particular hardware ports matrix
+ * specification of the tps23881. The device has two 4-ports groups and
+ * each 4-pair powered device has to be configured to use two consecutive
+ * logical channel in each 4 ports group (1 and 2 or 3 and 4). Also the
+ * hardware matrix has to be fully configured even with unused chan to be
+ * valid.
+ */
+static int
+tps23881_sort_port_matrix(struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS])
+{
+ struct tps23881_port_matrix tmp_port_matrix[TPS23881_MAX_CHANS] = {0};
+ int i, ret, port_cnt = 0, cnt_4ch_grp1 = 0, cnt_4ch_grp2 = 4;
+
+ /* Configure 4p port matrix */
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ int *cnt;
+
+ if (!port_matrix[i].exist || !port_matrix[i].is_4p)
+ continue;
+
+ if (port_matrix[i].hw_chan[0] < 4)
+ cnt = &cnt_4ch_grp1;
+ else
+ cnt = &cnt_4ch_grp2;
+
+ tmp_port_matrix[port_cnt].exist = true;
+ tmp_port_matrix[port_cnt].is_4p = true;
+ tmp_port_matrix[port_cnt].pi_id = i;
+ tmp_port_matrix[port_cnt].hw_chan[0] = port_matrix[i].hw_chan[0];
+ tmp_port_matrix[port_cnt].hw_chan[1] = port_matrix[i].hw_chan[1];
+
+ /* 4-pair ports have to be configured with consecutive
+ * logical channels 0 and 1, 2 and 3.
+ */
+ tmp_port_matrix[port_cnt].lgcl_chan[0] = (*cnt)++;
+ tmp_port_matrix[port_cnt].lgcl_chan[1] = (*cnt)++;
+
+ port_cnt++;
+ }
+
+ /* Configure 2p port matrix */
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ int *cnt;
+
+ if (!port_matrix[i].exist || port_matrix[i].is_4p)
+ continue;
+
+ if (port_matrix[i].hw_chan[0] < 4)
+ cnt = &cnt_4ch_grp1;
+ else
+ cnt = &cnt_4ch_grp2;
+
+ tmp_port_matrix[port_cnt].exist = true;
+ tmp_port_matrix[port_cnt].pi_id = i;
+ tmp_port_matrix[port_cnt].lgcl_chan[0] = (*cnt)++;
+ tmp_port_matrix[port_cnt].hw_chan[0] = port_matrix[i].hw_chan[0];
+
+ port_cnt++;
+ }
+
+ /* Complete the rest of the first 4 port group matrix even if
+ * channels are unused
+ */
+ while (cnt_4ch_grp1 < 4) {
+ ret = tps23881_get_unused_chan(tmp_port_matrix, port_cnt);
+ if (ret < 0) {
+ pr_err("tps23881: port matrix issue, no chan available\n");
+ return -ENODEV;
+ }
+
+ if (port_cnt >= TPS23881_MAX_CHANS) {
+ pr_err("tps23881: wrong number of channels\n");
+ return -ENODEV;
+ }
+ tmp_port_matrix[port_cnt].lgcl_chan[0] = cnt_4ch_grp1;
+ tmp_port_matrix[port_cnt].hw_chan[0] = ret;
+ cnt_4ch_grp1++;
+ port_cnt++;
+ }
+
+ /* Complete the rest of the second 4 port group matrix even if
+ * channels are unused
+ */
+ while (cnt_4ch_grp2 < 8) {
+ ret = tps23881_get_unused_chan(tmp_port_matrix, port_cnt);
+ if (ret < 0) {
+ pr_err("tps23881: port matrix issue, no chan available\n");
+ return -ENODEV;
+ }
+
+ if (port_cnt >= TPS23881_MAX_CHANS) {
+ pr_err("tps23881: wrong number of channels\n");
+ return -ENODEV;
+ }
+ tmp_port_matrix[port_cnt].lgcl_chan[0] = cnt_4ch_grp2;
+ tmp_port_matrix[port_cnt].hw_chan[0] = ret;
+ cnt_4ch_grp2++;
+ port_cnt++;
+ }
+
+ memcpy(port_matrix, tmp_port_matrix, sizeof(tmp_port_matrix));
+
+ return port_cnt;
+}
+
+/* Write port matrix to the hardware port matrix and the software port
+ * matrix.
+ */
+static int
+tps23881_write_port_matrix(struct tps23881_priv *priv,
+ struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS],
+ int port_cnt)
+{
+ struct i2c_client *client = priv->client;
+ u8 pi_id, lgcl_chan, hw_chan;
+ u16 val = 0;
+ int i, ret;
+
+ for (i = 0; i < port_cnt; i++) {
+ pi_id = port_matrix[i].pi_id;
+ lgcl_chan = port_matrix[i].lgcl_chan[0];
+ hw_chan = port_matrix[i].hw_chan[0] % 4;
+
+ /* Set software port matrix for existing ports */
+ if (port_matrix[i].exist)
+ priv->port[pi_id].chan[0] = lgcl_chan;
+
+ /* Set hardware port matrix for all ports */
+ val |= hw_chan << (lgcl_chan * 2);
+
+ if (!port_matrix[i].is_4p)
+ continue;
+
+ lgcl_chan = port_matrix[i].lgcl_chan[1];
+ hw_chan = port_matrix[i].hw_chan[1] % 4;
+
+ /* Set software port matrix for existing ports */
+ if (port_matrix[i].exist) {
+ priv->port[pi_id].is_4p = true;
+ priv->port[pi_id].chan[1] = lgcl_chan;
+ }
+
+ /* Set hardware port matrix for all ports */
+ val |= hw_chan << (lgcl_chan * 2);
+ }
+
+ /* Write hardware ports matrix */
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+tps23881_set_ports_conf(struct tps23881_priv *priv,
+ struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS])
+{
+ struct i2c_client *client = priv->client;
+ int i, ret;
+ u16 val;
+
+ /* Set operating mode */
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_OP_MODE, 0xaaaa);
+ if (ret < 0)
+ return ret;
+
+ /* Disable DC disconnect */
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DIS_EN, 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* Set port power allocation */
+ val = 0;
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ if (!port_matrix[i].exist)
+ continue;
+
+ if (port_matrix[i].is_4p)
+ val |= 0xf << ((port_matrix[i].lgcl_chan[0] / 2) * 4);
+ else
+ val |= 0x3 << ((port_matrix[i].lgcl_chan[0] / 2) * 4);
+ }
+ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_POWER, val);
+ if (ret < 0)
+ return ret;
+
+ /* Enable detection and classification */
+ val = 0;
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ if (!port_matrix[i].exist)
+ continue;
+
+ val |= BIT(port_matrix[i].lgcl_chan[0]) |
+ BIT(port_matrix[i].lgcl_chan[0] + 4);
+ if (port_matrix[i].is_4p)
+ 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, 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+tps23881_set_ports_matrix(struct tps23881_priv *priv,
+ struct device_node *chan_node[TPS23881_MAX_CHANS])
+{
+ struct tps23881_port_matrix port_matrix[TPS23881_MAX_CHANS] = {0};
+ int i, ret;
+
+ /* Update with values for every PSE PIs */
+ for (i = 0; i < TPS23881_MAX_CHANS; i++) {
+ ret = tps23881_match_port_matrix(&priv->pcdev.pi[i], i,
+ chan_node, port_matrix);
+ if (ret)
+ return ret;
+ }
+
+ ret = tps23881_sort_port_matrix(port_matrix);
+ if (ret < 0)
+ return ret;
+
+ ret = tps23881_write_port_matrix(priv, port_matrix, ret);
+ if (ret)
+ return ret;
+
+ ret = tps23881_set_ports_conf(priv, port_matrix);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int tps23881_setup_pi_matrix(struct pse_controller_dev *pcdev)
+{
+ struct device_node *chan_node[TPS23881_MAX_CHANS] = {NULL};
+ struct tps23881_priv *priv = to_tps23881_priv(pcdev);
+ int ret, i;
+
+ ret = tps23881_get_of_channels(priv, chan_node);
+ if (ret < 0) {
+ dev_warn(&priv->client->dev,
+ "Unable to parse port-matrix, default matrix will be used\n");
+ return 0;
+ }
+
+ ret = tps23881_set_ports_matrix(priv, chan_node);
+
+ for (i = 0; i < TPS23881_MAX_CHANS; i++)
+ of_node_put(chan_node[i]);
+
+ return ret;
+}
+
+static const struct pse_controller_ops tps23881_ops = {
+ .setup_pi_matrix = tps23881_setup_pi_matrix,
+ .pi_enable = tps23881_pi_enable,
+ .pi_disable = tps23881_pi_disable,
+ .pi_is_enabled = tps23881_pi_is_enabled,
+ .ethtool_get_status = tps23881_ethtool_get_status,
+};
+
+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_fw_conf {
+ u8 reg;
+ u8 val;
+};
+
+static const struct tps23881_fw_conf tps23881_parity_flash_conf[] = {
+ {.reg = 0x60, .val = 0x01},
+ {.reg = 0x62, .val = 0x00},
+ {.reg = 0x63, .val = 0x80},
+ {.reg = 0x60, .val = 0xC4},
+ {.reg = 0x1D, .val = 0xBC},
+ {.reg = 0xD7, .val = 0x02},
+ {.reg = 0x91, .val = 0x00},
+ {.reg = 0x90, .val = 0x00},
+ {.reg = 0xD7, .val = 0x00},
+ {.reg = 0x1D, .val = 0x00},
+ { /* sentinel */ }
+};
+
+static const struct tps23881_fw_conf tps23881_sram_flash_conf[] = {
+ {.reg = 0x60, .val = 0xC5},
+ {.reg = 0x62, .val = 0x00},
+ {.reg = 0x63, .val = 0x80},
+ {.reg = 0x60, .val = 0xC0},
+ {.reg = 0x1D, .val = 0xBC},
+ {.reg = 0xD7, .val = 0x02},
+ {.reg = 0x91, .val = 0x00},
+ {.reg = 0x90, .val = 0x00},
+ {.reg = 0xD7, .val = 0x00},
+ {.reg = 0x1D, .val = 0x00},
+ { /* sentinel */ }
+};
+
+static int tps23881_flash_fw_part(struct i2c_client *client,
+ const char *fw_name,
+ const struct tps23881_fw_conf *fw_conf)
+{
+ const struct firmware *fw = NULL;
+ int i, ret;
+
+ ret = request_firmware(&fw, fw_name, &client->dev);
+ if (ret)
+ return ret;
+
+ dev_info(&client->dev, "Flashing %s\n", fw_name);
+
+ /* Prepare device for RAM download */
+ while (fw_conf->reg) {
+ ret = i2c_smbus_write_byte_data(client, fw_conf->reg,
+ fw_conf->val);
+ if (ret < 0)
+ return ret;
+
+ fw_conf++;
+ }
+
+ /* Flash the firmware file */
+ for (i = 0; i < fw->size; i++) {
+ ret = i2c_smbus_write_byte_data(client,
+ TPS23881_REG_SRAM_DATA,
+ fw->data[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ release_firmware(fw);
+
+ return 0;
+}
+
+static int tps23881_flash_fw(struct i2c_client *client)
+{
+ int ret;
+
+ ret = tps23881_flash_fw_part(client, fw_parity_name,
+ tps23881_parity_flash_conf);
+ if (ret)
+ return ret;
+
+ ret = tps23881_flash_fw_part(client, fw_sram_name,
+ tps23881_sram_flash_conf);
+ if (ret)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, TPS23881_REG_SRAM_CTRL, 0x18);
+ if (ret < 0)
+ return ret;
+
+ mdelay(12);
+
+ return 0;
+}
+
+static int tps23881_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tps23881_priv *priv;
+ int ret;
+ u8 val;
+
+ 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, TPS23881_REG_DEVID);
+ if (ret < 0)
+ return ret;
+
+ if (ret != 0x22) {
+ dev_err(dev, "Wrong device ID\n");
+ return -ENXIO;
+ }
+
+ ret = tps23881_flash_fw(client);
+ 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);
+
+ /* Set configuration B, 16 bit access on a single device address */
+ ret = i2c_smbus_read_byte_data(client, TPS23881_REG_GEN_MASK);
+ if (ret < 0)
+ return ret;
+
+ val = ret | TPS23881_REG_NBITACC;
+ ret = i2c_smbus_write_byte_data(client, TPS23881_REG_GEN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+ priv->np = dev->of_node;
+
+ priv->pcdev.owner = THIS_MODULE;
+ priv->pcdev.ops = &tps23881_ops;
+ priv->pcdev.dev = dev;
+ priv->pcdev.types = PSE_C33;
+ priv->pcdev.nr_lines = TPS23881_MAX_CHANS;
+ ret = devm_pse_controller_register(dev, &priv->pcdev);
+ if (ret) {
+ return dev_err_probe(dev, ret,
+ "failed to register PSE controller\n");
+ }
+
+ return ret;
+}
+
+static const struct i2c_device_id tps23881_id[] = {
+ { "tps23881", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps23881_id);
+
+static const struct of_device_id tps23881_of_match[] = {
+ { .compatible = "ti,tps23881", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tps23881_of_match);
+
+static struct i2c_driver tps23881_driver = {
+ .probe = tps23881_i2c_probe,
+ .id_table = tps23881_id,
+ .driver = {
+ .name = "tps23881",
+ .of_match_table = tps23881_of_match,
+ },
+};
+module_i2c_driver(tps23881_driver);
+
+MODULE_AUTHOR("Kory Maincent <[email protected]>");
+MODULE_DESCRIPTION("TI TPS23881 PoE PSE Controller driver");
+MODULE_LICENSE("GPL");

--
2.25.1


2024-02-08 13:46:17

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

Add the PD692x0 I2C Power Sourcing Equipment controller device tree
bindings documentation.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v2:
- Enhance ports-matrix description.
- Replace additionalProperties by unevaluatedProperties.
- Drop i2c suffix.

Changes in v3:
- Remove ports-matrix parameter.
- Add description of all physical ports and managers.
- Add pse_pis subnode moving to the API of pse-controller binding.
- Remove the MAINTAINERS section for this driver as I will be maintaining
all pse-pd subsystem.
---
.../bindings/net/pse-pd/microchip,pd692x0.yaml | 157 +++++++++++++++++++++
1 file changed, 157 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml
new file mode 100644
index 000000000000..57ba5365157c
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/pse-pd/microchip,pd692x0.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PD692x0 Power Sourcing Equipment controller
+
+maintainers:
+ - Kory Maincent <[email protected]>
+
+allOf:
+ - $ref: pse-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - microchip,pd69200
+ - microchip,pd69210
+ - microchip,pd69220
+
+ reg:
+ maxItems: 1
+
+ managers:
+ $ref: "#/$defs/managers"
+ description:
+ List of the PD69208T4/PD69204T4/PD69208M PSE managers. Each manager
+ have 4 or 8 physical ports according to the chip version. No need to
+ specify the SPI chip select as it is automatically detected by the
+ PD692x0 PSE controller. The PSE managers have to be described from
+ the lowest chip select to the greatest one, which is the detection
+ behavior of the PD692x0 PSE controller. The PD692x0 support up to
+ 12 PSE managers which can expose up to 96 physical ports. All
+ physical ports available on a manager have to be described in the
+ incremental order even if they are not used.
+
+required:
+ - compatible
+ - reg
+ - pse_pis
+
+$defs:
+ manager:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ reg:
+ maxItems: 1
+
+ patternProperties:
+ '^port@[0-7]$':
+ type: object
+ required:
+ - reg
+
+ required:
+ - reg
+
+ managers:
+ type: object
+
+ properties:
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ patternProperties:
+ "^manager@0[0-9]|1[0-2]$":
+ $ref: "#/$defs/manager"
+
+ required:
+ - "#address-cells"
+ - "#size-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-pse@3c {
+ compatible = "microchip,pd69200";
+ reg = <0x3c>;
+
+ managers {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ manager@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phys0: port@0 {
+ reg = <0>;
+ };
+
+ phys1: port@1 {
+ reg = <1>;
+ };
+
+ phys2: port@2 {
+ reg = <2>;
+ };
+
+ phys3: port@3 {
+ reg = <3>;
+ };
+ };
+
+ manager@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phys4: port@0 {
+ reg = <0>;
+ };
+
+ phys5: port@1 {
+ reg = <1>;
+ };
+
+ phys6: port@2 {
+ reg = <2>;
+ };
+
+ phys7: port@3 {
+ reg = <3>;
+ };
+ };
+ };
+
+ pse_pis {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pse_pi0: pse_pi@0 {
+ reg = <0>;
+ #pse-cells = <0>;
+ pairset-names = "alternative-a", "alternative-b";
+ pairsets = <&phys0>, <&phys1>;
+ };
+ pse_pi1: pse_pi@1 {
+ reg = <1>;
+ #pse-cells = <0>;
+ pairset-names = "alternative-a";
+ pairsets = <&phys2>;
+ };
+ };
+ };
+ };

--
2.25.1


2024-02-08 13:46:30

by Kory Maincent

[permalink] [raw]
Subject: [PATCH net-next v3 13/17] net: pse-pd: Use regulator framework within PSE framework

Integrate the regulator framework to the PSE framework for enhanced
access to features such as voltage, power measurement, and limits, which
are akin to regulators. Additionally, PSE features like port priorities
could potentially enhance the regulator framework. Note that this
integration introduces some implementation complexity, including wrapper
callbacks and nested locks, but the potential benefits make it worthwhile.

Regulator are using enable counter with specific behavior.
Two calls to regulator_disable will trigger kernel warnings.
If the counter exceeds one, regulator_disable call won't disable the
PSE PI. These behavior isn't suitable for PSE control.
Added a boolean 'enabled' state to prevent multiple calls to
regulator_enable/disable. PSE mutex safeguards this boolean.
However, ethtool command or regulator sysfs framework can set/get this
boolean, leading to nested locking. The PSE nested lock function is a copy
of regulator_lock_nested function, ensuring no issues with its usage.

regulator_get uses the consumer device pointer therefore of_pse_control_get
is modified accordingly to pass the PSE consumer device alongside its
device node.

Sponsored-by: Dent Project <[email protected]>
Signed-off-by: Kory Maincent <[email protected]>
---

Changes in v3:
- New patch
---
drivers/net/mdio/fwnode_mdio.c | 29 ++--
drivers/net/pse-pd/pse_core.c | 264 ++++++++++++++++++++++++++++++++++---
drivers/net/pse-pd/pse_regulator.c | 48 ++++---
include/linux/pse-pd/pse.h | 20 ++-
4 files changed, 302 insertions(+), 59 deletions(-)

diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c
index fd02f5cbc853..aa20b2ac7f2d 100644
--- a/drivers/net/mdio/fwnode_mdio.c
+++ b/drivers/net/mdio/fwnode_mdio.c
@@ -17,19 +17,17 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors");

static struct pse_control *
-fwnode_find_pse_control(struct fwnode_handle *fwnode)
+dev_find_pse_control(struct device *dev)
{
struct pse_control *psec;
- struct device_node *np;

if (!IS_ENABLED(CONFIG_PSE_CONTROLLER))
return NULL;

- np = to_of_node(fwnode);
- if (!np)
+ if (!dev->of_node)
return NULL;

- psec = of_pse_control_get(np);
+ psec = of_pse_control_get(dev, dev->of_node);
if (PTR_ERR(psec) == -ENOENT)
return NULL;

@@ -120,15 +118,9 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,
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))
@@ -161,6 +153,12 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,
goto clean_phy;
}

+ psec = dev_find_pse_control(&phy->mdio.dev);
+ 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
@@ -172,12 +170,13 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,

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;
}
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 59427c79858c..222f4f67f0e1 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -8,25 +8,93 @@
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pse-pd/pse.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>

static DEFINE_MUTEX(pse_list_mutex);
+static DEFINE_MUTEX(pse_nesting_mutex);
static LIST_HEAD(pse_controller_list);

/**
* struct pse_control - a PSE control
* @pcdev: a pointer to the PSE controller device
* this PSE control belongs to
+ * @ps: PSE PI supply of the PSE control
* @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
*/
struct pse_control {
struct pse_controller_dev *pcdev;
+ struct regulator *ps;
struct list_head list;
unsigned int id;
struct kref refcnt;
};

+/**
+ * pse_lock - lock a single PSE
+ * @pcdev: PSE source
+ *
+ * This function can be called many times by one task on
+ * a single pse and its mutex will be locked only once.
+ * If a task, which is calling this function is other
+ * than the one, which initially locked the mutex, it will
+ * wait on mutex.
+ */
+static int pse_lock(struct pse_controller_dev *pcdev)
+{
+ bool lock = false;
+ int ret = 0;
+
+ mutex_lock(&pse_nesting_mutex);
+
+ if (!mutex_trylock(&pcdev->lock)) {
+ if (pcdev->lock_owner == current)
+ pcdev->ref_cnt++;
+ else
+ lock = true;
+
+ if (lock) {
+ mutex_unlock(&pse_nesting_mutex);
+ mutex_lock(&pcdev->lock);
+ mutex_lock(&pse_nesting_mutex);
+ }
+ } else {
+ lock = true;
+ }
+
+ if (lock) {
+ pcdev->ref_cnt++;
+ pcdev->lock_owner = current;
+ }
+
+ mutex_unlock(&pse_nesting_mutex);
+
+ return ret;
+}
+
+/**
+ * pse_unlock - unlock a single PSE
+ * @pcdev: PSE source
+ *
+ * This function unlocks the mutex when the
+ * reference counter reaches 0.
+ */
+static void pse_unlock(struct pse_controller_dev *pcdev)
+{
+ mutex_lock(&pse_nesting_mutex);
+
+ if (--pcdev->ref_cnt == 0) {
+ pcdev->lock_owner = NULL;
+ mutex_unlock(&pcdev->lock);
+ }
+
+ WARN_ON_ONCE(pcdev->ref_cnt < 0);
+
+ mutex_unlock(&pse_nesting_mutex);
+}
+
static int of_load_pse_pi_pairsets(struct device_node *node,
struct pse_pi *pi,
int npairsets)
@@ -160,13 +228,123 @@ static int of_load_pse_pis(struct pse_controller_dev *pcdev)
return ret;
}

+static int pse_pi_is_enabled(struct regulator_dev *rdev)
+{
+ struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
+ const struct pse_controller_ops *ops;
+ int id, ret;
+
+ ops = pcdev->ops;
+ if (!ops->pi_is_enabled)
+ return -EOPNOTSUPP;
+
+ id = rdev_get_id(rdev);
+ pse_lock(pcdev);
+ ret = ops->pi_is_enabled(pcdev, id);
+ if (ret >= 0)
+ pcdev->pi[id].enabled = ret;
+ pse_unlock(pcdev);
+
+ return ret;
+}
+
+static int pse_pi_enable(struct regulator_dev *rdev)
+{
+ struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
+ const struct pse_controller_ops *ops;
+ int id, ret;
+
+ ops = pcdev->ops;
+ if (!ops->pi_enable)
+ return -EOPNOTSUPP;
+
+ id = rdev_get_id(rdev);
+ pse_lock(pcdev);
+ ret = ops->pi_enable(pcdev, id);
+ if (!ret)
+ pcdev->pi[id].enabled = 1;
+ pse_unlock(pcdev);
+
+ return ret;
+}
+
+static int pse_pi_disable(struct regulator_dev *rdev)
+{
+ struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
+ const struct pse_controller_ops *ops;
+ int id, ret;
+
+ ops = pcdev->ops;
+ if (!ops->pi_disable)
+ return -EOPNOTSUPP;
+
+ id = rdev_get_id(rdev);
+ pse_lock(pcdev);
+ ret = ops->pi_disable(pcdev, id);
+ if (!ret)
+ pcdev->pi[id].enabled = 0;
+ pse_unlock(pcdev);
+
+ return ret;
+}
+
+static const struct regulator_ops pse_pi_ops = {
+ .is_enabled = pse_pi_is_enabled,
+ .enable = pse_pi_enable,
+ .disable = pse_pi_disable,
+};
+
+static const struct regulator_init_data pse_pi_initdata = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static int
+devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev,
+ char *name, int id)
+{
+ struct regulator_config rconfig = {0};
+ struct regulator_desc *rdesc;
+ struct regulator_dev *rdev;
+
+ rdesc = devm_kzalloc(pcdev->dev, sizeof(*rdesc), GFP_KERNEL);
+ if (!rdesc)
+ return -ENOMEM;
+
+ /* Regulator descriptor id have to be the same as its associated
+ * PSE PI id for the well functioning of the PSE controls.
+ */
+ rdesc->id = id;
+ rdesc->name = name;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->ops = &pse_pi_ops;
+ rdesc->owner = pcdev->owner;
+
+ rconfig.dev = pcdev->dev;
+ rconfig.driver_data = pcdev;
+ rconfig.init_data = &pse_pi_initdata;
+
+ rdev = devm_regulator_register(pcdev->dev, rdesc, &rconfig);
+ if (IS_ERR(rdev)) {
+ dev_err_probe(pcdev->dev, PTR_ERR(rdev),
+ "Failed to register regulator\n");
+ return PTR_ERR(rdev);
+ }
+
+ pcdev->pi[id].rdev = rdev;
+
+ return 0;
+}
+
/**
* pse_controller_register - register a PSE controller device
* @pcdev: a pointer to the initialized PSE controller device
*/
int pse_controller_register(struct pse_controller_dev *pcdev)
{
- int ret;
+ size_t reg_name_len;
+ int ret, i;

mutex_init(&pcdev->lock);
INIT_LIST_HEAD(&pcdev->pse_control_head);
@@ -184,6 +362,31 @@ int pse_controller_register(struct pse_controller_dev *pcdev)
return ret;
}

+ /* Each regulator name len is pcdev dev name + 7 char +
+ * int max digit number (10) + 1
+ */
+ reg_name_len = strlen(dev_name(pcdev->dev)) + 18;
+
+ /* Register PI regulators */
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ char *reg_name;
+
+ /* Do not register regulator for PIs not described */
+ if (!pcdev->of_legacy && !pcdev->pi[i].np)
+ continue;
+
+ reg_name = devm_kzalloc(pcdev->dev, reg_name_len, GFP_KERNEL);
+ if (!reg_name)
+ return -ENOMEM;
+
+ snprintf(reg_name, reg_name_len, "pse-%s_pi%d",
+ dev_name(pcdev->dev), i);
+
+ ret = devm_pse_pi_regulator_register(pcdev, reg_name, i);
+ if (ret)
+ return ret;
+ }
+
mutex_lock(&pse_list_mutex);
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
@@ -281,6 +484,9 @@ void pse_control_put(struct pse_control *psec)
if (IS_ERR_OR_NULL(psec))
return;

+ if (psec->pcdev->pi[psec->id].enabled)
+ regulator_disable(psec->ps);
+
mutex_lock(&pse_list_mutex);
__pse_control_put_internal(psec);
mutex_unlock(&pse_list_mutex);
@@ -288,9 +494,11 @@ void pse_control_put(struct pse_control *psec)
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 device *dev, struct pse_controller_dev *pcdev,
+ unsigned int index)
{
struct pse_control *psec;
+ int ret;

lockdep_assert_held(&pse_list_mutex);

@@ -310,6 +518,20 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index)
return ERR_PTR(-ENODEV);
}

+ psec->ps = devm_regulator_get_exclusive(dev,
+ rdev_get_name(pcdev->pi[index].rdev));
+ if (IS_ERR(psec->ps)) {
+ kfree(psec);
+ return ERR_CAST(psec->ps);
+ }
+
+ ret = regulator_is_enabled(psec->ps);
+ if (ret < 0) {
+ kfree(psec);
+ return ERR_PTR(ret);
+ }
+ pcdev->pi[index].enabled = ret;
+
psec->pcdev = pcdev;
list_add(&psec->list, &pcdev->pse_control_head);
psec->id = index;
@@ -344,7 +566,8 @@ static int psec_id_legacy_xlate(struct pse_controller_dev *pcdev,
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 *dev,
+ struct device_node *node)
{
struct pse_controller_dev *r, *pcdev;
struct of_phandle_args args;
@@ -394,7 +617,7 @@ struct pse_control *of_pse_control_get(struct device_node *node)
}

/* pse_list_mutex also protects the pcdev's pse_control list */
- psec = pse_control_get_internal(pcdev, psec_id);
+ psec = pse_control_get_internal(dev, pcdev, psec_id);

out:
mutex_unlock(&pse_list_mutex);
@@ -425,9 +648,9 @@ int pse_ethtool_get_status(struct pse_control *psec,
return -EOPNOTSUPP;
}

- mutex_lock(&psec->pcdev->lock);
+ pse_lock(psec->pcdev);
err = ops->ethtool_get_status(psec->pcdev, psec->id, extack, status);
- mutex_unlock(&psec->pcdev->lock);
+ pse_unlock(psec->pcdev);

return err;
}
@@ -443,20 +666,25 @@ int pse_ethtool_set_config(struct pse_control *psec,
struct netlink_ext_ack *extack,
const struct pse_control_config *config)
{
- const struct pse_controller_ops *ops;
- int err;
+ int err = 0;

- ops = psec->pcdev->ops;
-
- if (!ops->ethtool_set_config) {
- NL_SET_ERR_MSG(extack,
- "PSE driver does not configuration");
- return -EOPNOTSUPP;
+ /* Look at enabled status to not call regulator_enable or
+ * regulator_disable twice creating a regulator counter mismatch
+ */
+ pse_lock(psec->pcdev);
+ switch (config->c33_admin_control) {
+ case ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED:
+ if (!psec->pcdev->pi[psec->id].enabled)
+ err = regulator_enable(psec->ps);
+ break;
+ case ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED:
+ if (psec->pcdev->pi[psec->id].enabled)
+ err = regulator_disable(psec->ps);
+ break;
+ default:
+ err = -EOPNOTSUPP;
}
-
- mutex_lock(&psec->pcdev->lock);
- err = ops->ethtool_set_config(psec->pcdev, psec->id, extack, config);
- mutex_unlock(&psec->pcdev->lock);
+ pse_unlock(psec->pcdev);

return err;
}
diff --git a/drivers/net/pse-pd/pse_regulator.c b/drivers/net/pse-pd/pse_regulator.c
index e34ab8526067..a6f61489fd12 100644
--- a/drivers/net/pse-pd/pse_regulator.c
+++ b/drivers/net/pse-pd/pse_regulator.c
@@ -24,37 +24,41 @@ static struct pse_reg_priv *to_pse_reg(struct pse_controller_dev *pcdev)
}

static int
-pse_reg_ethtool_set_config(struct pse_controller_dev *pcdev, unsigned long id,
- struct netlink_ext_ack *extack,
- const struct pse_control_config *config)
+pse_reg_pi_enable(struct pse_controller_dev *pcdev, int id)
{
struct pse_reg_priv *priv = to_pse_reg(pcdev);
int ret;

- if (priv->admin_state == config->podl_admin_control)
- return 0;
-
- switch (config->podl_admin_control) {
- case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED:
- ret = regulator_enable(priv->ps);
- break;
- case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED:
- ret = regulator_disable(priv->ps);
- break;
- default:
- dev_err(pcdev->dev, "Unknown admin state %i\n",
- config->podl_admin_control);
- ret = -ENOTSUPP;
- }
-
+ ret = regulator_enable(priv->ps);
if (ret)
return ret;

- priv->admin_state = config->podl_admin_control;
+ priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED;
+ return 0;
+}
+
+static int
+pse_reg_pi_disable(struct pse_controller_dev *pcdev, int id)
+{
+ struct pse_reg_priv *priv = to_pse_reg(pcdev);
+ int ret;

+ ret = regulator_disable(priv->ps);
+ if (ret)
+ return ret;
+
+ priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED;
return 0;
}

+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_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id,
struct netlink_ext_ack *extack,
@@ -80,7 +84,9 @@ pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id,

static const struct pse_controller_ops pse_reg_ops = {
.ethtool_get_status = pse_reg_ethtool_get_status,
- .ethtool_set_config = pse_reg_ethtool_set_config,
+ .pi_enable = pse_reg_pi_enable,
+ .pi_is_enabled = pse_reg_pi_is_enabled,
+ .pi_disable = pse_reg_pi_disable,
};

static int
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index cf0eebb3b0a9..c64c709dbbc1 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -56,10 +56,10 @@ 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 (*ethtool_set_config)(struct pse_controller_dev *pcdev,
- unsigned long id, struct netlink_ext_ack *extack,
- const struct pse_control_config *config);
int (*setup_pi_matrix)(struct pse_controller_dev *pcdev);
+ 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);
};

struct module;
@@ -91,10 +91,14 @@ struct pse_pi_pairset {
*
* @pairset: table of the PSE PI pinout alternative for the two pairset
* @np: device node pointer of the PSE PI node
+ * @rdev: regulator represented by the PSE PI
+ * @enabled: PI enabled state
*/
struct pse_pi {
struct pse_pi_pairset pairset[2];
struct device_node *np;
+ struct regulator_dev *rdev;
+ bool enabled;
};

/**
@@ -108,6 +112,8 @@ struct pse_pi {
* @of_pse_n_cells: number of cells in PSE line specifiers
* @nr_lines: number of PSE controls in this controller device
* @lock: Mutex for serialization access to the PSE controller
+ * @lock_owner: current owner of the mutex
+ * @ref_cnt: mutex's reference count
* @types: types of the PSE controller
* @pi: table of PSE PIs described in this controller device
* @of_legacy: flag set if the pse_pis devicetree node is not used
@@ -121,6 +127,8 @@ struct pse_controller_dev {
int of_pse_n_cells;
unsigned int nr_lines;
struct mutex lock;
+ struct task_struct *lock_owner;
+ int ref_cnt;
u32 types;
struct pse_pi *pi;
bool of_legacy;
@@ -133,7 +141,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 *dev,
+ struct device_node *node);
void pse_control_put(struct pse_control *psec);

int pse_ethtool_get_status(struct pse_control *psec,
@@ -148,7 +157,8 @@ bool pse_has_c33(struct pse_control *psec);

#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 *dev,
+ struct device_node *node)
{
return ERR_PTR(-ENOENT);
}

--
2.25.1


2024-02-08 15:52:38

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs


On Thu, 08 Feb 2024 14:08:47 +0100, Kory Maincent wrote:
> Before hand we set "#pse-cell" to 1 to define a PSE controller with
> several PIs (Power Interface). The drawback of this was that we could not
> have any information on the PI except its number.
> Add support for pse_pis and pse_pi node to be able to have more information
> on the PI like the number of pairset used and the pairset pinout.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>
> ---
>
> Changes in v3:
> - New patch
> ---
> .../bindings/net/pse-pd/pse-controller.yaml | 101 ++++++++++++++++++++-
> 1 file changed, 98 insertions(+), 3 deletions(-)
>

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:
/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml:80:13: [warning] wrong indentation: expected 14 but found 12 (indentation)
/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml:80:15: [error] string value is redundantly quoted with any quotes (quoted-strings)
/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml:81:15: [error] string value is redundantly quoted with any quotes (quoted-strings)

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml: $defs:pse_pi:properties:pairset-names: {'description': 'Names of the pairsets as per IEEE 802.3-2022, Section 145.2.4. Valid values are "alternative-a" and "alternative-b". Each name should correspond to a phandle in the \'pairset\' property pointing to the power supply for that pairset.', '$ref': '/schemas/types.yaml#/definitions/string-array', 'minItems': 1, 'maxItems': 2, 'items': [{'enum': ['alternative-a', 'alternative-b']}]} should not be valid under {'required': ['maxItems']}
hint: "maxItems" is not needed with an "items" list
from schema $id: http://devicetree.org/meta-schemas/items.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml: $defs:pse_pi:properties:pairset-names: 'oneOf' conditional failed, one must be fixed:
[{'enum': ['alternative-a', 'alternative-b']}] is too short
False schema does not allow 1
hint: "minItems" is only needed if less than the "items" list length
from schema $id: http://devicetree.org/meta-schemas/items.yaml#

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


2024-02-08 16:01:24

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH net-next v3 16/17] dt-bindings: net: pse-pd: Add bindings for TPS23881 PSE controller


On Thu, 08 Feb 2024 14:08:53 +0100, Kory Maincent wrote:
> Add the TPS23881 I2C Power Sourcing Equipment controller device tree
> bindings documentation.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>
>
> ---
> Change in v3:
> - New patch.
> ---
> .../bindings/net/pse-pd/ti,tps2388x.yaml | 112 +++++++++++++++++++++
> 1 file changed, 112 insertions(+)
>

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/net/pse-pd/ti,tps2388x.example.dtb: /example-0/i2c/ethernet-pse@20: failed to match any schema with compatible: ['ti,tps2388x']

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


2024-02-09 11:51:58

by Kory Maincent

[permalink] [raw]
Subject: Re: [PATCH net-next v3 16/17] dt-bindings: net: pse-pd: Add bindings for TPS23881 PSE controller

On Thu, 08 Feb 2024 14:08:53 +0100
Kory Maincent <[email protected]> wrote:

> Add the TPS23881 I2C Power Sourcing Equipment controller device tree
> bindings documentation.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>
>
> ---
> Change in v3:
> - New patch.
> ---
> .../bindings/net/pse-pd/ti,tps2388x.yaml | 112
> +++++++++++++++++++++ 1 file changed, 112 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/pse-pd/ti,tps2388x.yaml
> b/Documentation/devicetree/bindings/net/pse-pd/ti,tps2388x.yaml new file mode

Oops forgot to rename this binding to ti,tps23881.yaml, sorry will do in next
version.

Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

2024-02-09 14:44:05

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

On Thu, Feb 08, 2024 at 02:08:47PM +0100, Kory Maincent wrote:
> Before hand we set "#pse-cell" to 1 to define a PSE controller with

#pse-cells

> several PIs (Power Interface). The drawback of this was that we could not
> have any information on the PI except its number.

Then increase it to what you need. The whole point of #foo-cells is that
it is variable depending on what the provider needs.

> Add support for pse_pis and pse_pi node to be able to have more information
> on the PI like the number of pairset used and the pairset pinout.

Please explain the problem you are trying to solve, not your solution. I
don't understand what the problem is to provide any useful suggestions
on the design.

>
> Sponsored-by: Dent Project <[email protected]>

Is this a recognized tag? First I've seen it.

> Signed-off-by: Kory Maincent <[email protected]>
> ---
>
> Changes in v3:
> - New patch
> ---
> .../bindings/net/pse-pd/pse-controller.yaml | 101 ++++++++++++++++++++-
> 1 file changed, 98 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml b/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
> index 2d382faca0e6..dd5fb53e527a 100644
> --- a/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
> +++ b/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
> @@ -13,6 +13,7 @@ description: Binding for the Power Sourcing Equipment (PSE) as defined in the
>
> maintainers:
> - Oleksij Rempel <[email protected]>
> + - Kory Maincent <[email protected]>
>
> properties:
> $nodename:
> @@ -22,11 +23,105 @@ properties:
> description:
> Used to uniquely identify a PSE instance within an IC. Will be
> 0 on PSE nodes with only a single output and at least 1 on nodes
> - controlling several outputs.
> + controlling several outputs which are not described in the pse_pis
> + subnode. This property is deprecated, please use pse_pis instead.
> enum: [0, 1]
>
> -required:
> - - "#pse-cells"
> + pse_pis:
> + $ref: "#/$defs/pse_pis"
> +
> +$defs:

$defs is for when you need multiple copies of the same thing. I don't
see that here.

> + pse_pis:
> + type: object
> + description:
> + Kind of a matrix to identify the concordance between a PSE Power
> + Interface and one or two (PoE4) physical ports.
> +
> + properties:
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> + patternProperties:
> + "^pse_pi@[0-9]+$":

Unit-addresses are hex.

> + $ref: "#/$defs/pse_pi"
> +
> + required:
> + - "#address-cells"
> + - "#size-cells"
> +
> + pse_pi:
> + description:
> + PSE PI device for power delivery via pairsets, compliant with IEEE
> + 802.3-2022, Section 145.2.4. Each pairset comprises a positive and a
> + negative VPSE pair, adhering to the pinout configurations detailed in
> + the standard.
> + type: object
> + properties:
> + reg:
> + maxItems: 1

As you are defining the addressing here, you need to define what the
"addresses" are.

> +
> + "#pse-cells":
> + const: 0
> +
> + pairset-names:
> + description:
> + Names of the pairsets as per IEEE 802.3-2022, Section 145.2.4. Valid
> + values are "alternative-a" and "alternative-b". Each name should
> + correspond to a phandle in the 'pairset' property pointing to the
> + power supply for that pairset.
> + $ref: /schemas/types.yaml#/definitions/string-array
> + minItems: 1
> + maxItems: 2
> + items:
> + - enum:
> + - "alternative-a"
> + - "alternative-b"

This leaves the 2nd entry undefined. You need the dictionary form of
'items' rather than a list. IOW, Drop the '-' under items.

> +
> + pairsets:
> + description:
> + List of phandles, each pointing to the power supply for the
> + corresponding pairset named in 'pairset-names'. This property aligns
> + with IEEE 802.3-2022, Section 33.2.3 and 145.2.4.
> + PSE Pinout Alternatives (as per IEEE 802.3-2022 Table 145–3)
> + | Conductor | Alternative A (MDI-X) | Alternative A (MDI) | Alternative B(X) | Alternative B(S) |
> + |-----------|-----------------------|---------------------|------------------|------------------|
> + | 1 | Negative VPSE | Positive VPSE | — | — |
> + | 2 | Negative VPSE | Positive VPSE | — | — |
> + | 3 | Positive VPSE | Negative VPSE | — | — |
> + | 4 | — | — | Negative VPSE | Positive VPSE |
> + | 5 | — | — | Negative VPSE | Positive VPSE |
> + | 6 | Positive VPSE | Negative VPSE | — | — |
> + | 7 | — | — | Positive VPSE | Negative VPSE |
> + | 8 | — | — | Positive VPSE | Negative VPSE |
> + $ref: /schemas/types.yaml#/definitions/phandle-array
> + minItems: 1
> + maxItems: 2
> +
> + required:
> + - reg
> + - "#pse-cells"
> + - pairset-names
> + - pairsets
> +
> +allOf:
> + - if:
> + required:
> + - "#pse-cells"
> + then:
> + not:
> + required:
> + - pse-pis
> +
> + - if:
> + required:
> + - pse-pis
> + then:
> + not:
> + required:
> + - "#pse-cells"
>
> additionalProperties: true
>
>
> --
> 2.25.1
>

2024-02-09 14:58:06

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Thu, Feb 08, 2024 at 02:08:51PM +0100, Kory Maincent wrote:
> Add the PD692x0 I2C Power Sourcing Equipment controller device tree
> bindings documentation.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>
> ---
>
> Changes in v2:
> - Enhance ports-matrix description.
> - Replace additionalProperties by unevaluatedProperties.
> - Drop i2c suffix.
>
> Changes in v3:
> - Remove ports-matrix parameter.
> - Add description of all physical ports and managers.
> - Add pse_pis subnode moving to the API of pse-controller binding.
> - Remove the MAINTAINERS section for this driver as I will be maintaining
> all pse-pd subsystem.
> ---
> .../bindings/net/pse-pd/microchip,pd692x0.yaml | 157 +++++++++++++++++++++
> 1 file changed, 157 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml
> new file mode 100644
> index 000000000000..57ba5365157c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml
> @@ -0,0 +1,157 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/pse-pd/microchip,pd692x0.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Microchip PD692x0 Power Sourcing Equipment controller
> +
> +maintainers:
> + - Kory Maincent <[email protected]>
> +
> +allOf:
> + - $ref: pse-controller.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - microchip,pd69200
> + - microchip,pd69210
> + - microchip,pd69220
> +
> + reg:
> + maxItems: 1
> +
> + managers:
> + $ref: "#/$defs/managers"
> + description:
> + List of the PD69208T4/PD69204T4/PD69208M PSE managers. Each manager
> + have 4 or 8 physical ports according to the chip version. No need to
> + specify the SPI chip select as it is automatically detected by the
> + PD692x0 PSE controller. The PSE managers have to be described from
> + the lowest chip select to the greatest one, which is the detection
> + behavior of the PD692x0 PSE controller. The PD692x0 support up to
> + 12 PSE managers which can expose up to 96 physical ports. All
> + physical ports available on a manager have to be described in the
> + incremental order even if they are not used.

Looks to me like you just need 3 PSE cells: <manager> <port> <A|B>

Really, no need for each piece of data to its own cell, so it could be
merged into 1 or 2 cells.

But cell data is generally supposed to be meaningful to the provider and
opaque to the consumer. It's not clear to me who needs to know
alternative A vs. B. That seems more like a property of the PHY than the
power provider?

Rob

2024-02-11 09:33:46

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Fri, Feb 09, 2024 at 02:57:27PM +0000, Rob Herring wrote:
> On Thu, Feb 08, 2024 at 02:08:51PM +0100, Kory Maincent wrote:
> > Add the PD692x0 I2C Power Sourcing Equipment controller device tree
> > bindings documentation.
> >
> > Sponsored-by: Dent Project <[email protected]>
> > Signed-off-by: Kory Maincent <[email protected]>
> > ---
> >
> > Changes in v2:
> > - Enhance ports-matrix description.
> > - Replace additionalProperties by unevaluatedProperties.
> > - Drop i2c suffix.
> >
> > Changes in v3:
> > - Remove ports-matrix parameter.
> > - Add description of all physical ports and managers.
> > - Add pse_pis subnode moving to the API of pse-controller binding.
> > - Remove the MAINTAINERS section for this driver as I will be maintaining
> > all pse-pd subsystem.
> > ---
> > .../bindings/net/pse-pd/microchip,pd692x0.yaml | 157 +++++++++++++++++++++
> > 1 file changed, 157 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml
> > new file mode 100644
> > index 000000000000..57ba5365157c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml
> > @@ -0,0 +1,157 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/net/pse-pd/microchip,pd692x0.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Microchip PD692x0 Power Sourcing Equipment controller
> > +
> > +maintainers:
> > + - Kory Maincent <[email protected]>
> > +
> > +allOf:
> > + - $ref: pse-controller.yaml#
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - microchip,pd69200
> > + - microchip,pd69210
> > + - microchip,pd69220
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + managers:
> > + $ref: "#/$defs/managers"
> > + description:
> > + List of the PD69208T4/PD69204T4/PD69208M PSE managers. Each manager
> > + have 4 or 8 physical ports according to the chip version. No need to
> > + specify the SPI chip select as it is automatically detected by the
> > + PD692x0 PSE controller. The PSE managers have to be described from
> > + the lowest chip select to the greatest one, which is the detection
> > + behavior of the PD692x0 PSE controller. The PD692x0 support up to
> > + 12 PSE managers which can expose up to 96 physical ports. All
> > + physical ports available on a manager have to be described in the
> > + incremental order even if they are not used.
>
> Looks to me like you just need 3 PSE cells: <manager> <port> <A|B>
>
> Really, no need for each piece of data to its own cell, so it could be
> merged into 1 or 2 cells.
>
> But cell data is generally supposed to be meaningful to the provider and
> opaque to the consumer. It's not clear to me who needs to know
> alternative A vs. B. That seems more like a property of the PHY than the
> power provider?

This is a bit complex question, so I decided to answer it with freshly
created documentation which should be included to this patch set:

PSE Power Interface (PSE PI) Documentation
==========================================

The Power Sourcing Equipment Power Interface (PSE PI) plays a pivotal role in
the architecture of Power over Ethernet (PoE) systems. It is essentially a
blueprint that outlines how one or multiple power sources are connected to the
eight-pin modular jack, commonly known as the Ethernet RJ45 port. This
connection scheme is crucial for enabling the delivery of power alongside data
over Ethernet cables.

Documentation and Standards
---------------------------

The IEEE 802.3 standard provides detailed documentation on the PSE PI.
Specifically:

- Section "33.2.3 PI pin assignments" covers the pin assignments for PoE
systems that utilize two pairs for power delivery.
- Section "145.2.4 PSE PI" addresses the configuration for PoE systems that
deliver power over all four pairs of an Ethernet cable.

PSE PI and Single Pair Ethernet
-------------------------------

Single Pair Ethernet (SPE) represents a different approach to Ethernet
connectivity, utilizing just one pair of conductors for both data and power
transmission. Unlike the configurations detailed in the PSE PI for standard
Ethernet, which can involve multiple power sourcing arrangements across four or
two pairs of wires, SPE operates on a simpler model due to its single-pair
design. As a result, the complexities of choosing between alternative pin
assignments for power delivery, as described in the PSE PI for multi-pair
Ethernet, are not applicable to SPE.

Understanding PSE PI
--------------------

The Power Sourcing Equipment Power Interface (PSE PI) is a framework defining
how Power Sourcing Equipment (PSE) delivers power to Powered Devices (PDs) over
Ethernet cables. It details two main configurations for power delivery, known
as Alternative A and Alternative B, which are distinguished not only by their
method of power transmission but also by the implications for polarity and data
transmission direction.

Alternative A and B Overview
----------------------------

- **Alternative A:** Utilizes the data-carrying pairs for power transmission in
10/100BaseT networks. The power delivery's polarity in this alternative can
vary based on the MDI (Medium Dependent Interface) or MDI-X (Medium Dependent
Interface Crossover) configuration.

- **Alternative B:** Delivers power over the spare pairs not used for data in
10/100BaseT networks. Unlike Alternative A, Alternative B's method separates
power from data lines within the cable. Though it is less influenced by data
transmission direction, Alternative B includes two configurations with
different polarities, known as variant X and variant S, to accommodate
different network requirements and device specifications.

Table 145–3—PSE Pinout Alternatives
-----------------------------------

The following table outlines the pin configurations for both Alternative A and
Alternative B.

+------------+-------------------+-----------------+-----------------+-----------------+
| Conductor | Alternative A | Alternative A | Alternative B | Alternative B |
| | (MDI-X) | (MDI) | (X) | (S) |
+============+===================+=================+=================+=================+
| 1 | Negative V | Positive V | - | - |
+------------+-------------------+-----------------+-----------------+-----------------+
| 2 | Negative V | Positive V | - | - |
+------------+-------------------+-----------------+-----------------+-----------------+
| 3 | Positive V | Negative V | - | - |
+------------+-------------------+-----------------+-----------------+-----------------+
| 4 | - | - | Negative V | Positive V |
+------------+-------------------+-----------------+-----------------+-----------------+
| 5 | - | - | Negative V | Positive V |
+------------+-------------------+-----------------+-----------------+-----------------+
| 6 | Positive V | Negative V | - | - |
+------------+-------------------+-----------------+-----------------+-----------------+
| 7 | - | - | Positive V | Negative V |
+------------+-------------------+-----------------+-----------------+-----------------+
| 8 | - | - | Positive V | Negative V |
+------------+-------------------+-----------------+-----------------+-----------------+

. note::
- "Positive V" and "Negative V" indicate the voltage polarity for each pin.
- "-" indicates that the pin is not used for power delivery in that
specific configuration.

PSE Power Interface (PSE PI) Connection Diagram
------------------------------------------------

The diagram below illustrates the connection architecture between the RJ45
port, the Ethernet PHY (Physical Layer), and the PSE PI (Power Sourcing
Equipment Power Interface), demonstrating how power and data are delivered
simultaneously through an Ethernet cable. The RJ45 port serves as the physical
interface for these connections, with each of its eight pins connected to both
the Ethernet PHY for data transmission and the PSE PI for power delivery.

. code-block::

+--------------------------+
| |
| RJ45 Port |
| |
+--+--+--+--+--+--+--+--+--+ +-------------+
1| 2| 3| 4| 5| 6| 7| 8| | |
| | | | | | | o-------------------+ |
| | | | | | o--|-------------------+ +<--- PSE 1
| | | | | o--|--|-------------------+ |
| | | | o--|--|--|-------------------+ |
| | | o--|--|--|--|-------------------+ PSE PI |
| | o--|--|--|--|--|-------------------+ |
| o--|--|--|--|--|--|-------------------+ +<--- PSE 2 (optional)
o--|--|--|--|--|--|--|-------------------+ |
| | | | | | | | | |
+--+--+--+--+--+--+--+--+--+ +-------------+
| |
| Ethernet PHY |
| |
+--------------------------+

Simple PSE PI Configuration for Alternative A
---------------------------------------------

The diagram below illustrates a straightforward PSE PI (Power Sourcing
Equipment Power Interface) configuration designed to support the Alternative A
setup for Power over Ethernet (PoE). This implementation is tailored to provide
power delivery through the data-carrying pairs of an Ethernet cable, suitable
for either MDI or MDI-X configurations, albeit supporting one variation at a
time.

. code-block::

+-------------+
| PSE PI |
8 -----+ +-------------+
7 -----+ Rail 1 |
6 -----+------+----------------------+
5 -----+ | |
4 -----+ / Rail 2 | PSE 1
3 -----+----´ +-------------+
2 -----+----+---------´ |
1 -----+---´ +-------------+
|
+-------------+

In this configuration:

- Pins 1 and 2, as well as pins 3 and 6, are utilized for power delivery in
addition to data transmission. This aligns with the standard wiring for
10/100BaseT Ethernet networks where these pairs are used for data.
- Rail 1 and Rail 2 represent the positive and negative voltage rails, with
Rail 1 connected to pins 1 and 2, and Rail 2 connected to pins 3 and 6.
More advanced PSE PI configurations may include integrated or external
switches to change the polarity of the voltage rails, allowing for
compatibility with both MDI and MDI-X configurations.

More complex PSE PI configurations may include additional components, to support
Alternative B, or to provide additional features such as power management, or
additional power delivery capabilities such as 2-pair or 4-pair power delivery.

. code-block::

+-------------+
| PSE PI |
| +---+
8 -----+--------+ | +-------------+
7 -----+--------+ | Rail 1 |
6 -----+--------+ +-----------------+
5 -----+--------+ | |
4 -----+--------+ | Rail 2 | PSE 1
3 -----+--------+ +----------------+
2 -----+--------+ | |
1 -----+--------+ | +-------------+
| +---+
+-------------+

Device Tree Configuration: Describing PSE PI Configurations
------------------------------------------------------------

The necessity for a separate PSE PI node in the device tree is influenced by
the intricacy of the Power over Ethernet (PoE) system's setup. Here are
descriptions of both simple and complex PSE PI configurations to illustrate
this decision-making process:

**Simple PSE PI Configuration:**
In a straightforward scenario, the PSE PI setup involves a direct, one-to-one
connection between a single PSE controller and an Ethernet port. This setup
typically supports basic PoE functionality without the need for dynamic
configuration or management of multiple power delivery modes. For such simple
configurations, detailing the PSE PI within the existing PSE controller's node
may suffice, as the system does not encompass additional complexity that
warrants a separate node. The primary focus here is on the clear and direct
association of power delivery to a specific Ethernet port.

**Complex PSE PI Configuration:**
Contrastingly, a complex PSE PI setup may encompass multiple PSE controllers or
auxiliary circuits that collectively manage power delivery to one Ethernet
port. Such configurations might support a range of PoE standards and require
the capability to dynamically configure power delivery based on the operational
mode (e.g., PoE2 versus PoE4) or specific requirements of connected devices. In
these instances, a dedicated PSE PI node becomes essential for accurately
documenting the system architecture. This node would serve to detail the
interactions between different PSE controllers, the support for various PoE
modes, and any additional logic required to coordinate power delivery across
the network infrastructure.

**Guidance:**

For simple PSE setups, including PSE PI information in the PSE controller node
might suffice due to the straightforward nature of these systems. However,
complex configurations, involving multiple components or advanced PoE features,
benefit from a dedicated PSE PI node. This method adheres to IEEE 802.3
specifications, improving documentation clarity and ensuring accurate
representation of the PoE system's complexity.

PSE PI Node: Essential Information
----------------------------------

The PSE PI (Power Sourcing Equipment Power Interface) node in a device tree can
include several key pieces of information critical for defining the power
delivery capabilities and configurations of a PoE (Power over Ethernet) system.
Below is a list of such information, along with explanations for their
necessity and reasons why they might not be found within a PSE controller node:

1. **Powered Pairs Configuration**
- *Description:* Identifies the pairs used for power delivery in the
Ethernet cable.
- *Necessity:* Essential to ensure the correct pairs are powered according
to the board's design.
- *PSE Controller Node:* Typically lacks details on physical pair usage,
focusing on power regulation.

2. **Polarity of Powered Pairs**
- *Description:* Specifies the polarity (positive or negative) for each
powered pair.
- *Necessity:* Critical for safe and effective power transmission to PDs.
- *PSE Controller Node:* Polarity management may exceed the standard
functionalities of PSE controllers.

3. **PSE Cells Association**
- *Description:* Details the association of PSE cells with Ethernet ports or
pairs in multi-cell configurations.
- *Necessity:* Allows for optimized power resource allocation in complex
systems.
- *PSE Controller Node:* Controllers may not manage cell associations
directly, focusing instead on power flow regulation.

4. **Support for PoE Standards**
- *Description:* Lists the PoE standards and configurations supported by the
system.
- *Necessity:* Ensures system compatibility with various PDs and adherence
to industry standards.
- *PSE Controller Node:* Specific capabilities may depend on the overall PSE
PI design rather than the controller alone. Multiple PSE cells per PI
do not necessarily imply support for multiple PoE standards.

5. **Protection Mechanisms**
- *Description:* Outlines additional protection mechanisms, such as
overcurrent protection and thermal management.
- *Necessity:* Provides extra safety and stability, complementing PSE
controller protections.
- *PSE Controller Node:* Some protections may be implemented via
board-specific hardware or algorithms external to the controller.


Regards,
Oleksij
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2024-02-14 13:21:18

by Kory Maincent

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

Hello Rob,

Thanks for your review!

On Fri, 9 Feb 2024 14:43:49 +0000
Rob Herring <[email protected]> wrote:

> On Thu, Feb 08, 2024 at 02:08:47PM +0100, Kory Maincent wrote:
> > Before hand we set "#pse-cell" to 1 to define a PSE controller with
>
> #pse-cells
>
> > several PIs (Power Interface). The drawback of this was that we could not
> > have any information on the PI except its number.
>
> Then increase it to what you need. The whole point of #foo-cells is that
> it is variable depending on what the provider needs.
>
> > Add support for pse_pis and pse_pi node to be able to have more information
> > on the PI like the number of pairset used and the pairset pinout.
>
> Please explain the problem you are trying to solve, not your solution. I
> don't understand what the problem is to provide any useful suggestions
> on the design.

Please see Oleksij's reply.
Thank you Oleksij, for the documentation!!

> >
> > Sponsored-by: Dent Project <[email protected]>
>
> Is this a recognized tag? First I've seen it.

This is not a standard tag but it has been used several times in the past.

> >
> > -required:
> > - - "#pse-cells"
> > + pse_pis:
> > + $ref: "#/$defs/pse_pis"
> > +
> > +$defs:
>
> $defs is for when you need multiple copies of the same thing. I don't
> see that here.

I made this choice for better readability but indeed it is used only once.
I will remove it then.

> > + pse_pis:
> > + type: object
> > + description:
> > + Kind of a matrix to identify the concordance between a PSE Power
> > + Interface and one or two (PoE4) physical ports.
> > +
> > + properties:
> > + "#address-cells":
> > + const: 1
> > +
> > + "#size-cells":
> > + const: 0
> > +
> > + patternProperties:
> > + "^pse_pi@[0-9]+$":
>
> Unit-addresses are hex.

Oops sorry for the mistake.

>
> > + $ref: "#/$defs/pse_pi"
> > +
> > + required:
> > + - "#address-cells"
> > + - "#size-cells"
> > +
> > + pse_pi:
> > + description:
> > + PSE PI device for power delivery via pairsets, compliant with IEEE
> > + 802.3-2022, Section 145.2.4. Each pairset comprises a positive and a
> > + negative VPSE pair, adhering to the pinout configurations detailed in
> > + the standard.
> > + type: object
> > + properties:
> > + reg:
> > + maxItems: 1
>
> As you are defining the addressing here, you need to define what the
> "addresses" are.

Yes I will add some documentation in next version.

> > + values are "alternative-a" and "alternative-b". Each name should
> > + correspond to a phandle in the 'pairset' property pointing to the
> > + power supply for that pairset.
> > + $ref: /schemas/types.yaml#/definitions/string-array
> > + minItems: 1
> > + maxItems: 2
> > + items:
> > + - enum:
> > + - "alternative-a"
> > + - "alternative-b"
>
> This leaves the 2nd entry undefined. You need the dictionary form of
> 'items' rather than a list. IOW, Drop the '-' under items.

Oh thanks! That is what I was looking for. I was struggling using the right
description.

Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

2024-02-14 15:42:44

by Kory Maincent

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

On Wed, 14 Feb 2024 14:13:10 +0100
Köry Maincent <[email protected]> wrote:

> Hello Rob,
>
> Thanks for your review!
>
> On Fri, 9 Feb 2024 14:43:49 +0000
> Rob Herring <[email protected]> wrote:
>
> > On Thu, Feb 08, 2024 at 02:08:47PM +0100, Kory Maincent wrote:
> > > Before hand we set "#pse-cell" to 1 to define a PSE controller with
> >
> > #pse-cells
> >
> > > several PIs (Power Interface). The drawback of this was that we could not
> > > have any information on the PI except its number.
> >
> > Then increase it to what you need. The whole point of #foo-cells is that
> > it is variable depending on what the provider needs.
> >
> > > Add support for pse_pis and pse_pi node to be able to have more
> > > information on the PI like the number of pairset used and the pairset
> > > pinout.
> >
> > Please explain the problem you are trying to solve, not your solution. I
> > don't understand what the problem is to provide any useful suggestions
> > on the design.
>
> Please see Oleksij's reply.
> Thank you Oleksij, for the documentation!!
>
> > >
> > > Sponsored-by: Dent Project <[email protected]>
> >
> > Is this a recognized tag? First I've seen it.
>
> This is not a standard tag but it has been used several times in the past.

Not so much used indeed:
$ git log --grep="Sponsored" | grep Sponsored
Sponsored by: The FreeBSD Foundation
Sponsored by: The FreeBSD Foundation
Sponsored by: The FreeBSD Foundation
Sponsored by: The FreeBSD Foundation
Sponsored-by: Google Chromium project
Sponsored: Google ChromeOS
Sponsored: Google ChromeOS

Is it ok to keep it?

Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

2024-02-14 17:14:09

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 01/17] MAINTAINERS: net: Add Oleksij to pse-pd maintainers

On Thu, Feb 08, 2024 at 02:08:38PM +0100, Kory Maincent wrote:
> Oleksij was the first to add support for pse-pd net subsystem.
> Add himself to the maintainers seems logical.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>

Oleksij, are you O.K. with this? It would be good to Acked-by: it.

Andrew

2024-02-14 17:17:25

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 02/17] of: property: Add fw_devlink support for pse parent

On Thu, Feb 08, 2024 at 02:08:39PM +0100, Kory Maincent wrote:
> This allows fw_devlink to create device links between consumers of
> a PSE and the supplier of the PSE.

It will be interesting to see how this turns out. fw_devlink often
gets itself into knots with MAC and PHY dependencies sometimes being
circular. If we get a PSE on an MDIO bus, this is going to get even
more interesting.

> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>

Reviewed-by: Andrew Lunn <[email protected]>

Andrew

2024-02-14 17:19:29

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 05/17] net: pse-pd: Introduce PSE types enumeration

On Thu, Feb 08, 2024 at 02:08:42PM +0100, Kory Maincent wrote:
> Introduce an enumeration to define PSE types (C33 or PoDL),
> utilizing a bitfield for potential future support of both types.
> Include 'pse_get_types' helper for external access to PSE type info.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>

Reviewed-by: Andrew Lunn <[email protected]>

Andrew

2024-02-14 17:29:03

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 07/17] netlink: specs: Modify pse attribute prefix

On Thu, Feb 08, 2024 at 02:08:44PM +0100, Kory Maincent wrote:
> Remove podl from the attribute prefix to prepare the support of PoE pse
> netlink spec.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>

Reviewed-by: Andrew Lunn <[email protected]>

Andrew

2024-02-14 17:31:10

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 09/17] MAINTAINERS: Add myself to pse networking maintainer

On Thu, Feb 08, 2024 at 02:08:46PM +0100, Kory Maincent wrote:
> As I add support for PoE in PSE networking subsystem it seems legitimate
> to be added to the maintainers.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>

Reviewed-by: Andrew Lunn <[email protected]>

Andrew

2024-02-14 17:43:11

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

> Alternative A and B Overview
> ----------------------------
>
> - **Alternative A:** Utilizes the data-carrying pairs for power transmission in
> 10/100BaseT networks. The power delivery's polarity in this alternative can
> vary based on the MDI (Medium Dependent Interface) or MDI-X (Medium Dependent
> Interface Crossover) configuration.
>
> - **Alternative B:** Delivers power over the spare pairs not used for data in
> 10/100BaseT networks. Unlike Alternative A, Alternative B's method separates
> power from data lines within the cable. Though it is less influenced by data
> transmission direction, Alternative B includes two configurations with
> different polarities, known as variant X and variant S, to accommodate
> different network requirements and device specifications.

Thanks for this documentation.

It might be worth pointing out that RJ-45 supports up to 4
pairs. However, 10/100BaseT only makes use of two pairs for data
transfer from the four. 1000BaseT and above make use of all four pairs
for data transfer. If you don't know this, it is not so obvious what
'data-carrying pairs' and 'spare pairs' mean.

And what happens for 1000BaseT when all four pairs are in use?

Andrew

2024-02-14 17:53:27

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 06/17] net: ethtool: pse-pd: Expand pse commands with the PSE PoE interface

On Thu, Feb 08, 2024 at 02:08:43PM +0100, Kory Maincent wrote:
> Add PSE PoE interface support in the ethtool pse command.
>
> Sponsored-by: Dent Project <[email protected]>
> Signed-off-by: Kory Maincent <[email protected]>

I'm not particularly knowledgeable with netlink, but...

Reviewed-by: Andrew Lunn <[email protected]>

Andrew

2024-02-15 08:23:47

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 01/17] MAINTAINERS: net: Add Oleksij to pse-pd maintainers

On Wed, Feb 14, 2024 at 06:13:39PM +0100, Andrew Lunn wrote:
> On Thu, Feb 08, 2024 at 02:08:38PM +0100, Kory Maincent wrote:
> > Oleksij was the first to add support for pse-pd net subsystem.
> > Add himself to the maintainers seems logical.
> >
> > Sponsored-by: Dent Project <[email protected]>
> > Signed-off-by: Kory Maincent <[email protected]>

Acked-by: Oleksij Rempel <[email protected]>

> Oleksij, are you O.K. with this? It would be good to Acked-by: it.
>
> Andrew
>
>

--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2024-02-15 09:01:03

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Wed, Feb 14, 2024 at 06:41:54PM +0100, Andrew Lunn wrote:
> > Alternative A and B Overview
> > ----------------------------
> >
> > - **Alternative A:** Utilizes the data-carrying pairs for power transmission in
> > 10/100BaseT networks. The power delivery's polarity in this alternative can
> > vary based on the MDI (Medium Dependent Interface) or MDI-X (Medium Dependent
> > Interface Crossover) configuration.
> >
> > - **Alternative B:** Delivers power over the spare pairs not used for data in
> > 10/100BaseT networks. Unlike Alternative A, Alternative B's method separates
> > power from data lines within the cable. Though it is less influenced by data
> > transmission direction, Alternative B includes two configurations with
> > different polarities, known as variant X and variant S, to accommodate
> > different network requirements and device specifications.
>
> Thanks for this documentation.
>
> It might be worth pointing out that RJ-45 supports up to 4
> pairs. However, 10/100BaseT only makes use of two pairs for data
> transfer from the four. 1000BaseT and above make use of all four pairs
> for data transfer. If you don't know this, it is not so obvious what
> 'data-carrying pairs' and 'spare pairs' mean.

@Kory, can you please update it.

> And what happens for 1000BaseT when all four pairs are in use?

Hm.. good question. I didn't found the answer in the spec. By combining all
puzzle parts I assume, different Alternative configurations are designed
to handle conflict between "PSE Physical Layer classification" and PHY
autoneg.

Here is how multi-pulse Physical Layer classification is done:
https://img.electronicdesign.com/files/base/ebm/electronicdesign/image/2020/07/Figure_5.5f2094553a61c.png

this is the source:
https://www.electronicdesign.com/technologies/power/whitepaper/21137799/silicon-labs-90-w-power-over-ethernet-explained

To avoid classification conflict with autoneg. Assuming, PHY on PD side
will be not powered until classification is completed. The only source
of pulses is the PHY on PSE side (if it is not under control of software
on PSE side or Midspan PSE is used), so aneg pulses should be send on
negative PoE pair? This all is just speculation, I would need to ask
some expert or do testing.

If this assumption is correct, PHY framework will need to know exact
layout of MDI-X setting and/or silent PHY until PSE classification is done.

--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2024-02-15 10:57:12

by Kory Maincent

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Thu, 15 Feb 2024 09:17:48 +0100
Oleksij Rempel <[email protected]> wrote:

> On Wed, Feb 14, 2024 at 06:41:54PM +0100, Andrew Lunn wrote:
> > > Alternative A and B Overview
> > > ----------------------------
> > >
> > > - **Alternative A:** Utilizes the data-carrying pairs for power
> > > transmission in 10/100BaseT networks. The power delivery's polarity in
> > > this alternative can vary based on the MDI (Medium Dependent Interface)
> > > or MDI-X (Medium Dependent Interface Crossover) configuration.
> > >
> > > - **Alternative B:** Delivers power over the spare pairs not used for
> > > data in 10/100BaseT networks. Unlike Alternative A, Alternative B's
> > > method separates power from data lines within the cable. Though it is
> > > less influenced by data transmission direction, Alternative B includes
> > > two configurations with different polarities, known as variant X and
> > > variant S, to accommodate different network requirements and device
> > > specifications.
> >
> > Thanks for this documentation.
> >
> > It might be worth pointing out that RJ-45 supports up to 4
> > pairs. However, 10/100BaseT only makes use of two pairs for data
> > transfer from the four. 1000BaseT and above make use of all four pairs
> > for data transfer. If you don't know this, it is not so obvious what
> > 'data-carrying pairs' and 'spare pairs' mean.
>
> @Kory, can you please update it.
>
> > And what happens for 1000BaseT when all four pairs are in use?
>
> Hm.. good question. I didn't found the answer in the spec. By combining all
> puzzle parts I assume, different Alternative configurations are designed
> to handle conflict between "PSE Physical Layer classification" and PHY
> autoneg.

Oleksij how did you get the definition of Alternative A uses the "data-carrying"
pairs for power transmission and Alternative B Delivers power over the "spare
pairs"?

On my understanding of the 2022 standard the definition is:
- Alternative A is for pinout conductors 1, 2, 3 and 6
- Alternative B is for pinout conductors 4, 5, 7, 8.

Then indeed if we are in 10/100BaseT Alternative A are "data-carrying
pairs" and Alternative B are "spare pairs" but that's not the case on
1000BaseT.

You can see it in the figures in the paragraph 145.2.3.

Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

2024-02-15 12:04:38

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Thu, Feb 15, 2024 at 11:41:23AM +0100, Köry Maincent wrote:
> On Thu, 15 Feb 2024 09:17:48 +0100
> Oleksij Rempel <[email protected]> wrote:
>
> > On Wed, Feb 14, 2024 at 06:41:54PM +0100, Andrew Lunn wrote:
> > > > Alternative A and B Overview
> > > > ----------------------------
> > > >
> > > > - **Alternative A:** Utilizes the data-carrying pairs for power
> > > > transmission in 10/100BaseT networks. The power delivery's polarity in
> > > > this alternative can vary based on the MDI (Medium Dependent Interface)
> > > > or MDI-X (Medium Dependent Interface Crossover) configuration.
> > > >
> > > > - **Alternative B:** Delivers power over the spare pairs not used for
> > > > data in 10/100BaseT networks. Unlike Alternative A, Alternative B's
> > > > method separates power from data lines within the cable. Though it is
> > > > less influenced by data transmission direction, Alternative B includes
> > > > two configurations with different polarities, known as variant X and
> > > > variant S, to accommodate different network requirements and device
> > > > specifications.
> > >
> > > Thanks for this documentation.
> > >
> > > It might be worth pointing out that RJ-45 supports up to 4
> > > pairs. However, 10/100BaseT only makes use of two pairs for data
> > > transfer from the four. 1000BaseT and above make use of all four pairs
> > > for data transfer. If you don't know this, it is not so obvious what
> > > 'data-carrying pairs' and 'spare pairs' mean.
> >
> > @Kory, can you please update it.
> >
> > > And what happens for 1000BaseT when all four pairs are in use?
> >
> > Hm.. good question. I didn't found the answer in the spec. By combining all
^^^^^^^^^^^^^^^^

> > puzzle parts I assume, different Alternative configurations are designed
^^^^^^^^^^^^^^^^^^^^^^

> > to handle conflict between "PSE Physical Layer classification" and PHY
> > autoneg.
>
> Oleksij how did you get the definition of Alternative A uses the "data-carrying"
> pairs for power transmission and Alternative B Delivers power over the "spare
> pairs"?
>
> On my understanding of the 2022 standard the definition is:
> - Alternative A is for pinout conductors 1, 2, 3 and 6
> - Alternative B is for pinout conductors 4, 5, 7, 8.
>
> Then indeed if we are in 10/100BaseT Alternative A are "data-carrying
> pairs" and Alternative B are "spare pairs" but that's not the case on
> 1000BaseT.
>
> You can see it in the figures in the paragraph 145.2.3.

Please, re-read my answer :)

Autoneg for 1000Mbit is not done on all 4 pairs. The only MDI/-X
dependent transfer processes only on one pair is autoneg. Every thing
else is extrapolated out of it.

--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2024-02-15 13:54:38

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

On Wed, Feb 14, 2024 at 04:41:50PM +0100, K?ry Maincent wrote:
> On Wed, 14 Feb 2024 14:13:10 +0100
> K?ry Maincent <[email protected]> wrote:
>
> > Hello Rob,
> >
> > Thanks for your review!
> >
> > On Fri, 9 Feb 2024 14:43:49 +0000
> > Rob Herring <[email protected]> wrote:
> >
> > > On Thu, Feb 08, 2024 at 02:08:47PM +0100, Kory Maincent wrote:
> > > > Before hand we set "#pse-cell" to 1 to define a PSE controller with
> > >
> > > #pse-cells
> > >
> > > > several PIs (Power Interface). The drawback of this was that we could not
> > > > have any information on the PI except its number.
> > >
> > > Then increase it to what you need. The whole point of #foo-cells is that
> > > it is variable depending on what the provider needs.
> > >
> > > > Add support for pse_pis and pse_pi node to be able to have more
> > > > information on the PI like the number of pairset used and the pairset
> > > > pinout.
> > >
> > > Please explain the problem you are trying to solve, not your solution. I
> > > don't understand what the problem is to provide any useful suggestions
> > > on the design.
> >
> > Please see Oleksij's reply.
> > Thank you Oleksij, for the documentation!!
> >
> > > >
> > > > Sponsored-by: Dent Project <[email protected]>
> > >
> > > Is this a recognized tag? First I've seen it.
> >
> > This is not a standard tag but it has been used several times in the past.
>
> Not so much used indeed:
> $ git log --grep="Sponsored" | grep Sponsored
> Sponsored by: The FreeBSD Foundation
> Sponsored by: The FreeBSD Foundation
> Sponsored by: The FreeBSD Foundation
> Sponsored by: The FreeBSD Foundation
> Sponsored-by: Google Chromium project
> Sponsored: Google ChromeOS
> Sponsored: Google ChromeOS
>
> Is it ok to keep it?

IMO, its use should be documented like other tags, or it should not be
used. Just write a sentence to the same effect.

Rob


2024-02-15 14:34:30

by Kory Maincent

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

On Thu, 15 Feb 2024 15:01:08 +0100
Andrew Lunn <[email protected]> wrote:

> > > Not so much used indeed:
> > > $ git log --grep="Sponsored" | grep Sponsored
> > > Sponsored by: The FreeBSD Foundation
> > > Sponsored by: The FreeBSD Foundation
> > > Sponsored by: The FreeBSD Foundation
> > > Sponsored by: The FreeBSD Foundation
> > > Sponsored-by: Google Chromium project
> > > Sponsored: Google ChromeOS
> > > Sponsored: Google ChromeOS
> > >
> > > Is it ok to keep it?
> >
> > IMO, its use should be documented like other tags, or it should not be
> > used. Just write a sentence to the same effect.
>
> Or include a patch to document it :-)

It seems someone has already tried to send a patch to add this tag but it has
not been accepted due to maintainers extra works bring by the tag:
https://lore.kernel.org/lkml/[email protected]/

I will replace it by a small sentence then.

Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

2024-02-15 17:22:42

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 10/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs

> > Not so much used indeed:
> > $ git log --grep="Sponsored" | grep Sponsored
> > Sponsored by: The FreeBSD Foundation
> > Sponsored by: The FreeBSD Foundation
> > Sponsored by: The FreeBSD Foundation
> > Sponsored by: The FreeBSD Foundation
> > Sponsored-by: Google Chromium project
> > Sponsored: Google ChromeOS
> > Sponsored: Google ChromeOS
> >
> > Is it ok to keep it?
>
> IMO, its use should be documented like other tags, or it should not be
> used. Just write a sentence to the same effect.

Or include a patch to document it :-)

Andrew

2024-02-15 17:52:13

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

> Hm.. good question. I didn't found the answer in the spec. By combining all
> puzzle parts I assume, different Alternative configurations are designed
> to handle conflict between "PSE Physical Layer classification" and PHY
> autoneg.
>
> Here is how multi-pulse Physical Layer classification is done:
> https://img.electronicdesign.com/files/base/ebm/electronicdesign/image/2020/07/Figure_5.5f2094553a61c.png
>
> this is the source:
> https://www.electronicdesign.com/technologies/power/whitepaper/21137799/silicon-labs-90-w-power-over-ethernet-explained
>
> To avoid classification conflict with autoneg. Assuming, PHY on PD side
> will be not powered until classification is completed. The only source
> of pulses is the PHY on PSE side (if it is not under control of software
> on PSE side or Midspan PSE is used), so aneg pulses should be send on
> negative PoE pair? This all is just speculation, I would need to ask
> some expert or do testing.
>
> If this assumption is correct, PHY framework will need to know exact
> layout of MDI-X setting and/or silent PHY until PSE classification is done.

Ideally, we don't want to define a DT binding, and then find it is
wrong for 1000BaseT and above and we need to change it.

So, either somebody needs to understand 1000BaseT and can say the
proposed binding works, or we explicitly document the binding is
limited to 10BaseT and 100BaseT.

I'm not sure the second solution will actually fly. This was being
tested with Marvell Prestera switch. I doubt it even has any Fast
Ethernet ports.

Andrew


2024-02-16 07:48:02

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Thu, Feb 15, 2024 at 06:51:55PM +0100, Andrew Lunn wrote:
> > Hm.. good question. I didn't found the answer in the spec. By combining all
> > puzzle parts I assume, different Alternative configurations are designed
> > to handle conflict between "PSE Physical Layer classification" and PHY
> > autoneg.
> >
> > Here is how multi-pulse Physical Layer classification is done:
> > https://img.electronicdesign.com/files/base/ebm/electronicdesign/image/2020/07/Figure_5.5f2094553a61c.png
> >
> > this is the source:
> > https://www.electronicdesign.com/technologies/power/whitepaper/21137799/silicon-labs-90-w-power-over-ethernet-explained
> >
> > To avoid classification conflict with autoneg. Assuming, PHY on PD side
> > will be not powered until classification is completed. The only source
> > of pulses is the PHY on PSE side (if it is not under control of software
> > on PSE side or Midspan PSE is used), so aneg pulses should be send on
> > negative PoE pair? This all is just speculation, I would need to ask
> > some expert or do testing.
> >
> > If this assumption is correct, PHY framework will need to know exact
> > layout of MDI-X setting and/or silent PHY until PSE classification is done.
>
> Ideally, we don't want to define a DT binding, and then find it is
> wrong for 1000BaseT and above and we need to change it.
>
> So, either somebody needs to understand 1000BaseT and can say the
> proposed binding works, or we explicitly document the binding is
> limited to 10BaseT and 100BaseT.

I asked the internet and found the answer: Some PSE/PD implementations
are not compatible with 1000BaseT.

See Figure 33–4—10BASE-T/100BASE-TX Endpoint PSE location overview.
Alternative B show a variant where power is injected directly to pairs
without using magnetics as it is done for Alternative A (phantom
delivery - over magnetics).

I assume, the reasoning for this kind of design is simple - price.
Otherwise magnetics will have special requirements:
https://www.coilcraft.com/de-de/edu/series/magnetics-for-power-over-ethernet/

So, we have following variants of 2 pairs PoE:
+---------+---------------+-------------------+---------------------+--------------------+
| Variant | Alternative | Polarity | Power Feeding Type | Compatibility with |
| | (a/b) | (Direct/Reverse) | (Direct/Phantom) | 1000BaseT |
+=========+===============+===================+=====================+====================+
| 1 | a | Direct | Phantom | Yes |
+---------+---------------+-------------------+---------------------+--------------------+
| 2 | a | Reverse | Phantom | Yes |
+---------+---------------+-------------------+---------------------+--------------------+
| 3 | b | Direct | Phantom | Yes |
+---------+---------------+-------------------+---------------------+--------------------+
| 4 | b | Reverse | Phantom | Yes |
+---------+---------------+-------------------+---------------------+--------------------+
| 5 | b | Direct | Direct | No |
+---------+---------------+-------------------+---------------------+--------------------+
| 6 | b | Reverse | Direct | No |
+---------+---------------+-------------------+---------------------+--------------------+

An advanced PSE may implement range of different variants direct in the PSE
controller or with additional ICs in the PSE PI. The same is about PD.

Let's take as example PD-IM-7608M eval board:
https://www.microchip.com/en-us/development-tool/PD-IM-7608M

According to the schematics:
https://ww1.microchip.com/downloads/en/DeviceDoc/PD-IM-7608M.zip
It supports only Variant 5 - Alternative B, with only one polarity,
and direct feeding without magnetics.

The simple PD may support only one variant:
https://community.fs.com/article/troubleshooting-poe-errors.html
" the power modes of PSE and PD are other factors that may cause PoE
faults. There are three PoE modes: Alternative A, alternative B, and
4-pair delivery. If a PD only supports PoE mode B power delivery, while
a PoE switch is based on Alternative A, as a result, the PD and PoE
switch can not work together."

For this case, it will be good if systems knows supported modes, so user
can get this information directly. For example with ethtool

--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2024-02-17 06:47:46

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

Hi Kory,

Can you please integrated this new insights to the PSE PI documentation.
Instead of 1000BaseT only, please use 1000/2.5G/5G/10GBASE-T as
documented in the spec.

On Fri, Feb 16, 2024 at 08:47:14AM +0100, Oleksij Rempel wrote:
> On Thu, Feb 15, 2024 at 06:51:55PM +0100, Andrew Lunn wrote:
> > > Hm.. good question. I didn't found the answer in the spec. By combining all
> > > puzzle parts I assume, different Alternative configurations are designed
> > > to handle conflict between "PSE Physical Layer classification" and PHY
> > > autoneg.
> > >
> > > Here is how multi-pulse Physical Layer classification is done:
> > > https://img.electronicdesign.com/files/base/ebm/electronicdesign/image/2020/07/Figure_5.5f2094553a61c.png
> > >
> > > this is the source:
> > > https://www.electronicdesign.com/technologies/power/whitepaper/21137799/silicon-labs-90-w-power-over-ethernet-explained
> > >
> > > To avoid classification conflict with autoneg. Assuming, PHY on PD side
> > > will be not powered until classification is completed. The only source
> > > of pulses is the PHY on PSE side (if it is not under control of software
> > > on PSE side or Midspan PSE is used), so aneg pulses should be send on
> > > negative PoE pair? This all is just speculation, I would need to ask
> > > some expert or do testing.
> > >
> > > If this assumption is correct, PHY framework will need to know exact
> > > layout of MDI-X setting and/or silent PHY until PSE classification is done.
> >
> > Ideally, we don't want to define a DT binding, and then find it is
> > wrong for 1000BaseT and above and we need to change it.
> >
> > So, either somebody needs to understand 1000BaseT and can say the
> > proposed binding works, or we explicitly document the binding is
> > limited to 10BaseT and 100BaseT.
>
> I asked the internet and found the answer: Some PSE/PD implementations
> are not compatible with 1000BaseT.
>
> See Figure 33–4—10BASE-T/100BASE-TX Endpoint PSE location overview.
> Alternative B show a variant where power is injected directly to pairs
> without using magnetics as it is done for Alternative A (phantom
> delivery - over magnetics).
>
> I assume, the reasoning for this kind of design is simple - price.
> Otherwise magnetics will have special requirements:
> https://www.coilcraft.com/de-de/edu/series/magnetics-for-power-over-ethernet/
>
> So, we have following variants of 2 pairs PoE:
> +---------+---------------+-------------------+---------------------+--------------------+
> | Variant | Alternative | Polarity | Power Feeding Type | Compatibility with |
> | | (a/b) | (Direct/Reverse) | (Direct/Phantom) | 1000BaseT |
> +=========+===============+===================+=====================+====================+
> | 1 | a | Direct | Phantom | Yes |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 2 | a | Reverse | Phantom | Yes |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 3 | b | Direct | Phantom | Yes |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 4 | b | Reverse | Phantom | Yes |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 5 | b | Direct | Direct | No |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 6 | b | Reverse | Direct | No |
> +---------+---------------+-------------------+---------------------+--------------------+
>
> An advanced PSE may implement range of different variants direct in the PSE
> controller or with additional ICs in the PSE PI. The same is about PD.
>
> Let's take as example PD-IM-7608M eval board:
> https://www.microchip.com/en-us/development-tool/PD-IM-7608M
>
> According to the schematics:
> https://ww1.microchip.com/downloads/en/DeviceDoc/PD-IM-7608M.zip
> It supports only Variant 5 - Alternative B, with only one polarity,
> and direct feeding without magnetics.
>
> The simple PD may support only one variant:
> https://community.fs.com/article/troubleshooting-poe-errors.html
> " the power modes of PSE and PD are other factors that may cause PoE
> faults. There are three PoE modes: Alternative A, alternative B, and
> 4-pair delivery. If a PD only supports PoE mode B power delivery, while
> a PoE switch is based on Alternative A, as a result, the PD and PoE
> switch can not work together."
>
> For this case, it will be good if systems knows supported modes, so user
> can get this information directly. For example with ethtool
>
> --
> Pengutronix e.K. | |
> Steuerwalder Str. 21 | http://www.pengutronix.de/ |
> 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
> Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2024-02-19 14:51:56

by Kory Maincent

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Fri, 16 Feb 2024 08:47:14 +0100
Oleksij Rempel <[email protected]> wrote:
> >
> > So, either somebody needs to understand 1000BaseT and can say the
> > proposed binding works, or we explicitly document the binding is
> > limited to 10BaseT and 100BaseT.
>
> I asked the internet and found the answer: Some PSE/PD implementations
> are not compatible with 1000BaseT.
>
> See Figure 33–4—10BASE-T/100BASE-TX Endpoint PSE location overview.
> Alternative B show a variant where power is injected directly to pairs
> without using magnetics as it is done for Alternative A (phantom
> delivery - over magnetics).
>
> So, we have following variants of 2 pairs PoE:
> +---------+---------------+-------------------+---------------------+--------------------+
> | Variant | Alternative | Polarity | Power Feeding Type |
> Compatibility with | | | (a/b) | (Direct/Reverse) |
> (Direct/Phantom) | 1000BaseT |
> +=========+===============+===================+=====================+====================+
> | 1 | a | Direct | Phantom | Yes
> |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 2 | a | Reverse | Phantom | Yes
> |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 3 | b | Direct | Phantom | Yes
> |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 4 | b | Reverse | Phantom | Yes
> |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 5 | b | Direct | Direct | No
> |
> +---------+---------------+-------------------+---------------------+--------------------+
> | 6 | b | Reverse | Direct | No
> |
> +---------+---------------+-------------------+---------------------+--------------------+

Maybe we could remove the polarity column on this table as it does not bring
more information. It is also already explained on the PI pinout alternatives
table.

Also we should document that a 4pairs PSE supporting only 10/100BaseT (which
mean no magnetics on pinout AlternativeB) may not be compatible with a 4pairs
1GBaseT PD.

> For this case, it will be good if systems knows supported modes, so user
> can get this information directly. For example with ethtool

Yes.

Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

2024-02-19 16:08:29

by Oleksij Rempel

[permalink] [raw]
Subject: Re: [PATCH net-next v3 14/17] dt-bindings: net: pse-pd: Add bindings for PD692x0 PSE controller

On Mon, Feb 19, 2024 at 03:31:06PM +0100, Köry Maincent wrote:
> On Fri, 16 Feb 2024 08:47:14 +0100
> Oleksij Rempel <[email protected]> wrote:
> > >
> > > So, either somebody needs to understand 1000BaseT and can say the
> > > proposed binding works, or we explicitly document the binding is
> > > limited to 10BaseT and 100BaseT.
> >
> > I asked the internet and found the answer: Some PSE/PD implementations
> > are not compatible with 1000BaseT.
> >
> > See Figure 33–4—10BASE-T/100BASE-TX Endpoint PSE location overview.
> > Alternative B show a variant where power is injected directly to pairs
> > without using magnetics as it is done for Alternative A (phantom
> > delivery - over magnetics).
> >
> > So, we have following variants of 2 pairs PoE:
> > +---------+---------------+-------------------+---------------------+--------------------+
> > | Variant | Alternative | Polarity | Power Feeding Type |
> > Compatibility with | | | (a/b) | (Direct/Reverse) |
> > (Direct/Phantom) | 1000BaseT |
> > +=========+===============+===================+=====================+====================+
> > | 1 | a | Direct | Phantom | Yes
> > |
> > +---------+---------------+-------------------+---------------------+--------------------+
> > | 2 | a | Reverse | Phantom | Yes
> > |
> > +---------+---------------+-------------------+---------------------+--------------------+
> > | 3 | b | Direct | Phantom | Yes
> > |
> > +---------+---------------+-------------------+---------------------+--------------------+
> > | 4 | b | Reverse | Phantom | Yes
> > |
> > +---------+---------------+-------------------+---------------------+--------------------+
> > | 5 | b | Direct | Direct | No
> > |
> > +---------+---------------+-------------------+---------------------+--------------------+
> > | 6 | b | Reverse | Direct | No
> > |
> > +---------+---------------+-------------------+---------------------+--------------------+
>
> Maybe we could remove the polarity column on this table as it does not bring
> more information. It is also already explained on the PI pinout alternatives
> table.

Ack. I'm still not sure if "Phantom" is correct description.

>
> Also we should document that a 4pairs PSE supporting only 10/100BaseT (which
> mean no magnetics on pinout AlternativeB) may not be compatible with a 4pairs
> 1GBaseT PD.

Ack. s/may not/is not/ :) and 4pairs PSE is not always compatible with
PoE4 as well. I assume this kind of knowledge we will get from PSE
driver.

Regards,
Oleksij

--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |