[PATCH v1 06/23] board: nbxv3: add VID-fuse driven core voltage
Vincent Jardin <[email protected]>
| Newsgroups | gmane.comp.boot-loaders.u-boot |
|---|---|
| Message-ID | <20260817153144.1951036-7-vjardin__10614.9832297646$1786980845$gmane$org@free.fr> |
Read the LX2160A DCFG_FUSESR.ALTVID / VID fuses, look up the binned target voltage, and command the rail through the UCLASS_REGULATOR set_value op Hooked at EVT_LAST_STAGE_INIT so the rail is at the VID-correct voltage before the kernel regulator framework probes the chip. Set nbxv3_vdd_mv to a millivolt integer in [600, 1100] to bypass the VID fuse and pin the rail at the wanted voltage. Useful for bench characterisation. Signed-off-by: Vincent Jardin <[email protected]> --- board/nxp/lx2160a/nbxv3/Makefile | 1 + board/nxp/lx2160a/nbxv3/vid_handoff.c | 146 ++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 board/nxp/lx2160a/nbxv3/vid_handoff.c diff --git a/board/nxp/lx2160a/nbxv3/Makefile b/board/nxp/lx2160a/nbxv3/Makefile index 24874138833..68fab9bf18f 100644 --- a/board/nxp/lx2160a/nbxv3/Makefile +++ b/board/nxp/lx2160a/nbxv3/Makefile @@ -5,3 +5,4 @@ obj-y += eth_nbxv3.o obj-y += mps_pmbus.o obj-y += psu_pmbus.o +obj-y += vid_handoff.o diff --git a/board/nxp/lx2160a/nbxv3/vid_handoff.c b/board/nxp/lx2160a/nbxv3/vid_handoff.c new file mode 100644 index 00000000000..4131bd96023 --- /dev/null +++ b/board/nxp/lx2160a/nbxv3/vid_handoff.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2026 Free Mobile, Vincent Jardin + * + * Per die core voltage trim for the Nodebox v3 +0V8_VDD rail + */ + +#include <command.h> +#include <dm.h> +#include <env.h> +#include <event.h> +#include <log.h> +#include <vsprintf.h> +#include <asm/io.h> +#include <asm/arch/immap_lsch3.h> +#include <power/regulator.h> + +#define NBXV3_VDD_REGULATOR_NAME "+0V8_VDD" +#define NBXV3_VDD_OVERRIDE_ENV "nbxv3_vdd_mv" +#define NBXV3_VDD_MIN_MV 600 +#define NBXV3_VDD_MAX_MV 1100 + +#define NBXV3_VID_LSB_uV 1953 +#define NBXV3_VID_TOLERANCE_uV 1953 +#define NBXV3_VID_STEP_uV 1953 +#define NBXV3_VID_MAX_ITER 50 +#define NBXV3_VID_PLATEAU_LIMIT 3 + +static const u16 lx2160a_vid_table[32] = { + [0] = 8250, + [1] = 7875, + [2] = 7750, + [16] = 8000, + [17] = 8125, + [18] = 8250, + [20] = 8500, +}; + +static int nbxv3_vid_handoff(void) +{ + struct ccsr_gur *gur = (void *)(CFG_SYS_FSL_GUTS_ADDR); + struct udevice *reg; + u32 fusesr; + u8 vid; + int target_mv, target_uV, actual_uV; + char *override_str; + unsigned long override_mv; + int ret; + + if (regulator_get_by_platname(NBXV3_VDD_REGULATOR_NAME, ®) || !reg) + return 0; + + fusesr = in_le32(&gur->dcfg_fusesr); + vid = (fusesr >> FSL_CHASSIS3_DCFG_FUSESR_ALTVID_SHIFT) & + FSL_CHASSIS3_DCFG_FUSESR_ALTVID_MASK; + if (vid == 0 || vid == FSL_CHASSIS3_DCFG_FUSESR_ALTVID_MASK) + vid = (fusesr >> FSL_CHASSIS3_DCFG_FUSESR_VID_SHIFT) & + FSL_CHASSIS3_DCFG_FUSESR_VID_MASK; + + target_mv = (lx2160a_vid_table[vid] + 5) / 10; + if (target_mv == 0) { + printf("MPQ8785 (%s): VID handoff fuse=0x%02x reserved/unknown, leaving rail untouched\n", + NBXV3_VDD_REGULATOR_NAME, vid); + return 0; + } + + /* Env override */ + override_str = env_get(NBXV3_VDD_OVERRIDE_ENV); + if (override_str && !strict_strtoul(override_str, 10, &override_mv) && + override_mv >= NBXV3_VDD_MIN_MV && + override_mv <= NBXV3_VDD_MAX_MV) { + printf("MPQ8785 (%s): VID handoff override %s=%lu mV (fuse VID=0x%02x would have been %d mV)\n", + NBXV3_VDD_REGULATOR_NAME, NBXV3_VDD_OVERRIDE_ENV, + override_mv, vid, target_mv); + target_mv = (int)override_mv; + } else if (override_str) { + printf("MPQ8785 (%s): VID handoff %s=\"%s\" out of range [%d, %d] mV, ignored\n", + NBXV3_VDD_REGULATOR_NAME, NBXV3_VDD_OVERRIDE_ENV, + override_str, NBXV3_VDD_MIN_MV, NBXV3_VDD_MAX_MV); + } + + target_uV = target_mv * 1000; + actual_uV = regulator_get_value(reg); + + if (actual_uV >= target_uV - NBXV3_VID_TOLERANCE_uV) { + printf("MPQ8785 (%s): VID handoff fuse=0x%02x target=%d mV (already within tolerance, rail at %d uV)\n", + NBXV3_VDD_REGULATOR_NAME, vid, target_mv, actual_uV); + return 0; + } + + printf("MPQ8785 (%s): VID handoff fuse=0x%02x %d uV -> target %d mV (closed-loop adjust)\n", + NBXV3_VDD_REGULATOR_NAME, vid, actual_uV, target_mv); + + /* + * Plateau detection: if the rail stops moving while we are + * still bumping VOUT_COMMAND, the chip is clamping at VOUT_MAX + * (or refusing to slew higher for some other reason). Bail + * with a WARNING and leave the rail at whatever it settled at. + */ + { + int cmd_uV = target_uV; + int last_actual_uV = -1; + int plateau = 0; + int iter; + + for (iter = 0; iter < NBXV3_VID_MAX_ITER; iter++) { + ret = regulator_set_value(reg, cmd_uV); + if (ret) { + printf("WARNING: MPQ8785 (%s): VID handoff set_value failed at iter %d (cmd %d uV, ret %d), rail at %d uV\n", + NBXV3_VDD_REGULATOR_NAME, iter, cmd_uV, + ret, actual_uV); + return 0; + } + + actual_uV = regulator_get_value(reg); + if (actual_uV >= target_uV - NBXV3_VID_TOLERANCE_uV) { + printf("MPQ8785 (%s): VID handoff settled at %d uV after %d iter (cmd %d uV, target %d uV)\n", + NBXV3_VDD_REGULATOR_NAME, actual_uV, + iter + 1, cmd_uV, target_uV); + return 0; + } + + if (last_actual_uV >= 0 && + actual_uV - last_actual_uV < NBXV3_VID_LSB_uV) { + plateau++; + if (plateau >= NBXV3_VID_PLATEAU_LIMIT) { + printf("WARNING: MPQ8785 (%s): VID handoff stalled at %d uV after %d iter (target %d uV, cmd %d uV) -- VOUT_MAX clamp or chip refused\n", + NBXV3_VDD_REGULATOR_NAME, + actual_uV, iter + 1, + target_uV, cmd_uV); + return 0; + } + } else { + plateau = 0; + } + last_actual_uV = actual_uV; + cmd_uV += NBXV3_VID_STEP_uV; + } + + printf("WARNING: MPQ8785 (%s): VID handoff did not converge after %d iter (rail %d uV, target %d uV, cmd %d uV)\n", + NBXV3_VDD_REGULATOR_NAME, iter, actual_uV, target_uV, + cmd_uV); + return 0; + } +} +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, nbxv3_vid_handoff); -- 2.43.0