[PATCH v1 08/23] board: nbxv3: add DPLL status (ZL30733, ZL30643)
Vincent Jardin <[email protected]>
| Newsgroups | gmane.comp.boot-loaders.u-boot |
|---|---|
| Message-ID | <20260817153144.1951036-9-vjardin__32043.101712928$1786980884$gmane$org@free.fr> |
Add a boot time probe for each Microsemi/Microchip DPLL on the board: - ZL30733 - ZL30643 Linux: full DPLL configuration (clock tree programming, output enables, holdover policy, line side rates) is the job of the Linux DPLL driver at runtime. U-Boot only verifies presence and identity, it does not program any output. Signed-off-by: Vincent Jardin <[email protected]> --- arch/arm/dts/fsl-lx2160a-nbxv3.dts | 8 ++ board/nxp/lx2160a/Kconfig | 7 + board/nxp/lx2160a/nbxv3/Makefile | 1 + board/nxp/lx2160a/nbxv3/dpll_zl30733.c | 183 +++++++++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 board/nxp/lx2160a/nbxv3/dpll_zl30733.c diff --git a/arch/arm/dts/fsl-lx2160a-nbxv3.dts b/arch/arm/dts/fsl-lx2160a-nbxv3.dts index 8e336a622f3..9dcc1bf1d5e 100644 --- a/arch/arm/dts/fsl-lx2160a-nbxv3.dts +++ b/arch/arm/dts/fsl-lx2160a-nbxv3.dts @@ -57,3 +57,11 @@ regulator-always-on; }; }; + +&dspi0 { /* DSPI1: on module management SPI */ + dpll@0 { + compatible = "microchip,zl30733"; + reg = <0>; + spi-max-frequency = <12500000>; + }; +}; diff --git a/board/nxp/lx2160a/Kconfig b/board/nxp/lx2160a/Kconfig index e46223d1b43..12d2e418aa5 100644 --- a/board/nxp/lx2160a/Kconfig +++ b/board/nxp/lx2160a/Kconfig @@ -76,4 +76,11 @@ config NBXV3_PMBUS select DM_REGULATOR_PMBUS_GENERIC select CMD_REGULATOR +config NBXV3_DPLL + bool + default y + select SPI + select DM_SPI + select FSL_DSPI + endif diff --git a/board/nxp/lx2160a/nbxv3/Makefile b/board/nxp/lx2160a/nbxv3/Makefile index 4ce2508249d..c8a676fb708 100644 --- a/board/nxp/lx2160a/nbxv3/Makefile +++ b/board/nxp/lx2160a/nbxv3/Makefile @@ -4,6 +4,7 @@ obj-y += eth_nbxv3.o obj-y += carrier.o +obj-y += dpll_zl30733.o obj-y += mps_pmbus.o obj-y += psu_pmbus.o obj-y += vid_handoff.o diff --git a/board/nxp/lx2160a/nbxv3/dpll_zl30733.c b/board/nxp/lx2160a/nbxv3/dpll_zl30733.c new file mode 100644 index 00000000000..4dfa4579cce --- /dev/null +++ b/board/nxp/lx2160a/nbxv3/dpll_zl30733.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2026 Free Mobile - Vincent Jardin + * + * Nodebox v3 - DPLL identification probe (both DPLLs). + * + * It reads a small set of read-only descriptor registers from each + * Microsemi/Microchip DPLL on the board and prints the result on the + * console at EVT_LAST_STAGE_INIT time, just before the prompt. + * It is purely a basic power on test diagnostic: no configuration + * register is written, the DPLL register profile loaded at power-on + * is left untouched. + */ + +#include <command.h> +#include <dm.h> +#include <event.h> +#include <log.h> +#include <spi.h> +#include <vsprintf.h> +#include <linux/delay.h> + +#define ZL30733_SPI_HZ 12500000 +#define ZL30733_SPI_MODE SPI_MODE_0 + +/* The two DPLLs sharing the ZL3073x register protocol on this board */ +static const struct nbxv3_dpll { + const char *label; /* expected part at this location */ + const char *devname; /* DM device name for the ad-hoc slave */ + int bus; /* U-Boot SPI bus number */ + int cs; /* chip select on that bus */ + u32 hz; /* SCK ceiling */ + const char *hint; /* what to check when reads are implausible */ +} nbxv3_dplls[] = { + { + .label = "ZL30733", .devname = "zl30733_dpll", + .bus = 0, .cs = 0, .hz = ZL30733_SPI_HZ, + .hint = "check PROG_DPLL_EN mux", + }, + { + .label = "ZL30643", .devname = "zl30643_dpll", + .bus = 2, .cs = 1, .hz = 10000000, + .hint = "check CS1 rework / RST_B", + }, +}; + +#define ZL30733_REG_INFO 0x00 +#define ZL30733_REG_ID_HI 0x01 +#define ZL30733_REG_ID_LO 0x02 +#define ZL30733_REG_REVISION 0x03 +#define ZL30733_REG_FW_VER 0x05 /* 6 bytes 0x05..0x0A */ +#define ZL30733_REG_FW_VER_LEN 6 +#define ZL30733_REG_RST_STATUS 0x18 +#define ZL30733_REG_PAGE_SEL 0x7F + +#define ZL30733_CMD_READ 0x80 /* OR'd into the 7-bit address */ + +#define ZL30733_INFO_DEFAULT 0x21 +#define ZL30733_INFO_ID_MASK 0x7f +#define ZL30733_INFO_EEPROM_BOOTED 0x80 +#define ZL30733_REV_DEFAULT 0x03 + +static int zl30733_read_block(struct spi_slave *slave, u8 addr, u8 *buf, + size_t len) +{ + u8 cmd = ZL30733_CMD_READ | (addr & 0x7F); + int ret; + + ret = spi_xfer(slave, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) + return ret; + + return spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END); +} + +static int zl30733_select_page0(struct spi_slave *slave) +{ + u8 cmd[2] = { ZL30733_REG_PAGE_SEL & 0x7F, 0x00 }; + + return spi_xfer(slave, sizeof(cmd) * 8, cmd, NULL, + SPI_XFER_BEGIN | SPI_XFER_END); +} + +static int zl30733_dump(struct spi_slave *slave, const struct nbxv3_dpll *d) +{ + u8 info, rev, rst; + u8 fw[ZL30733_REG_FW_VER_LEN]; + u8 idbuf[2]; + u16 id; + int ret; + bool plausible; + + ret = zl30733_select_page0(slave); + if (ret) { + printf("DPLL: %s page select failed (%d)\n", d->label, ret); + return ret; + } + + ret = zl30733_read_block(slave, ZL30733_REG_INFO, &info, 1); + if (ret) + return ret; + ret = zl30733_read_block(slave, ZL30733_REG_ID_HI, idbuf, sizeof(idbuf)); + if (ret) + return ret; + id = ((u16)idbuf[0] << 8) | idbuf[1]; + ret = zl30733_read_block(slave, ZL30733_REG_REVISION, &rev, 1); + if (ret) + return ret; + ret = zl30733_read_block(slave, ZL30733_REG_FW_VER, fw, sizeof(fw)); + if (ret) + return ret; + ret = zl30733_read_block(slave, ZL30733_REG_RST_STATUS, &rst, 1); + if (ret) + return ret; + + plausible = (info & ZL30733_INFO_ID_MASK) == ZL30733_INFO_DEFAULT && + id != 0x0000 && id != 0xFFFF; + + printf("DPLL: %s @ spi%d:%d info=0x%02x (%s) id=0x%04x rev=0x%02x\n", + d->label, d->bus, d->cs, info, + (info & ZL30733_INFO_EEPROM_BOOTED) ? "EEPROM boot" : "MCU default boot", + id, rev); + printf(" fw_ver=%02x:%02x:%02x:%02x:%02x:%02x reset_status=0x%02x", + fw[0], fw[1], fw[2], fw[3], fw[4], fw[5], rst); + if (!plausible) + printf(" [implausible; %s]", d->hint); + printf("\n"); + + return plausible ? 0 : 1; +} + +static int zl30733_probe_print(void) +{ + struct udevice *bus; + struct spi_slave *slave; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(nbxv3_dplls); i++) { + const struct nbxv3_dpll *d = &nbxv3_dplls[i]; + + ret = _spi_get_bus_and_cs(d->bus, d->cs, d->hz, + ZL30733_SPI_MODE, + "spi_generic_drv", d->devname, + &bus, &slave); + if (ret) { + printf("DPLL: %s spi%d:%d not reachable (%d)\n", + d->label, d->bus, d->cs, ret); + continue; + } + + ret = spi_claim_bus(slave); + if (ret) { + printf("DPLL: %s spi_claim_bus failed (%d)\n", + d->label, ret); + continue; + } + + zl30733_dump(slave, d); + + spi_release_bus(slave); + } + + return 0; +} + +static int nbxv3_dpll_last_stage_init(void) +{ + zl30733_probe_print(); + return 0; +} +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, nbxv3_dpll_last_stage_init); + +static int do_dpll_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return zl30733_probe_print() < 0 ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + dpll_info, 1, 0, do_dpll_info, + "display ZL30733 + ZL30643 DPLL identification + reset status", + "" +); -- 2.43.0