]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
imx8ulp: add upower api support
authorPeng Fan <peng.fan@nxp.com>
Sat, 7 Aug 2021 08:01:10 +0000 (16:01 +0800)
committerStefano Babic <sbabic@denx.de>
Mon, 9 Aug 2021 12:46:51 +0000 (14:46 +0200)
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 [new file with mode: 0644]
arch/arm/mach-imx/imx8ulp/Makefile
arch/arm/mach-imx/imx8ulp/upower/Makefile [new file with mode: 0644]
arch/arm/mach-imx/imx8ulp/upower/upower_api.c [new file with mode: 0644]
arch/arm/mach-imx/imx8ulp/upower/upower_api.h [new file with mode: 0644]
arch/arm/mach-imx/imx8ulp/upower/upower_hal.c [new file with mode: 0644]

diff --git a/arch/arm/include/asm/arch-imx8ulp/upower.h b/arch/arm/include/asm/arch-imx8ulp/upower.h
new file mode 100644 (file)
index 0000000..0f1875b
--- /dev/null
@@ -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
index 1ef6cd5c9131315dad7fccec06a41d849136bd0f..2c9938fcdf0ab2ae9ee2a5d6847255b82fdd6649 100644 (file)
@@ -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 (file)
index 0000000..f8b5da2
--- /dev/null
@@ -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 (file)
index 0000000..5e19b98
--- /dev/null
@@ -0,0 +1,485 @@
+// 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 (file)
index 0000000..5cd7802
--- /dev/null
@@ -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 (file)
index 0000000..b6811d5
--- /dev/null
@@ -0,0 +1,180 @@
+// 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("Failure %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("Failure %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("Failure %d\n", ret);
+
+       return 0;
+}