From: Vincent Jardin <[email protected]> fsl_guts_rcw_rmw() only serves the SerDes lane modes. Export it for other fields of the RCW: - fsl_guts_rcw_read() reads a status register - fsl_guts_rcw_override() changes fields of one under rcwcr_lock and waits for the status register to follow - fsl_guts_rcw_override_supported() tells whether the DCSR view of DCFG, which holds the override registers, is mapped
Signed-off-by: Vincent Jardin <[email protected]> --- drivers/soc/fsl/guts.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fsl/guts.h | 3 ++ 2 files changed, 76 insertions(+) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 28361c63e26c..d2e3f66d0da4 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -369,6 +369,79 @@ 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"); +/** + * fsl_guts_rcw_read() - read a RCW status register + * @index: one-based index, RCWSR1 to RCWSR32 + * @val: the value read + * + * Return: 0 on success, -ENODEV before the DCFG block is mapped, + * -EINVAL when @index is not 1 to 32 + */ +int fsl_guts_rcw_read(unsigned int index, u32 *val) +{ + if (!soc.dcfg_ccsr) + return -ENODEV; + if (index < 1 || index > ARRAY_SIZE(soc.dcfg_ccsr->rcwsr)) + return -EINVAL; + + *val = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[index - 1]); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(fsl_guts_rcw_read, "FSL_GUTS"); + +/** + * fsl_guts_rcw_override() - override fields of a RCW + * @index: one-based index of the RCWSR and RCWCR pair + * @val: the new value of the fields, in place + * @mask: the fields to change + * + * 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, -EOPNOTSUPP without a DCSR mapping of DCFG, + * negative error otherwise + */ +int fsl_guts_rcw_override(unsigned int index, u32 val, u32 mask) +{ + int err; + + if (!soc.dcfg_ccsr) + return -ENODEV; + if (!soc.dcfg_dcsr) + return -EOPNOTSUPP; + if (index < 1 || index > ARRAY_SIZE(soc.dcfg_ccsr->rcwsr)) + return -EINVAL; + + spin_lock(&soc.rcwcr_lock); + err = fsl_guts_rcw_rmw(index - 1, val & mask, mask); + spin_unlock(&soc.rcwcr_lock); + if (err) + pr_err("RCW override of RCWSR%u failed: %pe\n", index, + ERR_PTR(err)); + + return err; +} +EXPORT_SYMBOL_NS_GPL(fsl_guts_rcw_override, "FSL_GUTS"); + +/** + * fsl_guts_rcw_override_supported() - can a Reset Configuration Word be + * overridden? + * + * Return: true when the DCFG block is mapped with its DCSR view, the + * second reg of the dcfg node, which holds the override registers + */ +bool fsl_guts_rcw_override_supported(void) +{ + return soc.dcfg_ccsr && soc.dcfg_dcsr; +} +EXPORT_SYMBOL_NS_GPL(fsl_guts_rcw_override_supported, "FSL_GUTS"); + static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) { struct device_node *np; diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h index 8d5ffb985fee..7127de897c23 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_rcw_read(unsigned int index, u32 *val); +int fsl_guts_rcw_override(unsigned int index, u32 val, u32 mask); +bool fsl_guts_rcw_override_supported(void); /* Alternate function signal multiplex control */ #define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) -- 2.43.0
