From: Jonas Jelonek <jelonek.jonas@gmail.com>
To: Paolo Abeni <pabeni@redhat.com>
Cc: netdev@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
"Daniel Golle" <daniel@makrotopia.org>,
"Bjørn Mork" <bjorn@mork.no>,
"Conor Dooley" <conor+dt@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"David S . Miller" <davem@davemloft.net>,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
"Eric Dumazet" <edumazet@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Kory Maincent" <kory.maincent@bootlin.com>,
"Oleksij Rempel" <o.rempel@pengutronix.de>
Subject: Re: [PATCH net-next v4 2/2] net: pse-pd: add Realtek/Broadcom PSE MCU driver
Date: Mon, 6 Jul 2026 13:22:59 +0200 [thread overview]
Message-ID: <d5e32492-0585-4b28-a277-d58b9218443a@gmail.com> (raw)
In-Reply-To: <2ee45ab5-a329-4891-8326-ac8f14b6374a@redhat.com>
Hi Paolo,
On 03.07.26 09:55, Paolo Abeni wrote:
> On 6/30/26 12:56 PM, Jonas Jelonek wrote:
>> A range of PoE switches use a small microcontroller on the PCB to front
>> the actual PSE silicon. The host CPU talks to that MCU over I2C/SMBus or
>> UART using a fixed 12-byte request/response protocol with a trailing
>> checksum; the PSE chips are managed by the MCU and are not accessed
>> directly. The same protocol family is spoken by Realtek and Broadcom PSE
>> MCUs, diverging in opcode numbering and a few response layouts, which the
>> driver abstracts behind a per-dialect opcode table and parser hooks
>> selected by the compatible. The specific PSE chip behind the MCU is
>> detected at runtime and only influences per-chip constants (power scaling
>> and the per-port cap).
>>
>> The driver is split into a shared core and two transport modules:
>>
>> - PSE_REALTEK_MCU: protocol, message framing, dialect machinery, and the
>> pse_controller_ops glue.
>> - PSE_REALTEK_MCU_I2C / PSE_REALTEK_MCU_UART: transport modules
>> registering the MCU on an I2C bus or a serdev port respectively.
>>
>> The realtek-pse-mcu-* files and PSE_REALTEK_MCU* symbols match the
>> realtek,pse-mcu-rtk / realtek,pse-mcu-brcm compatibles: all name the
>> Realtek PSE-MCU front-end, not the MCU silicon or the PSE chip behind
>> it (see the binding for the prefix rationale). Broadcom PSE MCUs speak
>> the same protocol family and are handled by the same shared core
>> through the dialect abstraction selected by the '-brcm' compatible.
>>
>> Power budgeting is left to the MCU firmware; the driver advertises
>> PSE_BUDGET_EVAL_STRAT_DYNAMIC (controller-managed budget) accordingly.
>>
>> Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
>> ---
>> MAINTAINERS | 7 +
>> drivers/net/pse-pd/Kconfig | 28 +
>> drivers/net/pse-pd/Makefile | 3 +
>> drivers/net/pse-pd/realtek-pse-mcu-core.c | 1019 +++++++++++++++++++++
>> drivers/net/pse-pd/realtek-pse-mcu-i2c.c | 163 ++++
>> drivers/net/pse-pd/realtek-pse-mcu-uart.c | 156 ++++
>> drivers/net/pse-pd/realtek-pse-mcu.h | 87 ++
>> 7 files changed, 1463 insertions(+)
>> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-core.c
>> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-i2c.c
>> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-uart.c
>> create mode 100644 drivers/net/pse-pd/realtek-pse-mcu.h
> This is quite large, and shouls be split in smaller patches to help
> reviewers.
>
> [...]
>> +struct rtpse_mcu_dialect {
>> + struct rtpse_mcu_opcode opcode[RTPSE_MCU_NUM_CMDS];
>> +
>> + /*
>> + * Response parsers. Each dialect must supply its own; the core calls
>> + * these unconditionally rather than carrying a default that would
>> + * silently mis-decode bytes from a dialect that forgot to set them.
>> + */
>> + int (*parse_system_info)(const u8 *payload, struct rtpse_mcu_info *info);
> The 2 existing implementation always return 0; you may consider change
> it to a void function.
>
>> +static int rtpse_mcu_port_get_voltage(struct pse_controller_dev *pcdev, int id)
>> +{
>> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
>> + struct rtpse_mcu_port_measurement measurement;
>> + int ret;
>> + u32 uV;
>> +
>> + ret = rtpse_mcu_port_get_measurement(pse, id, &measurement);
>> + if (ret)
>> + return ret;
>> +
>> + /* 64.45mV per LSB */
>> + uV = (u32)measurement.voltage_raw * 64450U;
> This cast ^^^^^ should be unneeded.
>
>> + return min_t(u32, uV, INT_MAX);
>> +}
>> +
>> +static int rtpse_mcu_port_enable(struct pse_controller_dev *pcdev, int id)
>> +{
>> + return rtpse_mcu_port_set_state(to_rtpse_mcu_ctrl(pcdev), id, true);
>> +}
>> +
>> +static int rtpse_mcu_port_disable(struct pse_controller_dev *pcdev, int id)
>> +{
>> + return rtpse_mcu_port_set_state(to_rtpse_mcu_ctrl(pcdev), id, false);
>> +}
>> +
>> +static int rtpse_mcu_port_get_pw_limit(struct pse_controller_dev *pcdev, int id)
>> +{
>> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
>> + struct rtpse_mcu_port_ext_config config;
>> + int ret;
>> +
>> + ret = rtpse_mcu_port_get_ext_config(pse, id, &config);
>> + if (ret)
>> + return ret;
>> +
>> + return config.max_power * pse->chip->pw_read_lsb_mW;
>> +}
>> +
>> +static int rtpse_mcu_port_set_pw_limit(struct pse_controller_dev *pcdev, int id, int max_mW)
>> +{
>> + const struct rtpse_mcu_opcode *type_opc, *val_opc;
>> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
>> + const struct rtpse_mcu_chip_info *chip = pse->chip;
>> + unsigned int prg_val;
>> + int ret;
>> +
>> + if (max_mW < 0 || max_mW > chip->max_mW_per_port)
>> + return -ERANGE;
>> +
>> + type_opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_TYPE];
>> + val_opc = &pse->dialect->opcode[chip->pw_set_cmd];
>> + if (!type_opc->valid || !val_opc->valid)
>> + return -EOPNOTSUPP;
>> +
>> + /*
>> + * Switch the port to user-defined limit mode first, then program the
>> + * limit value. If the second cmd fails, the port is left in
>> + * user-defined mode but with the previous limit value; the next
>> + * successful set_pw_limit call recovers it.
>> + */
>> + ret = rtpse_mcu_port_cmd(pse, id, type_opc->op, RTPSE_MCU_PORT_PW_LIMIT_TYPE_USER);
>> + if (ret)
>> + return ret;
>> +
>> + prg_val = min_t(unsigned int, max_mW / chip->pw_set_lsb_mW, 0xff);
>> +
>> + return rtpse_mcu_port_cmd(pse, id, val_opc->op, prg_val);
>> +}
>> +
>> +static int rtpse_mcu_port_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
>> + struct pse_pw_limit_ranges *out)
>> +{
>> + struct ethtool_c33_pse_pw_limit_range *range;
>> + struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
>> +
>> + range = kzalloc_obj(*range, GFP_KERNEL);
> or just:
>
> range = kzalloc_obj(*range);
>
>
>> +static int rtpse_mcu_discover(struct rtpse_mcu_ctrl *pse, struct rtpse_mcu_info *info)
>> +{
>> + struct rtpse_mcu_ext_config ext_config;
>> + unsigned long deadline;
>> + int ret;
>> +
>> + /*
>> + * The MCU may not answer on the bus yet right after power-up or
>> + * enable-gpios assertion: depending on the transport it either stays
>> + * silent (-ETIMEDOUT) or does not ACK its address at all (-ENXIO /
>> + * -EREMOTEIO). Retry within a bounded wall-time window so a slow boot
>> + * still probes, while a genuinely unresponsive MCU fails with its real
>> + * error instead of deferring forever and masking it.
>> + */
>> + deadline = jiffies + msecs_to_jiffies(RTPSE_MCU_BOOT_TIMEOUT_MS);
>> + do {
>> + ret = rtpse_mcu_get_info(pse, info);
>> + if (ret != -ETIMEDOUT && ret != -ENXIO && ret != -EREMOTEIO &&
>> + ret != -EAGAIN)
>> + break;
>> + msleep(RTPSE_MCU_BOOT_RETRY_MS);
>> + } while (time_before(jiffies, deadline));
>> + if (ret)
>> + return dev_err_probe(pse->dev, ret, "failed to read MCU info\n");
>> +
>> + switch (info->device_id) {
>> + case RTPSE_MCU_DEVICE_ID_RTL8238B:
>> + pse->chip = &rtl8238b_info;
>> + break;
>> + case RTPSE_MCU_DEVICE_ID_RTL8239:
>> + pse->chip = &rtl8239_info;
>> + break;
>> + case RTPSE_MCU_DEVICE_ID_RTL8239C:
>> + pse->chip = &rtl8239c_info;
>> + break;
>> + case RTPSE_MCU_DEVICE_ID_BCM59111:
>> + pse->chip = &bcm59111_info;
>> + break;
>> + case RTPSE_MCU_DEVICE_ID_BCM59121:
>> + pse->chip = &bcm59121_info;
>> + break;
>> + default:
>> + return dev_err_probe(pse->dev, -EINVAL, "unknown PSE id 0x%x\n",
>> + info->device_id);
>> + }
>> +
>> + if (!info->max_ports || info->max_ports > RTPSE_MCU_MAX_PORTS)
>> + return dev_err_probe(pse->dev, -EINVAL,
>> + "MCU reports invalid port count %u\n", info->max_ports);
>> +
>> + ret = rtpse_mcu_get_ext_config(pse, &ext_config);
>> + if (ret)
>> + return dev_err_probe(pse->dev, ret, "failed to read MCU ext config\n");
>> +
>> + dev_info(pse->dev, "%s MCU, %s (id 0x%04x), %u ports across %u PSE chip(s)\n",
>> + pse->dialect->mcu_type_str(info->mcu_type), pse->chip->name,
>> + info->device_id, info->max_ports, ext_config.num_of_pses);
> The general guidance is to try to avoid unneeded print on dmsg, as they
> tend to scary admins, but I personally agree on message on modprobe.
>
> No strong opionion either ways.
>
> /P
>
Thanks for your remarks, I'll address them in v5.
Best regards,
Jonas
prev parent reply other threads:[~2026-07-06 11:23 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 10:56 [PATCH net-next v4 0/2] net: pse-pd: add Realtek/Broadcom PSE MCU support Jonas Jelonek
2026-06-30 10:56 ` [PATCH net-next v4 1/2] dt-bindings: net: pse-pd: add bindings for Realtek/Broadcom PSE MCU Jonas Jelonek
2026-06-30 10:56 ` [PATCH net-next v4 2/2] net: pse-pd: add Realtek/Broadcom PSE MCU driver Jonas Jelonek
2026-07-01 10:57 ` sashiko-bot
2026-07-02 7:13 ` Oleksij Rempel
2026-07-02 11:22 ` Jonas Jelonek
2026-07-03 7:55 ` Paolo Abeni
2026-07-06 11:22 ` Jonas Jelonek [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d5e32492-0585-4b28-a277-d58b9218443a@gmail.com \
--to=jelonek.jonas@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=bjorn@mork.no \
--cc=conor+dt@kernel.org \
--cc=daniel@makrotopia.org \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@google.com \
--cc=kory.maincent@bootlin.com \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=o.rempel@pengutronix.de \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox