[PATCH v4 00/20] imx: add i.MX95 support

Changes in v4: - split the SCMI pin controller driver and SCMI agent code - update i.MX95 SoC and clock related code, board basic support code and so on according to comments - move the function that loads ATF and tee.bin to spl_imx_container.c - add document for i.MX95 image container format
Signed-off-by: Alice Guo alice.guo@nxp.com --- Alice Guo (8): pinctrl: nxp: add a pin controller driver based on SCMI pin control protocol firmware: scmi: add pin control protocol support to SCMI agent clk: scmi: check the clock state/parent/rate control permissions sandbox: add SCMI clock control permissions to sandbox imx: Kconfig: IMX8_ROMAPI is not configured for i.MX95 binman: add a new entry type for packing DDR PHY firmware images tools: imx8image: add i.MX95 support doc: imx: add document for i.MX95 Image Container Format
Peng Fan (6): mailbox: add i.MX95 Messaging Unit (MU) driver scmi_protocols: add SCMI misc protocol protocol_id and message_id for getting the ROM passover data scmi_protocols: add SCMI Performance domain management protocol message IDs clk: scmi: add the command CLOCK_PARENT_SET scmi: add the macro SCMI_MSG imx9: scmi: add i.MX95 SoC and clock related code
Teo Hall (1): imx: add V2X container support on i.MX95
Viorel Suman (1): firmware: scmi: smt: Interrupt communication enable
Ye Li (4): scmi_protocols: update struct scmi_base_discover_list_protocols_out spl: imx: use trampoline buffer to load images to secure region imx9: add i.MX95 Kconfig and Makefile imx95_evk: add i.MX95 19x19 EVK board basic support
MAINTAINERS | 1 + arch/arm/dts/imx95-19x19-evk-u-boot.dtsi | 68 +++ arch/arm/dts/imx95-u-boot.dtsi | 157 +++++ arch/arm/include/asm/arch-imx/cpu.h | 2 + arch/arm/include/asm/arch-imx9/clock.h | 10 + arch/arm/include/asm/arch-imx9/imx-regs.h | 5 + arch/arm/include/asm/arch-imx9/sys_proto.h | 1 + arch/arm/include/asm/mach-imx/sys_proto.h | 39 ++ arch/arm/mach-imx/Kconfig | 2 +- arch/arm/mach-imx/image-container.c | 63 +- arch/arm/mach-imx/imx9/Kconfig | 14 + arch/arm/mach-imx/imx9/Makefile | 9 +- arch/arm/mach-imx/imx9/scmi/Makefile | 6 + arch/arm/mach-imx/imx9/scmi/clock.c | 105 ++++ arch/arm/mach-imx/imx9/scmi/clock_scmi.c | 135 +++++ arch/arm/mach-imx/imx9/scmi/container.cfg | 10 + arch/arm/mach-imx/imx9/scmi/imximage.cfg | 15 + arch/arm/mach-imx/imx9/scmi/soc.c | 827 ++++++++++++++++++++++++++ arch/sandbox/include/asm/scmi_test.h | 4 +- board/freescale/imx95_evk/Kconfig | 12 + board/freescale/imx95_evk/MAINTAINERS | 6 + board/freescale/imx95_evk/Makefile | 11 + board/freescale/imx95_evk/imx95_19x19_evk.env | 90 +++ board/freescale/imx95_evk/imx95_evk.c | 54 ++ board/freescale/imx95_evk/spl.c | 72 +++ common/spl/Kconfig | 6 + common/spl/spl_imx_container.c | 41 +- configs/imx95_19x19_evk_defconfig | 177 ++++++ doc/board/nxp/imx95_evk.rst | 114 ++++ doc/board/nxp/index.rst | 1 + doc/imx/imx95_container.txt | 136 +++++ drivers/clk/clk_scmi.c | 191 +++++- drivers/firmware/scmi/Kconfig | 6 + drivers/firmware/scmi/sandbox-scmi_agent.c | 56 +- drivers/firmware/scmi/scmi_agent-uclass.c | 11 + drivers/firmware/scmi/smt.c | 3 + drivers/firmware/scmi/smt.h | 10 + drivers/mailbox/Kconfig | 7 + drivers/mailbox/Makefile | 1 + drivers/mailbox/imx-mailbox.c | 417 +++++++++++++ drivers/pinctrl/nxp/Kconfig | 13 + drivers/pinctrl/nxp/Makefile | 1 + drivers/pinctrl/nxp/pinctrl-imx.c | 7 +- drivers/pinctrl/nxp/pinctrl-imx.h | 11 + drivers/pinctrl/nxp/pinctrl-scmi.c | 136 +++++ include/configs/imx95_evk.h | 27 + include/imx8image.h | 19 +- include/scmi_agent-uclass.h | 2 + include/scmi_agent.h | 11 + include/scmi_protocols.h | 105 +++- tools/binman/entries.rst | 10 + tools/binman/etype/nxp_header_ddrfw.py | 32 + tools/binman/ftest.py | 11 + tools/binman/test/346_nxp_ddrfw_imx95.dts | 24 + tools/imx8image.c | 143 ++++- tools/imx9_image.sh | 8 + 56 files changed, 3390 insertions(+), 65 deletions(-) --- base-commit: d6da3dbaef57fc1d319b6b552efa009e2489d7d9 change-id: 20250114-imx95-ac8bbccd71e5
Best regards,

From: Peng Fan peng.fan@nxp.com
i.MX95 Messaging Unit (MU) enables 2 processors on a chip to communicate and coordinate by passing messages (e.g. frame information, event notices and requests) through the MU interface. This patch provides a driver for i.MX95 MU using the common mailbox framework. Currently, SCMI exchanges on i.MX95 uses a mailbox transport with SMT format, and the hardware used is this MU.
This id ported from Linux (v6.12.8) driver drivers/mailbox/imx-mailbox.c.
Signed-off-by: Viorel Suman viorel.suman@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- MAINTAINERS | 1 + drivers/mailbox/Kconfig | 7 + drivers/mailbox/Makefile | 1 + drivers/mailbox/imx-mailbox.c | 417 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 426 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index ba31f86feb6da266cddd160a041d0594ac86d274..5ab2b72d0f93afaf70c4c2dcb8fc48210683b749 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -313,6 +313,7 @@ F: board/freescale/*mx*/ F: board/freescale/common/ F: common/spl/spl_imx_container.c F: doc/imx/ +F: drivers/mailbox/imx-mailbox.c F: drivers/serial/serial_mxc.c F: include/imx_container.h
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 67d5ac1a74228ccd82b2725c0bb745354d099948..4d9f004ebad7f57034a6cdae3bff9645fa0ee67d 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -21,6 +21,13 @@ config APPLE_MBOX such as the System Management Controller (SMC) and NVMe and this driver is required to get that functionality up and running.
+config IMX_MU_MBOX + bool "Enable i.MX MU MBOX support" + depends on DM_MAILBOX + help + Enable support for i.MX Messaging Unit for communication with other + processors on the SoC using mailbox interface + config SANDBOX_MBOX bool "Enable the sandbox mailbox test driver" depends on DM_MAILBOX && SANDBOX diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 6072fa1956b793636c35aa0d1419c9dcaae27095..574add60005fe73d2beba715a12e0157fe4b0552 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -5,6 +5,7 @@
obj-$(CONFIG_$(XPL_)DM_MAILBOX) += mailbox-uclass.o obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o +obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c new file mode 100644 index 0000000000000000000000000000000000000000..931844135325c5758e0adcd55d19f5837aa49609 --- /dev/null +++ b/drivers/mailbox/imx-mailbox.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <mailbox-uclass.h> +#include <linux/bitfield.h> +#include <linux/bug.h> +#include <linux/iopoll.h> +#include <linux/compat.h> + +/* This driver only exposes the status bits to keep with the + * polling methodology of u-boot. + */ +DECLARE_GLOBAL_DATA_PTR; + +#define IMX_MU_CHANS 24 + +#define IMX_MU_V2_PAR_OFF 0x4 +#define IMX_MU_V2_TR_MASK GENMASK(7, 0) +#define IMX_MU_V2_RR_MASK GENMASK(15, 8) + +enum imx_mu_chan_type { + IMX_MU_TYPE_TX = 0, /* Tx */ + IMX_MU_TYPE_RX = 1, /* Rx */ + IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */ + IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */ + IMX_MU_TYPE_RST = 4, /* Reset */ + IMX_MU_TYPE_TXDB_V2 = 5, /* Tx doorbell with S/W ACK */ +}; + +enum imx_mu_xcr { + IMX_MU_CR, + IMX_MU_GIER, + IMX_MU_GCR, + IMX_MU_TCR, + IMX_MU_RCR, + IMX_MU_xCR_MAX, +}; + +enum imx_mu_xsr { + IMX_MU_SR, + IMX_MU_GSR, + IMX_MU_TSR, + IMX_MU_RSR, + IMX_MU_xSR_MAX, +}; + +struct imx_mu_con_priv { + unsigned int idx; + enum imx_mu_chan_type type; + struct mbox_chan *chan; +}; + +enum imx_mu_type { + IMX_MU_V1, + IMX_MU_V2 = BIT(1), + IMX_MU_V2_S4 = BIT(15), + IMX_MU_V2_IRQ = BIT(16), +}; + +struct imx_mu { + void __iomem *base; + const struct imx_mu_dcfg *dcfg; + u32 num_tr; + u32 num_rr; + /* use pointers to channel as a way to reserve channels */ + struct mbox_chan *channels[IMX_MU_CHANS]; + struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; +}; + +struct imx_mu_dcfg { + int (*tx)(struct imx_mu *plat, struct imx_mu_con_priv *cp, const void *data); + int (*rx)(struct imx_mu *plat, struct imx_mu_con_priv *cp); + int (*rxdb)(struct imx_mu *plat, struct imx_mu_con_priv *cp); + int (*init)(struct imx_mu *plat); + int (*of_xlate)(struct mbox_chan *chan, struct ofnode_phandle_args *args); + enum imx_mu_type type; + u32 xTR; /* Transmit Register0 */ + u32 xRR; /* Receive Register0 */ + u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */ + u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */ +}; + +#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +#define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +#define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) + +/* General Purpose Interrupt Enable */ +#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +/* Receive Interrupt Enable */ +#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +/* Transmit Interrupt Enable */ +#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) +/* General Purpose Interrupt Request */ +#define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x)))) +/* MU reset */ +#define IMX_MU_xCR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(5)) +#define IMX_MU_xSR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(7)) + +static void imx_mu_write(struct imx_mu *plat, u32 val, u32 offs) +{ + iowrite32(val, plat->base + offs); +} + +static u32 imx_mu_read(struct imx_mu *plat, u32 offs) +{ + return ioread32(plat->base + offs); +} + +static u32 imx_mu_xcr_rmw(struct imx_mu *plat, enum imx_mu_xcr type, u32 set, u32 clr) +{ + u32 val; + + val = imx_mu_read(plat, plat->dcfg->xCR[type]); + val &= ~clr; + val |= set; + imx_mu_write(plat, val, plat->dcfg->xCR[type]); + + return val; +} + +/* check that the channel is open or owned by caller */ +static int imx_mu_check_channel(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + + if (plat->channels[chan->id] != NULL) { + /* if reserved check that caller owns */ + if (plat->channels[chan->id] == chan) + return 1; /* caller owns the channel */ + + return -EACCES; + } + + return 0; /* channel empty */ +} + +static int imx_mu_chan_request(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp; + enum imx_mu_chan_type type; + int idx; + + type = chan->id / 4; + idx = chan->id % 4; + + if (imx_mu_check_channel(chan) < 0) /* check if channel already in use */ + return -EPERM; + + plat->channels[chan->id] = chan; + chan->con_priv = kcalloc(1, sizeof(struct imx_mu_con_priv), 0); + if (!chan->con_priv) + return -ENOMEM; + cp = chan->con_priv; + cp->idx = idx; + cp->type = type; + cp->chan = chan; + + switch (type) { + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(plat, IMX_MU_RCR, IMX_MU_xCR_RIEn(plat->dcfg->type, idx), 0); + break; + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_TXDB: + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(plat, IMX_MU_GIER, IMX_MU_xCR_GIEn(plat->dcfg->type, idx), 0); + break; + default: + break; + } + + return 0; +} + +static int imx_mu_chan_free(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + + if (imx_mu_check_channel(chan) <= 0) /* check that the channel is also not empty */ + return -EINVAL; + + /* if you own channel and channel is NOT empty */ + plat->channels[chan->id] = NULL; + switch (cp->type) { + case IMX_MU_TYPE_TX: + imx_mu_xcr_rmw(plat, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(plat->dcfg->type, cp->idx)); + break; + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(plat, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(plat->dcfg->type, cp->idx)); + break; + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_TXDB: + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(plat, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx)); + break; + default: + break; + } + + kfree(cp); + + return 0; +} + +static int imx_mu_send(struct mbox_chan *chan, const void *data) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + + if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */ + return -EPERM; + + return plat->dcfg->tx(plat, cp, data); +} + +static int imx_mu_recv(struct mbox_chan *chan, void *data) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + u32 ctrl, val; + + if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */ + return -EPERM; + + switch (cp->type) { + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_RXDB: + /* check if GSR[GIRn] bit is set */ + if (readx_poll_timeout(ioread32, plat->base + plat->dcfg->xSR[IMX_MU_GSR], + val, val & BIT(cp->idx), 1000000) < 0) + return -EBUSY; + + ctrl = imx_mu_read(plat, plat->dcfg->xCR[IMX_MU_GIER]); + val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]); + val &= IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx) & + (ctrl & IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx)); + break; + default: + dev_warn(chan->dev, "Unhandled channel type %d\n", cp->type); + return -EOPNOTSUPP; + }; + + if (val == IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx)) + plat->dcfg->rxdb(plat, cp); + + return 0; +} + +static int imx_mu_of_to_plat(struct udevice *dev) +{ + struct imx_mu *plat = dev_get_plat(dev); + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -ENODEV; + + plat->base = (struct mu_type *)addr; + + return 0; +} + +static int imx_mu_init_generic(struct imx_mu *plat) +{ + unsigned int i; + unsigned int val; + + if (plat->num_rr > 4 || plat->num_tr > 4) { + WARN_ONCE(true, "%s not support TR/RR larger than 4\n", __func__); + return -EOPNOTSUPP; + } + + /* Set default MU configuration */ + for (i = 0; i < IMX_MU_xCR_MAX; i++) + imx_mu_write(plat, 0, plat->dcfg->xCR[i]); + + /* Clear any pending GIP */ + val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]); + imx_mu_write(plat, val, plat->dcfg->xSR[IMX_MU_GSR]); + + /* Clear any pending RSR */ + for (i = 0; i < plat->num_rr; i++) + imx_mu_read(plat, plat->dcfg->xRR + i * 4); + + return 0; +} + +static int imx_mu_generic_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args) +{ + enum imx_mu_chan_type type; + int idx, cid; + + if (args->args_count != 2) { + dev_err(chan->dev, "Invalid argument count %d\n", args->args_count); + return -EINVAL; + } + + type = args->args[0]; /* channel type */ + idx = args->args[1]; /* index */ + + cid = type * 4 + idx; + if (cid >= IMX_MU_CHANS) { + dev_err(chan->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", + cid, type, idx); + return -EINVAL; + } + + chan->id = cid; + + return 0; +} + +static int imx_mu_generic_tx(struct imx_mu *plat, struct imx_mu_con_priv *cp, + const void *data) +{ + switch (cp->type) { + case IMX_MU_TYPE_TXDB_V2: + imx_mu_xcr_rmw(plat, IMX_MU_GCR, IMX_MU_xCR_GIRn(plat->dcfg->type, cp->idx), 0); + break; + default: + dev_warn(cp->chan->dev, "Send data on wrong channel type: %d\n", cp->type); + return -EINVAL; + } + + return 0; +} + +static int imx_mu_generic_rxdb(struct imx_mu *plat, struct imx_mu_con_priv *cp) +{ + imx_mu_write(plat, IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx), + plat->dcfg->xSR[IMX_MU_GSR]); + + return 0; +} + +static const struct imx_mu_dcfg imx_mu_cfg_imx95 = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V2, + .xTR = 0x200, + .xRR = 0x280, + .xSR = {0xC, 0x118, 0x124, 0x12C}, + .xCR = {0x8, 0x110, 0x114, 0x120, 0x128}, +}; + +static const struct udevice_id ids[] = { + { .compatible = "fsl,imx95-mu", .data = (ulong)&imx_mu_cfg_imx95 }, + { } +}; + +int imx_mu_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + + return plat->dcfg->of_xlate(chan, args); +} + +struct mbox_ops imx_mu_ops = { + .of_xlate = imx_mu_of_xlate, + .request = imx_mu_chan_request, + .rfree = imx_mu_chan_free, + .send = imx_mu_send, + .recv = imx_mu_recv, +}; + +static void imx_mu_get_tr_rr(struct imx_mu *plat) +{ + u32 val; + + if (plat->dcfg->type & IMX_MU_V2) { + val = imx_mu_read(plat, IMX_MU_V2_PAR_OFF); + plat->num_tr = FIELD_GET(IMX_MU_V2_TR_MASK, val); + plat->num_rr = FIELD_GET(IMX_MU_V2_RR_MASK, val); + } else { + plat->num_tr = 4; + plat->num_rr = 4; + } +} + +static int imx_mu_probe(struct udevice *dev) +{ + struct imx_mu *plat = dev_get_plat(dev); + int ret; + + debug("%s(dev=%p)\n", __func__, dev); + + plat->dcfg = (void *)dev_get_driver_data(dev); + + imx_mu_get_tr_rr(plat); + + ret = plat->dcfg->init(plat); + if (ret) { + dev_err(dev, "Failed to init MU\n"); + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(imx_mu) = { + .name = "imx-mu", + .id = UCLASS_MAILBOX, + .of_match = ids, + .of_to_plat = imx_mu_of_to_plat, + .plat_auto = sizeof(struct imx_mu), + .probe = imx_mu_probe, + .ops = &imx_mu_ops, + .flags = DM_FLAG_PRE_RELOC, +};

On 1/15/25 2:28 PM, Alice Guo wrote:
This id ported from Linux (v6.12.8) driver drivers/mailbox/imx-mailbox.c.
'id' -> 'is' typo .
It is better to include the commit SHA from which this was ported over too, so the follow up patches in Linux can be easily ported over as they appear. If the last commit SHA from which this was ported over is known, then it is possible to do e.g. the following to find out any new commits:
linux$ git log 39d7d6177f0c..origin/master -- drivers/mailbox/imx-mailbox.c
"$ git log v6.12.8 -- drivers/mailbox/imx-mailbox.c" indicates this is the latest imx-mailbox.c commit in Linux 6.12.8:
39d7d6177f0c ("mailbox: imx: use device name in interrupt name")
[...]
+#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +#define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +#define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
+/* General Purpose Interrupt Enable */ +#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +/* Receive Interrupt Enable */ +#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +/* Transmit Interrupt Enable */ +#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) +/* General Purpose Interrupt Request */ +#define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x)))) +/* MU reset */ +#define IMX_MU_xCR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(5)) +#define IMX_MU_xSR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(7))
[...]
+static const struct imx_mu_dcfg imx_mu_cfg_imx95 = {
- .tx = imx_mu_generic_tx,
- .rxdb = imx_mu_generic_rxdb,
- .init = imx_mu_init_generic,
- .of_xlate = imx_mu_generic_of_xlate,
- .type = IMX_MU_V2,
- .xTR = 0x200,
- .xRR = 0x280,
- .xSR = {0xC, 0x118, 0x124, 0x12C},
- .xCR = {0x8, 0x110, 0x114, 0x120, 0x128},
+};
[...]
I would suggest these changes:
- Add the remaining SoC support, the plumbing is already there, all the conditional bits are already there for MU v1/v2 , so fill in the data for at least the V1 MU SoCs.
- Split this patch into separate submission or series, so it can get reviewed separately and land faster, possibly before the MX95 support gets fully sorted out.

From: Viorel Suman viorel.suman@nxp.com
i.MX95 System Manager uses interrupt driven communication which requires the caller to set Bit[0] of channel flags to 1. When transmission completes and the previous general purpose interrupt has been processed by the other core, i.MX95 System Manager will set General Purpose Interrupt Control Register (GCR). U-Boot polls General-purpose Status (GSR) to check if the operation is finished.
Signed-off-by: Viorel Suman viorel.suman@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- drivers/firmware/scmi/Kconfig | 6 ++++++ drivers/firmware/scmi/smt.c | 3 +++ drivers/firmware/scmi/smt.h | 10 ++++++++++ 3 files changed, 19 insertions(+)
diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 8cf85f0d7a12c9e58dc249539727081ec25dd7b3..eb62b88615b5cd95635571556e79ba40d1a387e9 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -41,3 +41,9 @@ config SCMI_AGENT_OPTEE help Enable the SCMI communication channel based on OP-TEE transport for compatible "linaro,scmi-optee". + +config SCMI_TRANSPORT_SMT_INTR + bool + depends on SCMI_FIRMWARE + help + Enable interrupt communication of shared memory based transport. diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 67d2f45002490ab64a6bc997eda93a8f4681d99b..ee7ae5d50b226b92a3961c181de83c730a621401 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -48,6 +48,9 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (!smt->buf) return -ENOMEM;
+ if (IS_ENABLED(CONFIG_SCMI_TRANSPORT_SMT_INTR)) + scmi_smt_enable_intr(smt, true); + #ifdef CONFIG_ARM if (dcache_status()) mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE), diff --git a/drivers/firmware/scmi/smt.h b/drivers/firmware/scmi/smt.h index 9d669a6c922666c48e7dce4c0affd7b47cb137e4..768b0f4c8a8d1cc7966530e724d278d72c2a167b 100644 --- a/drivers/firmware/scmi/smt.h +++ b/drivers/firmware/scmi/smt.h @@ -84,6 +84,16 @@ static inline void scmi_smt_put_channel(struct scmi_smt *smt) hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; }
+static inline void scmi_smt_enable_intr(struct scmi_smt *smt, bool enable) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + if (enable) + hdr->flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; + else + hdr->flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED; +} + int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt);
/*

On 1/15/25 2:28 PM, Alice Guo wrote:
From: Viorel Suman viorel.suman@nxp.com
i.MX95 System Manager uses interrupt driven communication which requires the caller to set Bit[0] of channel flags to 1. When transmission completes and the previous general purpose interrupt has been processed by the other core, i.MX95 System Manager will set General Purpose Interrupt Control Register (GCR). U-Boot polls General-purpose Status (GSR) to check if the operation is finished.
Signed-off-by: Viorel Suman viorel.suman@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com
drivers/firmware/scmi/Kconfig | 6 ++++++ drivers/firmware/scmi/smt.c | 3 +++ drivers/firmware/scmi/smt.h | 10 ++++++++++ 3 files changed, 19 insertions(+)
diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 8cf85f0d7a12c9e58dc249539727081ec25dd7b3..eb62b88615b5cd95635571556e79ba40d1a387e9 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -41,3 +41,9 @@ config SCMI_AGENT_OPTEE help Enable the SCMI communication channel based on OP-TEE transport for compatible "linaro,scmi-optee".
+config SCMI_TRANSPORT_SMT_INTR
- bool
- depends on SCMI_FIRMWARE
- help
Enable interrupt communication of shared memory based transport.
diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 67d2f45002490ab64a6bc997eda93a8f4681d99b..ee7ae5d50b226b92a3961c181de83c730a621401 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -48,6 +48,9 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (!smt->buf) return -ENOMEM;
- if (IS_ENABLED(CONFIG_SCMI_TRANSPORT_SMT_INTR))
scmi_smt_enable_intr(smt, true);
- #ifdef CONFIG_ARM if (dcache_status()) mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE),
diff --git a/drivers/firmware/scmi/smt.h b/drivers/firmware/scmi/smt.h index 9d669a6c922666c48e7dce4c0affd7b47cb137e4..768b0f4c8a8d1cc7966530e724d278d72c2a167b 100644 --- a/drivers/firmware/scmi/smt.h +++ b/drivers/firmware/scmi/smt.h @@ -84,6 +84,16 @@ static inline void scmi_smt_put_channel(struct scmi_smt *smt) hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; }
+static inline void scmi_smt_enable_intr(struct scmi_smt *smt, bool enable)
Is this expected to be called from somewhere else except drivers/firmware/scmi/smt.c ? If not, please move it into drivers/firmware/scmi/smt.c .
Drop the 'inline' , compiler can figure that out.
+{
- struct scmi_smt_header *hdr = (void *)smt->buf;
The cast should be (struct scmi_smt_header *) instead of (void *)
- if (enable)
hdr->flags |= SCMI_SHMEM_FLAG_INTR_ENABLED;
- else
hdr->flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED;
+}
- int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt);

From: Alice Guo alice.guo@nxp.com
This patch provides a pinctrl driver based on SCMI pin control protocol. Currently, only the PINCTRL_CONFIG_SET command is implemented.
Signed-off-by: Ranjani Vaidyanathan Ranjani.Vaidyanathan@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- drivers/pinctrl/nxp/Kconfig | 13 ++++ drivers/pinctrl/nxp/Makefile | 1 + drivers/pinctrl/nxp/pinctrl-imx.c | 7 +- drivers/pinctrl/nxp/pinctrl-imx.h | 11 +++ drivers/pinctrl/nxp/pinctrl-scmi.c | 136 +++++++++++++++++++++++++++++++++++++ include/scmi_protocols.h | 34 ++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-)
diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index 06c26f156f6c8f63204604d6065485629cfd9b61..ec63ab595650d0dfab7e3a7dc01d4af7814b6773 100644 --- a/drivers/pinctrl/nxp/Kconfig +++ b/drivers/pinctrl/nxp/Kconfig @@ -1,6 +1,19 @@ config PINCTRL_IMX bool
+config PINCTRL_IMX_SCMI + bool "IMX pinctrl SCMI driver" + depends on ARCH_IMX9 && PINCTRL_FULL + select PINCTRL_IMX + help + Say Y here to enable the imx pinctrl SCMI driver + + This provides a simple pinctrl driver for i.MX SoC which supports + SCMI. This feature depends on device tree configuration. This driver + is different from the linux one, this is a simple implementation, + only parses the 'fsl,pins' property and configure related + registers. + config PINCTRL_IMX_SCU bool
diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile index f10aa6ef188e37583b181bdf9d70ac191e506d75..3ec3e2a9c6fdb875da4ae9bf151df5666256883b 100644 --- a/drivers/pinctrl/nxp/Makefile +++ b/drivers/pinctrl/nxp/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_PINCTRL_IMX6) += pinctrl-imx6.o obj-$(CONFIG_PINCTRL_IMX7) += pinctrl-imx7.o obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o obj-$(CONFIG_PINCTRL_IMX8ULP) += pinctrl-imx8ulp.o +obj-$(CONFIG_PINCTRL_IMX_SCMI) += pinctrl-scmi.o obj-$(CONFIG_PINCTRL_IMX_SCU) += pinctrl-scu.o obj-$(CONFIG_PINCTRL_IMX8) += pinctrl-imx8.o obj-$(CONFIG_PINCTRL_IMX8M) += pinctrl-imx8m.o diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c index b1960c56b512cc113a810304dc3ac3f99b237a7e..0026aae00afe2b0aff40c3288c231b3065ef656a 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.c +++ b/drivers/pinctrl/nxp/pinctrl-imx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2016 Peng Fan van.freenix@gmail.com + * Copyright 2025 NXP */
#include <malloc.h> @@ -65,7 +66,9 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config)
npins = size / pin_size;
- if (info->flags & IMX8_USE_SCU) { + if (info->flags & IMX_USE_SCMI) { + return imx_pinctrl_scmi_conf_pins(dev, pin_data, npins); + } else if (info->flags & IMX8_USE_SCU) { imx_pinctrl_scu_conf_pins(info, pin_data, npins); } else { /* @@ -216,7 +219,7 @@ int imx_pinctrl_probe(struct udevice *dev, priv->dev = dev; priv->info = info;
- if (info->flags & IMX8_USE_SCU) + if (info->flags & (IMX8_USE_SCU | IMX_USE_SCMI)) return 0;
addr = ofnode_get_addr_size_index(dev_ofnode(dev), 0, &size); diff --git a/drivers/pinctrl/nxp/pinctrl-imx.h b/drivers/pinctrl/nxp/pinctrl-imx.h index fa4c084e2fc067fcb4e92c33312d4f430081fffd..f120e32d9da678d6e9ce583f0ba3c6c17d050f36 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.h +++ b/drivers/pinctrl/nxp/pinctrl-imx.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2016 Peng Fan van.freenix@gmail.com + * Copyright 2025 NXP */
#ifndef __DRIVERS_PINCTRL_IMX_H @@ -47,6 +48,7 @@ extern const struct pinctrl_ops imx_pinctrl_ops; #define ZERO_OFFSET_VALID 0x2 #define CFG_IBE_OBE 0x4 #define IMX8_USE_SCU 0x8 +#define IMX_USE_SCMI 0x10
#define IOMUXC_CONFIG_SION (0x1 << 4)
@@ -65,4 +67,13 @@ static inline int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, } #endif
+#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) +int imx_pinctrl_scmi_conf_pins(struct udevice *dev, u32 *pin_data, int npins); +#else +static inline int imx_pinctrl_scmi_conf_pins(struct udevice *dev, u32 *pin_data, int npins) +{ + return 0; +} +#endif + #endif /* __DRIVERS_PINCTRL_IMX_H */ diff --git a/drivers/pinctrl/nxp/pinctrl-scmi.c b/drivers/pinctrl/nxp/pinctrl-scmi.c new file mode 100644 index 0000000000000000000000000000000000000000..7d2ed6542dc5166ebb07ade6e931b83b370af386 --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-scmi.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/io.h> +#include <asm/mach-imx/iomux-v3.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <linux/bitops.h> +#include <misc.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> + +#include "pinctrl-imx.h" + +#if IS_ENABLED(CONFIG_IMX93) +#define DAISY_OFFSET 0x360 +#endif +#if IS_ENABLED(CONFIG_IMX95) +#define DAISY_OFFSET 0x408 +#endif + +/* SCMI pin control types */ +#define PINCTRL_TYPE_MUX 192 +#define PINCTRL_TYPE_CONFIG 193 +#define PINCTRL_TYPE_DAISY_ID 194 +#define PINCTRL_TYPE_DAISY_CFG 195 +#define PINCTRL_NUM_CFGS_SHIFT 2 + +static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 config_val, + u32 input_ofs, u32 input_val) +{ + int ret, num_cfgs = 0; + + /* Call SCMI API to set the pin mux and configuration. */ + struct scmi_pinctrl_config_set_out out; + struct scmi_pinctrl_config_set_in in = { + .identifier = mux_ofs / 4, + .function_id = 0xFFFFFFFF, + .attributes = 0, + }; + if (mux_ofs != 0) { + in.configs[num_cfgs].type = PINCTRL_TYPE_MUX; + in.configs[num_cfgs].val = mux; + num_cfgs++; + } + if (config_val != 0) { + in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG; + in.configs[num_cfgs].val = config_val; + num_cfgs++; + } + if (input_ofs != 0) { + in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_ID; + in.configs[num_cfgs].val = (input_ofs - DAISY_OFFSET) / 4; + num_cfgs++; + in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_CFG; + in.configs[num_cfgs].val = input_val; + num_cfgs++; + } + /* Update the number of configs sent in this call. */ + in.attributes = num_cfgs << PINCTRL_NUM_CFGS_SHIFT; + + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL, + SCMI_MSG_PINCTRL_CONFIG_SET, in, out); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret != 0 || out.status != 0) + dev_err(dev, "Failed to set PAD = %d, daisy = %d, scmi_err = %d, ret = %d\n", mux_ofs / 4, input_ofs / 4, out.status, ret); + + return ret; +} + +int imx_pinctrl_scmi_conf_pins(struct udevice *dev, u32 *pin_data, int npins) +{ + int mux_ofs, mux, config_val, input_reg, input_val; + int i, j = 0; + int ret; + + /* + * Refer to linux documentation for details: + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt + */ + for (i = 0; i < npins; i++) { + mux_ofs = pin_data[j++]; + /* Skip config_reg */ + j++; + input_reg = pin_data[j++]; + + mux = pin_data[j++]; + input_val = pin_data[j++]; + config_val = pin_data[j++]; + + if (config_val & IMX_PAD_SION) + mux |= IOMUXC_CONFIG_SION; + + config_val &= ~IMX_PAD_SION; + + ret = imx_pinconf_scmi_set(dev, mux_ofs, mux, config_val, input_reg, input_val); + if (ret && ret != -EPERM) + dev_err(dev, "Set pin %d, mux %d, val %d, error\n", + mux_ofs, mux, config_val); + } + + return ret; +} + +static struct imx_pinctrl_soc_info imx_pinctrl_scmi_soc_info __section(".data") = { + .flags = ZERO_OFFSET_VALID | IMX_USE_SCMI, +}; + +static int imx_scmi_pinctrl_probe(struct udevice *dev) +{ + struct imx_pinctrl_priv *priv = dev_get_priv(dev); + int ret; + + ret = devm_scmi_of_get_channel(dev); + if (ret) { + dev_err(dev, "get channel: %d\n", ret); + return ret; + } + + debug("%s %p %s\n", __func__, priv, dev->name); + return imx_pinctrl_probe(dev, &imx_pinctrl_scmi_soc_info); +} + +U_BOOT_DRIVER(scmi_pinctrl_imx) = { + .name = "scmi_pinctrl_imx", + .id = UCLASS_PINCTRL, + .probe = imx_scmi_pinctrl_probe, + .remove = imx_pinctrl_remove, + .priv_auto = sizeof(struct imx_pinctrl_priv), + .ops = &imx_pinctrl_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 7abb2a6f36ba53223bd9103fcc02f58506fbfc8d..2367467512a6b65253317e51bea71de396aa96f9 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -24,6 +24,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_ID_SENSOR = 0x15, SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17, + SCMI_PROTOCOL_ID_PINCTRL = 0x19, };
enum scmi_status_code { @@ -1005,4 +1006,37 @@ struct scmi_voltd_level_get_out { s32 voltage_level; };
+/* SCMI Pinctrl Protocol */ +enum scmi_pinctrl_message_id { + SCMI_MSG_PINCTRL_CONFIG_SET = 0x6 +}; + +struct scmi_pin_config { + u32 type; + u32 val; +}; + +/** + * struct scmi_pad_config_set_in - Message payload for PAD_CONFIG_SET command + * @identifier: Identifier for the pin or group. + * @function_id: Identifier for the function selected to be enabled for the + * selected pin or group. This field is set to 0xFFFFFFFF if no + * function should be enabled by the pin or group. + * @attributes: Bits[31:11] Reserved, must be zero. + * Bit[10] Function valid. + * Bits[9:2] Number of configurations to set. + * Bits[1:0] Selector: Whether the identifier field refers to a pin or a group. + * @configs: Array of configurations. + */ +struct scmi_pinctrl_config_set_in { + u32 identifier; + u32 function_id; + u32 attributes; + struct scmi_pin_config configs[4]; +}; + +struct scmi_pinctrl_config_set_out { + s32 status; +}; + #endif /* _SCMI_PROTOCOLS_H */

On 1/15/25 2:28 PM, Alice Guo wrote:
From: Alice Guo alice.guo@nxp.com
This patch provides a pinctrl driver based on SCMI pin control protocol. Currently, only the PINCTRL_CONFIG_SET command is implemented.
Signed-off-by: Ranjani Vaidyanathan Ranjani.Vaidyanathan@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com
drivers/pinctrl/nxp/Kconfig | 13 ++++ drivers/pinctrl/nxp/Makefile | 1 + drivers/pinctrl/nxp/pinctrl-imx.c | 7 +- drivers/pinctrl/nxp/pinctrl-imx.h | 11 +++ drivers/pinctrl/nxp/pinctrl-scmi.c | 136 +++++++++++++++++++++++++++++++++++++ include/scmi_protocols.h | 34 ++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-)
diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index 06c26f156f6c8f63204604d6065485629cfd9b61..ec63ab595650d0dfab7e3a7dc01d4af7814b6773 100644 --- a/drivers/pinctrl/nxp/Kconfig +++ b/drivers/pinctrl/nxp/Kconfig @@ -1,6 +1,19 @@ config PINCTRL_IMX bool
+config PINCTRL_IMX_SCMI
- bool "IMX pinctrl SCMI driver"
i.MX instead of IMX ? Please be sure to use the branding consistently across Kconfig entries and various text fields.
- depends on ARCH_IMX9 && PINCTRL_FULL
Is this really supposed to be available for all iMX9* or is this limited to iMX95 ?
- select PINCTRL_IMX
- help
Say Y here to enable the imx pinctrl SCMI driver
This Say Y stuff goes ...
This provides a simple pinctrl driver for i.MX SoC which supports
SCMI. This feature depends on device tree configuration. This driver
is different from the linux one, this is a simple implementation,
only parses the 'fsl,pins' property and configure related
registers.
... here, at the end.
config PINCTRL_IMX_SCU bool
I have posted a couple of pinctrl patches to [1] , that should simplify this pinctrl implementation. The entire series is also available in [2] , the updated mx95 pinctrl patch on top of it is [3] .
[1] https://lore.kernel.org/u-boot/20250124145520.185414-1-marex@denx.de/ [2] https://source.denx.de/u-boot/custodians/u-boot-usb/-/commits/test-pinctrl [3] https://source.denx.de/u-boot/custodians/u-boot-usb/-/commit/3191ef0497c6290...

From: Alice Guo alice.guo@nxp.com
This patch adds SCMI pin control protocol support so that the pin controller driver based on SCMI, such as drivers/pinctrl/nxp/pinctrl-scmi.c, can be bound to the SCMI agent device whose protocol id is 0x19.
Signed-off-by: Alice Guo alice.guo@nxp.com --- drivers/firmware/scmi/scmi_agent-uclass.c | 11 +++++++++++ include/scmi_agent-uclass.h | 2 ++ 2 files changed, 13 insertions(+)
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 8c907c3b0328095c4b35ba089ed608fcda48b567..5f101f4ede8b585f8c562a42886ea7be4ef09953 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -97,6 +97,9 @@ struct udevice *scmi_get_protocol(struct udevice *dev, case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: proto = priv->voltagedom_dev; break; + case SCMI_PROTOCOL_ID_PINCTRL: + proto = priv->pinctrl_dev; + break; default: dev_err(dev, "Protocol not supported\n"); proto = NULL; @@ -147,6 +150,9 @@ static int scmi_add_protocol(struct udevice *dev, case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: priv->voltagedom_dev = proto; break; + case SCMI_PROTOCOL_ID_PINCTRL: + priv->pinctrl_dev = proto; + break; default: dev_err(dev, "Protocol not supported\n"); return -EPROTO; @@ -436,6 +442,11 @@ static int scmi_bind_protocols(struct udevice *dev) drv = DM_DRIVER_GET(scmi_voltage_domain); } break; + case SCMI_PROTOCOL_ID_PINCTRL: + if (IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) && + scmi_protocol_is_supported(dev, protocol_id)) + drv = DM_DRIVER_GET(scmi_pinctrl_imx); + break; default: break; } diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index 33e0e18c30d4ae4ddaaa2cb3830c03cd7a559cc0..4beec43cb080b14a777cac1cb0f18ef53433596c 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -27,6 +27,7 @@ struct scmi_channel; * @clock_dev: SCMI clock protocol device * @resetdom_dev: SCMI reset domain protocol device * @voltagedom_dev: SCMI voltage domain protocol device + * @pinctrl_dev: SCMI pin control protocol device */ struct scmi_agent_priv { u32 version; @@ -43,6 +44,7 @@ struct scmi_agent_priv { struct udevice *clock_dev; struct udevice *resetdom_dev; struct udevice *voltagedom_dev; + struct udevice *pinctrl_dev; };
static inline u32 scmi_version(struct udevice *dev)

On 1/15/25 2:28 PM, Alice Guo wrote:
From: Alice Guo alice.guo@nxp.com
This patch adds SCMI pin control protocol support so that the pin controller driver based on SCMI, such as drivers/pinctrl/nxp/pinctrl-scmi.c, can be bound to the SCMI agent device whose protocol id is 0x19.
Signed-off-by: Alice Guo alice.guo@nxp.com
drivers/firmware/scmi/scmi_agent-uclass.c | 11 +++++++++++ include/scmi_agent-uclass.h | 2 ++ 2 files changed, 13 insertions(+)
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 8c907c3b0328095c4b35ba089ed608fcda48b567..5f101f4ede8b585f8c562a42886ea7be4ef09953 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -97,6 +97,9 @@ struct udevice *scmi_get_protocol(struct udevice *dev, case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: proto = priv->voltagedom_dev; break;
- case SCMI_PROTOCOL_ID_PINCTRL:
proto = priv->pinctrl_dev;
default: dev_err(dev, "Protocol not supported\n"); proto = NULL;break;
@@ -147,6 +150,9 @@ static int scmi_add_protocol(struct udevice *dev, case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: priv->voltagedom_dev = proto; break;
- case SCMI_PROTOCOL_ID_PINCTRL:
priv->pinctrl_dev = proto;
default: dev_err(dev, "Protocol not supported\n"); return -EPROTO;break;
@@ -436,6 +442,11 @@ static int scmi_bind_protocols(struct udevice *dev) drv = DM_DRIVER_GET(scmi_voltage_domain); } break;
case SCMI_PROTOCOL_ID_PINCTRL:
if (IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) &&
Is this pinctrl protocol really imx specific ?
If not, this needs to use some other config option to gate access to it.

From: Peng Fan peng.fan@nxp.com
SCMI misc protocol is intended for miscellaneous functions which are device specific and are usually defined to access bit fields. This patch adds SCMI misc protocol protocol_id and message_id for getting the ROM passover data.
Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- include/scmi_protocols.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 2367467512a6b65253317e51bea71de396aa96f9..6cda1f00df1f6baf6db65a28b4cab114f8540a4b 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -25,6 +25,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17, SCMI_PROTOCOL_ID_PINCTRL = 0x19, + SCMI_PROTOCOL_ID_IMX_MISC = 0x84, };
enum scmi_status_code { @@ -50,6 +51,10 @@ enum scmi_discovery_id { SCMI_PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, };
+enum scmi_imx_misc_message_id { + SCMI_MISC_ROM_PASSOVER_GET = 0x7 +}; + /* * SCMI Base Protocol */

On 1/15/25 2:28 PM, Alice Guo wrote:
From: Peng Fan peng.fan@nxp.com
SCMI misc protocol is intended for miscellaneous functions which are device specific and are usually defined to access bit fields. This patch adds SCMI misc protocol protocol_id and message_id for getting the ROM passover data.
Is this iMX specific or is this generic protocol ?
If generic , then the symbol SCMI_PROTOCOL_ID_IMX_MISC should not contain _IMX substring .

From: Peng Fan peng.fan@nxp.com
SCMI Performance domain management protocol is intended for performance management of groups of devices or APs that run in the same performance domain. The functionality provided by the callee-side can be used by passing the corresponding message_id.
Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- include/scmi_protocols.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 6cda1f00df1f6baf6db65a28b4cab114f8540a4b..08c86a642844570424dddbfcf9670d1e6757d8d1 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -1044,4 +1044,22 @@ struct scmi_pinctrl_config_set_out { s32 status; };
+/* SCMI Perf Protocol */ +enum scmi_perf_message_id { + SCMI_PERF_DOMAIN_ATTRIBUTES = 0x3, + SCMI_PERF_DESCRIBE_LEVELS = 0x4, + SCMI_PERF_LIMITS_SET = 0x5, + SCMI_PERF_LIMITS_GET = 0x6, + SCMI_PERF_LEVEL_SET = 0x7, + SCMI_PERF_LEVEL_GET = 0x8 +}; + +struct scmi_perf_in { + u32 domain_id; + u32 perf_level; +}; + +struct scmi_perf_out { + s32 status; +}; #endif /* _SCMI_PROTOCOLS_H */

From: Peng Fan peng.fan@nxp.com
This patch adds the command CLOCK_PARENT_SET that can be used to set the parent of a clock. ARM SCMI Version 3.2 supports to change the parent of a clock device.
Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- drivers/clk/clk_scmi.c | 20 ++++++++++++++++++++ include/scmi_protocols.h | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c index e42d2032d45e011b751c19b395b6f99d5c54e78e..84333cdd0ccfe566c269f776f39725c69883c25c 100644 --- a/drivers/clk/clk_scmi.c +++ b/drivers/clk/clk_scmi.c @@ -178,11 +178,31 @@ static int scmi_clk_probe(struct udevice *dev) return 0; }
+static int scmi_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct scmi_clk_parent_set_in in = { + .clock_id = clk->id, + .parent_clk = parent->id, + }; + struct scmi_clk_parent_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_PARENT_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev, &msg); + if (ret < 0) + return ret; + + return scmi_to_linux_errno(out.status); +} + static const struct clk_ops scmi_clk_ops = { .enable = scmi_clk_enable, .disable = scmi_clk_disable, .get_rate = scmi_clk_get_rate, .set_rate = scmi_clk_set_rate, + .set_parent = scmi_clk_set_parent, };
U_BOOT_DRIVER(scmi_clock) = { diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 08c86a642844570424dddbfcf9670d1e6757d8d1..d529f8e2697472e60d0cb9c275f34ef0ecaed3ca 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -737,6 +737,7 @@ enum scmi_clock_message_id { SCMI_CLOCK_RATE_SET = 0x5, SCMI_CLOCK_RATE_GET = 0x6, SCMI_CLOCK_CONFIG_SET = 0x7, + SCMI_CLOCK_PARENT_SET = 0xD };
#define SCMI_CLK_PROTO_ATTR_COUNT_MASK GENMASK(15, 0) @@ -839,6 +840,24 @@ struct scmi_clk_rate_set_out { s32 status; };
+/** + * struct scmi_clk_parent_state_in - Message payload for CLOCK_PARENT_SET command + * @clock_id: SCMI clock ID + * @parent_clk: SCMI clock ID + */ +struct scmi_clk_parent_set_in { + u32 clock_id; + u32 parent_clk; +}; + +/** + * struct scmi_clk_parent_set_out - Response payload for CLOCK_PARENT_SET command + * @status: SCMI command status + */ +struct scmi_clk_parent_set_out { + s32 status; +}; + /* * SCMI Reset Domain Protocol */

From: Alice Guo alice.guo@nxp.com
Clock driver based on SCMI clock management protocol in Linux checks clock state, parent and rate control permissions. To be consistent with the kernel driver, add this check here.
When using common clock framework (CCF), use the clock signal ID to get the clock registered by clk_register() in scmi_clk_probe(), and then obatin the struct clk_scmi variable with container_of().
Signed-off-by: Alice Guo alice.guo@nxp.com Signed-off-by: Ye Li ye.li@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- drivers/clk/clk_scmi.c | 173 ++++++++++++++++++++++++++++++++++++++++++++--- include/scmi_protocols.h | 26 ++++++- 2 files changed, 188 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c index 84333cdd0ccfe566c269f776f39725c69883c25c..0654f0cbf5fcca9c82b840c2fabd057ce1907979 100644 --- a/drivers/clk/clk_scmi.c +++ b/drivers/clk/clk_scmi.c @@ -12,6 +12,56 @@ #include <asm/types.h> #include <linux/clk-provider.h>
+struct clk_scmi { + struct clk clk; + u32 ctrl_flags; +}; + +static int scmi_clk_get_permissions(struct udevice *dev, int clkid, u32 *perm) +{ + u32 version; + int ret; + + ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &version); + if (ret) { + debug("get SCMI clock management protocol version failed\n"); + return ret; + } + + if (version >= CLOCK_PROTOCOL_VERSION_3_0) { + struct scmi_clk_get_permissions_in in = { + .clock_id = clkid, + }; + struct scmi_clk_get_permissions_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_CLOCK, + .message_id = SCMI_CLOCK_GET_PERMISSIONS, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) { + debug("get SCMI clock management protocol permissions failed\n"); + return ret; + } + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) { + debug("the status code of getting permissions: %d\n", ret); + return ret; + } + + *perm = out.permissions; + return 0; + } + + debug("SCMI clock management protocol version is less than 3.0.\n"); + return -EINVAL; +} + static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) { struct scmi_clk_protocol_attr_out out; @@ -32,7 +82,8 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) return 0; }
-static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name) +static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name, + u32 *attr) { struct scmi_clk_attribute_in in = { .clock_id = clkid, @@ -53,6 +104,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name) return ret;
*name = strdup(out.clock_name); + *attr = out.attributes;
return 0; } @@ -78,11 +130,49 @@ static int scmi_clk_gate(struct clk *clk, int enable)
static int scmi_clk_enable(struct clk *clk) { + struct clk_scmi *clkscmi; + struct clk *c; + int ret; + + if (CONFIG_IS_ENABLED(CLK_CCF)) { + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + clkscmi = container_of(c, struct clk_scmi, clk); + + if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL) + return scmi_clk_gate(clk, 1); + + /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */ + debug("SCMI CLOCK: the clock cannot be enabled by the agent.\n"); + return 0; + } + return scmi_clk_gate(clk, 1); }
static int scmi_clk_disable(struct clk *clk) { + struct clk_scmi *clkscmi; + struct clk *c; + int ret; + + if (CONFIG_IS_ENABLED(CLK_CCF)) { + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + clkscmi = container_of(c, struct clk_scmi, clk); + + if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL) + return scmi_clk_gate(clk, 0); + + /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */ + debug("SCMI CLOCK: the clock cannot be disabled by the agent.\n"); + return 0; + } + return scmi_clk_gate(clk, 0); }
@@ -108,7 +198,7 @@ static ulong scmi_clk_get_rate(struct clk *clk) return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); }
-static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) +static ulong __scmi_clk_set_rate(struct clk *clk, ulong rate) { struct scmi_clk_rate_set_in in = { .clock_id = clk->id, @@ -133,9 +223,33 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) return scmi_clk_get_rate(clk); }
+static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) +{ + struct clk_scmi *clkscmi; + struct clk *c; + int ret; + + if (CONFIG_IS_ENABLED(CLK_CCF)) { + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + clkscmi = container_of(c, struct clk_scmi, clk); + + if (clkscmi->ctrl_flags & SUPPORT_CLK_RATE_CONTROL) + return __scmi_clk_set_rate(clk, rate); + + /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */ + debug("SCMI CLOCK: the clock rate cannot be changed by the agent.\n"); + return 0; + } + + return __scmi_clk_set_rate(clk, rate); +} + static int scmi_clk_probe(struct udevice *dev) { - struct clk *clk; + struct clk_scmi *clk_scmi; size_t num_clocks, i; int ret;
@@ -156,29 +270,44 @@ static int scmi_clk_probe(struct udevice *dev)
for (i = 0; i < num_clocks; i++) { char *clock_name; + u32 attributes;
- if (!scmi_clk_get_attibute(dev, i, &clock_name)) { - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk || !clock_name) + if (!scmi_clk_get_attibute(dev, i, &clock_name, &attributes)) { + clk_scmi = kzalloc(sizeof(*clk_scmi), GFP_KERNEL); + if (!clk_scmi || !clock_name) ret = -ENOMEM; else - ret = clk_register(clk, dev->driver->name, + ret = clk_register(&clk_scmi->clk, dev->driver->name, clock_name, dev->name);
if (ret) { - free(clk); + free(clk_scmi); free(clock_name); + free(&attributes); return ret; }
- clk_dm(i, clk); + clk_dm(i, &clk_scmi->clk); + + if (CLK_HAS_RESTRICTIONS(attributes)) { + u32 perm; + + ret = scmi_clk_get_permissions(dev, i, &perm); + if (ret < 0) + clk_scmi->ctrl_flags = 0; + else + clk_scmi->ctrl_flags = perm; + } else { + clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | SUPPORT_CLK_PARENT_CONTROL | + SUPPORT_CLK_RATE_CONTROL; + } } }
return 0; }
-static int scmi_clk_set_parent(struct clk *clk, struct clk *parent) +static int __scmi_clk_set_parent(struct clk *clk, struct clk *parent) { struct scmi_clk_parent_set_in in = { .clock_id = clk->id, @@ -197,6 +326,30 @@ static int scmi_clk_set_parent(struct clk *clk, struct clk *parent) return scmi_to_linux_errno(out.status); }
+static int scmi_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_scmi *clkscmi; + struct clk *c; + int ret; + + if (CONFIG_IS_ENABLED(CLK_CCF)) { + ret = clk_get_by_id(clk->id, &c); + if (ret) + return ret; + + clkscmi = container_of(c, struct clk_scmi, clk); + + if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL) + return __scmi_clk_set_parent(clk, parent); + + /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */ + debug("SCMI CLOCK: the clock's parent cannot be changed by the agent.\n"); + return 0; + } + + return -ENOTSUPP; +} + static const struct clk_ops scmi_clk_ops = { .enable = scmi_clk_enable, .disable = scmi_clk_disable, diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index d529f8e2697472e60d0cb9c275f34ef0ecaed3ca..9607433c3276f9aa18044c414a5bde3143726cf4 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -731,13 +731,15 @@ int scmi_pwd_name_get(struct udevice *dev, u32 domain_id, u8 **name); /* * SCMI Clock Protocol */ +#define CLOCK_PROTOCOL_VERSION_3_0 0x30000
enum scmi_clock_message_id { SCMI_CLOCK_ATTRIBUTES = 0x3, SCMI_CLOCK_RATE_SET = 0x5, SCMI_CLOCK_RATE_GET = 0x6, SCMI_CLOCK_CONFIG_SET = 0x7, - SCMI_CLOCK_PARENT_SET = 0xD + SCMI_CLOCK_PARENT_SET = 0xD, + SCMI_CLOCK_GET_PERMISSIONS = 0xF };
#define SCMI_CLK_PROTO_ATTR_COUNT_MASK GENMASK(15, 0) @@ -776,6 +778,7 @@ struct scmi_clk_attribute_in { struct scmi_clk_attribute_out { s32 status; u32 attributes; +#define CLK_HAS_RESTRICTIONS(x) ((x) & BIT(1)) char clock_name[SCMI_CLOCK_NAME_LENGTH_MAX]; };
@@ -858,6 +861,27 @@ struct scmi_clk_parent_set_out { s32 status; };
+/** + * @clock_id: Identifier for the clock device. + */ +struct scmi_clk_get_permissions_in { + u32 clock_id; +}; + +/** + * @status: Negative 32-bit integers are used to return error status codes. + * @permissions: Bit[31] Clock state control, Bit[30] Clock parent control, + * Bit[29] Clock rate control, Bits[28:0] Reserved, must be zero + */ +struct scmi_clk_get_permissions_out { + s32 status; + u32 permissions; +}; + +#define SUPPORT_CLK_STAT_CONTROL BIT(31) +#define SUPPORT_CLK_PARENT_CONTROL BIT(30) +#define SUPPORT_CLK_RATE_CONTROL BIT(29) + /* * SCMI Reset Domain Protocol */

On 1/15/25 2:28 PM, Alice Guo wrote:
[...]
+static int scmi_clk_get_permissions(struct udevice *dev, int clkid, u32 *perm) +{
- u32 version;
- int ret;
- ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &version);
- if (ret) {
debug("get SCMI clock management protocol version failed\n");
return ret;
- }
- if (version >= CLOCK_PROTOCOL_VERSION_3_0) {
if (version < CLOCK_PROTOCOL_VERSION_3_0) return -EINVAL;
struct scmi_clk_get_permissions_in in = {
.clock_id = clkid,
};
[...]
+static int scmi_clk_set_parent(struct clk *clk, struct clk *parent) +{
- struct clk_scmi *clkscmi;
- struct clk *c;
- int ret;
- if (CONFIG_IS_ENABLED(CLK_CCF)) {
Either do this to reduce indent:
if (!CONFIG_IS_ENABLED(CLK_CCF)) return -ENOTSUPP;
Or even better, introduce dedicated clock ops structure for the CCF variant with these extra callbacks .
ret = clk_get_by_id(clk->id, &c);
if (ret)
return ret;
clkscmi = container_of(c, struct clk_scmi, clk);
if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL)
return __scmi_clk_set_parent(clk, parent);
/* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
debug("SCMI CLOCK: the clock's parent cannot be changed by the agent.\n");
return 0;
- }
- return -ENOTSUPP;
+}
[...]
@@ -858,6 +861,27 @@ struct scmi_clk_parent_set_out { s32 status; };
+/**
- @clock_id: Identifier for the clock device.
- */
+struct scmi_clk_get_permissions_in {
- u32 clock_id;
+};
+/**
- @status: Negative 32-bit integers are used to return error status codes.
- @permissions: Bit[31] Clock state control, Bit[30] Clock parent control,
Bit[29] Clock rate control, Bits[28:0] Reserved, must be zero
Please make sure the indent is consistent (it seems there are too many tabs here) and does not overflow 80 characters line length .

This patch is used to add SCMI clock control permissions to sandbox for testing.
Signed-off-by: Alice Guo alice.guo@nxp.com --- arch/sandbox/include/asm/scmi_test.h | 2 ++ drivers/firmware/scmi/sandbox-scmi_agent.c | 56 ++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 3 deletions(-)
diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h index 619f8f5098cdd82ba2343897166c34c82c753422..b30e536a35771ec3e1439c1e64fd09de3728dc50 100644 --- a/arch/sandbox/include/asm/scmi_test.h +++ b/arch/sandbox/include/asm/scmi_test.h @@ -27,10 +27,12 @@ struct sandbox_scmi_pwd { * @id: Identifier of the clock used in the SCMI protocol * @enabled: Clock state: true if enabled, false if disabled * @rate: Clock rate in Hertz + * @perm: Indicating state/parent/rate permission */ struct sandbox_scmi_clk { bool enabled; ulong rate; + u32 perm; };
/** diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index 19be280ec44845369d1f46509978ec9b5b59c60b..74a87832dcb14d63550e450f4bea2632844199e9 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -80,9 +80,9 @@ static struct sandbox_scmi_pwd scmi_pwdom[] = { };
static struct sandbox_scmi_clk scmi_clk[] = { - { .rate = 333 }, - { .rate = 200 }, - { .rate = 1000 }, + { .rate = 333, .perm = 0xE0000000 }, + { .rate = 200, .perm = 0xE0000000 }, + { .rate = 1000, .perm = 0xE0000000 }, };
static struct sandbox_scmi_reset scmi_reset[] = { @@ -700,6 +700,21 @@ static int sandbox_scmi_pwd_name_get(struct udevice *dev, struct scmi_msg *msg)
/* Clock Protocol */
+static int sandbox_scmi_clock_protocol_version(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_protocol_version_out *out = NULL; + + if (!msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + out = (struct scmi_protocol_version_out *)msg->out_msg; + out->version = 0x30000; + out->status = SCMI_SUCCESS; + + return 0; +} + static int sandbox_scmi_clock_protocol_attribs(struct udevice *dev, struct scmi_msg *msg) { @@ -740,6 +755,9 @@ static int sandbox_scmi_clock_attribs(struct udevice *dev, struct scmi_msg *msg) if (clk_state->enabled) out->attributes = 1;
+ /* Restricted clock */ + out->attributes |= BIT(1); + ret = snprintf(out->clock_name, sizeof(out->clock_name), "clk%u", in->clock_id); assert(ret > 0 && ret < sizeof(out->clock_name)); @@ -837,6 +855,34 @@ static int sandbox_scmi_clock_gate(struct udevice *dev, struct scmi_msg *msg) return 0; }
+static int sandbox_scmi_clock_permissions_get(struct udevice *dev, + struct scmi_msg *msg) +{ + struct scmi_clk_get_permissions_in *in = NULL; + struct scmi_clk_get_permissions_out *out = NULL; + struct sandbox_scmi_clk *clk_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_clk_get_permissions_in *)msg->in_msg; + out = (struct scmi_clk_get_permissions_out *)msg->out_msg; + + clk_state = get_scmi_clk_state(in->clock_id); + if (!clk_state) { + dev_err(dev, "Unexpected clock ID %u\n", in->clock_id); + + out->status = SCMI_NOT_FOUND; + } else { + out->permissions = clk_state->perm; + + out->status = SCMI_SUCCESS; + } + + return 0; +} + static int sandbox_scmi_rd_attribs(struct udevice *dev, struct scmi_msg *msg) { struct scmi_rd_attr_in *in = NULL; @@ -1193,6 +1239,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, return sandbox_proto_not_supported(msg);
switch (msg->message_id) { + case SCMI_PROTOCOL_VERSION: + return sandbox_scmi_clock_protocol_version(dev, msg); case SCMI_PROTOCOL_ATTRIBUTES: return sandbox_scmi_clock_protocol_attribs(dev, msg); case SCMI_CLOCK_ATTRIBUTES: @@ -1203,6 +1251,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, return sandbox_scmi_clock_rate_get(dev, msg); case SCMI_CLOCK_CONFIG_SET: return sandbox_scmi_clock_gate(dev, msg); + case SCMI_CLOCK_GET_PERMISSIONS: + return sandbox_scmi_clock_permissions_get(dev, msg); default: break; }

From: Ye Li ye.li@nxp.com
@protocols is an array of protocol identifiers that are implemented, excluding the Base protocol. The number of elements of @protocols is specified by callee-side. Currently, set it to 4 is enough for i.MX95.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com --- include/scmi_protocols.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 9607433c3276f9aa18044c414a5bde3143726cf4..3c55d228cb641bc6c0e5ca7ac885efe84b41c66d 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -141,11 +141,14 @@ struct scmi_base_discover_impl_version_out { * @status: SCMI command status * @num_protocols: Number of SCMI protocols in @protocol * @protocols: Array of packed SCMI protocol ID's + * + * The number of elements of @protocols is specified by callee-side. Currently, + * set it to 4 is enough for i.MX95. */ struct scmi_base_discover_list_protocols_out { s32 status; u32 num_protocols; - u32 protocols[3]; + u32 protocols[4]; };
/**

On 1/15/25 2:28 PM, Alice Guo wrote:
From: Ye Li ye.li@nxp.com
@protocols is an array of protocol identifiers that are implemented, excluding the Base protocol. The number of elements of @protocols is specified by callee-side. Currently, set it to 4 is enough for i.MX95.
Can you please try something like this instead ? That should be future-proof:
diff --git a/drivers/firmware/scmi/base.c b/drivers/firmware/scmi/base.c index f4e3974ff5b..92d278edfaf 100644 --- a/drivers/firmware/scmi/base.c +++ b/drivers/firmware/scmi/base.c @@ -258,17 +258,16 @@ static int scmi_base_discover_impl_version_int(struct udevice *dev, static int scmi_base_discover_list_protocols_int(struct udevice *dev, u8 **protocols) { - struct scmi_base_discover_list_protocols_out out; + struct scmi_base_discover_list_protocols_out *out; int cur; struct scmi_msg msg = { .protocol_id = SCMI_PROTOCOL_ID_BASE, .message_id = SCMI_BASE_DISCOVER_LIST_PROTOCOLS, .in_msg = (u8 *)&cur, .in_msg_sz = sizeof(cur), - .out_msg = (u8 *)&out, - .out_msg_sz = sizeof(out), }; u32 num_agents, num_protocols; + int size; u8 *buf; int i, ret;
@@ -276,29 +275,34 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev, if (ret) return ret;
- buf = calloc(sizeof(u8), num_protocols); - if (!buf) + size = sizeof(*out) + sizeof(u8) * num_protocols; + out = calloc(1, size); + if (!out) return -ENOMEM;
+ msg.out_msg = (u8 *)out; + msg.out_msg_sz = size; + buf = (u8 *)out->protocols; + cur = 0; do { ret = devm_scmi_process_msg(dev, &msg); if (ret) goto err; - if (out.status) { - ret = scmi_to_linux_errno(out.status); + if (out->status) { + ret = scmi_to_linux_errno(out->status); goto err; }
- for (i = 0; i < out.num_protocols; i++, cur++) - buf[cur] = out.protocols[i / 4] >> ((i % 4) * 8); + for (i = 0; i < out->num_protocols; i++, cur++) + buf[cur] = out->protocols[i / 4] >> ((i % 4) * 8); } while (cur < num_protocols);
*protocols = buf;
return num_protocols; err: - free(buf); + free(out);
return ret; } diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index a51234eda5c..29a7fd85cc0 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -148,7 +148,7 @@ struct scmi_base_discover_impl_version_out { struct scmi_base_discover_list_protocols_out { s32 status; u32 num_protocols; - u32 protocols[4]; + u32 protocols[]; };
/**

From: Peng Fan peng.fan@nxp.com
This patch adds the macro SCMI_MSG for protocols that do not need _in_array.
Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com --- include/scmi_agent.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/scmi_agent.h b/include/scmi_agent.h index 755986d6c424ccc881ac0943a88735cbcfacb001..5508a126a6dc504afa6d896c78c760b1af0d3deb 100644 --- a/include/scmi_agent.h +++ b/include/scmi_agent.h @@ -54,6 +54,17 @@ struct scmi_msg { .out_msg_sz = sizeof(_out_array), \ }
+/* Helper macro to match a message on output array references */ +#define SCMI_MSG(_protocol, _message, _out_array) \ + (struct scmi_msg){ \ + .protocol_id = (_protocol), \ + .message_id = (_message), \ + .in_msg = (uint8_t *)NULL, \ + .in_msg_sz = 0, \ + .out_msg = (uint8_t *)&(_out_array), \ + .out_msg_sz = sizeof(_out_array), \ + } + /** * devm_scmi_of_get_channel() - Get SCMI channel handle from SCMI agent DT node *

On 1/15/25 2:28 PM, Alice Guo wrote:
From: Peng Fan peng.fan@nxp.com
This patch adds the macro SCMI_MSG for protocols that do not need _in_array.
Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@nxp.com
include/scmi_agent.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/scmi_agent.h b/include/scmi_agent.h index 755986d6c424ccc881ac0943a88735cbcfacb001..5508a126a6dc504afa6d896c78c760b1af0d3deb 100644 --- a/include/scmi_agent.h +++ b/include/scmi_agent.h @@ -54,6 +54,17 @@ struct scmi_msg { .out_msg_sz = sizeof(_out_array), \ }
+/* Helper macro to match a message on output array references */ +#define SCMI_MSG(_protocol, _message, _out_array) \
- (struct scmi_msg){ \
.protocol_id = (_protocol), \
.message_id = (_message), \
.in_msg = (uint8_t *)NULL, \
Is the cast needed ?
.in_msg_sz = 0, \
Is the explicit assignment of in_msg/in_msg_sz even needed at all ?
.out_msg = (uint8_t *)&(_out_array), \
.out_msg_sz = sizeof(_out_array), \
- }
[...]

From: Peng Fan peng.fan@nxp.com
This patch adds i.MX95 SoC and clock related code. Because they are based on SCMI, put them in the scmi subfolder.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- arch/arm/include/asm/arch-imx/cpu.h | 2 + arch/arm/include/asm/arch-imx9/clock.h | 10 + arch/arm/include/asm/arch-imx9/imx-regs.h | 5 + arch/arm/include/asm/arch-imx9/sys_proto.h | 1 + arch/arm/include/asm/mach-imx/sys_proto.h | 39 ++ arch/arm/mach-imx/imx9/scmi/Makefile | 6 + arch/arm/mach-imx/imx9/scmi/clock.c | 105 ++++ arch/arm/mach-imx/imx9/scmi/clock_scmi.c | 135 +++++ arch/arm/mach-imx/imx9/scmi/soc.c | 809 +++++++++++++++++++++++++++++ arch/sandbox/include/asm/scmi_test.h | 2 +- 10 files changed, 1113 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/arch-imx/cpu.h b/arch/arm/include/asm/arch-imx/cpu.h index 0d7a573461690046b27498c7174459110a73689c..1f669c72d00833f07ca7a2563ae24536af9dc418 100644 --- a/arch/arm/include/asm/arch-imx/cpu.h +++ b/arch/arm/include/asm/arch-imx/cpu.h @@ -76,6 +76,8 @@ #define MXC_CPU_IMX9111 0xCD /* dummy ID */ #define MXC_CPU_IMX9101 0xCE /* dummy ID */
+#define MXC_CPU_IMX95 0x1C1 /* dummy ID */ + #define MXC_SOC_MX6 0x60 #define MXC_SOC_MX7 0x70 #define MXC_SOC_IMX8M 0x80 diff --git a/arch/arm/include/asm/arch-imx9/clock.h b/arch/arm/include/asm/arch-imx9/clock.h index 60d48b13b11f274c9e4c8caf20954de0431d9d6a..ffaf6b5f7d81d73af1702dff64cf9b15ea521ad9 100644 --- a/arch/arm/include/asm/arch-imx9/clock.h +++ b/arch/arm/include/asm/arch-imx9/clock.h @@ -255,5 +255,15 @@ int ccm_shared_gpr_tz_access(u32 gpr, bool non_secure, bool user_mode, bool lock void enable_usboh3_clk(unsigned char enable); int set_clk_enet(enum enet_freq type); int set_clk_eqos(enum enet_freq type); + +int imx_clk_scmi_enable(u32 clock_id, bool enable); +ulong imx_clk_scmi_set_rate(u32 clock_id, ulong rate); +ulong imx_clk_scmi_get_rate(u32 clock_id); +int imx_clk_scmi_set_parent(u32 clock_id, u32 parent_id); void set_arm_clk(ulong freq); + +int imx_clk_scmi_enable(u32 clock_id, bool enable); +ulong imx_clk_scmi_set_rate(u32 clock_id, ulong rate); +ulong imx_clk_scmi_get_rate(u32 clock_id); +int imx_clk_scmi_set_parent(u32 clock_id, u32 parent_id); #endif diff --git a/arch/arm/include/asm/arch-imx9/imx-regs.h b/arch/arm/include/asm/arch-imx9/imx-regs.h index ef9538bd42e8b2972d7e6829a402bc4f661e3cbb..f7f9c706e13e63df7ff0a90b3964e04bc15959bf 100644 --- a/arch/arm/include/asm/arch-imx9/imx-regs.h +++ b/arch/arm/include/asm/arch-imx9/imx-regs.h @@ -20,6 +20,11 @@ #define WDG4_BASE_ADDR 0x424a0000UL #define WDG5_BASE_ADDR 0x424b0000UL
+#define GPIO2_BASE_ADDR 0x43810000UL +#define GPIO3_BASE_ADDR 0x43820000UL +#define GPIO4_BASE_ADDR 0x43840000UL +#define GPIO5_BASE_ADDR 0x43850000UL + #define FSB_BASE_ADDR 0x47510000UL
#define ANATOP_BASE_ADDR 0x44480000UL diff --git a/arch/arm/include/asm/arch-imx9/sys_proto.h b/arch/arm/include/asm/arch-imx9/sys_proto.h index e4bf6a63424f7c54c1dcdf6b882a908940e5fdb1..df2148a53c7e43fcacad0798090df23e24347dbe 100644 --- a/arch/arm/include/asm/arch-imx9/sys_proto.h +++ b/arch/arm/include/asm/arch-imx9/sys_proto.h @@ -12,6 +12,7 @@ enum imx9_soc_voltage_mode { VOLT_LOW_DRIVE = 0, VOLT_NOMINAL_DRIVE, VOLT_OVER_DRIVE, + VOLT_SUPER_OVER_DRIVE, };
void soc_power_init(void); diff --git a/arch/arm/include/asm/mach-imx/sys_proto.h b/arch/arm/include/asm/mach-imx/sys_proto.h index 109a806852ab42d018ce45a4e96af5b57adb6a9c..0780f99b49a7768d77ed688157e3287195c857b1 100644 --- a/arch/arm/include/asm/mach-imx/sys_proto.h +++ b/arch/arm/include/asm/mach-imx/sys_proto.h @@ -97,6 +97,8 @@ struct bd_info; #define is_imx9302() (is_cpu_type(MXC_CPU_IMX9302)) #define is_imx9301() (is_cpu_type(MXC_CPU_IMX9301))
+#define is_imx95() (is_cpu_type(MXC_CPU_IMX95)) + #define is_imx9121() (is_cpu_type(MXC_CPU_IMX9121)) #define is_imx9111() (is_cpu_type(MXC_CPU_IMX9111)) #define is_imx9101() (is_cpu_type(MXC_CPU_IMX9101)) @@ -216,6 +218,43 @@ ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev); u32 rom_api_download_image(u8 *dest, u32 offset, u32 size); u32 rom_api_query_boot_infor(u32 info_type, u32 *info);
+#if IS_ENABLED(CONFIG_SCMI_FIRMWARE) +typedef struct rom_passover { + u16 tag; // Tag + u8 len; // Fixed value of 0x80 + u8 ver; // Version + u32 boot_mode; // Boot mode + u32 card_addr_mode; // SD card address mode + u32 bad_blks_of_img_set0; // NAND bad block count skipped 1 + u32 ap_mu_id; // AP MU ID + u32 bad_blks_of_img_set1; // NAND bad block count skipped 1 + u8 boot_stage; // Boot stage + u8 img_set_sel; // Image set booted from + u8 rsv0[2]; // Reserved + u32 img_set_end; // Offset of Image End + u32 rom_version; // ROM version + u8 boot_dev_state; // Boot device state + u8 boot_dev_inst; // Boot device type + u8 boot_dev_type; // Boot device instance + u8 rsv1; // Reserved + u32 dev_page_size; // Boot device page size + u32 cnt_header_ofs; // Container header offset + u32 img_ofs; // Image offset +} __packed rom_passover_t; + +/** + * struct scmi_rom_passover_out - Response payload for ROM_PASSOVER_GET command + * @status: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_rom_passover_get_out { + u32 status; + u32 numPassover; + u32 passover[(sizeof(rom_passover_t) + 8) / 4]; +}; + +#endif + /* For i.MX ULP */ #define BT0CFG_LPBOOT_MASK 0x1 #define BT0CFG_DUALBOOT_MASK 0x2 diff --git a/arch/arm/mach-imx/imx9/scmi/Makefile b/arch/arm/mach-imx/imx9/scmi/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4534db08d287899af5bb26283e4d20b942b421e4 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2025 NXP + +obj-y += soc.o +obj-y += clock_scmi.o clock.o diff --git a/arch/arm/mach-imx/imx9/scmi/clock.c b/arch/arm/mach-imx/imx9/scmi/clock.c new file mode 100644 index 0000000000000000000000000000000000000000..9ebd38097615351304dcc39a93652ab2ce046e93 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/clock.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/arch/clock.h> +#include <dm/uclass.h> +#include <scmi_agent.h> +#include "../../../../../dts/upstream/src/arm64/freescale/imx95-clock.h" + +u32 get_arm_core_clk(void) +{ + u32 val; + + val = imx_clk_scmi_get_rate(IMX95_CLK_SEL_A55C0); + if (val) + return val; + return imx_clk_scmi_get_rate(IMX95_CLK_A55); +} + +void set_arm_core_max_clk(void) +{ + int ret; + u32 arm_domain_id = 8; + + struct scmi_perf_in in = { + .domain_id = arm_domain_id, + .perf_level = 3, + }; + struct scmi_perf_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PERF, SCMI_PERF_LEVEL_SET, in, out); + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + printf("%s: %d\n", __func__, ret); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + printf("%s: %d\n", __func__, ret); +} + +int clock_init_early(void) +{ + return 0; +} + +/* Set bus and A55 core clock per voltage mode */ +int clock_init_late(void) +{ + set_arm_core_max_clk(); + + return 0; +} + +void init_uart_clk(u32 index) +{ + u32 clock_id; + + switch (index) { + case 0: + clock_id = IMX95_CLK_LPUART1; + break; + case 1: + clock_id = IMX95_CLK_LPUART2; + break; + case 2: + clock_id = IMX95_CLK_LPUART3; + break; + default: + return; + } + + /* 24MHz */ + imx_clk_scmi_enable(clock_id, false); + imx_clk_scmi_set_parent(clock_id, IMX95_CLK_24M); + imx_clk_scmi_set_rate(clock_id, 24000000); + imx_clk_scmi_enable(clock_id, true); +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return get_arm_core_clk(); + case MXC_IPG_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_BUSWAKEUP); + case MXC_CSPI_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_LPSPI1); + case MXC_ESDHC_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_USDHC1); + case MXC_ESDHC2_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_USDHC2); + case MXC_ESDHC3_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_USDHC3); + case MXC_UART_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_LPUART1); + case MXC_FLEXSPI_CLK: + return imx_clk_scmi_get_rate(IMX95_CLK_FLEXSPI1); + default: + return -1; + }; + + return -1; +}; diff --git a/arch/arm/mach-imx/imx9/scmi/clock_scmi.c b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c new file mode 100644 index 0000000000000000000000000000000000000000..23ffe0eed695b67977d441f7ee8830af9c4aabfe --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + * + * Peng Fan peng.fan@nxp.com + */ + +#include <dm/uclass.h> +#include <scmi_agent.h> + +DECLARE_GLOBAL_DATA_PTR; + +int imx_clk_scmi_enable(u32 clock_id, bool enable) +{ + struct scmi_clk_state_in in = { + .clock_id = clock_id, + .attributes = (enable) ? 1 : 0, + }; + struct scmi_clk_state_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_CONFIG_SET, + in, out); + int ret; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + return ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +ulong imx_clk_scmi_set_rate(u32 clock_id, ulong rate) +{ + struct scmi_clk_rate_set_in in = { + .clock_id = clock_id, + .flags = SCMI_CLK_RATE_ROUND_CLOSEST, + .rate_lsb = (u32)rate, + .rate_msb = (u32)((u64)rate >> 32), + }; + struct scmi_clk_rate_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_SET, + in, out); + int ret; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + return ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + struct scmi_clk_rate_get_in in_rate = { + .clock_id = clock_id, + }; + struct scmi_clk_rate_get_out out_rate; + + msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, SCMI_CLOCK_RATE_GET, in_rate, out_rate); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out_rate.status); + if (ret < 0) + return ret; + + return (ulong)(((u64)out_rate.rate_msb << 32) | out_rate.rate_lsb); +} + +ulong imx_clk_scmi_get_rate(u32 clock_id) +{ + struct scmi_clk_rate_get_in in = { + .clock_id = clock_id, + }; + struct scmi_clk_rate_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_GET, + in, out); + int ret; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + return ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); +} + +int imx_clk_scmi_set_parent(u32 clock_id, u32 parent_id) +{ + struct scmi_clk_parent_set_in in = { + .clock_id = clock_id, + .parent_clk = parent_id, + }; + struct scmi_clk_parent_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_PARENT_SET, + in, out); + int ret; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + return ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0 && ret != -EACCES) + printf("%s: %d, clock_id %u\n", __func__, ret, clock_id); + + return ret; +} diff --git a/arch/arm/mach-imx/imx9/scmi/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c new file mode 100644 index 0000000000000000000000000000000000000000..f1683b0484b84bf18b8612abc4daacf2f69d5e0b --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/soc.c @@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + * + * Peng Fan peng.fan@nxp.com + */ + +#include <asm/arch/clock.h> +#include <asm/arch/ddr.h> +#include <asm/arch/sys_proto.h> +#include <asm/armv8/mmu.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/mach-imx/ele_api.h> +#include <asm/setup.h> +#include <dm/uclass.h> +#include <dm/device.h> +#include <env_internal.h> +#include <fuse.h> +#include <imx_thermal.h> +#include <scmi_agent.h> + +DECLARE_GLOBAL_DATA_PTR; + +static rom_passover_t rom_passover_data = {0}; + +uint32_t scmi_get_rom_data(rom_passover_t *rom_data) +{ + /* Read ROM passover data */ + struct scmi_rom_passover_get_out out; + struct scmi_msg msg = SCMI_MSG(SCMI_PROTOCOL_ID_IMX_MISC, SCMI_MISC_ROM_PASSOVER_GET, out); + int ret; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + return ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret == 0 && out.status == 0) { + memcpy(rom_data, (struct rom_passover_t *)out.passover, sizeof(rom_passover_t)); + } else { + printf("Failed to get ROM passover data, scmi_err = %d, size_of(out) = %ld\n", + out.status, sizeof(out)); + return -EINVAL; + } + + return 0; +} + +#if IS_ENABLED(CONFIG_ENV_IS_IN_MMC) +__weak int board_mmc_get_env_dev(int devno) +{ + return devno; +} + +int mmc_get_env_dev(void) +{ + int ret; + u16 boot_type; + u8 boot_instance; + + volatile gd_t *pgd = gd; + rom_passover_t *rdata; +#if IS_ENABLED(CONFIG_XPL_BUILD) + rdata = &rom_passover_data; +#else + rom_passover_t rom_data = {0}; + + if (!pgd->reloc_off) + rdata = &rom_data; + else + rdata = &rom_passover_data; +#endif + if (rdata->tag == 0) { + ret = scmi_get_rom_data(rdata); + if (ret != 0) { + puts("SCMI: failure at rom_boot_info\n"); + return CONFIG_SYS_MMC_ENV_DEV; + } + } + boot_type = rdata->boot_dev_type; + boot_instance = rdata->boot_dev_inst; + set_gd(pgd); + + debug("boot_type %d, instance %d\n", boot_type, boot_instance); + + /* If not boot from sd/mmc, use default value */ + if (boot_type != BOOT_TYPE_SD && boot_type != BOOT_TYPE_MMC) + return env_get_ulong("mmcdev", 10, CONFIG_SYS_MMC_ENV_DEV); + + return board_mmc_get_env_dev(boot_instance); +} +#endif + +u32 get_cpu_speed_grade_hz(void) +{ + u32 speed, max_speed; + int ret; + u32 val, word, offset; + + word = 17; + offset = 14; + + ret = fuse_read(word / 8, word % 8, &val); + if (ret) + val = 0; /* If read fuse failed, return as blank fuse */ + + val >>= offset; + val &= 0xf; + + max_speed = 2300000000; + speed = max_speed - val * 100000000; + + if (is_imx95()) + max_speed = 2000000000; + + /* In case the fuse of speed grade not programmed */ + if (speed > max_speed) + speed = max_speed; + + return speed; +} + +u32 get_cpu_temp_grade(int *minc, int *maxc) +{ + int ret; + u32 val, word, offset; + + word = 17; + offset = 12; + + ret = fuse_read(word / 8, word % 8, &val); + if (ret) + val = 0; /* If read fuse failed, return as blank fuse */ + + val >>= offset; + val &= 0x3; + + if (minc && maxc) { + if (val == TEMP_AUTOMOTIVE) { + *minc = -40; + *maxc = 125; + } else if (val == TEMP_INDUSTRIAL) { + *minc = -40; + *maxc = 105; + } else if (val == TEMP_EXTCOMMERCIAL) { + *minc = -20; + *maxc = 105; + } else { + *minc = 0; + *maxc = 95; + } + } + return val; +} + +static void set_cpu_info(struct ele_get_info_data *info) +{ + gd->arch.soc_rev = info->soc; + gd->arch.lifecycle = info->lc; + memcpy((void *)&gd->arch.uid, &info->uid, 4 * sizeof(u32)); +} + +u32 get_cpu_rev(void) +{ + u32 rev = (gd->arch.soc_rev >> 24) - 0xa0; + + return (MXC_CPU_IMX95 << 12) | (CHIP_REV_1_0 + rev); +} + +#define UNLOCK_WORD 0xD928C520 /* unlock word */ +#define REFRESH_WORD 0xB480A602 /* refresh word */ + +static void disable_wdog(void __iomem *wdog_base) +{ + u32 val_cs = readl(wdog_base + 0x00); + + if (!(val_cs & 0x80)) + return; + + /* default is 32bits cmd */ + writel(REFRESH_WORD, (wdog_base + 0x04)); /* Refresh the CNT */ + + if (!(val_cs & 0x800)) { + writel(UNLOCK_WORD, (wdog_base + 0x04)); + while (!(readl(wdog_base + 0x00) & 0x800)) + ; + } + writel(0x0, (wdog_base + 0x0C)); /* Set WIN to 0 */ + writel(0x400, (wdog_base + 0x08)); /* Set timeout to default 0x400 */ + writel(0x2120, (wdog_base + 0x00)); /* Disable it and set update */ + + while (!(readl(wdog_base + 0x00) & 0x400)) + ; +} + +static struct mm_region imx9_mem_map[] = { + { + /* ROM */ + .virt = 0x0UL, + .phys = 0x0UL, + .size = 0x100000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { + /* TCM */ + .virt = 0x201c0000UL, + .phys = 0x201c0000UL, + .size = 0x80000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* OCRAM */ + .virt = 0x20480000UL, + .phys = 0x20480000UL, + .size = 0xA0000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { + /* AIPS */ + .virt = 0x40000000UL, + .phys = 0x40000000UL, + .size = 0x40000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* Flexible Serial Peripheral Interface */ + .virt = 0x28000000UL, + .phys = 0x28000000UL, + .size = 0x8000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* DRAM1 */ + .virt = PHYS_SDRAM, + .phys = PHYS_SDRAM, + .size = PHYS_SDRAM_SIZE, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { +#ifdef PHYS_SDRAM_2_SIZE + /* DRAM2 */ + .virt = 0x100000000UL, + .phys = 0x100000000UL, + .size = PHYS_SDRAM_2_SIZE, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_OUTER_SHARE + }, { +#endif + /* empty entry to split table entry 5 if needed when TEEs are used */ + 0, + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = imx9_mem_map; + +static unsigned int imx9_find_dram_entry_in_mem_map(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(imx9_mem_map); i++) + if (imx9_mem_map[i].phys == CFG_SYS_SDRAM_BASE) + return i; + + hang(); /* Entry not found, this must never happen. */ +} + +void enable_caches(void) +{ + /* If OPTEE runs, remove OPTEE memory from MMU table to avoid speculative prefetch + * If OPTEE does not run, still update the MMU table according to dram banks structure + * to set correct dram size from board_phys_sdram_size + */ + int i = 0; + /* + * please make sure that entry initial value matches + * imx9_mem_map for DRAM1 + */ + int entry = imx9_find_dram_entry_in_mem_map(); + u64 attrs = imx9_mem_map[entry].attrs; + + while (i < CONFIG_NR_DRAM_BANKS && + entry < ARRAY_SIZE(imx9_mem_map)) { + if (gd->bd->bi_dram[i].start == 0) + break; + imx9_mem_map[entry].phys = gd->bd->bi_dram[i].start; + imx9_mem_map[entry].virt = gd->bd->bi_dram[i].start; + imx9_mem_map[entry].size = gd->bd->bi_dram[i].size; + imx9_mem_map[entry].attrs = attrs; + debug("Added memory mapping (%d): %llx %llx\n", entry, + imx9_mem_map[entry].phys, imx9_mem_map[entry].size); + i++; entry++; + } + + icache_enable(); + dcache_enable(); +} + +__weak int board_phys_sdram_size(phys_size_t *size) +{ + phys_size_t start, end; + phys_size_t val; + + if (!size) + return -EINVAL; + + val = readl(REG_DDR_CS0_BNDS); + start = (val >> 16) << 24; + end = (val & 0xFFFF); + end = end ? end + 1 : 0; + end = end << 24; + *size = end - start; + + val = readl(REG_DDR_CS1_BNDS); + start = (val >> 16) << 24; + end = (val & 0xFFFF); + end = end ? end + 1 : 0; + end = end << 24; + *size += end - start; + + return 0; +} + +int dram_init(void) +{ + phys_size_t sdram_size; + int ret; + + ret = board_phys_sdram_size(&sdram_size); + if (ret) + return ret; + + /* rom_pointer[1] contains the size of TEE occupies */ + if (rom_pointer[1]) + gd->ram_size = sdram_size - rom_pointer[1]; + else + gd->ram_size = sdram_size; + + return 0; +} + +int dram_init_banksize(void) +{ + int bank = 0; + int ret; + phys_size_t sdram_size; + phys_size_t sdram_b1_size, sdram_b2_size; + + ret = board_phys_sdram_size(&sdram_size); + if (ret) + return ret; + + /* Bank 1 can't cross over 4GB space */ + if (sdram_size > 0x80000000) { + sdram_b1_size = 0x100000000UL - PHYS_SDRAM; + sdram_b2_size = sdram_size - sdram_b1_size; + } else { + sdram_b1_size = sdram_size; + sdram_b2_size = 0; + } + + gd->bd->bi_dram[bank].start = PHYS_SDRAM; + if (rom_pointer[1]) { + phys_addr_t optee_start = (phys_addr_t)rom_pointer[0]; + phys_size_t optee_size = (size_t)rom_pointer[1]; + + gd->bd->bi_dram[bank].size = optee_start - gd->bd->bi_dram[bank].start; + if ((optee_start + optee_size) < (PHYS_SDRAM + sdram_b1_size)) { + if (++bank >= CONFIG_NR_DRAM_BANKS) { + puts("CONFIG_NR_DRAM_BANKS is not enough\n"); + return -1; + } + + gd->bd->bi_dram[bank].start = optee_start + optee_size; + gd->bd->bi_dram[bank].size = PHYS_SDRAM + + sdram_b1_size - gd->bd->bi_dram[bank].start; + } + } else { + gd->bd->bi_dram[bank].size = sdram_b1_size; + } + + if (sdram_b2_size) { + if (++bank >= CONFIG_NR_DRAM_BANKS) { + puts("CONFIG_NR_DRAM_BANKS is not enough for SDRAM_2\n"); + return -1; + } + gd->bd->bi_dram[bank].start = 0x100000000UL; + gd->bd->bi_dram[bank].size = sdram_b2_size; + } + + return 0; +} + +phys_size_t get_effective_memsize(void) +{ + int ret; + phys_size_t sdram_size; + phys_size_t sdram_b1_size; + + ret = board_phys_sdram_size(&sdram_size); + if (!ret) { + /* Bank 1 can't cross over 4GB space */ + if (sdram_size > 0x80000000) { + sdram_b1_size = 0x100000000UL - PHYS_SDRAM; + } else { + sdram_b1_size = sdram_size; + } + + if (rom_pointer[1]) { + /* We will relocate u-boot to Top of dram1. Tee position has two cases: + * 1. At the top of dram1, Then return the size removed optee size. + * 2. In the middle of dram1, return the size of dram1. + */ + if ((rom_pointer[0] + rom_pointer[1]) == (PHYS_SDRAM + sdram_b1_size)) + return ((phys_addr_t)rom_pointer[0] - PHYS_SDRAM); + } + + return sdram_b1_size; + } else { + return PHYS_SDRAM_SIZE; + } +} + +void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) +{ + u32 val[2] = {}; + int ret; + + if (dev_id == 0) { + ret = fuse_read(39, 3, &val[0]); + if (ret) + goto err; + + ret = fuse_read(39, 4, &val[1]); + if (ret) + goto err; + + mac[0] = val[1] >> 8; + mac[1] = val[1]; + mac[2] = val[0] >> 24; + mac[3] = val[0] >> 16; + mac[4] = val[0] >> 8; + mac[5] = val[0]; + + } else { + ret = fuse_read(39, 5, &val[0]); + if (ret) + goto err; + + ret = fuse_read(39, 4, &val[1]); + if (ret) + goto err; + + if (is_soc_rev(CHIP_REV_1_0)) { + mac[0] = val[1] >> 24; + mac[1] = val[1] >> 16; + mac[2] = val[0] >> 24; + mac[3] = val[0] >> 16; + mac[4] = val[0] >> 8; + mac[5] = val[0]; + } else { + mac[0] = val[0] >> 24; + mac[1] = val[0] >> 16; + mac[2] = val[0] >> 8; + mac[3] = val[0]; + mac[4] = val[1] >> 24; + mac[5] = val[1] >> 16; + } + } + + debug("%s: MAC%d: %02x.%02x.%02x.%02x.%02x.%02x\n", + __func__, dev_id, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return; +err: + memset(mac, 0, 6); + printf("%s: fuse read err: %d\n", __func__, ret); +} + +const char *get_imx_type(u32 imxtype) +{ + switch (imxtype) { + case MXC_CPU_IMX95: + return "95";/* iMX95 FULL */ + default: + return "??"; + } +} + +int print_cpuinfo(void) +{ + u32 cpurev, max_freq; + int minc, maxc; + + cpurev = get_cpu_rev(); + + printf("CPU: i.MX%s rev%d.%d", + get_imx_type((cpurev & 0x1FF000) >> 12), + (cpurev & 0x000F0) >> 4, (cpurev & 0x0000F) >> 0); + + max_freq = get_cpu_speed_grade_hz(); + if (!max_freq || max_freq == mxc_get_clock(MXC_ARM_CLK)) { + printf(" at %dMHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000); + } else { + printf(" %d MHz (running at %d MHz)\n", max_freq / 1000000, + mxc_get_clock(MXC_ARM_CLK) / 1000000); + } + + puts("CPU: "); + switch (get_cpu_temp_grade(&minc, &maxc)) { + case TEMP_AUTOMOTIVE: + puts("Automotive temperature grade "); + break; + case TEMP_INDUSTRIAL: + puts("Industrial temperature grade "); + break; + case TEMP_EXTCOMMERCIAL: + if (is_imx93()) + puts("Extended Industrial temperature grade "); + else + puts("Extended Consumer temperature grade "); + break; + default: + puts("Consumer temperature grade "); + break; + } + printf("(%dC to %dC)", minc, maxc); + +#if defined(CONFIG_DM_THERMAL) + struct udevice *udev; + int ret, temp; + + if (IS_ENABLED(CONFIG_IMX_TMU)) + ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal", &udev); + else + ret = uclass_get_device(UCLASS_THERMAL, 0, &udev); + if (!ret) { + ret = thermal_get_temp(udev, &temp); + + if (!ret) + printf(" at %dC", temp); + else + debug(" - invalid sensor data\n"); + } else { + debug(" - invalid sensor device\n"); + } +#endif + puts("\n"); + + return 0; +} + +void build_info(void) +{ + u32 fw_version, sha1, res, status; + int ret; + + printf("\nBuildInfo:\n"); + + ret = ele_get_fw_status(&status, &res); + if (ret) { + printf(" - ELE firmware status failed %d, 0x%x\n", ret, res); + } else if ((status & 0xff) == 1) { + ret = ele_get_fw_version(&fw_version, &sha1, &res); + if (ret) { + printf(" - ELE firmware version failed %d, 0x%x\n", ret, res); + } else { + printf(" - ELE firmware version %u.%u.%u-%x", + (fw_version & (0x00ff0000)) >> 16, + (fw_version & (0x0000ff00)) >> 8, + (fw_version & (0x000000ff)), sha1); + ((fw_version & (0x80000000)) >> 31) == 1 ? puts("-dirty\n") : puts("\n"); + } + } else { + printf(" - ELE firmware not included\n"); + } + puts("\n"); +} + +int arch_misc_init(void) +{ + build_info(); + return 0; +} + +#ifdef CONFIG_OF_BOARD_FIXUP +#ifndef CONFIG_SPL_BUILD +int board_fix_fdt(void *fdt) +{ + return 0; +} +#endif +#endif + +int ft_system_setup(void *blob, struct bd_info *bd) +{ + return 0; +} + +#if defined(CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG) +void get_board_serial(struct tag_serialnr *serialnr) +{ + printf("UID: 0x%x 0x%x 0x%x 0x%x\n", + gd->arch.uid[0], gd->arch.uid[1], gd->arch.uid[2], gd->arch.uid[3]); + + serialnr->low = gd->arch.uid[0]; + serialnr->high = gd->arch.uid[3]; +} +#endif + +static void gpio_reset(ulong gpio_base) +{ + writel(0, gpio_base + 0x10); + writel(0, gpio_base + 0x14); + writel(0, gpio_base + 0x18); + writel(0, gpio_base + 0x1c); +} + +int arch_cpu_init(void) +{ + if (IS_ENABLED(CONFIG_SPL_BUILD)) { + disable_wdog((void __iomem *)WDG3_BASE_ADDR); + disable_wdog((void __iomem *)WDG4_BASE_ADDR); + + clock_init_early(); + + gpio_reset(GPIO2_BASE_ADDR); + gpio_reset(GPIO3_BASE_ADDR); + gpio_reset(GPIO4_BASE_ADDR); + gpio_reset(GPIO5_BASE_ADDR); + } + + return 0; +} + +int imx9_probe_mu(void) +{ + struct udevice *dev; + int node, ret; + u32 res; + struct ele_get_info_data info; + + ret = uclass_get_device_by_driver(UCLASS_SCMI_AGENT, DM_DRIVER_GET(scmi_mbox), &dev); + if (ret) + return ret; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + return ret; + + ret = devm_scmi_of_get_channel(dev); + if (ret) + return ret; + + ret = uclass_get_device_by_name(UCLASS_PINCTRL, "protocol@19", &dev); + if (ret) + return ret; + + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "fsl,imx93-mu-s4"); + + ret = uclass_get_device_by_of_offset(UCLASS_MISC, node, &dev); + if (ret) + return ret; + + if (gd->flags & GD_FLG_RELOC) + return 0; + + ret = ele_get_info(&info, &res); + if (ret) + return ret; + + set_cpu_info(&info); + + return 0; +} + +EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_F, imx9_probe_mu); +EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_R, imx9_probe_mu); + +int timer_init(void) +{ + gd->arch.tbl = 0; + gd->arch.tbu = 0; + +#ifdef CONFIG_SPL_BUILD + unsigned long freq = 24000000; + + asm volatile("msr cntfrq_el0, %0" : : "r" (freq) : "memory"); +#endif + + return 0; +} + +enum env_location env_get_location(enum env_operation op, int prio) +{ + enum boot_device dev = get_boot_device(); + enum env_location env_loc = ENVL_UNKNOWN; + + if (prio) + return env_loc; + + switch (dev) { +#if defined(CONFIG_ENV_IS_IN_SPI_FLASH) + case QSPI_BOOT: + env_loc = ENVL_SPI_FLASH; + break; +#endif +#if defined(CONFIG_ENV_IS_IN_MMC) + case SD1_BOOT: + case SD2_BOOT: + case SD3_BOOT: + case MMC1_BOOT: + case MMC2_BOOT: + case MMC3_BOOT: + env_loc = ENVL_MMC; + break; +#endif + default: +#if defined(CONFIG_ENV_IS_NOWHERE) + env_loc = ENVL_NOWHERE; +#endif + break; + } + + return env_loc; +} + +enum imx9_soc_voltage_mode soc_target_voltage_mode(void) +{ + u32 speed = get_cpu_speed_grade_hz(); + enum imx9_soc_voltage_mode voltage = VOLT_OVER_DRIVE; + + if (is_imx95()) { + if (speed == 2000000000) + voltage = VOLT_SUPER_OVER_DRIVE; + else if (speed == 1800000000) + voltage = VOLT_OVER_DRIVE; + else if (speed == 1400000000) + voltage = VOLT_NOMINAL_DRIVE; + else /* boot not support low drive mode according to AS */ + printf("Unexpected A55 freq %u, default to OD\n", speed); + } + + return voltage; +} + +#if IS_ENABLED(CONFIG_SCMI_FIRMWARE) +enum boot_device get_boot_device(void) +{ + volatile gd_t *pgd = gd; + int ret; + u16 boot_type; + u8 boot_instance; + enum boot_device boot_dev = 0; + rom_passover_t *rdata; + +#ifdef CONFIG_SPL_BUILD + rdata = &rom_passover_data; +#else + rom_passover_t rom_data = {0}; + + if (pgd->reloc_off == 0) + rdata = &rom_data; + else + rdata = &rom_passover_data; +#endif + if (rdata->tag == 0) { + ret = scmi_get_rom_data(rdata); + if (ret != 0) { + puts("SCMI: failure at rom_boot_info\n"); + return -1; + } + } + boot_type = rdata->boot_dev_type; + boot_instance = rdata->boot_dev_inst; + + set_gd(pgd); + + switch (boot_type) { + case BT_DEV_TYPE_SD: + boot_dev = boot_instance + SD1_BOOT; + break; + case BT_DEV_TYPE_MMC: + boot_dev = boot_instance + MMC1_BOOT; + break; + case BT_DEV_TYPE_NAND: + boot_dev = NAND_BOOT; + break; + case BT_DEV_TYPE_FLEXSPINOR: + boot_dev = QSPI_BOOT; + break; + case BT_DEV_TYPE_USB: + boot_dev = boot_instance + USB_BOOT; +#ifdef CONFIG_IMX95 + boot_dev -= 3; //iMX95 usb instance start at 3 +#endif + break; + default: + break; + } + + return boot_dev; +} +#endif diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h index b30e536a35771ec3e1439c1e64fd09de3728dc50..c9717118bcb90e7f26285ead30cff8612ed42956 100644 --- a/arch/sandbox/include/asm/scmi_test.h +++ b/arch/sandbox/include/asm/scmi_test.h @@ -110,7 +110,7 @@ struct sandbox_scmi_devices { size_t regul_count; };
-#ifdef CONFIG_SCMI_FIRMWARE +#if IS_ENABLED(CONFIG_SCMI_FIRMWARE) /** * sandbox_scmi_channel_id - Get the channel id * @dev: Reference to the SCMI protocol device

On 1/15/25 2:28 PM, Alice Guo wrote:
+void init_uart_clk(u32 index) +{
- u32 clock_id;
- switch (index) {
- case 0:
clock_id = IMX95_CLK_LPUART1;
break;
- case 1:
clock_id = IMX95_CLK_LPUART2;
break;
- case 2:
clock_id = IMX95_CLK_LPUART3;
break;
- default:
return;
- }
- /* 24MHz */
- imx_clk_scmi_enable(clock_id, false);
- imx_clk_scmi_set_parent(clock_id, IMX95_CLK_24M);
- imx_clk_scmi_set_rate(clock_id, 24000000);
- imx_clk_scmi_enable(clock_id, true);
+}
Is this ad-hoc clock management necessary even with DM_CLK ?
+unsigned int mxc_get_clock(enum mxc_clock clk) +{
- switch (clk) {
- case MXC_ARM_CLK:
return get_arm_core_clk();
- case MXC_IPG_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_BUSWAKEUP);
- case MXC_CSPI_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_LPSPI1);
- case MXC_ESDHC_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_USDHC1);
- case MXC_ESDHC2_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_USDHC2);
- case MXC_ESDHC3_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_USDHC3);
- case MXC_UART_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_LPUART1);
- case MXC_FLEXSPI_CLK:
return imx_clk_scmi_get_rate(IMX95_CLK_FLEXSPI1);
- default:
return -1;
- };
- return -1;
+};
Is this ad-hoc clock management necessary even with DM_CLK ?
diff --git a/arch/arm/mach-imx/imx9/scmi/clock_scmi.c b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c new file mode 100644 index 0000000000000000000000000000000000000000..23ffe0eed695b67977d441f7ee8830af9c4aabfe --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- Peng Fan peng.fan@nxp.com
- */
+#include <dm/uclass.h> +#include <scmi_agent.h>
+DECLARE_GLOBAL_DATA_PTR;
+int imx_clk_scmi_enable(u32 clock_id, bool enable)
Is this ad-hoc clock management necessary even with DM_CLK ?
[...]
+int print_cpuinfo(void) +{
- u32 cpurev, max_freq;
- int minc, maxc;
- cpurev = get_cpu_rev();
- printf("CPU: i.MX%s rev%d.%d",
get_imx_type((cpurev & 0x1FF000) >> 12),
(cpurev & 0x000F0) >> 4, (cpurev & 0x0000F) >> 0);
- max_freq = get_cpu_speed_grade_hz();
- if (!max_freq || max_freq == mxc_get_clock(MXC_ARM_CLK)) {
printf(" at %dMHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000);
- } else {
printf(" %d MHz (running at %d MHz)\n", max_freq / 1000000,
mxc_get_clock(MXC_ARM_CLK) / 1000000);
- }
- puts("CPU: ");
- switch (get_cpu_temp_grade(&minc, &maxc)) {
- case TEMP_AUTOMOTIVE:
puts("Automotive temperature grade ");
break;
- case TEMP_INDUSTRIAL:
puts("Industrial temperature grade ");
break;
- case TEMP_EXTCOMMERCIAL:
if (is_imx93())
puts("Extended Industrial temperature grade ");
else
puts("Extended Consumer temperature grade ");
break;
- default:
puts("Consumer temperature grade ");
break;
- }
- printf("(%dC to %dC)", minc, maxc);
+#if defined(CONFIG_DM_THERMAL)
Use "if (CONFIG_IS_ENABLED(...))" instead of preprocessor "#if ..." where possible so compiler can detect bitrot.
[...]
+int arch_misc_init(void) +{
- build_info();
- return 0;
+}
+#ifdef CONFIG_OF_BOARD_FIXUP +#ifndef CONFIG_SPL_BUILD
Can this ifdeffery be cleaned up ?
+int board_fix_fdt(void *fdt) +{
- return 0;
+} +#endif +#endif
+int ft_system_setup(void *blob, struct bd_info *bd) +{
- return 0;
+}
+#if defined(CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG)
Use if (CONFIG_IS_ENABLED()) or if (IS_ENABLED()) and please fix globally.

From: Ye Li ye.li@nxp.com
When SPL loading image to secure region, for example, ATF and tee to DDR secure region. Because the USDHC controller is non-secure master, it can't access this region and will cause loading issue.
So use a trampoline buffer in non-secure region, then use CPU to copy the image from trampoline buffer to destination secure region.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- arch/arm/mach-imx/imx9/scmi/soc.c | 18 +++++++++++++++++ common/spl/Kconfig | 6 ++++++ common/spl/spl_imx_container.c | 41 +++++++++++++++++++++++++++++++++------ 3 files changed, 59 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-imx/imx9/scmi/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c index f1683b0484b84bf18b8612abc4daacf2f69d5e0b..f3840761e1bb1f5f6b4ebbced26d8a8f41eb4867 100644 --- a/arch/arm/mach-imx/imx9/scmi/soc.c +++ b/arch/arm/mach-imx/imx9/scmi/soc.c @@ -807,3 +807,21 @@ enum boot_device get_boot_device(void) return boot_dev; } #endif + +bool arch_check_dst_in_secure(void *start, ulong size) +{ + ulong ns_end = CFG_SYS_SDRAM_BASE + PHYS_SDRAM_SIZE; +#ifdef PHYS_SDRAM_2_SIZE + ns_end += PHYS_SDRAM_2_SIZE; +#endif + + if ((ulong)start < CFG_SYS_SDRAM_BASE || (ulong)start + size > ns_end) + return true; + + return false; +} + +void *arch_get_container_trampoline(void) +{ + return (void *)((ulong)CFG_SYS_SDRAM_BASE + PHYS_SDRAM_SIZE - SZ_16M); +} diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 4e56d9909c80f8e888034df4a5098d1bb288d9a0..fc8175c853af4b0855e1854f0bb5f9a5002bbbae 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -362,6 +362,12 @@ config SPL_LOAD_IMX_CONTAINER Support booting U-Boot from an i.MX8 container image. If you are not using i.MX8, say 'n'.
+config SPL_IMX_CONTAINER_USE_TRAMPOLINE + bool + depends on SPL + help + Enable SPL load reader to load data to a trampoline buffer. + config IMX_CONTAINER_CFG string "i.MX8 Container config file" depends on SPL && SPL_LOAD_IMX_CONTAINER diff --git a/common/spl/spl_imx_container.c b/common/spl/spl_imx_container.c index 2c31777fcd3af2bc5c738f492a465df280197933..47fb2e65e34829cf3a1a107a5910d73edffe40bd 100644 --- a/common/spl/spl_imx_container.c +++ b/common/spl/spl_imx_container.c @@ -14,6 +14,16 @@ #include <asm/mach-imx/ahab.h> #endif
+__weak bool arch_check_dst_in_secure(void *start, ulong size) +{ + return false; +} + +__weak void *arch_get_container_trampoline(void) +{ + return NULL; +} + static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image, struct spl_load_info *info, struct container_hdr *container, @@ -22,6 +32,7 @@ static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image, { struct boot_img_t *images; ulong offset, overhead, size; + void *buf, *trampoline;
if (image_index > container->num_images) { debug("Invalid image number\n"); @@ -42,12 +53,30 @@ static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image,
debug("%s: container: %p offset: %lu size: %lu\n", __func__, container, offset, size); - if (info->read(info, offset, size, - map_sysmem(images[image_index].dst - overhead, - images[image_index].size)) < - images[image_index].size) { - printf("%s wrong\n", __func__); - return NULL; + + buf = map_sysmem(images[image_index].dst - overhead, images[image_index].size); + if (IS_ENABLED(CONFIG_SPL_IMX_CONTAINER_USE_TRAMPOLINE) && + arch_check_dst_in_secure(buf, size)) { + trampoline = arch_get_container_trampoline(); + if (!trampoline) { + printf("%s: trampoline size is zero\n", __func__); + return NULL; + } + + if (info->read(info, offset, size, trampoline) < images[image_index].size) { + printf("%s wrong\n", __func__); + return NULL; + } + + memcpy(buf, trampoline, images[image_index].size); + } else { + if (info->read(info, offset, size, + map_sysmem(images[image_index].dst - overhead, + images[image_index].size)) < + images[image_index].size) { + printf("%s wrong\n", __func__); + return NULL; + } }
#ifdef CONFIG_AHAB_BOOT

On 1/15/25 2:29 PM, Alice Guo wrote:
From: Ye Li ye.li@nxp.com
When SPL loading image to secure region, for example, ATF and tee to DDR secure region. Because the USDHC controller is non-secure master, it can't access this region and will cause loading issue.
So use a trampoline buffer in non-secure region, then use CPU to copy the image from trampoline buffer to destination secure region.
There is already common/bouncebuf.c , can you instead implement something similar, which would behave as a generic bounce buffer, except for the secure/nonsecure regions ?
The region configuration could come from DT or maybe from firmware ?

From: Ye Li ye.li@nxp.com
This patch adds i.MX95 Kconfig and Makefile. i.MX95 uses SCMI.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- arch/arm/mach-imx/imx9/Kconfig | 8 ++++++++ arch/arm/mach-imx/imx9/Makefile | 9 +++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig index 54c6b117fcff21be5c3800ebd1dc30f8ea12ac42..660529555e9b3d38a6fb70c77858bd9de7473461 100644 --- a/arch/arm/mach-imx/imx9/Kconfig +++ b/arch/arm/mach-imx/imx9/Kconfig @@ -21,6 +21,14 @@ config IMX91 select IMX9 select ARMV8_SPL_EXCEPTION_VECTORS
+config IMX95 + bool + select IMX9 + select ARMV8_SPL_EXCEPTION_VECTORS + select SCMI_FIRMWARE + select DM_MAILBOX + select SCMI_TRANSPORT_SMT_INTR + select SPL_IMX_CONTAINER_USE_TRAMPOLINE
config SYS_SOC default "imx9" diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile index 45a9105a75a558dada3c328ed0878050da2a41e1..53cc97c6b471f1bdc3f55b198788ca42e4ad9558 100644 --- a/arch/arm/mach-imx/imx9/Makefile +++ b/arch/arm/mach-imx/imx9/Makefile @@ -3,8 +3,13 @@ # Copyright 2022 NXP
obj-y += lowlevel_init.o + +ifeq ($(CONFIG_SCMI_FIRMWARE),y) +obj-y += scmi/ +else obj-y += soc.o clock.o clock_root.o trdc.o +endif
-#ifndef CONFIG_XPL_BUILD +ifneq ($(CONFIG_SPL_BUILD),y) obj-y += imx_bootaux.o -#endif +endif \ No newline at end of file

On 1/15/25 2:29 PM, Alice Guo wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 Kconfig and Makefile. i.MX95 uses SCMI.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
arch/arm/mach-imx/imx9/Kconfig | 8 ++++++++ arch/arm/mach-imx/imx9/Makefile | 9 +++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig index 54c6b117fcff21be5c3800ebd1dc30f8ea12ac42..660529555e9b3d38a6fb70c77858bd9de7473461 100644 --- a/arch/arm/mach-imx/imx9/Kconfig +++ b/arch/arm/mach-imx/imx9/Kconfig @@ -21,6 +21,14 @@ config IMX91 select IMX9 select ARMV8_SPL_EXCEPTION_VECTORS
+config IMX95
- bool
- select IMX9
- select ARMV8_SPL_EXCEPTION_VECTORS
- select SCMI_FIRMWARE
- select DM_MAILBOX
- select SCMI_TRANSPORT_SMT_INTR
- select SPL_IMX_CONTAINER_USE_TRAMPOLINE
Keep the list sorted.

From: Alice Guo alice.guo@nxp.com
i.MX95 only supports low power boot, which means A55 is kicked by M33. There is no ROM runs on A55 in such case so that deselect IMX8_ROMAPI for i.MX95.
Signed-off-by: Alice Guo alice.guo@nxp.com --- arch/arm/mach-imx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 134e42028c3787d78e28af03d40c41a8b2b1981b..daf6443aa5b40b6410f2645937239a4ec785fa34 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -180,7 +180,7 @@ config DDRMC_VF610_CALIBRATION
config IMX8_ROMAPI def_bool y - depends on IMX8MN || IMX8MP || IMX8ULP || IMX9 + depends on IMX8MN || IMX8MP || IMX8ULP || (IMX9 && !IMX95)
config SPL_IMX_ROMAPI_LOADADDR hex "Default load address to load image through ROM API"

On 1/15/25 2:29 PM, Alice Guo wrote:
From: Alice Guo alice.guo@nxp.com
i.MX95 only supports low power boot, which means A55 is kicked by M33. There is no ROM runs on A55 in such case so that deselect IMX8_ROMAPI for i.MX95.
Signed-off-by: Alice Guo alice.guo@nxp.com
arch/arm/mach-imx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 134e42028c3787d78e28af03d40c41a8b2b1981b..daf6443aa5b40b6410f2645937239a4ec785fa34 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -180,7 +180,7 @@ config DDRMC_VF610_CALIBRATION
config IMX8_ROMAPI def_bool y
- depends on IMX8MN || IMX8MP || IMX8ULP || IMX9
- depends on IMX8MN || IMX8MP || IMX8ULP || (IMX9 && !IMX95)
Would it make sense to introduce IMX91 and IMX93 symbols instead and select those two instead of this negative match ?

From: Alice Guo alice.guo@nxp.com
i.MX95 needs to combine DDR PHY firmware images and their byte counts together, so add a new entry type nxp-header-ddrfw for this requirement.
Signed-off-by: Alice Guo alice.guo@nxp.com --- tools/binman/entries.rst | 10 ++++++++++ tools/binman/etype/nxp_header_ddrfw.py | 32 +++++++++++++++++++++++++++++++ tools/binman/ftest.py | 11 +++++++++++ tools/binman/test/346_nxp_ddrfw_imx95.dts | 24 +++++++++++++++++++++++ 4 files changed, 77 insertions(+)
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 780e9817fb6496b5f2a4aef6ed1c0b4c8d9a4ba2..6da3f0b904643360ca11da32e31129f5f54d279c 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1663,6 +1663,16 @@ Properties / Entry arguments:
+.. _etype_nxp_header_ddrfw: + +Entry: nxp-header-ddrfw: add a header to DDR PHY firmware images +--------------------------------------------------- + +This entry is used to combine DDR PHY firmware images and their byte counts +together. See imx95_evk.rst for how to get DDR PHY Firmware Images. + + + .. _etype_opensbi:
Entry: opensbi: RISC-V OpenSBI fw_dynamic blob diff --git a/tools/binman/etype/nxp_header_ddrfw.py b/tools/binman/etype/nxp_header_ddrfw.py new file mode 100644 index 0000000000000000000000000000000000000000..80e0611df609107d6e092a5a6d8aec05b80a6347 --- /dev/null +++ b/tools/binman/etype/nxp_header_ddrfw.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2025 NXP + +from binman.etype.section import Entry_section + +class Entry_nxp_header_ddrfw(Entry_section): + """Add a header to DDR PHY firmware images + + This entry is used for i.MX95 to combine DDR PHY firmware images and their + byte counts together. + + See imx95_evk.rst for how to get DDR PHY Firmware Images. + """ + + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def BuildSectionData(self, required): + section_data = bytearray() + header_data = bytearray() + + for entry in self._entries.values(): + entry_data = entry.GetData(required) + + if not required and entry_data is None: + return None + + section_data += entry_data + header_data += entry.contents_size.to_bytes(4, 'little') + + return header_data + section_data diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index a553ca9e5642b685b908e817972b42bfba1d8a0a..62acc1d885cf6565999672d25584602929c3c4c3 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -104,6 +104,8 @@ PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big') PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big') TI_BOARD_CONFIG_DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' TI_UNSECURE_DATA = b'unsecuredata' +IMX_LPDDR_IMEM_DATA = b'qwertyuiop1234567890' +IMX_LPDDR_DMEM_DATA = b'asdfghjklzxcvbnm'
# Subdirectory of the input dir to use to put test FDTs TEST_FDT_SUBDIR = 'fdts' @@ -202,6 +204,8 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA) TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA) TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA) + TestFunctional._MakeInputFile('lpddr5_imem.bin', IMX_LPDDR_IMEM_DATA) + TestFunctional._MakeInputFile('lpddr5_dmem.bin', IMX_LPDDR_DMEM_DATA)
cls._elf_testdir = os.path.join(cls._indir, 'elftest') elf_test.BuildElfTestFiles(cls._elf_testdir) @@ -7826,6 +7830,13 @@ fdt fdtmap Extract the devicetree blob from the fdtmap """Test that binman can produce an iMX8 image""" self._DoTestFile('339_nxp_imx8.dts')
+ def testNxpHeaderDdrfw(self): + """Test that binman can add a header to DDR PHY firmware images""" + data = self._DoReadFile('346_nxp_ddrfw_imx95.dts') + self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') + + len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') + + IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data) + def testFitSignSimple(self): """Test that image with FIT and signature nodes can be signed""" if not elf.ELF_TOOLS: diff --git a/tools/binman/test/346_nxp_ddrfw_imx95.dts b/tools/binman/test/346_nxp_ddrfw_imx95.dts new file mode 100644 index 0000000000000000000000000000000000000000..889f6f29860746c09a91aaad7658976559760d9c --- /dev/null +++ b/tools/binman/test/346_nxp_ddrfw_imx95.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + imx-lpddr { + type = "nxp-header-ddrfw"; + + imx-lpddr-imem { + filename = "lpddr5_imem.bin"; + type = "blob-ext"; + }; + + imx-lpddr-dmem { + filename = "lpddr5_dmem.bin"; + type = "blob-ext"; + }; + }; + }; +};

From: Alice Guo alice.guo@nxp.com
i.MX95 uses binman to invoke mkimage to create image container. 2 image containers are needed currently. The first one is composed of ahab-container.img, LPDDR firmware images, OEI images, System Manager image and u-boot-spl.bin. The second one is comsisted of ARM Trusted firmware and u-boot.bin.
Because DDR OEI image and LPDDR firmware images have to be packaged together and named as m33-oei-ddrfw.bin by binman, so imx9_image.sh does not check if m33-oei-ddrfw.bin exists.
When using "make imx95_19x19_evk_defconfig; make", imx9_image.sh will delete the line for u-boot.bin in container.cfg. In fact, binman is always called after the u-boot.bin is built, so imx9_image.sh does not check if u-boot.bin exists.
Signed-off-by: Alice Guo alice.guo@nxp.com --- include/imx8image.h | 19 +++++-- tools/imx8image.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++----- tools/imx9_image.sh | 8 +++ 3 files changed, 153 insertions(+), 17 deletions(-)
diff --git a/include/imx8image.h b/include/imx8image.h index 6b95e93fb50980f9e86099b3103142dcbe38764a..b48e2b039698a225e152bddfe4b29be2fcbdc730 100644 --- a/include/imx8image.h +++ b/include/imx8image.h @@ -157,7 +157,9 @@ enum imx8image_cmd { CMD_SOC_TYPE, CMD_CONTAINER, CMD_IMAGE, - CMD_DATA + CMD_DATA, + CMD_DUMMY_V2X, + CMD_HOLD };
enum imx8image_core_type { @@ -169,7 +171,9 @@ enum imx8image_core_type { CFG_A35, CFG_A55, CFG_A53, - CFG_A72 + CFG_A72, + CFG_M33, + CFG_OEI };
enum imx8image_fld_types { @@ -208,7 +212,10 @@ typedef enum option_type { FILEOFF, MSG_BLOCK, SENTINEL, - UPOWER + UPOWER, + OEI, + DUMMY_V2X, + HOLD } option_type_t;
typedef struct { @@ -227,12 +234,16 @@ typedef struct { #define CORE_CA35 4 #define CORE_CA72 5 #define CORE_SECO 6 +#define CORE_M33 7
#define CORE_ULP_CM33 0x1 #define CORE_ULP_CA35 0x2 #define CORE_ULP_UPOWER 0x4 #define CORE_ULP_SENTINEL 0x6
+#define CORE_IMX95_M33P 0 +#define CORE_IMX95_A55C0 2 + #define SC_R_OTP 357U #define SC_R_DEBUG 354U #define SC_R_ROM_0 236U @@ -246,10 +257,12 @@ typedef struct { #define IMG_TYPE_EXEC 0x03 /* Executable image type */ #define IMG_TYPE_DATA 0x04 /* Data image type */ #define IMG_TYPE_DCD_DDR 0x05 /* DCD/DDR image type */ +#define IMG_TYPE_OEI 0x05 /* Optional Executable image type */ #define IMG_TYPE_SECO 0x06 /* SECO image type */ #define IMG_TYPE_SENTINEL 0x06 /* SENTINEL image type */ #define IMG_TYPE_PROV 0x07 /* Provisioning image type */ #define IMG_TYPE_DEK 0x08 /* DEK validation type */ +#define IMG_TYPE_V2X_DUMMY 0x0E /* V2X Dummy image */
#define IMG_TYPE_SHIFT 0 #define IMG_TYPE_MASK 0x1f diff --git a/tools/imx8image.c b/tools/imx8image.c index 15510d3e712495c3508e130fcb2b4df42b07136b..9c4b43174558ec669f9a196c08756bcc6522b752 100644 --- a/tools/imx8image.c +++ b/tools/imx8image.c @@ -7,6 +7,7 @@
#include "imx8image.h" #include <image.h> +#include <linux/sizes.h>
static int p_idx; static int sector_size; @@ -54,6 +55,8 @@ static table_entry_t imx8image_cmds[] = { {CMD_CONTAINER, "CONTAINER", "new container", }, {CMD_IMAGE, "IMAGE", "new image", }, {CMD_DATA, "DATA", "new data", }, + {CMD_DUMMY_V2X, "DUMMY_V2X", "v2x", }, + {CMD_HOLD, "HOLD", "hold", }, {-1, "", "", }, };
@@ -66,6 +69,8 @@ static table_entry_t imx8image_core_entries[] = { {CFG_A55, "A55", "A55 core", }, {CFG_A53, "A53", "A53 core", }, {CFG_A72, "A72", "A72 core", }, + {CFG_OEI, "OEI", "OEI", }, + {CFG_M33, "M33", "M33 core", }, {-1, "", "", }, };
@@ -144,6 +149,14 @@ static void parse_cfg_cmd(image_t *param_stack, int32_t cmd, char *token, exit(EXIT_FAILURE); } break; + case CMD_DUMMY_V2X: + param_stack[p_idx].option = DUMMY_V2X; + param_stack[p_idx++].entry = (uint32_t)strtoll(token, NULL, 0); + break; + case CMD_HOLD: + param_stack[p_idx].option = HOLD; + param_stack[p_idx].entry = (uint32_t)strtoll(token, NULL, 0); + param_stack[p_idx++].filename = NULL; default: break; } @@ -221,6 +234,16 @@ static void parse_cfg_fld(image_t *param_stack, int32_t *cmd, char *token, (*cmd == CMD_DATA) ? DATA : AP; param_stack[p_idx].filename = token; break; + case CFG_OEI: + param_stack[p_idx].option = OEI; + param_stack[p_idx].filename = token; + param_stack[p_idx].ext = CORE_CM4_0; + break; + case CFG_M33: + param_stack[p_idx].option = M40; + param_stack[p_idx].ext = 0; + param_stack[p_idx].filename = token; + break; } break; case CFG_LOAD_ADDR: @@ -238,9 +261,15 @@ static void parse_cfg_fld(image_t *param_stack, int32_t *cmd, char *token, case CFG_A53: case CFG_A55: case CFG_A72: + case CFG_M33: param_stack[p_idx++].entry = (uint32_t)strtoll(token, NULL, 0); break; + case CFG_OEI: + param_stack[p_idx].dst = (uint32_t)strtoll(token, NULL, 0); + param_stack[p_idx].entry = param_stack[p_idx].dst + 1; + p_idx++; + break; } default: break; @@ -548,6 +577,7 @@ static void set_image_array_entry(flash_header_v3_t *container, char *tmp_filename, bool dcd_skip) { uint64_t entry = image_stack->entry; + uint64_t dst = image_stack->dst; uint64_t core = image_stack->ext; uint32_t meta; char *tmp_name = ""; @@ -557,7 +587,9 @@ static void set_image_array_entry(flash_header_v3_t *container, img->offset = offset; /* Is re-adjusted later */ img->size = size;
- set_image_hash(img, tmp_filename, IMAGE_HASH_ALGO_DEFAULT); + if (type != DUMMY_V2X) { + set_image_hash(img, tmp_filename, IMAGE_HASH_ALGO_DEFAULT); + }
switch (type) { case SECO: @@ -579,6 +611,27 @@ static void set_image_array_entry(flash_header_v3_t *container, img->dst = 0xe4000000; /* S400 IRAM base */ img->entry = 0xe4000000; break; + case OEI: + if (soc != IMX9) { + fprintf(stderr, "Error: invalid core id: %" PRIi64 "\n", core); + exit(EXIT_FAILURE); + } + + img->hab_flags |= IMG_TYPE_OEI; + if (core == CORE_CM4_0) { + img->hab_flags |= CORE_ULP_CM33 << BOOT_IMG_FLAGS_CORE_SHIFT; + meta = CORE_IMX95_M33P; + + } else { + img->hab_flags |= CORE_ULP_CA35 << BOOT_IMG_FLAGS_CORE_SHIFT; + meta = CORE_IMX95_A55C0; + } + tmp_name = "OEI"; + img->dst = (dst ? dst : entry); + img->entry = entry; + img->meta = meta; + custom_partition = 0; + break; case AP: if (soc == QX && core == CORE_CA35) { meta = IMAGE_A35_DEFAULT_META(custom_partition); @@ -586,8 +639,10 @@ static void set_image_array_entry(flash_header_v3_t *container, meta = IMAGE_A53_DEFAULT_META(custom_partition); } else if (soc == QM && core == CORE_CA72) { meta = IMAGE_A72_DEFAULT_META(custom_partition); - } else if (((soc == ULP) || (soc == IMX9)) && core == CORE_CA35) { + } else if ((soc == ULP) && core == CORE_CA35) { meta = 0; + } else if ((soc == IMX9) && core == CORE_CA35) { + meta = CORE_IMX95_A55C0; } else { fprintf(stderr, "Error: invalid AP core id: %" PRIu64 "\n", @@ -686,6 +741,15 @@ static void set_image_array_entry(flash_header_v3_t *container, img->entry = 0x28300200; } break; + case DUMMY_V2X: + img->hab_flags |= IMG_TYPE_V2X_DUMMY; + img->hab_flags |= CORE_SC << BOOT_IMG_FLAGS_CORE_SHIFT; + tmp_name = "V2X Dummy"; + set_image_hash(img, "/dev/null", IMAGE_HASH_ALGO_DEFAULT); + img->dst = entry; + img->entry = entry; + img->size = 0; /* dummy image has no size */ + break; default: fprintf(stderr, "unrecognized image type (%d)\n", type); exit(EXIT_FAILURE); @@ -708,15 +772,26 @@ void set_container(flash_header_v3_t *container, uint16_t sw_version, fprintf(stdout, "container flags: 0x%x\n", container->flags); }
-static int get_container_image_start_pos(image_t *image_stack, uint32_t align) +static int get_container_image_start_pos(image_t *image_stack, uint32_t align, uint32_t *v2x) { image_t *img_sp = image_stack; /*8K total container header*/ int file_off = CONTAINER_IMAGE_ARRAY_START_OFFSET; FILE *fd = NULL; - flash_header_v3_t header; + flash_header_v3_t *header; + flash_header_v3_t *header2; + void *p; int ret;
+ p = calloc(1, SZ_4K); + if (!p) { + fprintf(stderr, "Fail to alloc 4K memory\n"); + exit(EXIT_FAILURE); + } + + header = p; + header2 = p + FIRST_CONTAINER_HEADER_LENGTH; + while (img_sp->option != NO_IMG) { if (img_sp->option == APPEND) { fd = fopen(img_sp->filename, "r"); @@ -725,7 +800,7 @@ static int get_container_image_start_pos(image_t *image_stack, uint32_t align) exit(EXIT_FAILURE); }
- ret = fread(&header, sizeof(header), 1, fd); + ret = fread(header, SZ_4K, 1, fd); if (ret != 1) { printf("Failure Read header %d\n", ret); exit(EXIT_FAILURE); @@ -733,19 +808,27 @@ static int get_container_image_start_pos(image_t *image_stack, uint32_t align)
fclose(fd);
- if (header.tag != IVT_HEADER_TAG_B0) { - fprintf(stderr, "header tag mismatched file %s\n", img_sp->filename); + if (header->tag != IVT_HEADER_TAG_B0) { + fprintf(stderr, "header tag mismatched \n"); exit(EXIT_FAILURE); } else { - file_off += - header.img[header.num_images - 1].size; - file_off = ALIGN(file_off, align); + if (header2->tag != IVT_HEADER_TAG_B0) { + file_off += header->img[header->num_images - 1].size; + file_off = ALIGN(file_off, align); + } else { + file_off = header2->img[header2->num_images - 1].offset + FIRST_CONTAINER_HEADER_LENGTH; + file_off += header2->img[header2->num_images - 1].size; + file_off = ALIGN(file_off, align); + fprintf(stderr, "Has 2nd container %x\n", file_off); + *v2x = true; + } } }
img_sp++; }
+ free(p); return file_off; }
@@ -837,6 +920,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size, char *tmp_filename = NULL; uint32_t size = 0; uint32_t file_padding = 0; + uint32_t v2x = false; int ret;
int container = -1; @@ -860,7 +944,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size, set_imx_hdr_v3(&imx_header, 0); set_imx_hdr_v3(&imx_header, 1);
- file_off = get_container_image_start_pos(image_stack, sector_size); + file_off = get_container_image_start_pos(image_stack, sector_size, &v2x); fprintf(stdout, "container image offset (aligned):%x\n", file_off);
/* step through image stack and generate the header */ @@ -869,6 +953,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size, /* stop once we reach null terminator */ while (img_sp->option != NO_IMG) { switch (img_sp->option) { + case OEI: case AP: case M40: case M41: @@ -891,6 +976,30 @@ static int build_container(soc_type_t soc, uint32_t sector_size, file_off += ALIGN(sbuf.st_size, sector_size); break;
+ case DUMMY_V2X: + if (container < 0) { + fprintf(stderr, "No container found\n"); + exit(EXIT_FAILURE); + } + tmp_filename = "dummy"; + set_image_array_entry(&imx_header.fhdr[container], + soc, + img_sp, + file_off, + 0, + tmp_filename, + dcd_skip); + img_sp->src = file_off; + break; + + case HOLD: + if (container < 0) { + fprintf(stderr, "No container found\n"); + exit(EXIT_FAILURE); + } + file_off += ALIGN(img_sp->entry, sector_size); + break; + case SECO: case SENTINEL: if (container < 0) { @@ -962,11 +1071,15 @@ static int build_container(soc_type_t soc, uint32_t sector_size, do { if (img_sp->option == APPEND) { copy_file(ofd, img_sp->filename, 0, 0); - file_padding += FIRST_CONTAINER_HEADER_LENGTH; + if (v2x) + file_padding += FIRST_CONTAINER_HEADER_LENGTH * 2; + else + file_padding += FIRST_CONTAINER_HEADER_LENGTH; } img_sp++; } while (img_sp->option != NO_IMG);
+ fprintf(stderr, "%s: %x %d\n", __func__, file_padding, v2x); /* Add padding or skip appended container */ ret = lseek(ofd, file_padding, SEEK_SET); if (ret < 0) { @@ -979,6 +1092,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size, /* Note: Image offset are not contained in the image */ tmp = flatten_container_header(&imx_header, container + 1, &size, file_padding); + fprintf(stderr, "error writing image hdr %x\n", size); /* Write image header */ if (write(ofd, tmp, size) != size) { fprintf(stderr, "error writing image hdr\n"); @@ -999,7 +1113,8 @@ static int build_container(soc_type_t soc, uint32_t sector_size, img_sp->option == AP || img_sp->option == DATA || img_sp->option == SCD || img_sp->option == SCFW || img_sp->option == SECO || img_sp->option == MSG_BLOCK || - img_sp->option == UPOWER || img_sp->option == SENTINEL) { + img_sp->option == UPOWER || img_sp->option == SENTINEL || + img_sp->option == OEI) { copy_file_aligned(ofd, img_sp->filename, img_sp->src, sector_size); } @@ -1030,7 +1145,7 @@ int imx8image_copy_image(int outfd, struct image_tool_params *mparams) fprintf(stdout, "CONTAINER SW VERSION:\t0x%04x\n", sw_version);
build_container(soc, sector_size, emmc_fastboot, - img_sp, dcd_skip, fuse_version, sw_version, outfd); + img_sp, false, fuse_version, sw_version, outfd);
return 0; } diff --git a/tools/imx9_image.sh b/tools/imx9_image.sh index ca78a57a19a06ade4792da6d2435cbc4fa07de62..6523d1a0ad109db2da29bae122a20b084c2522ed 100755 --- a/tools/imx9_image.sh +++ b/tools/imx9_image.sh @@ -18,6 +18,14 @@ for f in $blobs; do continue fi
+ if [ $f = "m33-oei-ddrfw.bin" ]; then + continue + fi + + if [ $f = "u-boot.bin" ]; then + continue + fi + if [ ! -f $tmp ]; then echo "WARNING '$tmp' not found, resulting binary may be not-functional" >&2

On 1/15/25 2:29 PM, Alice Guo wrote:
From: Alice Guo alice.guo@nxp.com
i.MX95 uses binman to invoke mkimage to create image container. 2 image containers are needed currently. The first one is composed of ahab-container.img, LPDDR firmware images, OEI images, System Manager image and u-boot-spl.bin. The second one is comsisted of ARM Trusted
comsisted -- typo.
[...]
@@ -586,8 +639,10 @@ static void set_image_array_entry(flash_header_v3_t *container, meta = IMAGE_A53_DEFAULT_META(custom_partition); } else if (soc == QM && core == CORE_CA72) { meta = IMAGE_A72_DEFAULT_META(custom_partition);
} else if (((soc == ULP) || (soc == IMX9)) && core == CORE_CA35) {
} else if ((soc == ULP) && core == CORE_CA35) { meta = 0;
} else if ((soc == IMX9) && core == CORE_CA35) {
meta = CORE_IMX95_A55C0;
Why is this core == CORE_CA35 and not core == CORE_CA55 here ?

From: Teo Hall teo.hall@nxp.com
This patch adds V2X container support on i.MX95.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Teo Hall teo.hall@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com --- arch/arm/mach-imx/image-container.c | 63 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 23 deletions(-)
diff --git a/arch/arm/mach-imx/image-container.c b/arch/arm/mach-imx/image-container.c index 2afe9d38a0681862098a6566a873873f24069b88..54ae7721888dbc93fdaba8e5aedc52c4506bb1c1 100644 --- a/arch/arm/mach-imx/image-container.c +++ b/arch/arm/mach-imx/image-container.c @@ -231,45 +231,62 @@ static unsigned long get_boot_device_offset(void *dev, int dev_type) return offset; }
-static int get_imageset_end(void *dev, int dev_type) +static ulong get_imageset_end(void *dev, int dev_type) { - unsigned long offset1 = 0, offset2 = 0; - int value_container[2]; + unsigned long offset[3] = {}; + int value_container[3] = {}; u16 hdr_length;
- offset1 = get_boot_device_offset(dev, dev_type); - offset2 = CONTAINER_HDR_ALIGNMENT + offset1; + offset[0] = get_boot_device_offset(dev, dev_type);
- value_container[0] = get_dev_container_size(dev, dev_type, offset1, &hdr_length); + value_container[0] = get_dev_container_size(dev, dev_type, offset[0], &hdr_length); if (value_container[0] < 0) { printf("Parse seco container failed %d\n", value_container[0]); - return value_container[0]; + return 0; }
debug("seco container size 0x%x\n", value_container[0]);
- value_container[1] = get_dev_container_size(dev, dev_type, offset2, &hdr_length); - if (value_container[1] < 0) { - debug("Parse scu container failed %d, only seco container\n", - value_container[1]); - /* return seco container total size */ - return value_container[0] + offset1; + if (is_imx95()) { + offset[1] = ALIGN(hdr_length, CONTAINER_HDR_ALIGNMENT) + offset[0]; + + value_container[1] = get_dev_container_size(dev, dev_type, offset[1], &hdr_length); + if (value_container[1] < 0) { + printf("Parse v2x container failed %d\n", value_container[1]); + return value_container[0] + offset[0]; /* return seco container total size */ + } + + debug("v2x container size 0x%x\n", value_container[1]); + + offset[2] = ALIGN(hdr_length, CONTAINER_HDR_ALIGNMENT) + offset[1]; + } else { + /* Skip offset[1] */ + offset[2] = ALIGN(hdr_length, CONTAINER_HDR_ALIGNMENT) + offset[0]; }
- debug("scu container size 0x%x\n", value_container[1]); + value_container[2] = get_dev_container_size(dev, dev_type, offset[2], &hdr_length); + if (value_container[2] < 0) { + debug("Parse scu container image failed %d, only seco container\n", value_container[2]); + if (is_imx95()) + return value_container[1] + offset[1]; /* return seco + v2x container total size */ + else + return value_container[0] + offset[0]; /* return seco container total size */ + }
- return value_container[1] + offset2; + debug("scu container size 0x%x\n", value_container[2]); + + return value_container[2] + offset[2]; }
#ifdef CONFIG_SPL_SPI_LOAD unsigned int spl_spi_get_uboot_offs(struct spi_flash *flash) { - int end; + ulong end;
end = get_imageset_end(flash, QSPI_DEV); end = ROUND(end, SZ_1K);
- printf("Load image from QSPI 0x%x\n", end); + printf("Load image from QSPI 0x%lx\n", end);
return end; } @@ -279,12 +296,12 @@ unsigned int spl_spi_get_uboot_offs(struct spi_flash *flash) unsigned long arch_spl_mmc_get_uboot_raw_sector(struct mmc *mmc, unsigned long raw_sect) { - int end; + ulong end;
end = get_imageset_end(mmc, MMC_DEV); end = ROUND(end, SZ_1K);
- printf("Load image from MMC/SD 0x%x\n", end); + printf("Load image from MMC/SD 0x%lx\n", end);
return end / mmc->read_bl_len; } @@ -312,12 +329,12 @@ int spl_mmc_emmc_boot_partition(struct mmc *mmc) #ifdef CONFIG_SPL_NAND_SUPPORT uint32_t spl_nand_get_uboot_raw_page(void) { - int end; + ulong end;
end = get_imageset_end((void *)NULL, NAND_DEV); end = ROUND(end, SZ_16K);
- printf("Load image from NAND 0x%x\n", end); + printf("Load image from NAND 0x%lx\n", end);
return end; } @@ -326,7 +343,7 @@ uint32_t spl_nand_get_uboot_raw_page(void) #ifdef CONFIG_SPL_NOR_SUPPORT unsigned long spl_nor_get_uboot_base(void) { - int end; + ulong end;
/* Calculate the image set end, * if it is less than CFG_SYS_UBOOT_BASE(0x8281000), @@ -339,7 +356,7 @@ unsigned long spl_nor_get_uboot_base(void) else end = ROUND(end, SZ_1K);
- printf("Load image from NOR 0x%x\n", end); + printf("Load image from NOR 0x%lx\n", end);
return end; }

From: Alice Guo alice.guo@nxp.com
This patch add a document for i.MX95 Image Container Format.
Signed-off-by: Alice Guo alice.guo@nxp.com --- doc/imx/imx95_container.txt | 136 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+)
diff --git a/doc/imx/imx95_container.txt b/doc/imx/imx95_container.txt new file mode 100644 index 0000000000000000000000000000000000000000..2ad573450697ae344d8703f43d050ddd1fef7a85 --- /dev/null +++ b/doc/imx/imx95_container.txt @@ -0,0 +1,136 @@ +i.MX95 Image Container Format +----------------------------- + +The image container set consists of some image containers, and image container +contains boot images. Each image container has its own container header which is +defined in Figure 1. All container headers are placed together in a continuous +8KB space at the beginning of the image container set - image container set header. + +ROM code addresses image containers in image container set one by one based on +their headers’ order in image container set header. + +If ELE container exists, its container header must be the 1st one in the image +container set header. + +If V2X container exists, its container header must be the 2nd one in the image +container set header. V2X must be combined with ELE container. + +The information of boot images are recorded in image container header. System +ROM code needs to retrieve the information from the image container header, like +the offset on boot source, the target address in RAM, the length of boot image. +The order of ROM code handling these boot images is based on the order of each +boot image information present in image container header. + +Figure 1: +------------------- +--------------+--------------+--------------+--------------+ + ^ |Tag |Length |Length |Version | + | +--------------+--------------+--------------+--------------+ + | | Flags | + | +--------------+--------------+--------------+--------------+ + | |# of Images |Fuse version |SW version | +Image | +--------------+--------------+--------------+--------------+ +Conatiner | |Reserved |Signature Block Offset | +Header | ------- +--------------+--------------+--------------+--------------+ + | ^ |Image0: Offset, Size, LoadAddr, EntryPoint, Flags, Hash, IV| + | | +--------------+--------------+--------------+--------------+ + | Image | |Image1: Offset, Size, LoadAddr, EntryPoint, Flags, Hash, IV| + | Array | +--------------+--------------+--------------+--------------+ + | | | ... | + | | +--------------+--------------+--------------+--------------+ + v v |ImageN: Offset, Size, LoadAddr, EntryPoint, Flags, Hash, IV| +------------------- +--------------+--------------+--------------+--------------+ + | ... | + ----------- +--------------+--------------+--------------+--------------+ <-- SignOffset + ^ |Tag |Length |Length |Version | + | +--------------+--------------+--------------+--------------+ + | |SRK table offset |Certificate Offset | + | +--------------+--------------+--------------+--------------+ + | |Blob Offset |Signature Offset | + | +--------------+--------------+--------------+--------------+ + Signature | | SRK Table | + Block | +--------------+--------------+--------------+--------------+ + | | Signature | + | +--------------+--------------+--------------+--------------+ + | | Certificate (optional) | + | +--------------+--------------+--------------+--------------+ + v | Blob (optional) | + ----------- +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ <-- Image0Offset + | Image0 | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ <-- ImageNOffset + | ImageN | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + +i.MX95 Low Power Boot Image Sets Layout +--------------------------------------- + + Image container sets are handled by M33 ROM. + + --------------- +--------------+--------------+--------------+--------------+ + ^ | 1st Container Header | + | +--------------+--------------+--------------+--------------+ + ELE + | | Padding for 1KB alignment | + OEM | +--------------+--------------+--------------+--------------+ + Conatiner | | 2nd Container Header | + Set | +--------------+--------------+--------------+--------------+ + Header | | Padding for 1KB alignment | + | +--------------+--------------+--------------+--------------+ + | | 3rd Container Header | + | +--------------+--------------+--------------+--------------+ + v | Padding for 1KB alignment | + --------------- +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + | mx95a0-ahab-container.img | + +--------------+--------------+--------------+--------------+ + | ... | +------------------- +--------------+--------------+--------------+--------------+ + ^ | oei-m33-ddr.bin | + | +--------------+--------------+--------------+--------------+ + | | ddrfw-header.bin | + | +--------------+--------------+--------------+--------------+ + | | lpddr5_imem_v202311.bin | + | +--------------+--------------+--------------+--------------+ +m33-oei-ddrfw.bin | | lpddr5_dmem_v202311.bin | + | +--------------+--------------+--------------+--------------+ + | | ddrfw-qb-header.bin | + | +--------------+--------------+--------------+--------------+ + | | lpddr5_imem_qb_v202311.bin | + | +--------------+--------------+--------------+--------------+ + v | lpddr5_dmem_qb_v202311.bin | +------------------- +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + | oei-m33-tcm.bin | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + | m33_image.bin | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + | u-boot-spl.bin | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + + --------------- +--------------+--------------+--------------+--------------+ + u-boot-atf ^ | 1st Container Header | + Container | +--------------+--------------+--------------+--------------+ + Header v | Padding for 1KB alignment | + --------------- +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + | bl31.bin | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+ + | u-boot.bin | + +--------------+--------------+--------------+--------------+ + | ... | + +--------------+--------------+--------------+--------------+

From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- arch/arm/dts/imx95-19x19-evk-u-boot.dtsi | 68 ++++++++++ arch/arm/dts/imx95-u-boot.dtsi | 157 +++++++++++++++++++++++ arch/arm/mach-imx/imx9/Kconfig | 6 + arch/arm/mach-imx/imx9/scmi/container.cfg | 10 ++ arch/arm/mach-imx/imx9/scmi/imximage.cfg | 15 +++ board/freescale/imx95_evk/Kconfig | 12 ++ board/freescale/imx95_evk/MAINTAINERS | 6 + board/freescale/imx95_evk/Makefile | 11 ++ board/freescale/imx95_evk/imx95_19x19_evk.env | 90 +++++++++++++ board/freescale/imx95_evk/imx95_evk.c | 54 ++++++++ board/freescale/imx95_evk/spl.c | 72 +++++++++++ configs/imx95_19x19_evk_defconfig | 177 ++++++++++++++++++++++++++ doc/board/nxp/imx95_evk.rst | 114 +++++++++++++++++ doc/board/nxp/index.rst | 1 + include/configs/imx95_evk.h | 27 ++++ 15 files changed, 820 insertions(+)
diff --git a/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..2902e8f711c3ef705df27ca4fc7a89d1bd7ffc71 --- /dev/null +++ b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include "imx95-u-boot.dtsi" + +&gpio1 { + reg = <0 0x47400000 0 0x1000>, <0 0x47400040 0 0x40>; +}; + +&lpuart1 { + clocks = <&scmi_clk IMX95_CLK_LPUART1>, <&scmi_clk IMX95_CLK_LPUART1>; + clock-names = "ipg", "per"; + bootph-pre-ram; +}; + +®_usdhc2_vmmc { + bootph-pre-ram; +}; + +&usdhc1 { + bootph-pre-ram; +}; + +&usdhc2 { + bootph-pre-ram; +}; + +&wdog3 { + status = "disabled"; +}; + +&pinctrl_uart1 { + bootph-pre-ram; +}; + +&pinctrl_usdhc1 { + bootph-pre-ram; +}; + +&pinctrl_usdhc1_100mhz { + bootph-pre-ram; +}; + +&pinctrl_usdhc1_200mhz { + bootph-pre-ram; +}; + +&pinctrl_usdhc2 { + bootph-pre-ram; +}; + +&pinctrl_usdhc2_100mhz { + bootph-pre-ram; +}; + +&pinctrl_usdhc2_200mhz { + bootph-pre-ram; +}; + +&pinctrl_usdhc2_gpio { + bootph-pre-ram; +}; + +&pinctrl_reg_usdhc2_vmmc { + bootph-pre-ram; +}; diff --git a/arch/arm/dts/imx95-u-boot.dtsi b/arch/arm/dts/imx95-u-boot.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..b0dcf765a779e5cbf63911c12f089370fc886dbf --- /dev/null +++ b/arch/arm/dts/imx95-u-boot.dtsi @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +/ { + binman { + multiple-images; + + m33-oei-ddrfw { + pad-byte = <0x00>; + align-size = <0x8>; + filename = "m33-oei-ddrfw.bin"; + + oei-m33-ddr { + align-size = <0x4>; + filename = "oei-m33-ddr.bin"; + type = "blob-ext"; + }; + + imx-lpddr { + type = "nxp-header-ddrfw"; + + imx-lpddr-imem { + filename = "lpddr5_imem_v202311.bin"; + type = "blob-ext"; + }; + + imx-lpddr-dmem { + filename = "lpddr5_dmem_v202311.bin"; + type = "blob-ext"; + }; + }; + + imx-lpddr-qb { + type = "nxp-header-ddrfw"; + + imx-lpddr-imem-qb { + filename = "lpddr5_imem_qb_v202311.bin"; + type = "blob-ext"; + }; + + imx-lpddr-dmem-qb { + filename = "lpddr5_dmem_qb_v202311.bin"; + type = "blob-ext"; + }; + }; + }; + + imx-boot { + filename = "flash.bin"; + pad-byte = <0x00>; + + spl { + align = <0x400>; + align-size = <0x400>; + type = "mkimage"; + args = "-n spl/u-boot-spl.cfgout -T imx8image"; + }; + + u-boot { + type = "mkimage"; + args = "-n u-boot-container.cfgout -T imx8image"; + }; + }; + }; +}; + +&aips1 { + bootph-all; +}; + +&aips2 { + bootph-all; +}; + +&aips3 { + bootph-pre-ram; +}; + +&clk_ext1 { + bootph-all; +}; + +&elemu3 { + compatible = "fsl,imx93-mu-s4"; + status = "okay"; + bootph-all; +}; + +&{/firmware} { + bootph-all; +}; + +&{/firmware/scmi} { + bootph-all; +}; + +&{/firmware/scmi/protocol@11} { + bootph-all; +}; + +&{/firmware/scmi/protocol@13} { + bootph-all; +}; + +&{/firmware/scmi/protocol@14} { + bootph-all; +}; + +&{/firmware/scmi/protocol@19} { + bootph-all; +}; + +&gpio2 { + reg = <0 0x43810000 0 0x1000>, <0 0x43810040 0 0x40>; + bootph-pre-ram; +}; + +&gpio3 { + reg = <0 0x43820000 0 0x1000>, <0 0x43820040 0 0x40>; + bootph-pre-ram; +}; + +&gpio4 { + reg = <0 0x43840000 0 0x1000>, <0 0x43840040 0 0x40>; + bootph-pre-ram; +}; + +&gpio5 { + reg = <0 0x43850000 0 0x1000>, <0 0x43850040 0 0x40>; + bootph-pre-ram; +}; + +&mu2 { + bootph-all; +}; + +&osc_24m { + bootph-all; +}; + +&{/soc} { + bootph-all; +}; + +&sram0 { + bootph-all; +}; + +&scmi_buf0 { + bootph-all; +}; + +&scmi_buf1 { + bootph-all; +}; diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig index 660529555e9b3d38a6fb70c77858bd9de7473461..4dd73f0a9e525ebbb8559639f914504ec53f15dd 100644 --- a/arch/arm/mach-imx/imx9/Kconfig +++ b/arch/arm/mach-imx/imx9/Kconfig @@ -68,6 +68,11 @@ config TARGET_PHYCORE_IMX93 select OF_BOARD_FIXUP select OF_BOARD_SETUP
+config TARGET_IMX95_19X19_EVK + bool "imx95_19x19_evk" + select IMX95 + imply OF_UPSTREAM + endchoice
source "board/freescale/imx91_evk/Kconfig" @@ -75,6 +80,7 @@ source "board/freescale/imx93_evk/Kconfig" source "board/freescale/imx93_qsb/Kconfig" source "board/phytec/phycore_imx93/Kconfig" source "board/variscite/imx93_var_som/Kconfig" +source "board/freescale/imx95_evk/Kconfig"
endif
diff --git a/arch/arm/mach-imx/imx9/scmi/container.cfg b/arch/arm/mach-imx/imx9/scmi/container.cfg new file mode 100644 index 0000000000000000000000000000000000000000..ac5a0a281812faf9edff53b49cc19ed769352d71 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/container.cfg @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2025 NXP + */ + +BOOT_FROM SD +SOC_TYPE IMX9 +CONTAINER +IMAGE A55 bl31.bin 0x8a200000 +IMAGE A55 u-boot.bin CONFIG_TEXT_BASE \ No newline at end of file diff --git a/arch/arm/mach-imx/imx9/scmi/imximage.cfg b/arch/arm/mach-imx/imx9/scmi/imximage.cfg new file mode 100644 index 0000000000000000000000000000000000000000..678d15c99be78598ff8dd7a4218530e43e9db2c0 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/imximage.cfg @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2025 NXP + */ + +BOOT_FROM SD +SOC_TYPE IMX9 +APPEND mx95a0-ahab-container.img +CONTAINER +IMAGE OEI m33-oei-ddrfw.bin 0x1ffc0000 +HOLD 0x10000 +IMAGE OEI oei-m33-tcm.bin 0x1ffc0000 +IMAGE M33 m33_image.bin 0x1ffc0000 +IMAGE A55 spl/u-boot-spl.bin 0x20480000 +DUMMY_V2X 0x8b000000 \ No newline at end of file diff --git a/board/freescale/imx95_evk/Kconfig b/board/freescale/imx95_evk/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..f9a67353e5db6d3ec94784cf091e45d8817d541c --- /dev/null +++ b/board/freescale/imx95_evk/Kconfig @@ -0,0 +1,12 @@ +if TARGET_IMX95_19X19_EVK + +config SYS_BOARD + default "imx95_evk" + +config SYS_VENDOR + default "freescale" + +config SYS_CONFIG_NAME + default "imx95_evk" + +endif diff --git a/board/freescale/imx95_evk/MAINTAINERS b/board/freescale/imx95_evk/MAINTAINERS new file mode 100644 index 0000000000000000000000000000000000000000..5caf763e0cc390c46fc54ee627bc966b2b110958 --- /dev/null +++ b/board/freescale/imx95_evk/MAINTAINERS @@ -0,0 +1,6 @@ +i.MX95 EVK BOARD +M: Alice Guo alice.guo@nxp.com +S: Maintained +F: board/freescale/imx95_evk/ +F: include/configs/imx95_evk.h +F: configs/imx95_19x19_evk_defconfig diff --git a/board/freescale/imx95_evk/Makefile b/board/freescale/imx95_evk/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0f5cec385c35059ac5bbcf31868855c10a6c2fe4 --- /dev/null +++ b/board/freescale/imx95_evk/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += imx95_evk.o + +ifdef CONFIG_SPL_BUILD +obj-y += spl.o +endif diff --git a/board/freescale/imx95_evk/imx95_19x19_evk.env b/board/freescale/imx95_evk/imx95_19x19_evk.env new file mode 100644 index 0000000000000000000000000000000000000000..8d2b5dfcf07d251c77440bae44d62bff6253dfc7 --- /dev/null +++ b/board/freescale/imx95_evk/imx95_19x19_evk.env @@ -0,0 +1,90 @@ +sec_boot=no +initrd_addr=0x93800000 +emmc_dev=0 +sd_dev=1 +scriptaddr=0x93500000 +kernel_addr_r=CONFIG_SYS_LOAD_ADDR +image=Image +splashimage=0xA0000000 +console=ttyLP0,115200 earlycon +fdt_addr_r=0x93000000 +fdt_addr=0x93000000 +cntr_addr=0xA8000000 +cntr_file=os_cntr_signed.bin +boot_fit=no +fdtfile=CONFIG_DEFAULT_FDT_FILE +bootm_size=0x10000000 +mmcautodetect=yes +mmcargs=setenv bootargs console=${console} root=${mmcroot} +loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}; +bootscript=echo Running bootscript from mmc ...; source +loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image} +loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile} +loadcntr=fatload mmc ${mmcdev}:${mmcpart} ${cntr_addr} ${cntr_file} +auth_os=auth_cntr ${cntr_addr} +boot_os=booti ${loadaddr} - ${fdt_addr_r}; +mmcboot=echo Booting from mmc ...; + run mmcargs; + if test ${sec_boot} = yes; then + if run auth_os; then + run boot_os; + else + echo ERR: failed to authenticate; + fi; + else + if test ${boot_fit} = yes || test ${boot_fit} = try; then + bootm ${loadaddr}; + else + if run loadfdt; then + run boot_os; + else + echo WARN: Cannot load the DT; + fi; + fi; + fi; +netargs=setenv bootargs console=${console} root=/dev/nfs + ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp +netboot=echo Booting from net ...; + run netargs; + if test ${ip_dyn} = yes; then + setenv get_cmd dhcp; + else + setenv get_cmd tftp; + fi; + if test ${sec_boot} = yes; then + ${get_cmd} ${cntr_addr} ${cntr_file}; + if run auth_os; then + run boot_os; + else + echo ERR: failed to authenticate; + fi; + else + ${get_cmd} ${loadaddr} ${image}; + if test ${boot_fit} = yes || test ${boot_fit} = try; then + bootm ${loadaddr}; + else + if ${get_cmd} ${fdt_addr_r} ${fdtfile}; then + run boot_os; + else + echo WARN: Cannot load the DT; + fi; + fi; + fi; +bsp_bootcmd=echo Running BSP bootcmd ...; + mmc dev ${mmcdev}; if mmc rescan; then + if run loadbootscript; then + run bootscript; + else + if test ${sec_boot} = yes; then + if run loadcntr; then + run mmcboot; + else run netboot; + fi; + else + if run loadimage; then + run mmcboot; + else run netboot; + fi; + fi; + fi; + fi; \ No newline at end of file diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0b32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h" + +DECLARE_GLOBAL_DATA_PTR; + +int board_early_init_f(void) +{ + /* UART1: A55, UART2: M33, UART3: M7 */ + init_uart_clk(0); + + return 0; +} + +int board_init(void) +{ + return 0; +} + +int board_late_init(void) +{ +#ifdef CONFIG_ENV_IS_IN_MMC + board_late_mmc_env_init(); +#endif + + return 0; +} + +int board_phys_sdram_size(phys_size_t *size) +{ + *size = PHYS_SDRAM_SIZE + PHYS_SDRAM_2_SIZE; + + return 0; +} diff --git a/board/freescale/imx95_evk/spl.c b/board/freescale/imx95_evk/spl.c new file mode 100644 index 0000000000000000000000000000000000000000..63050379237d7204dd38ba87e50229e5339dccea --- /dev/null +++ b/board/freescale/imx95_evk/spl.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/arch/clock.h> +#include <asm/arch/mu.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/sections.h> +#include <hang.h> +#include <init.h> +#include <spl.h> + +DECLARE_GLOBAL_DATA_PTR; + +int spl_board_boot_device(enum boot_device boot_dev_spl) +{ + switch (boot_dev_spl) { + case SD1_BOOT: + case MMC1_BOOT: + return BOOT_DEVICE_MMC1; + case SD2_BOOT: + case MMC2_BOOT: + return BOOT_DEVICE_MMC2; + case USB_BOOT: + return BOOT_DEVICE_BOARD; + default: + return BOOT_DEVICE_NONE; + } +} + +void spl_board_init(void) +{ + puts("Normal Boot\n"); +} + +void board_init_f(ulong dummy) +{ + int ret; + + /* Clear the BSS. */ + memset(__bss_start, 0, __bss_end - __bss_start); + +#ifdef CONFIG_SPL_RECOVER_DATA_SECTION + if (IS_ENABLED(CONFIG_SPL_BUILD)) + spl_save_restore_data(); +#endif + + timer_init(); + + /* Need dm_init() to run before any SCMI calls can be made. */ + spl_early_init(); + + /* Need enable SCMI drivers and ELE driver before enabling console */ + ret = imx9_probe_mu(); + if (ret) + hang(); /* if MU not probed, nothing can output, just hang here */ + + arch_cpu_init(); + + board_early_init_f(); + + preloader_console_init(); + + debug("SOC: 0x%x\n", gd->arch.soc_rev); + debug("LC: 0x%x\n", gd->arch.lifecycle); + + /* Will set ARM freq to max rate */ + clock_init_late(); + + board_init_r(NULL, 0); +} diff --git a/configs/imx95_19x19_evk_defconfig b/configs/imx95_19x19_evk_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..4d6b194528e7f3124e6626b28be0d6344758f6e5 --- /dev/null +++ b/configs/imx95_19x19_evk_defconfig @@ -0,0 +1,177 @@ +CONFIG_ARM=y +CONFIG_ARCH_IMX9=y +CONFIG_TEXT_BASE=0x90200000 +CONFIG_SYS_MALLOC_LEN=0x2000000 +CONFIG_SYS_MALLOC_F_LEN=0x10000 +CONFIG_SPL_LIBCOMMON_SUPPORT=y +CONFIG_SPL_LIBGENERIC_SUPPORT=y +CONFIG_NR_DRAM_BANKS=3 +CONFIG_ENV_SIZE=0x4000 +CONFIG_ENV_OFFSET=0x700000 +CONFIG_ENV_SECT_SIZE=0x10000 +CONFIG_DM_GPIO=y +CONFIG_DEFAULT_DEVICE_TREE="freescale/imx95-19x19-evk" +CONFIG_SPL_TEXT_BASE=0x20480000 +CONFIG_TARGET_IMX95_19X19_EVK=y +CONFIG_SYS_PROMPT="u-boot=> " +CONFIG_SPL_SERIAL=y +CONFIG_SPL_DRIVERS_MISC=y +CONFIG_SPL_STACK=0x204d6000 +CONFIG_SPL=y +CONFIG_SPL_LOAD_IMX_CONTAINER=y +CONFIG_IMX_CONFIG="arch/arm/mach-imx/imx9/scmi/imximage.cfg" +CONFIG_IMX_CONTAINER_CFG="arch/arm/mach-imx/imx9/scmi/container.cfg" +CONFIG_SYS_LOAD_ADDR=0x90400000 +CONFIG_SYS_MEMTEST_START=0x90000000 +CONFIG_SYS_MEMTEST_END=0xA0000000 +CONFIG_REMAKE_ELF=y +CONFIG_SYS_MONITOR_LEN=524288 +CONFIG_DEFAULT_FDT_FILE="imx95-19x19-evk.dtb" +CONFIG_ARCH_MISC_INIT=y +CONFIG_BOARD_EARLY_INIT_F=y +CONFIG_BOARD_LATE_INIT=y +CONFIG_SPL_MAX_SIZE=0x20000 +CONFIG_SPL_HAS_BSS_LINKER_SECTION=y +CONFIG_SPL_BSS_START_ADDR=0x204d6000 +CONFIG_SPL_BSS_MAX_SIZE=0x2000 +CONFIG_SPL_BOARD_INIT=y +CONFIG_SPL_MMC=y +CONFIG_SPL_GPIO=y +# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set +CONFIG_SPL_SYS_MALLOC=y +CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y +CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x93200000 +CONFIG_SPL_SYS_MALLOC_SIZE=0x80000 +CONFIG_SPL_SYS_MMCSD_RAW_MODE=y +CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR=y +CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x1040 +CONFIG_SPL_POWER=y +CONFIG_SDP_LOADADDR=0x90400000 +CONFIG_SPL_WATCHDOG=y +CONFIG_SYS_MAXARGS=64 +CONFIG_SYS_CBSIZE=2048 +CONFIG_SYS_PBSIZE=2074 +CONFIG_CMD_ERASEENV=y +CONFIG_CMD_NVEDIT_EFI=y +CONFIG_CMD_CRC32=y +CONFIG_CRC32_VERIFY=y +CONFIG_CMD_MEMTEST=y +CONFIG_CMD_CLK=y +CONFIG_CMD_DFU=y +CONFIG_CMD_FUSE=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_GPT=y +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_PART=y +CONFIG_CMD_POWEROFF=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_MII=y +CONFIG_CMD_PING=y +CONFIG_CMD_SNTP=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_EFIDEBUG=y +CONFIG_CMD_RTC=y +CONFIG_CMD_TIME=y +CONFIG_CMD_GETTIME=y +CONFIG_CMD_TIMER=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_HASH=y +CONFIG_CMD_EXT2=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_OF_CONTROL=y +CONFIG_SPL_OF_CONTROL=y +CONFIG_ENV_OVERWRITE=y +CONFIG_ENV_IS_NOWHERE=y +CONFIG_ENV_IS_IN_MMC=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_SYS_MMC_ENV_DEV=1 +CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y +CONFIG_USE_ETHPRIME=y +CONFIG_ETHPRIME="eth0" +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SPL_DM=y +CONFIG_REGMAP=y +CONFIG_SYSCON=y +CONFIG_DFU_MMC=y +CONFIG_DFU_RAM=y +CONFIG_IMX_RGPIO2P=y +CONFIG_MTD=y +CONFIG_DM_SPI_FLASH=y +CONFIG_DM_SPI=y +CONFIG_NXP_FSPI=y +CONFIG_SPI=y +CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_MT35XU=y +CONFIG_SF_DEFAULT_BUS=0 +CONFIG_SF_DEFAULT_SPEED=40000000 +CONFIG_DM_PCA953X=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_IMX_LPI2C=y +CONFIG_SUPPORT_EMMC_BOOT=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS400_ES_SUPPORT=y +CONFIG_MMC_HS400_SUPPORT=y +CONFIG_FSL_USDHC=y +CONFIG_PHY=y +CONFIG_MII=y +CONFIG_PINCTRL=y +CONFIG_SPL_PINCTRL=y +CONFIG_SPL_DM_REGULATOR=y +CONFIG_SPL_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR_GPIO=y +CONFIG_DM_RTC=y +CONFIG_DM_SERIAL=y +CONFIG_FSL_LPUART=y +CONFIG_ULP_WATCHDOG=y +CONFIG_LZO=y +CONFIG_BZIP2=y +CONFIG_HUSH_PARSER=y +CONFIG_OF_LIBFDT_OVERLAY=y +CONFIG_OF_SYSTEM_SETUP=y +CONFIG_ADP5585_GPIO=y +CONFIG_SPL_I2C=y +CONFIG_POWER_DOMAIN=y +# SCMI options +CONFIG_DM_MAILBOX=y +CONFIG_SCMI_FIRMWARE=y +CONFIG_SCMI_AGENT_SMCCC=n +CONFIG_CLK_SCMI=y +CONFIG_CLK=y +CONFIG_SPL_CLK_SCMI=y +CONFIG_SPL_CLK=y +CONFIG_PINCTRL_IMX_SCMI=y +CONFIG_SCMI_POWER_DOMAIN=y +CONFIG_SPL_DM_MAILBOX=y +CONFIG_SPL_FIRMWARE=y +#CONFIG_SPL_POWER_DOMAIN=y +CONFIG_SPL_IMX_MU_MBOX=y +CONFIG_IMX_MU_MBOX=y +#CONFIG_REMOTEPROC_SCMI_LMM=y +#CONFIG_CMD_REMOTEPROC=y +# CONFIG_RESET_SCMI=y +# CONFIG_DM_REGULATOR_SCMI=y +CONFIG_SPL_OF_TRANSLATE=y +CONFIG_PCI=y +CONFIG_PCIE_ECAM_GENERIC=y +CONFIG_PCI_SCAN_SHOW=y +CONFIG_CMD_PCI=y +CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y +CONFIG_PHYLIB=y +CONFIG_PHYLIB_10G=y +CONFIG_PHY_AQUANTIA=y +CONFIG_PHY_REALTEK=y +CONFIG_FSL_ENETC=y +CONFIG_SYS_RX_ETH_BUFFER=8 +CONFIG_IMX8_ROMAPI=n +CONFIG_CLK_CCF=y +CONFIG_SPL_CLK_CCF=y +CONFIG_SPL_RECOVER_DATA_SECTION=y +CONFIG_ENV_SOURCE_FILE="imx95_19x19_evk" diff --git a/doc/board/nxp/imx95_evk.rst b/doc/board/nxp/imx95_evk.rst new file mode 100644 index 0000000000000000000000000000000000000000..5554e96f999240638819b556c082f9bb968c7a0e --- /dev/null +++ b/doc/board/nxp/imx95_evk.rst @@ -0,0 +1,114 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +imx95_evk +======================= + +U-Boot for the NXP i.MX95 19x19 EVK board + +Quick Start +----------- + +- Get ahab-container.img +- Get DDR PHY Firmware Images +- Get and Build OEI Images +- Get and Build System Manager Image +- Get and Build the ARM Trusted Firmware +- Build the Bootloader Image +- Boot + +Get ahab-container.img +-------------------------------------- + +Note: srctree is U-Boot source directory + +.. code-block:: bash + + $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-ele-imx-1.3.0-17945fc.bin + $ sh firmware-ele-imx-1.3.0-17945fc.bin --auto-accept + $ cp firmware-ele-imx-1.3.0-17945fc/mx95a0-ahab-container.img $(srctree) + +Get DDR PHY Firmware Images +-------------------------------------- + +Note: srctree is U-Boot source directory + +.. code-block:: bash + + $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.26-d4c33ab.bin + $ sh firmware-imx-8.26-d4c33ab.bin --auto-accept + $ cp firmware-imx-8.26-d4c33ab/firmware/ddr/synopsys/lpddr5*v202311.bin $(srctree) + +Get and Build OEI Images +-------------------------------------- + +Note: srctree is U-Boot source directory +Get OEI from: https://github.com/nxp-imx/imx-oei +branch: master + +.. code-block:: bash + + $ sudo apt -y install make gcc g++-multilib srecord + $ wget https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-g... + $ tar xvf arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz + $ export TOOLS=$PWD + $ git clone -b master https://github.com/nxp-imx/imx-oei.git + $ cd imx-oei + $ make board=mx95lp5 oei=ddr DEBUG=1 + $ cp build/mx95lp5/ddr/oei-m33-ddr.bin $(srctree) + + $ make board=mx95lp5 oei=tcm DEBUG=1 + $ cp build/mx95lp5/tcm/oei-m33-tcm.bin $(srctree) + +Get and Build System Manager Image +-------------------------------------- + +Note: srctree is U-Boot source directory +Get System Manager from: https://github.com/nxp-imx/imx-sm +branch: master + +.. code-block:: bash + + $ sudo apt -y install make gcc g++-multilib srecord + $ wget https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-g... + $ tar xvf arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz + $ export TOOLS=$PWD + $ git clone -b master https://github.com/nxp-imx/imx-sm.git + $ cd imx-sm + $ make config=mx95evk all + $ cp build/mx95evk/m33_image.bin $(srctree) + +Get and Build the ARM Trusted Firmware +-------------------------------------- + +Note: srctree is U-Boot source directory +Get ATF from: https://github.com/nxp-imx/imx-atf/ +branch: lf_v2.10 + +.. code-block:: bash + + $ export CROSS_COMPILE=aarch64-poky-linux- + $ unset LDFLAGS + $ git clone -b lf_v2.10 https://github.com/nxp-imx/imx-atf.git + $ cd imx-atf + $ make PLAT=imx95 bl31 + $ cp build/imx95/release/bl31.bin $(srctree) + +Build the Bootloader Image +------------ + +.. code-block:: bash + + $ export CROSS_COMPILE=aarch64-poky-linux- + $ make imx95_19x19_evk_defconfig + $ make + +Copy imx-boot-imx95.bin to the MicroSD card: + +.. code-block:: bash + + $ sudo dd if=flash.bin of=/dev/sd[x] bs=1k seek=32 conv=fsync + +Boot +---- + +Set i.MX95 boot device to MicroSD card diff --git a/doc/board/nxp/index.rst b/doc/board/nxp/index.rst index 8ca4b5619863f616dbedc7eb0ff8a286a3fbbf48..e7ec725cc04e9755ba3dac37561c26ebc3f1c716 100644 --- a/doc/board/nxp/index.rst +++ b/doc/board/nxp/index.rst @@ -15,6 +15,7 @@ NXP Semiconductors imx91_11x11_evk imx93_9x9_qsb imx93_11x11_evk + imx95_evk imxrt1020-evk imxrt1050-evk imxrt1170-evk diff --git a/include/configs/imx95_evk.h b/include/configs/imx95_evk.h new file mode 100644 index 0000000000000000000000000000000000000000..b077fa7af7d1e56a00278d0f3feb9fd7b0c55f42 --- /dev/null +++ b/include/configs/imx95_evk.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2025 NXP + */ + +#ifndef __IMX95_EVK_H +#define __IMX95_EVK_H + +#include <linux/sizes.h> +#include <linux/stringify.h> +#include <asm/arch/imx-regs.h> + +#define CFG_SYS_UBOOT_BASE \ + (QSPI0_AMBA_BASE + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR * 512) + +#define CFG_SYS_INIT_RAM_ADDR 0x90000000 +#define CFG_SYS_INIT_RAM_SIZE 0x200000 + +#define CFG_SYS_SDRAM_BASE 0x90000000 +#define PHYS_SDRAM 0x90000000 +/* Totally 16GB */ +#define PHYS_SDRAM_SIZE 0x70000000 /* 2GB - 256MB DDR */ +#define PHYS_SDRAM_2_SIZE 0x380000000 /* 14GB */ + +#define WDOG_BASE_ADDR WDG3_BASE_ADDR + +#endif

On Wed, Jan 15, 2025 at 5:30 AM Alice Guo alice.guo@oss.nxp.com wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
<snip...>
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0b32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Hi Alice,
Most of the above are not needed (and should be alphabetized anyway)
The only ones needed are: #include <asm/arch/clock.h> #include <asm/gpio.h> #include <asm/mach-imx/sys_proto.h>
+DECLARE_GLOBAL_DATA_PTR;
not needed as you don't use gd
Best Regards,
Tim

On Thu, Jan 16, 2025 at 11:38 AM Tim Harvey tharvey@gateworks.com wrote:
On Wed, Jan 15, 2025 at 5:30 AM Alice Guo alice.guo@oss.nxp.com wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
<snip...>
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0b32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Hi Alice,
Most of the above are not needed (and should be alphabetized anyway)
The only ones needed are: #include <asm/arch/clock.h> #include <asm/gpio.h> #include <asm/mach-imx/sys_proto.h>
+DECLARE_GLOBAL_DATA_PTR;
not needed as you don't use gd
Best Regards,
Tim
Hi Alic,
One more thing to do for next series. Refresh your imx95_19x19_evk_defconfig as its very out of sync:
make imx95_19x19_evk_defconfig make savedefconfig cp defconfig configs/imx95_19x19_evk_defconfig
Best Regards,
Tim

On 1/16/25 8:42 PM, Tim Harvey wrote:
On Thu, Jan 16, 2025 at 11:38 AM Tim Harvey tharvey@gateworks.com wrote:
On Wed, Jan 15, 2025 at 5:30 AM Alice Guo alice.guo@oss.nxp.com wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
<snip...>
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0b32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Hi Alice,
Most of the above are not needed (and should be alphabetized anyway)
The only ones needed are: #include <asm/arch/clock.h> #include <asm/gpio.h> #include <asm/mach-imx/sys_proto.h>
+DECLARE_GLOBAL_DATA_PTR;
not needed as you don't use gd
Best Regards,
Tim
Hi Alic,
One more thing to do for next series. Refresh your imx95_19x19_evk_defconfig as its very out of sync:
make imx95_19x19_evk_defconfig make savedefconfig cp defconfig configs/imx95_19x19_evk_defconfig
And please enable CONFIG_FIT and CONFIG_FIT_VERBOSE .

On Thu, Jan 16, 2025 at 09:03:35PM +0100, Marek Vasut wrote:
On 1/16/25 8:42 PM, Tim Harvey wrote:
On Thu, Jan 16, 2025 at 11:38 AM Tim Harvey tharvey@gateworks.com wrote:
On Wed, Jan 15, 2025 at 5:30 AM Alice Guo alice.guo@oss.nxp.com wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
<snip...>
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0b32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Hi Alice,
Most of the above are not needed (and should be alphabetized anyway)
The only ones needed are: #include <asm/arch/clock.h> #include <asm/gpio.h> #include <asm/mach-imx/sys_proto.h>
+DECLARE_GLOBAL_DATA_PTR;
not needed as you don't use gd
Best Regards,
Tim
Hi Alic,
One more thing to do for next series. Refresh your imx95_19x19_evk_defconfig as its very out of sync:
make imx95_19x19_evk_defconfig make savedefconfig cp defconfig configs/imx95_19x19_evk_defconfig
And please enable CONFIG_FIT and CONFIG_FIT_VERBOSE .
Which should already be there if using bootstd to boot and not distro boot scripts, another thing that should be done now and not later.

On Thu, Jan 16, 2025 at 11:42 AM Tim Harvey tharvey@gateworks.com wrote:
On Thu, Jan 16, 2025 at 11:38 AM Tim Harvey tharvey@gateworks.com wrote:
On Wed, Jan 15, 2025 at 5:30 AM Alice Guo alice.guo@oss.nxp.com wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
<snip...>
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0b32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Hi Alice,
Most of the above are not needed (and should be alphabetized anyway)
The only ones needed are: #include <asm/arch/clock.h> #include <asm/gpio.h> #include <asm/mach-imx/sys_proto.h>
+DECLARE_GLOBAL_DATA_PTR;
not needed as you don't use gd
Best Regards,
Tim
Hi Alic,
One more thing to do for next series. Refresh your imx95_19x19_evk_defconfig as its very out of sync:
make imx95_19x19_evk_defconfig make savedefconfig cp defconfig configs/imx95_19x19_evk_defconfig
Hi Alice,
I'm also seeing the following files left around after a build that are also not removed after a clean/distclean: mkimage-out.imx-boot.spl mkimage-out.imx-boot.u-boot mkimage.imx-boot.spl mkimage.imx-boot.u-boot
Best Regards,
Tim

-----邮件原件----- 发件人: Tim Harvey tharvey@gateworks.com 发送时间: 2025年1月17日 6:48 收件人: Alice Guo (OSS) alice.guo@oss.nxp.com 抄送: Tom Rini trini@konsulko.com; Stefano Babic sbabic@denx.de; Fabio Estevam festevam@gmail.com; dl-uboot-imx uboot-imx@nxp.com; Lukasz Majewski lukma@denx.de; Sean Anderson seanga2@gmail.com; Simon Glass sjg@chromium.org; Alper Nebi Yasak alpernebiyasak@gmail.com; marex@denx.de; u-boot@lists.denx.de; Alice Guo alice.guo@nxp.com; Ye Li ye.li@nxp.com; Peng Fan peng.fan@nxp.com 主题: [EXT] Re: [PATCH v4 20/20] imx95_evk: add i.MX95 19x19 EVK board basic support
Caution: This is an external email. Please take care when clicking links or opening attachments. When in doubt, report the message using the 'Report this email' button
On Thu, Jan 16, 2025 at 11:42 AM Tim Harvey tharvey@gateworks.com wrote:
On Thu, Jan 16, 2025 at 11:38 AM Tim Harvey tharvey@gateworks.com
wrote:
On Wed, Jan 15, 2025 at 5:30 AM Alice Guo alice.guo@oss.nxp.com
wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
<snip...>
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index
0000000000000000000000000000000000000000..1ed15ca47b157067a7376b0 b
32101dc7f9899f4f --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> #include <asm/arch/clock.h> +#include <fdt_support.h> #include <asm/io.h> #include +<linux/bitfield.h> #include <linux/bitops.h> #include +<linux/delay.h> #include <miiphy.h> #include <netdev.h> #include +<asm/gpio.h> #include <asm/mach-imx/sys_proto.h> #include +<scmi_agent.h> #include <scmi_protocols.h> #include +"../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Hi Alice,
Most of the above are not needed (and should be alphabetized anyway)
The only ones needed are: #include <asm/arch/clock.h> #include <asm/gpio.h> #include <asm/mach-imx/sys_proto.h>
+DECLARE_GLOBAL_DATA_PTR;
not needed as you don't use gd
Best Regards,
Tim
Hi Alic,
One more thing to do for next series. Refresh your imx95_19x19_evk_defconfig as its very out of sync:
make imx95_19x19_evk_defconfig make savedefconfig cp defconfig configs/imx95_19x19_evk_defconfig
Hi Alice,
I'm also seeing the following files left around after a build that are also not removed after a clean/distclean: mkimage-out.imx-boot.spl mkimage-out.imx-boot.u-boot mkimage.imx-boot.spl mkimage.imx-boot.u-boot
Best Regards,
Tim
Will improve these in v5.
Best Regards, Alice Guo

On 1/15/25 2:29 PM, Alice Guo wrote:
From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
Messaging unit for EdgeLock Secure Enclave, messaging unit for System Manager, uSDHC for SD Card, gpio, lpuart are supported now.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com
arch/arm/dts/imx95-19x19-evk-u-boot.dtsi | 68 ++++++++++ arch/arm/dts/imx95-u-boot.dtsi | 157 +++++++++++++++++++++++ arch/arm/mach-imx/imx9/Kconfig | 6 + arch/arm/mach-imx/imx9/scmi/container.cfg | 10 ++ arch/arm/mach-imx/imx9/scmi/imximage.cfg | 15 +++ board/freescale/imx95_evk/Kconfig | 12 ++ board/freescale/imx95_evk/MAINTAINERS | 6 + board/freescale/imx95_evk/Makefile | 11 ++ board/freescale/imx95_evk/imx95_19x19_evk.env | 90 +++++++++++++ board/freescale/imx95_evk/imx95_evk.c | 54 ++++++++ board/freescale/imx95_evk/spl.c | 72 +++++++++++ configs/imx95_19x19_evk_defconfig | 177 ++++++++++++++++++++++++++ doc/board/nxp/imx95_evk.rst | 114 +++++++++++++++++ doc/board/nxp/index.rst | 1 + include/configs/imx95_evk.h | 27 ++++ 15 files changed, 820 insertions(+)
diff --git a/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..2902e8f711c3ef705df27ca4fc7a89d1bd7ffc71 --- /dev/null +++ b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include "imx95-u-boot.dtsi"
+&gpio1 {
- reg = <0 0x47400000 0 0x1000>, <0 0x47400040 0 0x40>;
Why is this override here ?
+};
+&lpuart1 {
- clocks = <&scmi_clk IMX95_CLK_LPUART1>, <&scmi_clk IMX95_CLK_LPUART1>;
- clock-names = "ipg", "per";
Are these overrides necessary ?
[...]
diff --git a/board/freescale/imx95_evk/imx95_19x19_evk.env b/board/freescale/imx95_evk/imx95_19x19_evk.env new file mode 100644 index 0000000000000000000000000000000000000000..8d2b5dfcf07d251c77440bae44d62bff6253dfc7 --- /dev/null +++ b/board/freescale/imx95_evk/imx95_19x19_evk.env @@ -0,0 +1,90 @@ +sec_boot=no +initrd_addr=0x93800000 +emmc_dev=0 +sd_dev=1 +scriptaddr=0x93500000 +kernel_addr_r=CONFIG_SYS_LOAD_ADDR +image=Image +splashimage=0xA0000000 +console=ttyLP0,115200 earlycon +fdt_addr_r=0x93000000 +fdt_addr=0x93000000 +cntr_addr=0xA8000000 +cntr_file=os_cntr_signed.bin +boot_fit=no +fdtfile=CONFIG_DEFAULT_FDT_FILE +bootm_size=0x10000000 +mmcautodetect=yes +mmcargs=setenv bootargs console=${console} root=${mmcroot} +loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}; +bootscript=echo Running bootscript from mmc ...; source +loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image} +loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile} +loadcntr=fatload mmc ${mmcdev}:${mmcpart} ${cntr_addr} ${cntr_file} +auth_os=auth_cntr ${cntr_addr} +boot_os=booti ${loadaddr} - ${fdt_addr_r}; +mmcboot=echo Booting from mmc ...;
Did you consider switching to modern "bootflow"/"bootmethod" ?
[...]
+++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2025 NXP
- */
+#include <env.h> +#include <init.h> +#include <asm/global_data.h> +#include <asm/arch-imx9/ccm_regs.h> +#include <asm/arch/clock.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/gpio.h> +#include <asm/mach-imx/sys_proto.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include "../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include "../../../dts/upstream/src/arm64/freescale/imx95-power.h"
Are all these headers really needed for this trivial C file ? I think not, so please remove all the unnecessary ones .
+DECLARE_GLOBAL_DATA_PTR;
+int board_early_init_f(void) +{
- /* UART1: A55, UART2: M33, UART3: M7 */
- init_uart_clk(0);
- return 0;
+}
+int board_init(void) +{
- return 0;
+}
+int board_late_init(void) +{ +#ifdef CONFIG_ENV_IS_IN_MMC
- board_late_mmc_env_init();
Use if (CONFIG_IS_ENABLED()) ... please fix globally .
[...]
+++ b/include/configs/imx95_evk.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2025 NXP
- */
+#ifndef __IMX95_EVK_H +#define __IMX95_EVK_H
+#include <linux/sizes.h> +#include <linux/stringify.h> +#include <asm/arch/imx-regs.h>
+#define CFG_SYS_UBOOT_BASE \
- (QSPI0_AMBA_BASE + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR * 512)
Is this option really necessary ?
Try and look at CONFIG_POSITION_INDEPENDENT .
+#define CFG_SYS_INIT_RAM_ADDR 0x90000000 +#define CFG_SYS_INIT_RAM_SIZE 0x200000
+#define CFG_SYS_SDRAM_BASE 0x90000000 +#define PHYS_SDRAM 0x90000000 +/* Totally 16GB */ +#define PHYS_SDRAM_SIZE 0x70000000 /* 2GB - 256MB DDR */ +#define PHYS_SDRAM_2_SIZE 0x380000000 /* 14GB */
+#define WDOG_BASE_ADDR WDG3_BASE_ADDR
Is this watchdog config option needed ?
participants (5)
-
Alice Guo
-
Alice Guo (OSS)
-
Marek Vasut
-
Tim Harvey
-
Tom Rini