[PATCH 00/17] (no cover subject)

This patch set adds the basic support of i.MX95 and has been tested on i.MX95 19x19 EVK.
Signed-off-by: Alice Guo alice.guo@oss.nxp.com --- Alice Guo (1): imx: Kconfig: IMX8_ROMAPI is not configured for i.MX95
Peng Fan (7): mailbox: add i.MX95 Messaging Unit (MU) driver pinctrl: nxp: add SCMI pin control protocol 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 imx9: scmi: add i.MX95 SoC and clock related code scmi: add the macro SCMI_MSG
Teo Hall (1): imx: add V2X container support on i.MX95
Viorel Suman (1): firmware: scmi: smt: Interrupt communication enable
Ye Li (7): clk: scmi: check the clock state/parent/rate control permissions scmi_protocols: update struct scmi_base_discover_list_protocols_out imx9: add i.MX95 Kconfig and Makefile imx9: scmi: soc: Override h_spl_load_read with trampoline buffer imx95_evk: add i.MX95 19x19 EVK board basic support imx: Support loading container image from RAM device imx: Fix ROMAPI driver to load container image
arch/arm/dts/imx95-19x19-evk-u-boot.dtsi | 172 ++++++ arch/arm/include/asm/arch-imx/cpu.h | 3 + arch/arm/include/asm/arch-imx9/clock.h | 10 +- arch/arm/include/asm/arch-imx9/gpio.h | 2 + arch/arm/include/asm/arch-imx9/imx-regs.h | 7 +- arch/arm/include/asm/arch-imx9/sys_proto.h | 3 +- arch/arm/include/asm/mach-imx/sys_proto.h | 44 +- arch/arm/mach-imx/Kconfig | 2 +- arch/arm/mach-imx/image-container.c | 90 ++- arch/arm/mach-imx/imx9/Kconfig | 13 + arch/arm/mach-imx/imx9/Makefile | 11 +- arch/arm/mach-imx/imx9/scmi/Makefile | 6 + arch/arm/mach-imx/imx9/scmi/clock.c | 328 +++++++++++ arch/arm/mach-imx/imx9/scmi/clock_scmi.c | 148 +++++ arch/arm/mach-imx/imx9/scmi/container.cfg | 7 + arch/arm/mach-imx/imx9/scmi/soc.c | 872 +++++++++++++++++++++++++++++ arch/arm/mach-imx/spl_imx_romapi.c | 47 +- board/freescale/imx95_evk/Kconfig | 12 + board/freescale/imx95_evk/Makefile | 11 + board/freescale/imx95_evk/imx95_evk.c | 75 +++ board/freescale/imx95_evk/spl.c | 119 ++++ common/spl/spl_mmc.c | 2 +- common/spl/spl_ram.c | 34 +- configs/imx95_19x19_evk_defconfig | 189 +++++++ drivers/clk/clk_scmi.c | 134 ++++- drivers/firmware/scmi/scmi_agent-uclass.c | 11 + drivers/firmware/scmi/smt.c | 4 + 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 | 148 +++++ include/scmi_agent-uclass.h | 2 + include/scmi_agent.h | 11 + include/scmi_protocols.h | 101 +++- 40 files changed, 3156 insertions(+), 65 deletions(-) --- base-commit: 28dc47038edc4e93f32d75a357131bcf01a18d85 change-id: 20241016-imx95-4c20a5becc0f
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 harware used is this MU.
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 --- drivers/mailbox/Kconfig | 7 + drivers/mailbox/Makefile | 1 + drivers/mailbox/imx-mailbox.c | 417 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 425 insertions(+)
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 59e8d0de93cacfcbd9e10b0796a7abc53653715e..96a6adb709526325b30f849c7cc1125f55de314f 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -5,6 +5,7 @@
obj-$(CONFIG_$(SPL_)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..584950b20d0afe0aac8a2b0c6146282292b43859 --- /dev/null +++ b/drivers/mailbox/imx-mailbox.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017-2023 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 Wed, Oct 16, 2024 at 03:17:54PM +0800, Alice Guo wrote:
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 harware used is this MU.
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
drivers/mailbox/Kconfig | 7 + drivers/mailbox/Makefile | 1 + drivers/mailbox/imx-mailbox.c | 417 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 425 insertions(+)
Fabio already noted the general MAINTAINERS missing part, but all of these new drivers and so forth should be covered under some entry as well in the top level MAINTAINERS file.

From: Viorel Suman viorel.suman@nxp.com
When interrupt driven communication is supported, the transport allows the caller to choose between interrupt and polling driven communications. To make the choice, the channel flags are used. i.MX95 uses interrupt driven communication so that Channel flags should be set to 1.
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/smt.c | 4 ++++ drivers/firmware/scmi/smt.h | 10 ++++++++++ 2 files changed, 14 insertions(+)
diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 67d2f45002490ab64a6bc997eda93a8f4681d99b..e5b902dcb0e641065f4c0faae6bc6660a7e2c6af 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -48,6 +48,10 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (!smt->buf) return -ENOMEM;
+ /* Command should complete via an interrupt so that set Channel flags to 1. */ + if (IS_ENABLED(CONFIG_IMX95)) + 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);
/*

From: Peng Fan peng.fan@nxp.com
This patch provides a driver for the SCMI pin control protocol which is based on ARM's System Control and Management Interface (SCMI) 3.2. 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/firmware/scmi/scmi_agent-uclass.c | 11 +++ 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_agent-uclass.h | 2 + include/scmi_protocols.h | 34 ++++++++ 8 files changed, 213 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 8c907c3b0328095c4b35ba089ed608fcda48b567..6b9d8361c236114b6ca6c36390ab1aeab9e60e0f 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/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index 06c26f156f6c8f63204604d6065485629cfd9b61..0086f9825837bf568bd7633a21da860e410eeeee 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 ff466c4910414db3ad8790cd59596a9836b29e28..cdadf7a3f10e3be940fe5660a7103143a7f517b3 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 2024 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 { /* @@ -215,7 +218,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) || (info->flags & IMX_USE_SCMI)) return 0;
addr = devfdt_get_addr_size_index(dev, 0, &size); diff --git a/drivers/pinctrl/nxp/pinctrl-imx.h b/drivers/pinctrl/nxp/pinctrl-imx.h index fa4c084e2fc067fcb4e92c33312d4f430081fffd..b2f8865ded0ae380fc0156baa5eaea505c12f501 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 2024 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
+#ifdef 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..19b1b433a5ced0bb85e52f4d19bdeab2825f275e --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-scmi.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <misc.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <asm/io.h> +#include <asm/mach-imx/iomux-v3.h> +#include <linux/bitops.h> + +#include "pinctrl-imx.h" + +#if defined(CONFIG_IMX93) +#define DAISY_OFFSET 0x360 +#endif +#if defined(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_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) 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 */

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 */

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: Ye Li ye.li@nxp.com
SCMI clock management protocol driver in Linux checks clock state, parent and rate control permissions. To be consistent with the kernel driver, add this check here.
Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- drivers/clk/clk_scmi.c | 116 +++++++++++++++++++++++++++++++++++++++++++---- include/scmi_protocols.h | 25 +++++++++- 2 files changed, 130 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c index 84333cdd0ccfe566c269f776f39725c69883c25c..cbc7be718a5e123be8cd0865d71cff3577d506a2 100644 --- a/drivers/clk/clk_scmi.c +++ b/drivers/clk/clk_scmi.c @@ -12,6 +12,53 @@ #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 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) + return ret; + + return out.permissions; + } else { + debug("SCMI clock management protocol version is less than 3.0.\n"); + return -EOPNOTSUPP; + } +} + static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks) { struct scmi_clk_protocol_attr_out out; @@ -78,12 +125,26 @@ static int scmi_clk_gate(struct clk *clk, int enable)
static int scmi_clk_enable(struct clk *clk) { - return scmi_clk_gate(clk, 1); + struct clk_scmi *clkscmi = container_of(clk, 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 not have permission */ + debug("SCMI CLOCK: the clock cannot be enabled by the agent.\n"); + return 0; }
static int scmi_clk_disable(struct clk *clk) { - return scmi_clk_gate(clk, 0); + struct clk_scmi *clkscmi = container_of(clk, 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 not have permission */ + debug("SCMI CLOCK: the clock cannot be disabled by the agent.\n"); + return 0; }
static ulong scmi_clk_get_rate(struct clk *clk) @@ -108,7 +169,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 +194,21 @@ 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 = container_of(clk, 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 not have permission */ + debug("SCMI CLOCK: the clock rate cannot be changed by the agent.\n"); + return 0; +} + static int scmi_clk_probe(struct udevice *dev) { - struct clk *clk; + struct clk_scmi *clk_scmi; size_t num_clocks, i; int ret;
@@ -158,27 +231,38 @@ static int scmi_clk_probe(struct udevice *dev) char *clock_name;
if (!scmi_clk_get_attibute(dev, i, &clock_name)) { - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk || !clock_name) + 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); return ret; }
- clk_dm(i, clk); + clk_dm(i, &clk_scmi->clk); + + ret = scmi_clk_get_permissions(dev, i); + if (ret > 0) { + clk_scmi->ctrl_flags = ret; + } else if (ret == -EOPNOTSUPP) { + clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | + SUPPORT_CLK_RATE_CONTROL; + } else { + debug("SCMI CLOCK: getting permissions failed.\n"); + clk_scmi->ctrl_flags = 0; + } } }
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 +281,18 @@ 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 = container_of(clk, 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 not have permission */ + debug("SCMI CLOCK: the clock's parent cannot be changed by the agent.\n"); + return 0; +} + 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..8a5830d582d8baf4b4781ab860f09f49d335a023 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) @@ -858,6 +860,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 */

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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 8a5830d582d8baf4b4781ab860f09f49d335a023..b3ca85e1697dced50f9d42600255044fa9fa4aab 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -145,7 +145,7 @@ struct scmi_base_discover_impl_version_out { struct scmi_base_discover_list_protocols_out { s32 status; u32 num_protocols; - u32 protocols[3]; + u32 protocols[4]; };
/**

On Wed, Oct 16, 2024 at 03:18:01PM +0800, 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.
This needs to be in the file too for documentation generation to pick up, thanks.

Hi Tom,
-----邮件原件----- 发件人: Tom Rini trini@konsulko.com 发送时间: 2024年10月17日 2:07 收件人: Alice Guo (OSS) alice.guo@oss.nxp.com 抄送: Lukasz Majewski lukma@denx.de; Sean Anderson seanga2@gmail.com; Stefano Babic sbabic@denx.de; Fabio Estevam festevam@gmail.com; Marek Vasut marex@denx.de; dl-uboot-imx uboot-imx@nxp.com; u-boot@lists.denx.de; Ye Li ye.li@nxp.com; Alice Guo alice.guo@nxp.com 主题: [EXT] Re: [PATCH 08/17] scmi_protocols: update struct scmi_base_discover_list_protocols_out
On Wed, Oct 16, 2024 at 03:18:01PM +0800, 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.
This needs to be in the file too for documentation generation to pick up, thanks.
May I ask which file you mention?
Best Regards, Alice Guo
-- Tom

On Thu, Oct 31, 2024 at 09:57:27AM +0000, Alice Guo (OSS) wrote:
Hi Tom,
-----邮件原件----- 发件人: Tom Rini trini@konsulko.com 发送时间: 2024年10月17日 2:07 收件人: Alice Guo (OSS) alice.guo@oss.nxp.com 抄送: Lukasz Majewski lukma@denx.de; Sean Anderson seanga2@gmail.com; Stefano Babic sbabic@denx.de; Fabio Estevam festevam@gmail.com; Marek Vasut marex@denx.de; dl-uboot-imx uboot-imx@nxp.com; u-boot@lists.denx.de; Ye Li ye.li@nxp.com; Alice Guo alice.guo@nxp.com 主题: [EXT] Re: [PATCH 08/17] scmi_protocols: update struct scmi_base_discover_list_protocols_out
On Wed, Oct 16, 2024 at 03:18:01PM +0800, 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.
This needs to be in the file too for documentation generation to pick up, thanks.
May I ask which file you mention?
The file you were changing in this patch.

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: Alice Guo alice.guo@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com --- arch/arm/include/asm/arch-imx/cpu.h | 3 + arch/arm/include/asm/arch-imx9/clock.h | 10 +- arch/arm/include/asm/arch-imx9/gpio.h | 2 + arch/arm/include/asm/arch-imx9/imx-regs.h | 7 +- arch/arm/include/asm/arch-imx9/sys_proto.h | 3 +- arch/arm/include/asm/mach-imx/sys_proto.h | 40 ++ arch/arm/mach-imx/imx9/scmi/Makefile | 6 + arch/arm/mach-imx/imx9/scmi/clock.c | 328 ++++++++++++ arch/arm/mach-imx/imx9/scmi/clock_scmi.c | 148 +++++ arch/arm/mach-imx/imx9/scmi/container.cfg | 7 + arch/arm/mach-imx/imx9/scmi/soc.c | 832 +++++++++++++++++++++++++++++ 11 files changed, 1382 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/arch-imx/cpu.h b/arch/arm/include/asm/arch-imx/cpu.h index b0468a1a1364e13e1a7eb211979c5d3f280e0c16..5ba67c9a1aa08115bb78467e23e96eff386d97cb 100644 --- a/arch/arm/include/asm/arch-imx/cpu.h +++ b/arch/arm/include/asm/arch-imx/cpu.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * (C) Copyright 2014 Freescale Semiconductor, Inc. + * Copyright 2024 NXP */
#define MXC_CPU_MX23 0x23 @@ -71,6 +72,8 @@ #define MXC_CPU_IMX9302 0xC9 /* dummy ID */ #define MXC_CPU_IMX9301 0xCA /* 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 76f12118592cd0a83cbcaa57f8d582847ffd68f3..cb8e5a724b0e287c15687019da5721069f2bb253 100644 --- a/arch/arm/include/asm/arch-imx9/clock.h +++ b/arch/arm/include/asm/arch-imx9/clock.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2022 NXP + * Copyright 2024 NXP * - * Peng Fan <peng.fan at nxp.com> + * Peng Fan peng.fan@nxp.com */
#ifndef __CLOCK_IMX9__ @@ -248,5 +248,11 @@ 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 set_clk_netc(enum enet_freq type); 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/gpio.h b/arch/arm/include/asm/arch-imx9/gpio.h index 40732022e7e56e9e460350a97a50b47bb2e98fa0..1917a121aa05140a677a6225195117c8e0c182e2 100644 --- a/arch/arm/include/asm/arch-imx9/gpio.h +++ b/arch/arm/include/asm/arch-imx9/gpio.h @@ -6,6 +6,8 @@ #ifndef __ASM_ARCH_IMX9_GPIO_H #define __ASM_ARCH_IMX9_GPIO_H
+#include <asm/types.h> + struct gpio_regs { u32 gpio_pdor; u32 gpio_psor; diff --git a/arch/arm/include/asm/arch-imx9/imx-regs.h b/arch/arm/include/asm/arch-imx9/imx-regs.h index ef9538bd42e8b2972d7e6829a402bc4f661e3cbb..5512d66ee0a44348845d2a559bd99327115bc1d0 100644 --- a/arch/arm/include/asm/arch-imx9/imx-regs.h +++ b/arch/arm/include/asm/arch-imx9/imx-regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2022 NXP + * Copyright 2024 NXP */
#ifndef __ASM_ARCH_IMX9_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..aac43c74a44a127549c67059e70f570aae5f680c 100644 --- a/arch/arm/include/asm/arch-imx9/sys_proto.h +++ b/arch/arm/include/asm/arch-imx9/sys_proto.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright (C) 2022 NXP + * Copyright (C) 2024 NXP */
#ifndef __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 31ace977d2bd9264fd0f0a43e0c79e18d5a65673..878a4821996392e36d9bb02613276e502f463eef 100644 --- a/arch/arm/include/asm/mach-imx/sys_proto.h +++ b/arch/arm/include/asm/mach-imx/sys_proto.h @@ -2,6 +2,7 @@ /* * (C) Copyright 2009 * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * Copyright 2024 NXP */
#ifndef _SYS_PROTO_H_ @@ -97,6 +98,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_imxrt1020() (is_cpu_type(MXC_CPU_IMXRT1020)) #define is_imxrt1050() (is_cpu_type(MXC_CPU_IMXRT1050))
@@ -210,6 +213,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);
+#ifdef CONFIG_SCMI_FIRMWARE +typedef struct rom_passover { + uint16_t tag; //!< Tag + uint8_t len; //!< Fixed value of 0x80 + uint8_t ver; //!< Version + uint32_t boot_mode; //!< Boot mode + uint32_t card_addr_mode; //!< SD card address mode + uint32_t bad_blks_of_img_set0; //!< NAND bad block count skipped 1 + uint32_t ap_mu_id; //!< AP MU ID + uint32_t bad_blks_of_img_set1; //!< NAND bad block count skipped 1 + uint8_t boot_stage; //!< Boot stage + uint8_t img_set_sel; //!< Image set booted from + uint8_t rsv0[2]; //!< Reserved + uint32_t img_set_end; //!< Offset of Image End + uint32_t rom_version; //!< ROM version + uint8_t boot_dev_state; //!< Boot device state + uint8_t boot_dev_inst; //!< Boot device type + uint8_t boot_dev_type; //!< Boot device instance + uint8_t rsv1; //!< Reserved + uint32_t dev_page_size; //!< Boot device page size + uint32_t cnt_header_ofs; //!< Container header offset + uint32_t img_ofs; //!< Image offset +} __attribute__ ((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..78f24c6e5151e2d7280b9c456c0477a3b6a958c4 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2024 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..b46ff3f9944d2b08db56f7ef892fba715afd2655 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/clock.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <asm/arch/ccm_regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <command.h> +#include <errno.h> +#ifdef CONFIG_CLK_SCMI +#include "../../../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <linux/clk-provider.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +u32 get_arm_core_clk(void) +{ + u32 val; + + /* TODO: */ + 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); +} + +void enable_usboh3_clk(unsigned char enable) +{ +} + +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; +} + +u32 get_lpuart_clk(void) +{ + return imx_clk_scmi_get_rate(IMX95_CLK_LPUART1); +} + +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); +} + +/* I2C check */ +u32 imx_get_i2cclk(u32 i2c_num) +{ + if (i2c_num > 7) + return -EINVAL; + switch (i2c_num) { + case 0: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C1); + case 1: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C2); + case 2: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C3); + case 3: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C4); + case 4: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C5); + case 5: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C6); + case 6: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C7); + case 7: + return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C8); + default: + return 0; + } + + return 0; +} + +int enable_i2c_clk(unsigned char enable, u32 i2c_num) +{ + u32 clock_id; + + if (i2c_num > 7) + return -EINVAL; + + switch (i2c_num) { + case 0: + clock_id = IMX95_CLK_LPI2C1; + break; + case 1: + clock_id = IMX95_CLK_LPI2C2; + break; + case 2: + clock_id = IMX95_CLK_LPI2C3; + break; + case 3: + clock_id = IMX95_CLK_LPI2C4; + break; + case 4: + clock_id = IMX95_CLK_LPI2C5; + break; + case 5: + clock_id = IMX95_CLK_LPI2C6; + break; + case 6: + clock_id = IMX95_CLK_LPI2C7; + break; + case 7: + clock_id = IMX95_CLK_LPI2C8; + break; + default: + return 0; + } + + /* 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); + + return 0; +} + +/* dfs_clkout[1]: 800.00MHz */ +void init_clk_usdhc(u32 usdhc_id) +{ + u32 clock_id; + + switch (usdhc_id) { + case 0: + clock_id = IMX95_CLK_USDHC1; + break; + case 1: + clock_id = IMX95_CLK_USDHC2; + break; + case 2: + clock_id = IMX95_CLK_USDHC3; + break; + default: + return; + }; + + /* 400MHz */ + imx_clk_scmi_enable(clock_id, false); + imx_clk_scmi_set_parent(clock_id, IMX95_CLK_SYSPLL1_PFD1); + imx_clk_scmi_set_rate(clock_id, 400000000); + imx_clk_scmi_enable(clock_id, true); +} + +int set_clk_netc(enum enet_freq type) +{ + ulong rate; + + switch (type) { + case ENET_125MHZ: + rate = MHZ(250); /* 250Mhz */ + break; + case ENET_50MHZ: + rate = MHZ(100); /* 100Mhz */ + break; + case ENET_25MHZ: + rate = MHZ(50); /* 50Mhz */ + break; + default: + return -EINVAL; + } + + /* disable the clock first */ + imx_clk_scmi_enable(IMX95_CLK_ENETREF, false); + imx_clk_scmi_set_parent(IMX95_CLK_ENETREF, IMX95_CLK_SYSPLL1_PFD0); + imx_clk_scmi_set_rate(IMX95_CLK_ENETREF, rate); + imx_clk_scmi_enable(IMX95_CLK_ENETREF, true); + + return 0; +} + +#ifdef CONFIG_SPL_BUILD +void dram_pll_init(ulong pll_val) +{ + /* Try to configure the DDR PLL. */ + u64 ddr_rate = pll_val; + /*vco_range 2.5G - 5G */ + u64 vco_rate = ddr_rate * DIV_ROUND_UP(MHZ(3000), ddr_rate); + u64 v_rate, rate; + + v_rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMPLL_VCO, vco_rate); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMPLL, ddr_rate); + + debug("%s vco:%llu rate:%llu\n", __func__, v_rate, rate); +} + +void dram_enable_bypass(ulong clk_val) +{ + u64 rate; + + switch (clk_val) { + case MHZ(625): + imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD2); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val); + break; + case MHZ(400): + imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD1); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val); + break; + case MHZ(333): + imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD0); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, 333333333); + break; + case MHZ(200): + imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD1); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val); + break; + case MHZ(100): + imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD1); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val); + break; + default: + printf("No matched freq table %lu\n", clk_val); + return; + } + + debug("%s:%llu\n", __func__, rate); + + /* Set DRAM APB to 133Mhz */ + imx_clk_scmi_set_parent(IMX95_CLK_DRAMAPB, IMX95_CLK_SYSPLL1_PFD1_DIV2); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMAPB, 133333333); + + /* Switch from DRAM clock root from PLL to CCM */ + imx_clk_scmi_set_parent(IMX95_CLK_SEL_DRAM, IMX95_CLK_DRAMALT); +} + +void dram_disable_bypass(void) +{ + u64 rate; + /* Set DRAM APB to 133Mhz */ + imx_clk_scmi_set_parent(IMX95_CLK_DRAMAPB, IMX95_CLK_SYSPLL1_PFD1_DIV2); + rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMAPB, 133333333); + + /*Set the DRAM_GPR_SEL to be sourced from DRAM_PLL.*/ + imx_clk_scmi_set_parent(IMX95_CLK_SEL_DRAM, IMX95_CLK_DRAMPLL); + rate = imx_clk_scmi_get_rate(IMX95_CLK_SEL_DRAM); + printf("%s:SEL_DRAM: %llu\n", __func__, rate); +} + +#endif + +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..543abe9fed481c796602b2b2eeeec6c8f9a94862 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + * + * Peng Fan peng.fan@nxp.com + */ + +#include <command.h> +#include <errno.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/ccm_regs.h> +#include <asm/mach-imx/sys_proto.h> +#include <asm/global_data.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include "../../../../../dts/upstream/src/arm64/freescale/imx95-clock.h" +#include <linux/clk-provider.h> +#include <scmi_agent.h> +#include <scmi_protocols.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/container.cfg b/arch/arm/mach-imx/imx9/scmi/container.cfg new file mode 100644 index 0000000000000000000000000000000000000000..464d43f85d97153de35d6d8f0e5a544c26fa8ab5 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/container.cfg @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +/* Empty, so not generate flash.bin in u-boot */ +/* TODO, i.MX95 binman is under developing, after done, bring back to let U-Boot generate flash.bin */ 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..fefb1a6f4ca58722a5d93c39e4f6a7aaf85aa177 --- /dev/null +++ b/arch/arm/mach-imx/imx9/scmi/soc.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + * + * Peng Fan peng.fan@nxp.com + */ + +#include <cpu_func.h> +#include <init.h> +#include <log.h> +#include <asm/arch/imx-regs.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/ccm_regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/trdc.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/mach-imx/syscounter.h> +#include <asm/armv8/mmu.h> +#include <dm/uclass.h> +#include <dm/device.h> +#include <env.h> +#include <env_internal.h> +#include <errno.h> +#include <fdt_support.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> +#include <asm/setup.h> +#include <asm/bootm.h> +#include <asm/arch-imx/cpu.h> +#include <asm/mach-imx/ele_api.h> +#include <linux/delay.h> +#include <fuse.h> +#include <imx_thermal.h> +#include <thermal.h> +#include <imx_sip.h> +#include <linux/arm-smccc.h> +#include <asm/arch/ddr.h> +#ifdef CONFIG_SCMI_FIRMWARE +#include <scmi_agent.h> +#include <scmi_protocols.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +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; +} + +#ifdef 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; +#ifdef CONFIG_SPL_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

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 | 7 +++++++ arch/arm/mach-imx/imx9/Makefile | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig index 4d32c28670da3e2aab35f3cd009ff3c7cabd4edc..f594f998895b9578ea43e83ecb6579e1bce9c88e 100644 --- a/arch/arm/mach-imx/imx9/Kconfig +++ b/arch/arm/mach-imx/imx9/Kconfig @@ -16,6 +16,13 @@ config IMX93 select IMX9 select ARMV8_SPL_EXCEPTION_VECTORS
+config IMX95 + bool + select IMX9 + select ARMV8_SPL_EXCEPTION_VECTORS + select SCMI_FIRMWARE + select DM_MAILBOX + config SYS_SOC default "imx9"
diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile index e1b09ab53411d2e104ae795b909a32919e02ca6a..8d43ea2b267e2608df2e8f140c91b626dd969842 100644 --- a/arch/arm/mach-imx/imx9/Makefile +++ b/arch/arm/mach-imx/imx9/Makefile @@ -1,10 +1,15 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Copyright 2022 NXP +# Copyright 2024 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_SPL_BUILD +ifneq ($(CONFIG_SPL_BUILD),y) obj-y += imx_bootaux.o -#endif +endif \ No newline at end of file

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 override h_spl_load_read to use a trampoline buffer in nonsecure 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 | 40 +++++++++++++++++++++++++++++++++++++++ common/spl/spl_mmc.c | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/imx9/scmi/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c index fefb1a6f4ca58722a5d93c39e4f6a7aaf85aa177..8a7df24afecc1d6d77b7162be639b56af95fd975 100644 --- a/arch/arm/mach-imx/imx9/scmi/soc.c +++ b/arch/arm/mach-imx/imx9/scmi/soc.c @@ -41,6 +41,8 @@ #include <scmi_agent.h> #include <scmi_protocols.h> #endif +#include <spl.h> +#include <mmc.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -830,3 +832,41 @@ enum boot_device get_boot_device(void) return boot_dev; } #endif + +ulong h_spl_load_read(struct spl_load_info *load, ulong off, + ulong size, void *buf) +{ + struct blk_desc *bd = load->priv; + lbaint_t sector = off >> bd->log2blksz; + lbaint_t count = size >> bd->log2blksz; + ulong trampoline_sz = SZ_16M; + void *trampoline = (void *)((ulong)CFG_SYS_SDRAM_BASE + PHYS_SDRAM_SIZE - trampoline_sz); + ulong ns_ddr_end = CFG_SYS_SDRAM_BASE + PHYS_SDRAM_SIZE; + ulong read_count, trampoline_cnt = trampoline_sz >> bd->log2blksz, actual, total; + +#ifdef PHYS_SDRAM_2_SIZE + ns_ddr_end += PHYS_SDRAM_2_SIZE; +#endif + + /* Check if the buf is in non-secure world, otherwise copy from trampoline */ + if ((ulong)buf < CFG_SYS_SDRAM_BASE || (ulong)buf + (count * sector) > ns_ddr_end) { + total = 0; + while (count) { + read_count = trampoline_cnt > count ? count : trampoline_cnt; + actual = blk_dread(bd, sector, read_count, trampoline); + if (actual != read_count) { + printf("Error in blk_dread, %lu, %lu\n", read_count, actual); + return 0; + } + memcpy(buf, trampoline, actual * 512); + buf += actual * 512; + sector += actual; + total += actual; + count -= actual; + } + + return total << bd->log2blksz; + } + + return blk_dread(bd, sector, count, buf) << bd->log2blksz; +} diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c index 1f69659321620739d066bd806acd1d282b66bad3..d15a6945f9c24ee427b847c036413c52475c44a8 100644 --- a/common/spl/spl_mmc.c +++ b/common/spl/spl_mmc.c @@ -17,7 +17,7 @@ #include <image.h> #include <imx_container.h>
-static ulong h_spl_load_read(struct spl_load_info *load, ulong off, +ulong __weak h_spl_load_read(struct spl_load_info *load, ulong off, ulong size, void *buf) { struct blk_desc *bd = load->priv;

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 *

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..56577b9ade349740b6d98ab933797d7072985de0 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -179,7 +179,7 @@ config DDRMC_VF610_CALIBRATION the ddrmc_cr_setting on relevant board file.
config IMX8_ROMAPI - def_bool y + def_bool y if (!IMX95) depends on IMX8MN || IMX8MP || IMX8ULP || IMX9
config SPL_IMX_ROMAPI_LOADADDR

On Wed, Oct 16, 2024 at 03:18:06PM +0800, 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..56577b9ade349740b6d98ab933797d7072985de0 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -179,7 +179,7 @@ config DDRMC_VF610_CALIBRATION the ddrmc_cr_setting on relevant board file.
config IMX8_ROMAPI
- def_bool y
- def_bool y if (!IMX95) depends on IMX8MN || IMX8MP || IMX8ULP || IMX9
config SPL_IMX_ROMAPI_LOADADDR
If we're going to add parens here, it should be: depends on IMX8MN || IMX8MP || IMX8ULP || (IMX9 && !IMX95)
And if down the line there's more IMX9 exclusions, the i.MX9 family that does use this needs it own symbol so it can be here just like how all of the i.MX8 families are spelled out, thanks.

From: Ye Li ye.li@nxp.com
This patch adds i.MX95 19x19 EVK board basic support.
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 | 172 ++++++++++++++++++++++++++++ arch/arm/mach-imx/imx9/Kconfig | 6 + board/freescale/imx95_evk/Kconfig | 12 ++ board/freescale/imx95_evk/Makefile | 11 ++ board/freescale/imx95_evk/imx95_evk.c | 75 ++++++++++++ board/freescale/imx95_evk/spl.c | 119 +++++++++++++++++++ configs/imx95_19x19_evk_defconfig | 189 +++++++++++++++++++++++++++++++ include/configs/imx95_evk.h | 148 ++++++++++++++++++++++++ 8 files changed, 732 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..205f8ce5f9dd73421a6ded86c78d7c5f03adc5a6 --- /dev/null +++ b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 NXP + */ + +&{/soc} { + bootph-all; + bootph-pre-ram; +}; + +&aips1 { + bootph-all; + bootph-pre-ram; +}; + +&aips2 { + bootph-all; + bootph-pre-ram; +}; + +&aips3 { + bootph-pre-ram; +}; + +&gpio1 { + reg = <0 0x47400000 0 0x1000>, <0 0x47400040 0 0x40>; +}; + +&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; +}; + +&lpuart1 { + clocks = <&scmi_clk IMX95_CLK_LPUART1>, <&scmi_clk IMX95_CLK_LPUART1>; + clock-names = "ipg", "per"; + bootph-pre-ram; +}; + +&mu2 { + bootph-all; + bootph-pre-ram; +}; + +&osc_24m { + bootph-all; + bootph-pre-ram; +}; + +&clk_ext1 { + bootph-all; + bootph-pre-ram; +}; + +&sram0 { + bootph-all; + bootph-pre-ram; +}; + +&scmi_buf0 { + bootph-all; + bootph-pre-ram; +}; + +&scmi_buf1 { + bootph-all; + bootph-pre-ram; +}; + +&{/firmware} { + bootph-all; + bootph-pre-ram; +}; + +&{/firmware/scmi} { + bootph-all; + bootph-pre-ram; +}; + +&{/firmware/scmi/protocol@11} { + bootph-all; + bootph-pre-ram; +}; + +&{/firmware/scmi/protocol@13} { + bootph-all; + bootph-pre-ram; +}; + +&{/firmware/scmi/protocol@14} { + bootph-all; + bootph-pre-ram; +}; + +&{/firmware/scmi/protocol@19} { + bootph-all; + bootph-pre-ram; +}; + +&pinctrl_uart1 { + bootph-pre-ram; +}; + +&usdhc1 { + bootph-pre-ram; +}; + +&pinctrl_usdhc1 { + bootph-pre-ram; +}; + +&pinctrl_usdhc1_100mhz { + bootph-pre-ram; +}; + +&pinctrl_usdhc1_200mhz { + bootph-pre-ram; +}; + +&usdhc2 { + 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; +}; + +®_usdhc2_vmmc { + bootph-pre-ram; +}; + +&pinctrl_reg_usdhc2_vmmc { + bootph-pre-ram; +}; + +&elemu3 { + compatible = "fsl,imx93-mu-s4"; + status = "okay"; + bootph-all; + bootph-pre-ram; +}; + +&wdog3 { + status = "disabled"; +}; diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig index f594f998895b9578ea43e83ecb6579e1bce9c88e..934a802698f9552977ab84783e72b917553decb4 100644 --- a/arch/arm/mach-imx/imx9/Kconfig +++ b/arch/arm/mach-imx/imx9/Kconfig @@ -46,11 +46,17 @@ config TARGET_PHYCORE_IMX93 select IMX93 select IMX9_LPDDR4X
+config TARGET_IMX95_19X19_EVK + bool "imx95_19x19_evk" + select IMX95 + imply OF_UPSTREAM + endchoice
source "board/freescale/imx93_evk/Kconfig" source "board/phytec/phycore_imx93/Kconfig" source "board/variscite/imx93_var_som/Kconfig" +source "board/freescale/imx95_evk/Kconfig"
endif
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/Makefile b/board/freescale/imx95_evk/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d5cff91c961e0045514b44f508fa40650bc24116 --- /dev/null +++ b/board/freescale/imx95_evk/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2023 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_evk.c b/board/freescale/imx95_evk/imx95_evk.c new file mode 100644 index 0000000000000000000000000000000000000000..7a0617e3cb337ff1d2b6916b5f4889614a7e18c5 --- /dev/null +++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 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 <usb.h> +#include <dwc3-uboot.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> + +#ifdef CONFIG_SCMI_FIRMWARE +#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" +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int board_early_init_f(void) +{ + /* UART1: A55, UART2: M33, UART3: M7 */ + init_uart_clk(0); + + return 0; +} + +#if CONFIG_IS_ENABLED(NET) +int board_phy_config(struct phy_device *phydev) +{ + if (phydev->drv->config) + phydev->drv->config(phydev); + return 0; +} +#endif + +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; +} + +#ifdef CONFIG_OF_BOARD_SETUP +int ft_board_setup(void *blob, struct bd_info *bd) +{ + return 0; +} +#endif + +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..1c8782b64641ce295ec2c1a4390f45b3b7fe0bc2 --- /dev/null +++ b/board/freescale/imx95_evk/spl.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <command.h> +#include <cpu_func.h> +#include <clk.h> +#include <hang.h> +#include <image.h> +#include <init.h> +#include <log.h> +#include <spl.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/boot_mode.h> +#include <asm/mach-imx/syscounter.h> +#include <asm/mach-imx/ele_api.h> +#include <asm/sections.h> +#include <dm/uclass.h> +#include <dm/device.h> +#include <dm/uclass-internal.h> +#include <dm/device-internal.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <asm/arch/clock.h> +#include <asm/arch/ccm_regs.h> +#ifdef CONFIG_SCMI_FIRMWARE +#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" +#endif + +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"); +} + +extern int imx9_probe_mu(void); + +void board_init_f(ulong dummy) +{ + int ret; + bool ddrmix_power = false; + + /* 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(); + + printf("SOC: 0x%x\n", gd->arch.soc_rev); + printf("LC: 0x%x\n", gd->arch.lifecycle); + + /* Will set ARM freq to max rate */ + clock_init_late(); + + /* Check is DDR MIX is already powered up. */ + u32 state = 0; + struct udevice *dev; + + ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev); + if (ret) + printf("%s: %d\n", __func__, ret); + + ret = scmi_pwd_state_get(dev, IMX95_PD_DDR, &state); + if (ret) { + printf("scmi_pwd_state_get Failed %d for DDRMIX\n", ret); + } else { + if (state == BIT(30)) { + panic("DDRMIX is powered OFF, Please initialize DDR with OEI \n"); + } else { + printf("DDRMIX is powered UP \n"); + ddrmix_power = true; + } + } + + 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..24ac94fb3a23ca7b367e44d0698dfd28ac327dc4 --- /dev/null +++ b/configs/imx95_19x19_evk_defconfig @@ -0,0 +1,189 @@ +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_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_DISTRO_DEFAULTS=y +CONFIG_BOOTCOMMAND="run distro_bootcmd;run bsp_bootcmd" +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_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_OF_BOARD_SETUP=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_BINMAN_FDT is not set diff --git a/include/configs/imx95_evk.h b/include/configs/imx95_evk.h new file mode 100644 index 0000000000000000000000000000000000000000..caac8bffe08648c7eb044dbc2fbdbd8c50474ac1 --- /dev/null +++ b/include/configs/imx95_evk.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 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) + +#ifdef CONFIG_AHAB_BOOT +#define AHAB_ENV "sec_boot=yes\0" +#else +#define AHAB_ENV "sec_boot=no\0" +#endif + +#define BOOTENV + +/* Initial environment variables */ +#define CFG_EXTRA_ENV_SETTINGS \ + BOOTENV \ + AHAB_ENV \ + "initrd_addr=0x93800000\0" \ + "initrd_high=0xffffffffffffffff\0" \ + "emmc_dev=0\0"\ + "sd_dev=1\0" \ + "prepare_mcore=setenv mcore_clk clk-imx95.mcore_booted;\0" \ + "scriptaddr=0x93500000\0" \ + "kernel_addr_r=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \ + "image=Image\0" \ + "splashimage=0xA0000000\0" \ + "console=ttyLP0,115200 earlycon\0" \ + "fdt_addr_r=0x93000000\0" \ + "fdt_addr=0x93000000\0" \ + "fdt_high=0xffffffffffffffff\0" \ + "cntr_addr=0xA8000000\0" \ + "cntr_file=os_cntr_signed.bin\0" \ + "boot_fit=no\0" \ + "fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0" \ + "bootm_size=0x10000000\0" \ + "mmcdev=" __stringify(CONFIG_SYS_MMC_ENV_DEV)"\0" \ + "mmcpart=1\0" \ + "mmcroot=/dev/mmcblk1p2 rootwait rw\0" \ + "mmcautodetect=yes\0" \ + "mmcargs=setenv bootargs ${mcore_clk} console=${console} root=${mmcroot}\0 " \ + "loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \ + "bootscript=echo Running bootscript from mmc ...; " \ + "source\0" \ + "loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \ + "loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile}\0" \ + "loadcntr=fatload mmc ${mmcdev}:${mmcpart} ${cntr_addr} ${cntr_file}\0" \ + "auth_os=auth_cntr ${cntr_addr}\0" \ + "boot_os=booti ${loadaddr} - ${fdt_addr_r};\0" \ + "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;\0" \ + "netargs=setenv bootargs ${mcore_clk} console=${console} " \ + "root=/dev/nfs " \ + "ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp\0" \ + "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;\0" \ + "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;" + +/* Link Definitions */ + +#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 CFG_SYS_FSL_USDHC_NUM 2 + +/* Using ULP WDOG for reset */ +#define WDOG_BASE_ADDR WDG3_BASE_ADDR + +/* USB configs */ +#if defined(CONFIG_CMD_NET) +#define PHY_ANEG_TIMEOUT 20000 +/* Number of Rx BD rings: 8 per ENETC instance */ +#endif + +#endif

Hi Alice,
On Wed, Oct 16, 2024 at 4:19 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.
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
A MAINTAINERS file is missing. CI flags this as an error.
Please make sure to run the series through CI before posting v2.
A general question: how is the flash.bin generated?
I don't see binman support here. Why?
We also need a doc/board/nxp/imx95_evk.rts file that explains how to build and boot the board, the firmware files it needs, etc.
+++ b/board/freescale/imx95_evk/imx95_evk.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2023 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 <usb.h> +#include <dwc3-uboot.h>
Why do you add USB headers if there is no USB support?
+#ifdef CONFIG_SCMI_FIRMWARE
Remove the ifdef and include the headers unconditionally.
+DECLARE_GLOBAL_DATA_PTR;
+int board_early_init_f(void) +{
/* UART1: A55, UART2: M33, UART3: M7 */
init_uart_clk(0);
Shouldn't DM_SERIAL enable the UART clock?
+#if CONFIG_IS_ENABLED(NET) +int board_phy_config(struct phy_device *phydev) +{
if (phydev->drv->config)
phydev->drv->config(phydev);
return 0;
Is this needed?
+#ifdef CONFIG_OF_BOARD_SETUP +int ft_board_setup(void *blob, struct bd_info *bd) +{
return 0;
This function is not useful and can be removed.
+extern int imx9_probe_mu(void);
No extern in C files, please.
printf("SOC: 0x%x\n", gd->arch.soc_rev);
printf("LC: 0x%x\n", gd->arch.lifecycle);
Make this debug() instead.
+CONFIG_SPL_POWER=y +CONFIG_SDP_LOADADDR=0x90400000
No blank lines in defconfig, please.
Generate the defconfig via 'make savedefconfig'
+/* Initial environment variables */ +#define CFG_EXTRA_ENV_SETTINGS \
BOOTENV \
AHAB_ENV \
"initrd_addr=0x93800000\0" \
"initrd_high=0xffffffffffffffff\0" \
"emmc_dev=0\0"\
"sd_dev=1\0" \
"prepare_mcore=setenv mcore_clk clk-imx95.mcore_booted;\0" \
"scriptaddr=0x93500000\0" \
"kernel_addr_r=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
"image=Image\0" \
"splashimage=0xA0000000\0" \
"console=ttyLP0,115200 earlycon\0" \
"fdt_addr_r=0x93000000\0" \
"fdt_addr=0x93000000\0" \
"fdt_high=0xffffffffffffffff\0" \
"cntr_addr=0xA8000000\0" \
"cntr_file=os_cntr_signed.bin\0" \
"boot_fit=no\0" \
"fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0" \
"bootm_size=0x10000000\0" \
"mmcdev=" __stringify(CONFIG_SYS_MMC_ENV_DEV)"\0" \
"mmcpart=1\0" \
"mmcroot=/dev/mmcblk1p2 rootwait rw\0" \
"mmcautodetect=yes\0" \
"mmcargs=setenv bootargs ${mcore_clk} console=${console} root=${mmcroot}\0 " \
"loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \
"bootscript=echo Running bootscript from mmc ...; " \
"source\0" \
"loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \
"loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile}\0" \
"loadcntr=fatload mmc ${mmcdev}:${mmcpart} ${cntr_addr} ${cntr_file}\0" \
"auth_os=auth_cntr ${cntr_addr}\0" \
"boot_os=booti ${loadaddr} - ${fdt_addr_r};\0" \
"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;\0" \
"netargs=setenv bootargs ${mcore_clk} console=${console} " \
"root=/dev/nfs " \
"ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp\0" \
"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;\0" \
"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;"
Move this to board/freescale/imx95_evk/imx95_evk.env

On Wed, Oct 16, 2024 at 08:36:12AM -0300, Fabio Estevam wrote:
Hi Alice,
On Wed, Oct 16, 2024 at 4:19 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.
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]
"initrd_addr=0x93800000\0" \
"initrd_high=0xffffffffffffffff\0" \
"emmc_dev=0\0"\
"sd_dev=1\0" \
"prepare_mcore=setenv mcore_clk clk-imx95.mcore_booted;\0" \
"scriptaddr=0x93500000\0" \
"kernel_addr_r=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
"image=Image\0" \
"splashimage=0xA0000000\0" \
"console=ttyLP0,115200 earlycon\0" \
"fdt_addr_r=0x93000000\0" \
"fdt_addr=0x93000000\0" \
"fdt_high=0xffffffffffffffff\0" \
[snip]
Move this to board/freescale/imx95_evk/imx95_evk.env
And disabling relocation with fdt_high / initrd_high is not allowed and checkpatch.pl calls this an error too.

Hi Fabio,
-----邮件原件----- 发件人: Fabio Estevam festevam@gmail.com 发送时间: 2024年10月16日 19:36 收件人: Alice Guo (OSS) alice.guo@oss.nxp.com 抄送: Tom Rini trini@konsulko.com; Lukasz Majewski lukma@denx.de; Sean Anderson seanga2@gmail.com; Stefano Babic sbabic@denx.de; Marek Vasut marex@denx.de; dl-uboot-imx uboot-imx@nxp.com; u-boot@lists.denx.de; Ye Li ye.li@nxp.com; Alice Guo alice.guo@nxp.com; Peng Fan peng.fan@nxp.com 主题: [EXT] Re: [PATCH 14/17] 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
Hi Alice,
On Wed, Oct 16, 2024 at 4:19 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.
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
A MAINTAINERS file is missing. CI flags this as an error.
Please make sure to run the series through CI before posting v2.
A general question: how is the flash.bin generated?
I don't see binman support here. Why?
We also need a doc/board/nxp/imx95_evk.rts file that explains how to build and boot the board, the firmware files it needs, etc.
We will have new revision silicon that not compatible with current silicon container format. The current version will not goes into mass production, so I prefer to defer the binman work when new revision silicon arrives. With current patchset, I could add doc to use imx-mkimage to pack images. With new revision silicons ready, we could switch to binman and update doc then. Is this ok for your?
Best Regards, Alice Guo

Hi Alice,
On Fri, Nov 1, 2024 at 6:43 AM Alice Guo (OSS) alice.guo@oss.nxp.com wrote:
We will have new revision silicon that not compatible with current silicon container format. The current version will not goes into mass production, so I prefer to defer the binman work when new revision silicon arrives. With current patchset, I could add doc to use imx-mkimage to pack images. With new revision silicons ready, we could switch to binman and update doc then. Is this ok for your?
I cannot accept this proposal. I'm sorry.
imx-mkimage is a poor custom solution developed by NXP and I don't want people to depend on it when using U-Boot mainline.
Just add binman support from the beginning.

On Fri, Nov 01, 2024 at 09:43:14AM +0000, Alice Guo (OSS) wrote:
Hi Fabio,
-----邮件原件----- 发件人: Fabio Estevam festevam@gmail.com 发送时间: 2024年10月16日 19:36 收件人: Alice Guo (OSS) alice.guo@oss.nxp.com 抄送: Tom Rini trini@konsulko.com; Lukasz Majewski lukma@denx.de; Sean Anderson seanga2@gmail.com; Stefano Babic sbabic@denx.de; Marek Vasut marex@denx.de; dl-uboot-imx uboot-imx@nxp.com; u-boot@lists.denx.de; Ye Li ye.li@nxp.com; Alice Guo alice.guo@nxp.com; Peng Fan peng.fan@nxp.com 主题: [EXT] Re: [PATCH 14/17] 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
Hi Alice,
On Wed, Oct 16, 2024 at 4:19 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.
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
A MAINTAINERS file is missing. CI flags this as an error.
Please make sure to run the series through CI before posting v2.
A general question: how is the flash.bin generated?
I don't see binman support here. Why?
We also need a doc/board/nxp/imx95_evk.rts file that explains how to build and boot the board, the firmware files it needs, etc.
We will have new revision silicon that not compatible with current silicon container format. The current version will not goes into mass production, so I prefer to defer the binman work when new revision silicon arrives. With current patchset, I could add doc to use imx-mkimage to pack images. With new revision silicons ready, we could switch to binman and update doc then. Is this ok for your?
I would expect the binman support to be easily adjusted to the new format when it's available for you to test and we can easily enough mark the first format as deprecated to start with so removing it later is also known up-front.

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: Ye Li ye.li@nxp.com
For some debug case, we have preloaded the full boot image to RAM device. Add the support that SPL can load the u-boot-atf container from the RAM device.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com --- arch/arm/mach-imx/image-container.c | 22 ++++++++++++++++++++++ common/spl/spl_ram.c | 34 ++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/arch/arm/mach-imx/image-container.c b/arch/arm/mach-imx/image-container.c index 54ae7721888dbc93fdaba8e5aedc52c4506bb1c1..27dc162f2517b6d75b0ed95510c09385f7cbc859 100644 --- a/arch/arm/mach-imx/image-container.c +++ b/arch/arm/mach-imx/image-container.c @@ -21,6 +21,7 @@ #define NAND_DEV 2 #define QSPI_NOR_DEV 3 #define ROM_API_DEV 4 +#define RAM_DEV 5
/* The unit of second image offset number which provision by the fuse bits */ #define SND_IMG_OFF_UNIT (0x100000UL) @@ -148,6 +149,11 @@ static int get_dev_container_size(void *dev, int dev_type, unsigned long offset, } #endif
+#ifdef CONFIG_SPL_RAM_SUPPORT + if (dev_type == RAM_DEV) + memcpy(buf, (const void *)offset, CONTAINER_HDR_ALIGNMENT); +#endif + ret = get_container_size((ulong)buf, header_length);
free(buf); @@ -222,6 +228,8 @@ static unsigned long get_boot_device_offset(void *dev, int dev_type) CONTAINER_HDR_NAND_OFFSET; } else if (dev_type == QSPI_NOR_DEV) { offset = CONTAINER_HDR_QSPI_OFFSET + 0x08000000; + } else if (dev_type == RAM_DEV) { + offset = (unsigned long)dev + CONTAINER_HDR_MMCSD_OFFSET; } else { printf("Not supported dev_type: %d\n", dev_type); } @@ -382,3 +390,17 @@ ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev) return end; } #endif + +#ifdef CONFIG_SPL_RAM_SUPPORT +unsigned long spl_ram_get_uboot_base(void) +{ + ulong end; + + end = get_imageset_end((void *)CONFIG_SPL_LOAD_FIT_ADDRESS, RAM_DEV); + end = ROUND(end, SZ_1K); + + printf("Load image from RAM 0x%lx\n", end); + + return end; +} +#endif diff --git a/common/spl/spl_ram.c b/common/spl/spl_ram.c index 71b7a8374bbbf534e1b2c28e02205af035fcfd8b..c3a1df59e3f3bcc19a6cdff21ec66a4bd4df7b93 100644 --- a/common/spl/spl_ram.c +++ b/common/spl/spl_ram.c @@ -16,24 +16,25 @@ #include <spl.h> #include <linux/libfdt.h>
-static ulong spl_ram_load_read(struct spl_load_info *load, ulong sector, - ulong count, void *buf) +unsigned long __weak spl_ram_get_uboot_base(void) { ulong addr = 0;
- debug("%s: sector %lx, count %lx, buf %lx\n", - __func__, sector, count, (ulong)buf); - if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) { addr = IF_ENABLED_INT(CONFIG_SPL_LOAD_FIT, CONFIG_SPL_LOAD_FIT_ADDRESS); } - addr += sector; - if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD)) - addr += image_load_offset;
- memcpy(buf, (void *)addr, count); + return addr; +} + +static ulong spl_ram_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + debug("%s: sector %lx, count %lx, buf %lx\n", + __func__, sector, count, (ulong)buf);
+ memcpy(buf, (void *)(sector), count); return count; }
@@ -44,10 +45,7 @@ static int spl_ram_load_image(struct spl_image_info *spl_image, ulong addr = 0; int ret;
- if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) { - addr = IF_ENABLED_INT(CONFIG_SPL_LOAD_FIT, - CONFIG_SPL_LOAD_FIT_ADDRESS); - } + header = (struct legacy_img_hdr *)spl_ram_get_uboot_base();
if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD)) { ret = image_pre_load(addr); @@ -70,7 +68,15 @@ static int spl_ram_load_image(struct spl_image_info *spl_image,
debug("Found FIT\n"); spl_load_init(&load, spl_ram_load_read, NULL, 1); - ret = spl_load_simple_fit(spl_image, &load, 0, header); + ret = spl_load_simple_fit(spl_image, &load, (ulong)header, header); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + struct spl_load_info load; + + memset(&load, 0, sizeof(load)); + load.bl_len = 1; + load.read = spl_ram_load_read; + + ret = spl_load_imx_container(spl_image, &load, (ulong)header); } else { ulong u_boot_pos = spl_get_image_pos();

From: Ye Li ye.li@nxp.com
When using ROM API, the size and offset must be aligned with page size. However when loading container image, the rule is not followed for container header. It is ok for eMMC/SD/flexspinor, but fails for flexspi NAND which page size is 4K.
Fix the issue by providing spl_romapi_read API to handle the unaligned read and around container header size to page size
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/include/asm/mach-imx/sys_proto.h | 4 +-- arch/arm/mach-imx/image-container.c | 5 ++-- arch/arm/mach-imx/spl_imx_romapi.c | 47 +++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/arch/arm/include/asm/mach-imx/sys_proto.h b/arch/arm/include/asm/mach-imx/sys_proto.h index 878a4821996392e36d9bb02613276e502f463eef..076ff41652315d2d8276424843862ef30008b3ad 100644 --- a/arch/arm/include/asm/mach-imx/sys_proto.h +++ b/arch/arm/include/asm/mach-imx/sys_proto.h @@ -207,8 +207,8 @@ enum boot_stage_type { extern struct rom_api *g_rom_api; extern unsigned long rom_pointer[];
-ulong spl_romapi_raw_seekable_read(u32 offset, u32 size, void *buf); -ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev); +ulong spl_romapi_read(u32 offset, u32 size, void *buf); +ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev, u32 pagesize);
u32 rom_api_download_image(u8 *dest, u32 offset, u32 size); u32 rom_api_query_boot_infor(u32 info_type, u32 *info); diff --git a/arch/arm/mach-imx/image-container.c b/arch/arm/mach-imx/image-container.c index 27dc162f2517b6d75b0ed95510c09385f7cbc859..73dd8fb2f2bf1523032bdf17da2e347598403527 100644 --- a/arch/arm/mach-imx/image-container.c +++ b/arch/arm/mach-imx/image-container.c @@ -141,7 +141,7 @@ static int get_dev_container_size(void *dev, int dev_type, unsigned long offset,
#ifdef CONFIG_SPL_BOOTROM_SUPPORT if (dev_type == ROM_API_DEV) { - ret = spl_romapi_raw_seekable_read(offset, CONTAINER_HDR_ALIGNMENT, buf); + ret = spl_romapi_read(offset, CONTAINER_HDR_ALIGNMENT, buf); if (!ret) { printf("Read container image from ROM API failed\n"); return -EIO; @@ -376,7 +376,7 @@ u32 __weak spl_arch_boot_image_offset(u32 image_offset, u32 rom_bt_dev) return image_offset; }
-ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev) +ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev, u32 pagesize) { ulong end;
@@ -384,6 +384,7 @@ ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev)
end = get_imageset_end((void *)(ulong)image_offset, ROM_API_DEV); end = ROUND(end, SZ_1K); + end = ROUND(end, pagesize);
printf("Load image from 0x%lx by ROM_API\n", end);
diff --git a/arch/arm/mach-imx/spl_imx_romapi.c b/arch/arm/mach-imx/spl_imx_romapi.c index 3982f4cca184716f0000a28767c14a57567ed54d..6a5d66a8e157b6030318211d4ca04b799ceb2527 100644 --- a/arch/arm/mach-imx/spl_imx_romapi.c +++ b/arch/arm/mach-imx/spl_imx_romapi.c @@ -14,8 +14,10 @@
DECLARE_GLOBAL_DATA_PTR;
+u32 rom_dev_page_size; + /* Caller need ensure the offset and size to align with page size */ -ulong spl_romapi_raw_seekable_read(u32 offset, u32 size, void *buf) +static ulong spl_romapi_raw_seekable_read(u32 offset, u32 size, void *buf) { int ret;
@@ -31,7 +33,46 @@ ulong spl_romapi_raw_seekable_read(u32 offset, u32 size, void *buf) return 0; }
-ulong __weak spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev) +ulong spl_romapi_read(u32 offset, u32 size, void *buf) +{ + u32 off_in_page, aligned_size, readsize; + int ret; + u8 *tmp; + + if (!rom_dev_page_size) { + ret = rom_api_query_boot_infor(QUERY_PAGE_SZ, &rom_dev_page_size); + if (ret != ROM_API_OKAY) { + puts("ROMAPI: Failure query boot infor pagesize/offset\n"); + return 0; + } + } + + off_in_page = offset % rom_dev_page_size; + aligned_size = ALIGN(size + off_in_page, rom_dev_page_size); + + if (aligned_size != size) { + tmp = malloc(aligned_size); + if (!tmp) { + printf("%s: Failed to malloc %u bytes\n", __func__, aligned_size); + return 0; + } + + readsize = spl_romapi_raw_seekable_read(offset - off_in_page, aligned_size, tmp); + if (readsize != aligned_size) { + printf("%s: Failed read %u, actual %u\n", __func__, aligned_size, readsize); + free(tmp); + return 0; + } + + memcpy(buf, tmp + off_in_page, size); + free(tmp); + return size; + } + + return spl_romapi_raw_seekable_read(offset, size, buf); +} + +ulong __weak spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev, u32 pagesize) { u32 sector = 0;
@@ -94,7 +135,7 @@ static int spl_romapi_load_image_seekable(struct spl_image_info *spl_image, printf("image offset 0x%x, pagesize 0x%x, ivt offset 0x%x\n", image_offset, pagesize, offset);
- offset = spl_romapi_get_uboot_base(image_offset, rom_bt_dev); + offset = spl_romapi_get_uboot_base(image_offset, rom_bt_dev, pagesize);
size = ALIGN(sizeof(struct legacy_img_hdr), pagesize); ret = rom_api_download_image((u8 *)header, offset, size);

On 10/16/24 9:17 AM, Alice Guo wrote:
This patch set adds the basic support of i.MX95 and has been tested on i.MX95 19x19 EVK.
I have a somewhat more generic question -- can we start U-Boot SPL and U-Boot first, and start the ELE/OpTee/... whatever firmwares AFTER U-Boot has started, so they can be updated just like the kernel can be updated ?

Subject: Re: [PATCH 00/17] (no cover subject)
On 10/16/24 9:17 AM, Alice Guo wrote:
This patch set adds the basic support of i.MX95 and has been tested
on
i.MX95 19x19 EVK.
I have a somewhat more generic question -- can we start U-Boot SPL and U-Boot first, and start the ELE/OpTee/... whatever firmwares AFTER U-Boot has started, so they can be updated just like the kernel can be updated ?
ELE Firmware packed in flash.bin is used by ROM, out of control of SPL/U-Boot.
OP-Tee is optional, but if it is there, it could only be loaded by SPL and kicked by ATF
Regards, Peng.

On 11/4/24 1:20 PM, Peng Fan wrote:
Subject: Re: [PATCH 00/17] (no cover subject)
On 10/16/24 9:17 AM, Alice Guo wrote:
This patch set adds the basic support of i.MX95 and has been tested
on
i.MX95 19x19 EVK.
I have a somewhat more generic question -- can we start U-Boot SPL and U-Boot first, and start the ELE/OpTee/... whatever firmwares AFTER U-Boot has started, so they can be updated just like the kernel can be updated ?
ELE Firmware packed in flash.bin is used by ROM, out of control of SPL/U-Boot.
Used by ROM how ? Details please ?
OP-Tee is optional, but if it is there, it could only be loaded by SPL and kicked by ATF
Why ? U-Boot running in EL3 can start OpTee OS, so what is the problem ?

Subject: Re: [PATCH 00/17] (no cover subject)
On 11/4/24 1:20 PM, Peng Fan wrote:
Subject: Re: [PATCH 00/17] (no cover subject)
On 10/16/24 9:17 AM, Alice Guo wrote:
This patch set adds the basic support of i.MX95 and has been
tested
on
i.MX95 19x19 EVK.
I have a somewhat more generic question -- can we start U-Boot SPL and U-Boot first, and start the ELE/OpTee/... whatever firmwares AFTER U-Boot has started, so they can be updated just like the
kernel
can be updated ?
ELE Firmware packed in flash.bin is used by ROM, out of control of SPL/U-Boot.
Used by ROM how ? Details please ?
ELE Firmware is for secure enclave usage. 8ULP/93 also has it. I could not share more details.
OP-Tee is optional, but if it is there, it could only be loaded by SPL and kicked by ATF
Why ? U-Boot running in EL3 can start OpTee OS, so what is the problem ?
Ah. As we know there is a wrapper in ATF controlled with spd_optee. Technically let uboot in EL3 to kick optee is feasible, but I am not sure people would do this.
Regards, Peng.

On 11/4/24 1:39 PM, Peng Fan wrote:
Subject: Re: [PATCH 00/17] (no cover subject)
On 11/4/24 1:20 PM, Peng Fan wrote:
Subject: Re: [PATCH 00/17] (no cover subject)
On 10/16/24 9:17 AM, Alice Guo wrote:
This patch set adds the basic support of i.MX95 and has been
tested
on
i.MX95 19x19 EVK.
I have a somewhat more generic question -- can we start U-Boot SPL and U-Boot first, and start the ELE/OpTee/... whatever firmwares AFTER U-Boot has started, so they can be updated just like the
kernel
can be updated ?
ELE Firmware packed in flash.bin is used by ROM, out of control of SPL/U-Boot.
Used by ROM how ? Details please ?
ELE Firmware is for secure enclave usage. 8ULP/93 also has it. I could not share more details.
Is this some magic proprietary closed source goo ?
Why can this not be started from U-Boot just like OpTee-OS which implements TEE for Secure TEE trustlets (or whatever that is called) ?
OP-Tee is optional, but if it is there, it could only be loaded by SPL and kicked by ATF
Why ? U-Boot running in EL3 can start OpTee OS, so what is the problem ?
Ah. As we know there is a wrapper in ATF controlled with spd_optee. Technically let uboot in EL3 to kick optee is feasible, but I am not sure people would do this.
Because if there is a bug in OpTee, it can be safely updated just like the kernel can be safely updated -- just boot the other copy as a fallback. If the Optee is baked into your flash.bin , you have to update bootloader, which is dangerous.
participants (6)
-
Alice Guo
-
Alice Guo (OSS)
-
Fabio Estevam
-
Marek Vasut
-
Peng Fan
-
Tom Rini