All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
To: sbabic@denx.de, festevam@gmail.com
Cc: uboot-imx@nxp.com, u-boot@lists.denx.de, Peng Fan <peng.fan@nxp.com>
Subject: [PATCH V2 43/46] imx8ulp: add upower api support
Date: Tue, 29 Jun 2021 10:32:37 +0800	[thread overview]
Message-ID: <20210629023240.22394-44-peng.fan@oss.nxp.com> (raw)
In-Reply-To: <20210629023240.22394-1-peng.fan@oss.nxp.com>

From: Peng Fan <peng.fan@nxp.com>

Add upower api support, this is modified from upower firmware exported
package.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 arch/arm/include/asm/arch-imx8ulp/upower.h    |  15 +
 arch/arm/mach-imx/imx8ulp/Makefile            |   4 +
 arch/arm/mach-imx/imx8ulp/upower/Makefile     |   6 +
 arch/arm/mach-imx/imx8ulp/upower/upower_api.c | 486 ++++++++++++++++++
 arch/arm/mach-imx/imx8ulp/upower/upower_api.h | 258 ++++++++++
 arch/arm/mach-imx/imx8ulp/upower/upower_hal.c | 179 +++++++
 6 files changed, 948 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-imx8ulp/upower.h
 create mode 100644 arch/arm/mach-imx/imx8ulp/upower/Makefile
 create mode 100644 arch/arm/mach-imx/imx8ulp/upower/upower_api.c
 create mode 100644 arch/arm/mach-imx/imx8ulp/upower/upower_api.h
 create mode 100644 arch/arm/mach-imx/imx8ulp/upower/upower_hal.c

diff --git a/arch/arm/include/asm/arch-imx8ulp/upower.h b/arch/arm/include/asm/arch-imx8ulp/upower.h
new file mode 100644
index 0000000000..0f1875bbd6
--- /dev/null
+++ b/arch/arm/include/asm/arch-imx8ulp/upower.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 NXP
+ */
+
+#ifndef __ASM_ARCH_IMX8ULP_UPOWER_H
+#define __ASM_ARCH_IMX8ULP_UPOWER_H
+
+#include <asm/types.h>
+
+int upower_init(void);
+int upower_pmic_i2c_write(u32 reg_addr, u32 reg_val);
+int upower_pmic_i2c_read(u32 reg_addr, u32 *reg_val);
+
+#endif
diff --git a/arch/arm/mach-imx/imx8ulp/Makefile b/arch/arm/mach-imx/imx8ulp/Makefile
index 1ef6cd5c91..2c9938fcdf 100644
--- a/arch/arm/mach-imx/imx8ulp/Makefile
+++ b/arch/arm/mach-imx/imx8ulp/Makefile
@@ -5,3 +5,7 @@
 
 obj-y += lowlevel_init.o
 obj-y += soc.o clock.o iomux.o pcc.o cgc.o rdc.o
+
+ifeq ($(CONFIG_SPL_BUILD),y)
+obj-y += upower/
+endif
diff --git a/arch/arm/mach-imx/imx8ulp/upower/Makefile b/arch/arm/mach-imx/imx8ulp/upower/Makefile
new file mode 100644
index 0000000000..f8b5da2ad3
--- /dev/null
+++ b/arch/arm/mach-imx/imx8ulp/upower/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2020 NXP
+#
+
+obj-y += upower_api.o upower_hal.o
diff --git a/arch/arm/mach-imx/imx8ulp/upower/upower_api.c b/arch/arm/mach-imx/imx8ulp/upower/upower_api.c
new file mode 100644
index 0000000000..98781d3d5d
--- /dev/null
+++ b/arch/arm/mach-imx/imx8ulp/upower/upower_api.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <linux/types.h>
+#include <string.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/io.h>
+#include "upower_api.h"
+
+enum upwr_api_state api_state;
+enum soc_domain pwr_domain;
+void *sh_buffer[UPWR_SG_COUNT];
+struct upwr_code_vers fw_rom_version;
+struct upwr_code_vers fw_ram_version;
+u32 fw_launch_option;
+u32 sg_busy;
+struct mu_type *mu;
+upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT];
+upwr_callb user_callback[UPWR_SG_COUNT];
+UPWR_RX_CALLB_FUNC_T  sgrp_callback[UPWR_SG_COUNT];
+u32 sg_rsp_siz[UPWR_SG_COUNT];
+
+#define UPWR_MU_MSG_SIZE            (2)
+#define UPWR_SG_BUSY(sg) (sg_busy & (1 << (sg)))
+#define UPWR_USR_CALLB(sg, cb)		\
+	do {				\
+		user_callback[sg] = cb;	\
+	} while (0)
+#define UPWR_MSG_HDR(hdr, sg, fn)		\
+	(hdr).domain   = (u32)pwr_domain;	\
+	(hdr).srvgrp   = sg;			\
+	(hdr).function = fn
+
+static u32 upwr_ptr2offset(u64 ptr, enum upwr_sg sg, size_t siz, size_t offset, const void *vptr)
+{
+	if (ptr >= UPWR_DRAM_SHARED_BASE_ADDR &&
+	    ((ptr - UPWR_DRAM_SHARED_BASE_ADDR) < UPWR_DRAM_SHARED_SIZE)) {
+		return (u32)(ptr - UPWR_DRAM_SHARED_BASE_ADDR);
+	}
+
+	/* pointer is outside the shared memory, copy the struct to buffer */
+	memcpy(offset + (char *)sh_buffer[sg], (void *)vptr, siz);
+
+	return (u32)((u64)sh_buffer[sg] + offset - UPWR_DRAM_SHARED_BASE_ADDR);
+}
+
+enum upwr_req_status upwr_req_status(enum upwr_sg sg, u32 *sgfptr, enum upwr_resp *errptr,
+				     int *retptr)
+{
+	enum upwr_req_status status;
+
+	status = (sg_rsp_msg[sg].hdr.errcode == UPWR_RESP_OK) ? UPWR_REQ_OK : UPWR_REQ_ERR;
+
+	return status;
+}
+
+void upwr_copy2tr(struct mu_type *mu, const u32 *msg, u32 size)
+{
+	int i;
+
+	for (i = size - 1; i > -1; i--)
+		writel(msg[i], &mu->tr[i]);
+}
+
+int upwr_tx(const u32 *msg, u32 size)
+{
+	if (size > UPWR_MU_MSG_SIZE)
+		return -2;
+	if (!size)
+		return -2;
+
+	if (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY)
+		return -1;  /* not all TE bits in 1: some data to send still */
+
+	upwr_copy2tr(mu, msg, size);
+	writel(1 << (size - 1), &mu->tcr);
+
+	return 0;
+}
+
+void upwr_srv_req(enum upwr_sg sg, u32 *msg, u32 size)
+{
+	sg_busy |= 1 << sg;
+
+	upwr_tx(msg, size);
+}
+
+int upwr_pwm_power_on(const u32 swton[], const u32 memon[], upwr_callb callb)
+{
+	upwr_pwm_pwron_msg txmsg;
+	u64 ptrval; /* needed for X86, ARM64 */
+	size_t stsize = 0;
+
+	if (api_state != UPWR_API_READY)
+		return -3;
+	if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT))
+		return -1;
+
+	UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb);
+
+	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_ON);
+
+	if (!swton)
+		txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */
+	else
+		txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT,
+						  (stsize = UPWR_PMC_SWT_WORDS * 4), 0, swton);
+
+	if (!memon)
+		txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */
+	else
+		txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT, UPWR_PMC_MEM_WORDS * 4,
+						  stsize, memon);
+
+	upwr_srv_req(UPWR_SG_PWRMGMT, (u32 *)&txmsg, sizeof(txmsg) / 4);
+
+	return 0;
+}
+
+enum upwr_req_status upwr_poll_req_status(enum upwr_sg sg, u32 *sgfptr,
+					  enum upwr_resp *errptr, int *retptr,
+					  u32 attempts)
+{
+	u32 i;
+	enum upwr_req_status ret;
+
+	if (!attempts) {
+		ret = UPWR_REQ_BUSY;
+		while (ret == UPWR_REQ_BUSY)
+			ret = upwr_req_status(sg, sgfptr, errptr, retptr);
+		return ret;
+	}
+
+	for (i = 0; i < attempts; i++) {
+		ret = upwr_req_status(sg, sgfptr, errptr, retptr);
+		if (ret != UPWR_REQ_BUSY)
+			break;
+	}
+
+	return ret;
+}
+
+int upwr_xcp_i2c_access(u16 addr, int8_t data_size, uint8_t subaddr_size, u32 subaddr,
+			u32 wdata, const upwr_callb callb)
+{
+	u64 ptrval = (u64)sh_buffer[UPWR_SG_EXCEPT];
+	struct upwr_i2c_access *i2c_acc_ptr = (struct upwr_i2c_access *)ptrval;
+	struct upwr_pointer_msg txmsg;
+
+	if (api_state != UPWR_API_READY)
+		return -3;
+	if (UPWR_SG_BUSY(UPWR_SG_EXCEPT))
+		return -1;
+
+	UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb);
+
+	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_I2C);
+
+	i2c_acc_ptr->addr = addr;
+	i2c_acc_ptr->subaddr = subaddr;
+	i2c_acc_ptr->subaddr_size = subaddr_size;
+	i2c_acc_ptr->data = wdata;
+	i2c_acc_ptr->data_size = data_size;
+
+	txmsg.ptr = upwr_ptr2offset(ptrval,
+				    UPWR_SG_EXCEPT,
+				    (size_t)sizeof(struct upwr_i2c_access),
+				    0,
+				    i2c_acc_ptr);
+
+	upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4);
+
+	return 0;
+}
+
+int upwr_xcp_set_ddr_retention(enum soc_domain domain, u32 enable, const upwr_callb callb)
+{
+	union upwr_down_1w_msg txmsg;
+
+	if (api_state != UPWR_API_READY)
+		return -3;
+	if (UPWR_SG_BUSY(UPWR_SG_EXCEPT))
+		return -1;
+
+	UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb);
+
+	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_DDR_RETN);
+	txmsg.hdr.domain = (u32)domain;
+	txmsg.hdr.arg    = (u32)enable;
+
+	upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4);
+
+	return 0;
+}
+
+int upwr_rx(u32 *msg, u32 *size)
+{
+	u32 len = readl(&mu->rsr);
+
+	len = (len == 0x0) ? 0 :
+	      (len == 0x1) ? 1 :
+	      #if UPWR_MU_MSG_SIZE > 1
+	      (len == 0x3) ? 2 :
+	      #if UPWR_MU_MSG_SIZE > 2
+	      (len == 0x7) ? 3 :
+	      #if UPWR_MU_MSG_SIZE > 3
+	      (len == 0xF) ? 4 :
+	      #endif
+	      #endif
+	      #endif
+	      0xFFFFFFFF; /* something wrong */
+
+	if (len == 0xFFFFFFFF)
+		return -3;
+
+	*size = len;
+	if (!len)
+		return -1;
+
+	/* copy the received message to the rx queue, so the interrupts are cleared; */
+	for (u32 i = 0; i < len; i++)
+		msg[i] = readl(&mu->rr[i]);
+
+	return 0;
+}
+
+void msg_copy(u32 *dest, u32 *src, u32 size)
+{
+	*dest = *src;
+	if (size > 1)
+		*(dest + 1) = *(src + 1);
+}
+
+void upwr_mu_int_callback(void)
+{
+	enum upwr_sg sg;	/* service group number */
+	UPWR_RX_CALLB_FUNC_T sg_callb; /* service group callback */
+	struct upwr_up_2w_msg rxmsg;
+	u32 size;	/* in words */
+
+	if (upwr_rx((u32 *)&rxmsg, &size) < 0) {
+		UPWR_API_ASSERT(0);
+		return;
+	}
+
+	sg = (enum upwr_sg)rxmsg.hdr.srvgrp;
+
+	/* copy msg to the service group buffer */
+	msg_copy((u32 *)&sg_rsp_msg[sg], (u32 *)&rxmsg, size);
+	sg_rsp_siz[sg] = size;
+	sg_busy &= ~(1 << sg);
+
+	sg_callb = sgrp_callback[sg];
+	if (!sg_callb) {
+		upwr_callb user_callb = user_callback[sg];
+
+		/* no service group callback; call the user callback if any */
+		if (!user_callb)
+			goto done; /* no user callback */
+
+		/* make the user callback */
+		user_callb(sg, rxmsg.hdr.function, (enum upwr_resp)rxmsg.hdr.errcode,
+			   (int)(size == 2) ? rxmsg.word2 : rxmsg.hdr.ret);
+		goto done;
+	}
+
+	/* finally make the group callback */
+	sg_callb();
+	/* don't uninstall the group callback, it's permanent */
+done:
+	if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) /* shutdown error: */
+		api_state = UPWR_API_INITLZED;
+}
+
+void upwr_txrx_isr(void)
+{
+	if (readl(&mu->rsr))
+		upwr_mu_int_callback();
+}
+
+void upwr_start_callb(void)
+{
+	switch (api_state) {
+	case UPWR_API_START_WAIT:
+	{
+		upwr_rdy_callb start_callb = (upwr_rdy_callb)user_callback[UPWR_SG_EXCEPT];
+
+		union upwr_ready_msg *msg = (union upwr_ready_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT];
+
+		/* message sanity check */
+		UPWR_API_ASSERT(msg->hdr.srvgrp   == UPWR_SG_EXCEPT);
+		UPWR_API_ASSERT(msg->hdr.function == UPWR_XCP_START);
+		UPWR_API_ASSERT(msg->hdr.errcode  == UPWR_RESP_OK);
+
+		fw_ram_version.soc_id = fw_rom_version.soc_id;
+		fw_ram_version.vmajor = msg->args.vmajor;
+		fw_ram_version.vminor = msg->args.vminor;
+		fw_ram_version.vfixes = msg->args.vfixes;
+
+		/*
+		 * vmajor == vminor == vfixes == 0 indicates start error
+		 * in this case, go back to the INITLZED state
+		 */
+
+		if (fw_ram_version.vmajor || fw_ram_version.vminor || fw_ram_version.vfixes) {
+			api_state = UPWR_API_READY;
+
+			/* initialization is over: uninstall the callbacks just in case */
+			UPWR_USR_CALLB(UPWR_SG_EXCEPT, NULL);
+			sgrp_callback[UPWR_SG_EXCEPT] = NULL;
+
+			if (!fw_launch_option) {
+				/* launched ROM firmware: RAM fw versions must be all 0s */
+				fw_ram_version.vmajor =
+				fw_ram_version.vminor =
+				fw_ram_version.vfixes = 0;
+			}
+		} else {
+			api_state = UPWR_API_INITLZED;
+		}
+
+		start_callb(msg->args.vmajor, msg->args.vminor, msg->args.vfixes);
+	}
+	break;
+
+	default:
+		UPWR_API_ASSERT(0);
+		break;
+	}
+}
+
+int upwr_init(enum soc_domain domain, struct mu_type *muptr)
+{
+	u32 dom_buffer_base = ((UPWR_API_BUFFER_ENDPLUS + UPWR_API_BUFFER_BASE) / 2);
+	union upwr_init_msg *msg = (union upwr_init_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT];
+	enum upwr_sg sg; /* service group number */
+	u32 size; /* in words */
+	int j;
+
+	mu = muptr;
+	writel(0, &mu->tcr);
+	writel(0, &mu->rcr);
+
+	api_state = UPWR_API_INIT_WAIT;
+	pwr_domain = domain;
+	sg_busy = 0;
+
+	/* initialize the versions, in case they are polled */
+	fw_rom_version.soc_id =
+	fw_rom_version.vmajor =
+	fw_rom_version.vminor =
+	fw_rom_version.vfixes = 0;
+
+	fw_ram_version.soc_id =
+	fw_ram_version.vmajor =
+	fw_ram_version.vminor =
+	fw_ram_version.vfixes = 0;
+
+	sh_buffer[UPWR_SG_EXCEPT] = (void *)(ulong)dom_buffer_base;
+	sh_buffer[UPWR_SG_PWRMGMT] = (void *)(ulong)(dom_buffer_base +
+						     sizeof(union upwr_xcp_union));
+	sh_buffer[UPWR_SG_DELAYM] = NULL;
+	sh_buffer[UPWR_SG_VOLTM] = NULL;
+	sh_buffer[UPWR_SG_CURRM] = NULL;
+	sh_buffer[UPWR_SG_TEMPM] = NULL;
+	sh_buffer[UPWR_SG_DIAG] = NULL;
+	/* (no buffers service groups other than xcp and pwm for now) */
+
+	for (j = 0; j < UPWR_SG_COUNT; j++) {
+		user_callback[j] = NULL;
+	        /* service group Exception gets the initialization callbacks */
+		sgrp_callback[j] = (j == UPWR_SG_EXCEPT) ? upwr_start_callb : NULL;
+
+		/* response messages with an initial consistent content */
+		sg_rsp_msg[j].hdr.errcode = UPWR_RESP_SHUTDOWN;
+	}
+
+	if (readl(&mu->fsr) & BIT(0)) {
+		/* send a ping message down to get the ROM version back */
+		upwr_xcp_ping_msg ping_msg;
+
+		ping_msg.hdr.domain = pwr_domain;
+		ping_msg.hdr.srvgrp = UPWR_SG_EXCEPT;
+		ping_msg.hdr.function = UPWR_XCP_PING;
+
+		if (readl(&mu->rsr) & BIT(0)) /* first clean any Rx message left over */
+			upwr_rx((u32 *)msg, &size);
+
+		while (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY)
+			;
+
+		/*
+		 * now send the ping message;
+		 * do not use upwr_tx, which needs API initilized;
+		 * just write to the MU TR register(s)
+		 */
+		setbits_le32(&mu->fcr, BIT(0)); /* flag urgency status */
+		upwr_copy2tr(mu, (u32 *)&ping_msg, sizeof(ping_msg) / 4);
+	}
+
+	do {
+		/* poll for the MU Rx status: wait for an init message, either
+		 * 1st sent from uPower after reset or as a response to a ping
+		 */
+		while (!readl(&mu->rsr) & BIT(0))
+			;
+
+		clrbits_le32(&mu->fcr, BIT(0));
+
+		if (upwr_rx((u32 *)msg, &size) < 0)
+			return -4;
+
+		if (size != (sizeof(union upwr_init_msg) / 4)) {
+			if (readl(&mu->fsr) & BIT(0))
+				continue; /* discard left over msg */
+			else
+				return -4;
+		}
+
+		sg = (enum upwr_sg)msg->hdr.srvgrp;
+		if (sg != UPWR_SG_EXCEPT) {
+			if (readl(&mu->fsr) & BIT(0))
+				continue;
+			else
+				return -4;
+		}
+
+		if ((enum upwr_xcp_f)msg->hdr.function   != UPWR_XCP_INIT) {
+			if (readl(&mu->fsr) & BIT(0))
+				continue;
+			else
+				return -4;
+		}
+
+		break;
+	} while (true);
+
+	fw_rom_version.soc_id = msg->args.soc;
+	fw_rom_version.vmajor = msg->args.vmajor;
+	fw_rom_version.vminor = msg->args.vminor;
+	fw_rom_version.vfixes = msg->args.vfixes;
+
+	api_state = UPWR_API_INITLZED;
+
+	return 0;
+} /* upwr_init */
+
+int upwr_start(u32 launchopt, const upwr_rdy_callb rdycallb)
+{
+	upwr_start_msg txmsg;
+
+	if (api_state != UPWR_API_INITLZED)
+		return -3;
+
+	UPWR_USR_CALLB(UPWR_SG_EXCEPT, (upwr_callb)rdycallb);
+
+	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_START);
+
+	txmsg.hdr.arg = launchopt;
+	fw_launch_option = launchopt;
+
+	if (upwr_tx((u32 *)&txmsg, sizeof(txmsg) / 4) < 0) {
+		/* catastrophic error, but is it possible to happen? */
+		UPWR_API_ASSERT(0);
+		return -1;
+	}
+
+	api_state = UPWR_API_START_WAIT;
+
+	return 0;
+}
+
+u32 upwr_rom_version(u32 *vmajor, u32 *vminor, u32 *vfixes)
+{
+	u32 soc;
+
+	soc = fw_rom_version.soc_id;
+	*vmajor = fw_rom_version.vmajor;
+	*vminor = fw_rom_version.vminor;
+	*vfixes = fw_rom_version.vfixes;
+
+	return soc;
+}
+
diff --git a/arch/arm/mach-imx/imx8ulp/upower/upower_api.h b/arch/arm/mach-imx/imx8ulp/upower/upower_api.h
new file mode 100644
index 0000000000..c35455a262
--- /dev/null
+++ b/arch/arm/mach-imx/imx8ulp/upower/upower_api.h
@@ -0,0 +1,258 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright 2020 NXP
+ */
+
+enum soc_domain {
+	RTD_DOMAIN = 0,
+	APD_DOMAIN = 1,
+	UPWR_MAIN_DOMAINS,                           /* RTD, AVD */
+	AVD_DOMAIN = UPWR_MAIN_DOMAINS,
+	UPWR_DOMAIN_COUNT,                           /* RTD, APD, AVD */
+	PSD_DOMAIN = UPWR_DOMAIN_COUNT,
+	UPWR_ALL_DOMAINS                             /* RTD, APD, AVD, PSD */
+};
+
+enum upwr_api_state {
+	UPWR_API_INIT_WAIT,        /* waiting for ROM firmware initialization */
+	UPWR_API_INITLZED,         /* ROM firmware initialized */
+	UPWR_API_START_WAIT,       /* waiting for start services */
+	UPWR_API_READY             /* ready to receive service requests */
+};
+
+enum upwr_sg { /* Service Groups in priority order, high to low */
+	UPWR_SG_EXCEPT,   /* 0 = exception           */
+	UPWR_SG_PWRMGMT, /* 1 = power management    */
+	UPWR_SG_DELAYM,   /* 2 = delay   measurement */
+	UPWR_SG_VOLTM,   /* 3 = voltage measurement */
+	UPWR_SG_CURRM,    /* 4 = current measurement */
+	UPWR_SG_TEMPM,    /* 5 = temperature measurement */
+	UPWR_SG_DIAG,     /* 6 = diagnostic  */
+	UPWR_SG_COUNT
+};
+
+enum  upwr_xcp_f {		/* Exception Functions */
+	/* 0 = init msg (not a service request itself) */
+	UPWR_XCP_INIT,
+	/* 0 = also ping request, since its response is an init msg */
+	UPWR_XCP_PING = UPWR_XCP_INIT,
+	UPWR_XCP_START,    /* 1 = service start: upwr_start (not a service request itself) */
+	UPWR_XCP_SHUTDOWN, /*  2 = service shutdown: upwr_xcp_shutdown */
+	UPWR_XCP_CONFIG,   /*  3 = uPower configuration: upwr_xcp_config */
+	UPWR_XCP_SW_ALARM, /*  4 = uPower software alarm: upwr_xcp_sw_alarm */
+	UPWR_XCP_I2C,      /*  5 = I2C access: upwr_xcp_i2c_access */
+	UPWR_XCP_SPARE_6,  /*  6 = spare */
+	UPWR_XCP_SET_DDR_RETN,  /*  7 = set/clear ddr retention */
+	UPWR_XCP_SPARE_8,  /*  8 = spare */
+	UPWR_XCP_SPARE_9,  /*  9 = spare */
+	UPWR_XCP_SPARE_10, /* 10 = spare */
+	UPWR_XCP_SPARE_11, /* 11 = spare */
+	UPWR_XCP_SPARE_12, /* 12 = spare */
+	UPWR_XCP_SPARE_13, /* 13 = spare */
+	UPWR_XCP_SPARE_14, /* 14 = spare */
+	UPWR_XCP_SPARE_15, /* 15 = spare */
+	UPWR_XCP_F_COUNT
+};
+
+enum upwr_resp { /* response error codes */
+	UPWR_RESP_OK = 0,     /* no error */
+	UPWR_RESP_SG_BUSY,    /* service group is busy */
+	UPWR_RESP_SHUTDOWN,   /* services not up or shutting down */
+	UPWR_RESP_BAD_REQ,    /* invalid request */
+	UPWR_RESP_BAD_STATE,  /* system state doesn't allow perform the request */
+	UPWR_RESP_UNINSTALLD, /* service or function not installed */
+	UPWR_RESP_UNINSTALLED =
+	UPWR_RESP_UNINSTALLD, /* service or function not installed (alias) */
+	UPWR_RESP_RESOURCE,   /* resource not available */
+	UPWR_RESP_TIMEOUT,    /* service timeout */
+	UPWR_RESP_COUNT
+};
+
+#define UPWR_SRVGROUP_BITS	(4)
+#define UPWR_FUNCTION_BITS	(4)
+#define UPWR_PWDOMAIN_BITS	(4)
+#define UPWR_HEADER_BITS	(UPWR_SRVGROUP_BITS + UPWR_FUNCTION_BITS + UPWR_PWDOMAIN_BITS)
+#define UPWR_ARG_BITS		(32 - UPWR_HEADER_BITS)
+
+#define UPWR_DUAL_OFFSET_BITS    ((UPWR_ARG_BITS + 32) >> 1)
+
+struct upwr_msg_hdr {
+	u32 domain   :UPWR_PWDOMAIN_BITS;           /* power domain */
+	u32 srvgrp   :UPWR_SRVGROUP_BITS;          /* service group */
+	u32 function :UPWR_FUNCTION_BITS;             /* function */
+	u32 arg      :UPWR_ARG_BITS;     /* function-specific argument */
+};
+
+union upwr_down_1w_msg {
+	struct upwr_msg_hdr hdr;
+	u32 word; /* message first word */
+};
+
+#define upwr_start_msg union upwr_down_1w_msg
+#define upwr_xcp_ping_msg union upwr_down_1w_msg
+
+#define UPWR_RESP_ERR_BITS (4)
+#define UPWR_RESP_HDR_BITS (UPWR_RESP_ERR_BITS + \
+			    UPWR_SRVGROUP_BITS + UPWR_FUNCTION_BITS)
+#define UPWR_RESP_RET_BITS (32 - UPWR_RESP_HDR_BITS)
+
+struct upwr_resp_hdr {
+	u32 errcode :UPWR_RESP_ERR_BITS;
+	u32 srvgrp  :UPWR_SRVGROUP_BITS;      /* service group */
+	u32 function:UPWR_FUNCTION_BITS;
+	u32 ret     :UPWR_RESP_RET_BITS;      /* return value, if any */
+};
+
+struct upwr_up_2w_msg {
+	struct upwr_resp_hdr   hdr;
+	u32               word2;  /* message second word */
+};
+
+#define upwr_up_max_msg struct upwr_up_2w_msg
+
+union upwr_2pointer_msg {
+	struct upwr_msg_hdr  hdr;
+	struct {
+		u64 : UPWR_HEADER_BITS;
+		u64 ptr0 : UPWR_DUAL_OFFSET_BITS;
+		u64 ptr1 : UPWR_DUAL_OFFSET_BITS;
+	} ptrs;
+};
+
+#define upwr_pwm_pwron_msg union upwr_2pointer_msg
+
+struct upwr_pointer_msg {
+	struct upwr_msg_hdr  hdr;
+	u32             ptr; /* config struct offset */
+};
+
+struct upwr_i2c_access { /* structure pointed by message upwr_xcp_i2c_msg */
+	u16         addr;
+	s8           data_size;
+	u8          subaddr_size;
+	u32         subaddr;
+	u32         data;
+};
+
+enum upwr_req_status {
+	UPWR_REQ_OK,     /* request succeeded */
+	UPWR_REQ_ERR,    /* request failed */
+	UPWR_REQ_BUSY    /* request execution ongoing */
+};
+
+#define UPWR_SOC_BITS    (7)
+#define UPWR_VMINOR_BITS (4)
+#define UPWR_VFIXES_BITS (4)
+#define UPWR_VMAJOR_BITS  \
+	(32 - UPWR_HEADER_BITS - UPWR_SOC_BITS - UPWR_VMINOR_BITS - UPWR_VFIXES_BITS)
+union upwr_init_msg {
+	struct upwr_resp_hdr hdr;
+	struct {
+		u32 rsv:UPWR_RESP_HDR_BITS;
+		u32 soc:UPWR_SOC_BITS;        /* SoC identification */
+		u32 vmajor:UPWR_VMAJOR_BITS;  /* firmware major version */
+		u32 vminor:UPWR_VMINOR_BITS;  /* firmware minor version */
+		u32 vfixes:UPWR_VFIXES_BITS;  /* firmware fixes version */
+	} args;
+};
+
+#define UPWR_RAM_VMINOR_BITS (7)
+#define UPWR_RAM_VFIXES_BITS (6)
+#define UPWR_RAM_VMAJOR_BITS (32 - UPWR_HEADER_BITS - UPWR_RAM_VFIXES_BITS - UPWR_RAM_VMINOR_BITS)
+
+union upwr_ready_msg {
+	struct upwr_resp_hdr hdr;
+	struct {
+		u32 rsv:UPWR_RESP_HDR_BITS;
+		u32 vmajor:UPWR_RAM_VMAJOR_BITS; /* RAM fw major version */
+		u32 vminor:UPWR_RAM_VMINOR_BITS; /* RAM fw minor version */
+		u32 vfixes:UPWR_RAM_VFIXES_BITS; /* RAM fw fixes version */
+	} args;
+};
+
+struct upwr_reg_access_t {
+	u32 addr;
+	u32 data;
+	u32 mask; /* mask=0 commands read */
+};
+
+union upwr_xcp_union {
+	struct upwr_reg_access_t reg_access;
+};
+
+enum {			/* Power Management Functions */
+	UPWR_PWM_REGCFG,	/* 0 = regulator config: upwr_pwm_reg_config */
+	UPWR_PWM_DEVMODE = UPWR_PWM_REGCFG, /* deprecated, for old compile */
+	UPWR_PWM_VOLT,		/* 1 = voltage change: upwr_pwm_chng_reg_voltage */
+	UPWR_PWM_SWITCH,	/* 2 = switch control: upwr_pwm_chng_switch_mem */
+	UPWR_PWM_PWR_ON,	/* 3 = switch/RAM/ROM power on: upwr_pwm_power_on  */
+	UPWR_PWM_PWR_OFF,	/* 4 = switch/RAM/ROM power off: upwr_pwm_power_off */
+	UPWR_PWM_RETAIN,	/* 5 = retain memory array: upwr_pwm_mem_retain */
+	UPWR_PWM_DOM_BIAS,	/* 6 = Domain bias control: upwr_pwm_chng_dom_bias */
+	UPWR_PWM_MEM_BIAS,	/* 7 = Memory bias control: upwr_pwm_chng_mem_bias */
+	UPWR_PWM_PMICCFG,	/* 8 = PMIC configuration:  upwr_pwm_pmic_config */
+	UPWR_PWM_PMICMOD = UPWR_PWM_PMICCFG, /* deprecated, for old compile */
+	UPWR_PWM_PES,		/* 9 = Power Event Sequencer */
+	UPWR_PWM_CONFIG,	/* 10= apply power mode defined configuration */
+	UPWR_PWM_CFGPTR,	/* 11= configuration pointer */
+	UPWR_PWM_DOM_PWRON,	/* 12 = domain power on: upwr_pwm_dom_power_on */
+	UPWR_PWM_BOOT,		/* 13 = boot start: upwr_pwm_boot_start */
+	UPWR_PWM_FREQ,		/* 14 = domain frequency setup */
+	UPWR_PWM_PARAM,		/* 15 = power management parameters */
+	UPWR_PWM_F_COUNT
+};
+
+#ifndef UPWR_PMC_SWT_WORDS
+#define UPWR_PMC_SWT_WORDS		(1)
+#endif
+
+#ifndef UPWR_PMC_MEM_WORDS
+#define UPWR_PMC_MEM_WORDS		(2)
+#endif
+
+#define UPWR_API_ASSERT(c) do { } while (0)
+
+struct upwr_code_vers {
+	u32 soc_id;
+	u32 vmajor;
+	u32 vminor;
+	u32 vfixes;
+};
+
+#define UPWR_MU_MSG_SIZE	(2)
+
+#define UPWR_MU_TSR_EMPTY	((u32)((1 << UPWR_MU_MSG_SIZE) - 1))
+
+#ifndef UPWR_DRAM_SHARED_BASE_ADDR
+#define UPWR_DRAM_SHARED_BASE_ADDR	(0x28330000)
+#endif
+
+#ifndef UPWR_DRAM_SHARED_SIZE
+#define UPWR_DRAM_SHARED_SIZE		(2048)
+#endif
+
+#define UPWR_DRAM_SHARED_ENDPLUS	(UPWR_DRAM_SHARED_BASE_ADDR + UPWR_DRAM_SHARED_SIZE)
+
+#ifndef UPWR_API_BUFFER_BASE
+#define UPWR_API_BUFFER_BASE		(0x28330600)
+#endif
+
+#ifndef UPWR_API_BUFFER_ENDPLUS
+#define UPWR_API_BUFFER_ENDPLUS		(UPWR_DRAM_SHARED_ENDPLUS - 64)
+#endif
+
+typedef void (*upwr_rdy_callb)(u32 vmajor, u32 vminor, u32 vfixes);
+typedef void (*upwr_callb)(enum upwr_sg sg, u32 func, enum upwr_resp errcode, int ret);
+int upwr_init(enum soc_domain domain, struct mu_type *muptr);
+int upwr_start(u32 launchopt, const upwr_rdy_callb rdycallb);
+u32 upwr_rom_version(u32 *vmajor, u32 *vminor, u32 *vfixes);
+typedef void (*UPWR_RX_CALLB_FUNC_T)(void);
+
+int upwr_xcp_set_ddr_retention(enum soc_domain domain, u32 enable, const upwr_callb callb);
+int upwr_pwm_power_on(const u32 swton[], const u32 memon[], upwr_callb  callb);
+int upwr_xcp_i2c_access(u16 addr, s8 data_size, u8 subaddr_size, u32 subaddr,
+			u32 wdata, const upwr_callb callb);
+enum upwr_req_status upwr_poll_req_status(enum upwr_sg sg, u32 *sgfptr,
+					  enum upwr_resp *errptr, int *retptr,
+					  u32 attempts);
+void upwr_txrx_isr(void);
diff --git a/arch/arm/mach-imx/imx8ulp/upower/upower_hal.c b/arch/arm/mach-imx/imx8ulp/upower/upower_hal.c
new file mode 100644
index 0000000000..97bb20b2bb
--- /dev/null
+++ b/arch/arm/mach-imx/imx8ulp/upower/upower_hal.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright 2021 NXP
+ */
+
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/imx-regs.h>
+#include <linux/delay.h>
+
+#include "upower_api.h"
+
+#define UPOWER_AP_MU1_ADDR	0x29280000
+static struct mu_type *muptr = (struct mu_type *)UPOWER_AP_MU1_ADDR;
+
+void upower_wait_resp(void)
+{
+	while (!(readl(&muptr->rsr) & BIT(0))) {
+		debug("%s: poll the mu:%x\n", __func__, readl(&muptr->rsr));
+		udelay(100);
+	}
+
+	upwr_txrx_isr();
+}
+
+u32 upower_status(int status)
+{
+	u32 ret = -1;
+	switch (status) {
+	case 0:
+		debug("%s: finished successfully!\n", __func__);
+		ret = 0;
+		break;
+	case -1:
+		printf("%s: memory allocation or resource failed!\n", __func__);
+		break;
+	case -2:
+		printf("%s: invalid argument!\n", __func__);
+		break;
+	case -3:
+		printf("%s: called in an invalid API state!\n", __func__);
+		break;
+	default:
+		printf("%s: invalid return status\n", __func__);
+		break;
+	}
+	return ret;
+}
+
+void user_upwr_rdy_callb(u32 soc, u32 vmajor, u32 vminor)
+{
+	printf("%s: soc=%x\n", __func__, soc);
+	printf("%s: RAM version:%d.%d\n", __func__, vmajor, vminor);
+}
+
+int upower_pmic_i2c_write(u32 reg_addr, u32 reg_val)
+{
+	int ret, ret_val;
+	enum upwr_resp err_code;
+
+	ret = upwr_xcp_i2c_access(0x32, 1, 1, reg_addr, reg_val, NULL);
+	if (ret) {
+		printf("pmic i2c write failed ret %d\n", ret);
+		return ret;
+	}
+
+	upower_wait_resp();
+	ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK) {
+		printf("i2c poll Failure %d, err_code %d, ret_val 0x%x\n", ret, err_code, ret_val);
+		return ret;
+	}
+
+	debug("PMIC write reg[0x%x], val[0x%x]\n", reg_addr, reg_val);
+
+	return 0;
+}
+
+int upower_pmic_i2c_read(u32 reg_addr, u32 *reg_val)
+{
+	int ret, ret_val;
+	enum upwr_resp err_code;
+
+	if (!reg_val)
+		return -1;
+
+	ret = upwr_xcp_i2c_access(0x32, -1, 1, reg_addr, 0, NULL);
+	if (ret) {
+		printf("pmic i2c read failed ret %d\n", ret);
+		return ret;
+	}
+
+	upower_wait_resp();
+	ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK) {
+		printf("i2c poll Failure %d, err_code %d, ret_val 0x%x\n", ret, err_code, ret_val);
+		return ret;
+	}
+
+	*reg_val = ret_val;
+
+	debug("PMIC read reg[0x%x], val[0x%x]\n", reg_addr, *reg_val);
+
+	return 0;
+}
+
+int upower_init(void)
+{
+	u32 fw_major, fw_minor, fw_vfixes;
+	u32 soc_id;
+	int status;
+
+	u32 swton;
+	u64 memon;
+	int ret, ret_val;
+
+	do {
+		status = upwr_init(1, muptr);
+		if (upower_status(status)) {
+			printf("%s: upower init failure\n", __func__);
+			break;
+		}
+
+		soc_id = upwr_rom_version(&fw_major, &fw_minor, &fw_vfixes);
+		if (!soc_id) {
+			printf("%s:, soc_id not initialized\n", __func__);
+			break;
+		}
+
+		printf("%s: soc_id=%d\n", __func__, soc_id);
+		printf("%s: version:%d.%d.%d\n", __func__, fw_major, fw_minor, fw_vfixes);
+
+		printf("%s: start uPower RAM service\n", __func__);
+		status = upwr_start(1, user_upwr_rdy_callb);
+		upower_wait_resp();
+		if (upower_status(status)) {
+			printf("%s: upower init failure\n", __func__);
+			break;
+		}
+	} while(0);
+
+	swton = 0xfff80;
+	ret = upwr_pwm_power_on(&swton, NULL, NULL);
+	if (ret)
+		printf("Turn on switches fail %d\n", ret);
+	else
+		printf("Turn on switches ok\n");
+	upower_wait_resp();
+	ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK)
+		printf("Faliure %d\n", ret);
+
+	memon = 0x3FFFFFFFFFFFFCUL;
+	ret = upwr_pwm_power_on(NULL, (const u32 *)&memon, NULL);
+	if (ret)
+		printf("Turn on memories fail %d\n", ret);
+	else
+		printf("Turn on memories ok\n");
+	upower_wait_resp();
+	ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK)
+		printf("Faliure %d\n", ret);
+
+	mdelay(1);
+
+	ret = upwr_xcp_set_ddr_retention(APD_DOMAIN, 0, NULL);
+	if (ret)
+		printf("Clear DDR retention fail %d\n", ret);
+	else
+		printf("Clear DDR retention ok\n");
+
+	upower_wait_resp();
+
+	ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, NULL, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK)
+		printf("Faliure %d\n", ret);
+
+	return 0;
+}
-- 
2.30.0


  parent reply	other threads:[~2021-06-29  2:07 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-29  2:31 [PATCH V2 00/46] imx: add i.MX8ULP support Peng Fan (OSS)
2021-06-29  2:31 ` [PATCH V2 01/46] arm: imx: add i.MX8ULP basic Kconfig option Peng Fan (OSS)
2021-06-29  2:31 ` [PATCH V2 02/46] arm: imx: add i.MX8ULP cpu type and helper Peng Fan (OSS)
2021-06-29  2:31 ` [PATCH V2 03/46] arm: imx: sys_proto: move boot mode define to common header Peng Fan (OSS)
2021-06-29  2:31 ` [PATCH V2 04/46] arm: imx8ulp: support print cpu info Peng Fan (OSS)
2021-06-29  2:31 ` [PATCH V2 05/46] imx: imx8ulp: add get reset cause Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 06/46] arm: imx: basic i.MX8ULP support Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 07/46] arm: imx8: Move container parser and image to mach-imx common folder Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 08/46] arm: imx8: Move container image header file to mach-imx Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 09/46] arm: imx: parse-container: guard included header files Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 10/46] arm: imx8ulp: add container support Peng Fan (OSS)
2021-07-17 12:49   ` Stefano Babic
2021-07-19  1:41     ` Peng Fan
2021-06-29  2:32 ` [PATCH V2 11/46] arm: imx: move container Kconfig under mach-imx Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 12/46] driver: misc: Add MU and S400 API to communicate with Sentinel Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 13/46] net: fec_mxc: support i.MX8ULP Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 14/46] pinctrl: Add pinctrl driver for imx8ulp Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 15/46] driver: serial: fsl_lpuart: support i.MX8ULP Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 16/46] arm: imx8ulp: add clock support Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 17/46] drivers: mmc: fsl_esdhc_imx: support i.MX8ULP Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 18/46] arm: imx8ulp: soc: Change to use CMC1 to get bootcfg Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 19/46] arm: imx8ulp: Enable full L2 cache in SPL Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 20/46] arm: imx8ulp: disable wdog3 Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 21/46] arm: imx8ulp: Update the reset vector in u-boot Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 22/46] drivers: misc: s400_api: Update S400_SUCCESS_IND to 0xd6 Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 23/46] drivers: misc: imx8ulp: Add S400 API for image authentication Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 24/46] drivers: misc: imx8ulp: Update S400 API for release RDC Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 25/46] drivers: misc: s400_api: Update API for fuse read and write Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 26/46] arm: imx8ulp: release and configure XRDC at early phase Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 27/46] arm: imx8ulp: add rdc support Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 28/46] arm: imx8ulp: add trdc release request Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 29/46] arm: imx8ulp: release trdc and assign lpav from RTD to APD Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 30/46] imx8ulp: unify rdc functions Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 31/46] arm: imx8ulp: Probe the S400 MU device in arch init Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 32/46] arm: iMX8ULP: Add boot device relevant functions Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 33/46] arm: imx8ulp: Allocate DCNANO and MIPI_DSI to AD domain Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 34/46] arm: imx8ulp: add dummy imx_get_mac_from_fuse Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 35/46] arm: imx8ulp: add iomuxc support Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 36/46] driver: misc: imx8ulp: Add fuse driver for imx8ulp Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 37/46] imx8ulp: soc: correct reset cause Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 38/46] imx8ulp: Use DGO_GP5 to get boot config Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 39/46] imx8ulp: Add workaround for eMMC boot Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 40/46] mx7ulp: Update unlock and refresh sequences in sWDOG driver Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 41/46] mx7ulp: wdog: Wait for WDOG unlock and reconfiguration to complete Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 42/46] imx8ulp: move struct mu_type to common header Peng Fan (OSS)
2021-06-29  2:32 ` Peng Fan (OSS) [this message]
2021-06-29  2:32 ` [PATCH V2 44/46] ddr: Add DDR driver for iMX8ULP Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 45/46] arm: dts: add i.MX8ULP dtsi Peng Fan (OSS)
2021-06-29  2:32 ` [PATCH V2 46/46] arm: imx: add i.MX8ULP EVK support Peng Fan (OSS)
2021-07-15  3:29 ` [PATCH V2 00/46] imx: add i.MX8ULP support Peng Fan (OSS)
2021-07-15  8:11   ` Stefano Babic

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210629023240.22394-44-peng.fan@oss.nxp.com \
    --to=peng.fan@oss.nxp.com \
    --cc=festevam@gmail.com \
    --cc=peng.fan@nxp.com \
    --cc=sbabic@denx.de \
    --cc=u-boot@lists.denx.de \
    --cc=uboot-imx@nxp.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.