linux/drivers/net/pse-pd/realtek-pse-mcu.h
Jonas Jelonek 44566e614d net: pse-pd: add Realtek PSE MCU core
A range of managed Realtek-based 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. Two generations of the protocol exist -
both Realtek's - diverging in opcode numbering and a few response
layouts; the driver handles this with a per-dialect opcode table and
parser hooks for the responses that differ, 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).

This core module implements the protocol, message framing, the dialect
machinery and the pse_controller_ops glue, and exports a registration
helper for transport modules. The I2C and UART transports that drive it
follow in the next patches; the core (PSE_REALTEK_MCU) is selected
automatically by those transports and is not user-selectable on its own.

The realtek-pse-mcu-* files and PSE_REALTEK_MCU* symbols match the
realtek,pse-mcu-* compatibles (see the binding for the naming rationale).
The two protocol generations - gen1 on older Broadcom-PSE boards, gen2 on
Realtek's own PSE silicon - are both Realtek's, handled by the same shared
core, each selecting its dialect via the compatible.

Power budgeting is left to the MCU firmware; the driver advertises
PSE_BUDGET_EVAL_STRAT_DYNAMIC accordingly.

Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://patch.msgid.link/20260813222036.873930-3-jelonek.jonas@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-08-18 15:04:33 +02:00

95 lines
3.1 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _REALTEK_PSE_MCU_H
#define _REALTEK_PSE_MCU_H
#include <linux/mutex.h>
#include <linux/pse-pd/pse.h>
#include <linux/types.h>
/*
* Time the MCU itself needs between accepting a request and having a
* response ready. These are properties of the MCU firmware, not of the
* underlying transport: the core paces transactions by RTPSE_MCU_RESPONSE_MS
* and both transports size their per-transaction recv ceiling from
* RTPSE_MCU_RESPONSE_MAX_MS, since some commands are documented as
* needing up to ~1s to produce a reply.
*/
#define RTPSE_MCU_RESPONSE_MS 25
#define RTPSE_MCU_RESPONSE_MAX_MS 1000
/*
* Total time to keep retrying the first MCU read at probe, and the pause
* between attempts. Right after reset-gpios is deasserted the MCU may not
* answer on the bus yet; give it a bounded window to come up before
* declaring the probe failed.
*/
#define RTPSE_MCU_BOOT_TIMEOUT_MS 3000
#define RTPSE_MCU_BOOT_RETRY_MS 100
#define RTPSE_MCU_MSG_SIZE 12
struct rtpse_mcu_msg {
u8 opcode;
u8 seq_num;
u8 payload[9];
u8 checksum;
} __packed;
/*
* MCU status opcodes (seen on the Gen1 dialect; Gen2 never emits them).
* INCOMPLETE/BAD_CSUM are terminal; NOT_READY is transient.
*/
#define RTPSE_MCU_OPCODE_INCOMPLETE 0xfd /* -EBADE */
#define RTPSE_MCU_OPCODE_BAD_CSUM 0xfe /* -EBADMSG */
#define RTPSE_MCU_OPCODE_NOT_READY 0xff /* -EAGAIN */
/*
* A polling transport can stop here: the reply to this request (opcode and
* seq_num) or a terminal error. The seq_num rejects a stale normal reply; the
* terminal errors match unconditionally, as a request the MCU couldn't parse
* carries no seq_num to correlate against.
*/
static inline bool rtpse_mcu_resp_is_final(const struct rtpse_mcu_msg *req,
const struct rtpse_mcu_msg *resp)
{
return (resp->opcode == req->opcode && resp->seq_num == req->seq_num) ||
resp->opcode == RTPSE_MCU_OPCODE_INCOMPLETE ||
resp->opcode == RTPSE_MCU_OPCODE_BAD_CSUM;
}
/* Opaque to transports; defined in realtek-pse-mcu-core.c. */
struct rtpse_mcu_dialect;
struct rtpse_mcu_chip_info;
struct rtpse_mcu_ctrl;
/* Per-compatible match data (the of_match .data). */
struct rtpse_mcu_match_data {
const struct rtpse_mcu_dialect *dialect;
bool native_i2c; /* raw-I2C framing (vs SMBus); I2C transport only */
};
struct rtpse_mcu_transport_ops {
int (*send)(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req);
int (*recv)(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req,
struct rtpse_mcu_msg *resp);
};
struct rtpse_mcu_ctrl {
struct device *dev;
struct pse_controller_dev pcdev;
struct mutex mutex; /* serializes MCU request/response transactions */
const struct rtpse_mcu_dialect *dialect;
const struct rtpse_mcu_chip_info *chip;
const struct rtpse_mcu_transport_ops *transport;
u8 seq; /* rolling request seq_num, echoed by the MCU */
};
int rtpse_mcu_register(struct rtpse_mcu_ctrl *pse);
extern const struct rtpse_mcu_match_data rtpse_mcu_gen1_data;
extern const struct rtpse_mcu_match_data rtpse_mcu_gen2_data;
extern const struct rtpse_mcu_match_data rtpse_mcu_gen2_i2c_data;
#endif