mirror of
https://github.com/torvalds/linux.git
synced 2026-10-17 14:30:03 +02:00
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>
95 lines
3.1 KiB
C
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
|