[PATCH v5 3/5] soc: fsl: guts: add the RCW override of the pad muxes
From: Vincent Jardin via B4 Relay
Date: Sun Nov 15 2026 - 06:06:05 EST
From: Vincent Jardin <vjardin@xxxxxxx>
fsl_guts_rcw_rmw() only serves the SerDes lane modes. Export it for
the pad-mux fields of the RCW, and for them only:
- fsl_guts_pmux_read() reads the pad-mux fields of a status register
- fsl_guts_pmux_override() changes pad-mux fields of one under
rcwcr_lock and waits for the status register to follow
- fsl_guts_pmux_override_supported() tells whether the SoC has pad-mux
fields and whether the DCSR view of DCFG, which holds the override
registers, is mapped
Each SoC lists in its fsl_soc_data the fields that can be overridden,
per RCWSR: a register or a field outside of this list is refused, so
the SerDes protocols stay with fsl_guts_lane_set_mode(). The LX2160A
lists the pad muxes of RCWSR12 to RCWSR14 and of RCWSR27.
Suggested-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
Signed-off-by: Vincent Jardin <vjardin@xxxxxxx>
---
drivers/soc/fsl/guts.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/fsl/guts.h | 3 ++
2 files changed, 124 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
index 28361c63e26c..d8b8de6fbe32 100644
--- a/drivers/soc/fsl/guts.c
+++ b/drivers/soc/fsl/guts.c
@@ -53,6 +53,12 @@ struct fsl_soc_serdes_rcw_override {
int val;
};
+/* Pad-mux fields of the one-based RCWSRn, the ones that may be overridden */
+struct fsl_soc_pmux_mask {
+ unsigned int rcwsr;
+ u32 mask;
+};
+
struct fsl_soc_data {
const char *sfp_compat;
u32 uid_offset;
@@ -62,6 +68,7 @@ struct fsl_soc_data {
int (*serdes_get_rcw_override)(int index, int lane,
enum lynx_lane_mode lane_mode,
struct fsl_soc_serdes_rcw_override *override);
+ const struct fsl_soc_pmux_mask *pmux;
};
enum qoriq_die {
@@ -369,6 +376,107 @@ int fsl_guts_lane_set_mode(int serdes_idx, int lane, enum lynx_lane_mode lane_mo
}
EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_set_mode, "FSL_GUTS");
+static const struct fsl_soc_pmux_mask *
+fsl_guts_get_pmux_mask(unsigned int rcwsr)
+{
+ const struct fsl_soc_pmux_mask *pmux;
+
+ if (!soc.data || !soc.data->pmux)
+ return NULL;
+
+ for (pmux = soc.data->pmux; pmux->rcwsr; pmux++) {
+ if (pmux->rcwsr == rcwsr)
+ return pmux;
+ }
+
+ return NULL;
+}
+
+/**
+ * fsl_guts_pmux_read() - read the pad-mux fields of a RCW status register
+ * @rcwsr: one-based index of the status register, as in RCWSR12
+ * @val: the pad-mux fields read, the other fields of the register are 0
+ *
+ * Return: 0 on success, -ENODEV before the DCFG block is mapped, -EINVAL
+ * when the register holds no pad-mux field on this SoC
+ */
+int fsl_guts_pmux_read(unsigned int rcwsr, u32 *val)
+{
+ const struct fsl_soc_pmux_mask *pmux;
+
+ if (!soc.dcfg_ccsr)
+ return -ENODEV;
+
+ pmux = fsl_guts_get_pmux_mask(rcwsr);
+ if (!pmux)
+ return -EINVAL;
+
+ *val = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[rcwsr - 1]) & pmux->mask;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(fsl_guts_pmux_read, "FSL_GUTS");
+
+/**
+ * fsl_guts_pmux_override() - override pad-mux fields of the RCW
+ * @rcwsr: one-based index of the RCWSR and RCWCR pair, as in RCWSR12
+ * @val: the new value of the fields, in place
+ * @mask: the fields to change
+ *
+ * Only the pad-mux fields that the SoC lists can be changed: a SerDes
+ * protocol is changed with fsl_guts_lane_set_mode(), and the other fields
+ * of the RCW are refused.
+ *
+ * The fields outside @mask keep the value the status register shows. The
+ * override register is written and the status register polled until it
+ * shows the result, as fsl_guts_lane_set_mode() does for a SerDes lane.
+ *
+ * Context: takes rcwcr_lock, shared with fsl_guts_lane_set_mode(), and
+ * busy-polls the status register for up to RCW_TIMEOUT_US. Does not
+ * sleep; must not be called from interrupt context.
+ *
+ * Return: 0 on success, -ENODEV before the DCFG block is mapped,
+ * -EOPNOTSUPP without a DCSR mapping of DCFG, -EINVAL when @mask is not
+ * made of pad-mux fields of the register, negative error otherwise
+ */
+int fsl_guts_pmux_override(unsigned int rcwsr, u32 val, u32 mask)
+{
+ const struct fsl_soc_pmux_mask *pmux;
+ int err;
+
+ if (!soc.dcfg_ccsr)
+ return -ENODEV;
+ if (!soc.dcfg_dcsr)
+ return -EOPNOTSUPP;
+
+ pmux = fsl_guts_get_pmux_mask(rcwsr);
+ if (!pmux || !mask || (mask & ~pmux->mask))
+ return -EINVAL;
+
+ spin_lock(&soc.rcwcr_lock);
+ err = fsl_guts_rcw_rmw(rcwsr - 1, val & mask, mask);
+ spin_unlock(&soc.rcwcr_lock);
+ if (err)
+ pr_err("RCW override of RCWSR%u failed: %pe\n", rcwsr,
+ ERR_PTR(err));
+
+ return err;
+}
+EXPORT_SYMBOL_NS_GPL(fsl_guts_pmux_override, "FSL_GUTS");
+
+/**
+ * fsl_guts_pmux_override_supported() - can the pad muxes be overridden?
+ *
+ * Return: true when the SoC lists pad-mux fields and the DCFG block is
+ * mapped with its DCSR view, the second reg of the dcfg node, which holds
+ * the override registers
+ */
+bool fsl_guts_pmux_override_supported(void)
+{
+ return soc.dcfg_ccsr && soc.dcfg_dcsr && soc.data && soc.data->pmux;
+}
+EXPORT_SYMBOL_NS_GPL(fsl_guts_pmux_override_supported, "FSL_GUTS");
+
static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset)
{
struct device_node *np;
@@ -579,6 +687,18 @@ static const struct fsl_soc_data ls1028a_data = {
.num_serdes_lanes = 4,
};
+static const struct fsl_soc_pmux_mask lx2160a_pmux[] = {
+ { 12, GENMASK(29, 0) }, /* IIC2..SDHC1_DIR */
+ { 13, GENMASK(29, 0) }, /* SDHC2_DAT74..EVT43 */
+ { 14, GENMASK(10, 0) }, /* EVT20..IIC1 */
+ { 27, GENMASK(7, 0) }, /* EC1, EC2, USB_EXT, GTX_CLK, SDHC1_DS */
+ {}
+};
+
+static const struct fsl_soc_data lx2160a_data = {
+ .pmux = lx2160a_pmux,
+};
+
/*
* Table for matching compatible strings, for device tree
* guts node, for Freescale QorIQ SOCs.
@@ -606,7 +726,7 @@ static const struct of_device_id fsl_guts_of_match[] = {
{ .compatible = "fsl,ls1088a-dcfg", .data = &ls1088a_data},
{ .compatible = "fsl,ls1012a-dcfg", },
{ .compatible = "fsl,ls1046a-dcfg", .data = &ls1046a_data},
- { .compatible = "fsl,lx2160a-dcfg", },
+ { .compatible = "fsl,lx2160a-dcfg", .data = &lx2160a_data},
{ .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data},
{}
};
diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h
index 8d5ffb985fee..ae245afeb7b9 100644
--- a/include/linux/fsl/guts.h
+++ b/include/linux/fsl/guts.h
@@ -142,6 +142,9 @@ int fsl_guts_lane_validate(int serdes_idx, int lane,
enum lynx_lane_mode lane_mode);
int fsl_guts_lane_set_mode(int serdes_idx, int lane,
enum lynx_lane_mode lane_mode);
+int fsl_guts_pmux_read(unsigned int rcwsr, u32 *val);
+int fsl_guts_pmux_override(unsigned int rcwsr, u32 val, u32 mask);
+bool fsl_guts_pmux_override_supported(void);
/* Alternate function signal multiplex control */
#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x))
--
2.43.0