Prepare v2023.10
[platform/kernel/u-boot.git] / arch / arm / mach-imx / imx_bootaux.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Freescale Semiconductor, Inc.
4  */
5
6 #include <common.h>
7 #include <log.h>
8 #include <asm/io.h>
9 #include <asm/mach-imx/sys_proto.h>
10 #include <command.h>
11 #include <elf.h>
12 #include <imx_sip.h>
13 #include <linux/arm-smccc.h>
14 #include <linux/compiler.h>
15 #include <cpu_func.h>
16
17 #ifndef CONFIG_IMX8
18 /* Just to avoid build error */
19 #if IS_ENABLED(CONFIG_IMX8M)
20 #define SRC_M4C_NON_SCLR_RST_MASK       BIT(0)
21 #define SRC_M4_ENABLE_MASK              BIT(0)
22 #define SRC_M4_REG_OFFSET               0
23 #endif
24
25 __weak const struct rproc_att *imx_bootaux_get_hostmap(void)
26 {
27         return NULL;
28 }
29
30 static const struct rproc_att *get_host_mapping(unsigned long auxcore)
31 {
32         const struct rproc_att *mmap = imx_bootaux_get_hostmap();
33
34         while (mmap && mmap->size) {
35                 if (mmap->da <= auxcore &&
36                     mmap->da + mmap->size > auxcore)
37                         return mmap;
38                 mmap++;
39         }
40
41         return NULL;
42 }
43
44 /*
45  * A very simple elf loader for the auxilary core, assumes the image
46  * is valid, returns the entry point address.
47  * Translates load addresses in the elf file to the U-Boot address space.
48  */
49 static u32 load_elf_image_m_core_phdr(unsigned long addr, u32 *stack)
50 {
51         Elf32_Ehdr *ehdr; /* ELF header structure pointer */
52         Elf32_Phdr *phdr; /* Program header structure pointer */
53         int num = 0;
54         int i;
55
56         ehdr = (Elf32_Ehdr *)addr;
57         phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
58
59         /* Load each program header */
60         for (i = 0; i < ehdr->e_phnum; ++i, ++phdr) {
61                 const struct rproc_att *mmap = get_host_mapping(phdr->p_paddr);
62                 void *dst, *src;
63
64                 if (phdr->p_type != PT_LOAD)
65                         continue;
66
67                 if (!mmap) {
68                         printf("Invalid aux core address: %08x\n",
69                                phdr->p_paddr);
70                         return 0;
71                 }
72
73                 dst = (void *)(ulong)(phdr->p_paddr - mmap->da) + mmap->sa;
74                 src = (void *)addr + phdr->p_offset;
75
76                 debug("Loading phdr %i to 0x%p (%i bytes)\n",
77                       i, dst, phdr->p_filesz);
78
79                 if (phdr->p_filesz) {
80                         memcpy(dst, src, phdr->p_filesz);
81                         /* Stack in __isr_vector is the first section/word */
82                         if (!num)
83                                 *stack = *(uint32_t *)src;
84                         num++;
85                 }
86                 if (phdr->p_filesz != phdr->p_memsz)
87                         memset(dst + phdr->p_filesz, 0x00,
88                                phdr->p_memsz - phdr->p_filesz);
89                 flush_cache((unsigned long)dst &
90                             ~(CONFIG_SYS_CACHELINE_SIZE - 1),
91                             ALIGN(phdr->p_filesz, CONFIG_SYS_CACHELINE_SIZE));
92         }
93
94         return ehdr->e_entry;
95 }
96
97 int arch_auxiliary_core_up(u32 core_id, ulong addr)
98 {
99         u32 stack, pc;
100
101         if (!addr)
102                 return -EINVAL;
103
104         /*
105          * handling ELF64 binaries
106          * isn't supported yet.
107          */
108         if (valid_elf_image(addr)) {
109                 pc = load_elf_image_m_core_phdr(addr, &stack);
110                 if (!pc)
111                         return CMD_RET_FAILURE;
112
113                 if (!IS_ENABLED(CONFIG_ARM64))
114                         stack = 0x0;
115         } else {
116                 /*
117                  * Assume binary file with vector table at the beginning.
118                  * Cortex-M4 vector tables start with the stack pointer (SP)
119                  * and reset vector (initial PC).
120                  */
121                 stack = *(u32 *)addr;
122                 pc = *(u32 *)(addr + 4);
123         }
124
125         printf("## Starting auxiliary core stack = 0x%08X, pc = 0x%08X...\n",
126                stack, pc);
127
128         /* Set the stack and pc to MCU bootROM */
129         writel(stack, MCU_BOOTROM_BASE_ADDR);
130         writel(pc, MCU_BOOTROM_BASE_ADDR + 4);
131
132         flush_dcache_all();
133
134         /* Enable MCU */
135         if (IS_ENABLED(CONFIG_IMX8M)) {
136                 arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_MCU_START, 0, 0, 0, 0, 0, 0, NULL);
137         } else {
138                 clrsetbits_le32(SRC_BASE_ADDR + SRC_M4_REG_OFFSET,
139                                 SRC_M4C_NON_SCLR_RST_MASK, SRC_M4_ENABLE_MASK);
140         }
141
142         return 0;
143 }
144
145 int arch_auxiliary_core_check_up(u32 core_id)
146 {
147         struct arm_smccc_res res;
148         unsigned int val;
149
150         if (IS_ENABLED(CONFIG_IMX8M)) {
151                 arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_MCU_STARTED, 0, 0, 0, 0, 0, 0, &res);
152                 return res.a0;
153         }
154
155         val = readl(SRC_BASE_ADDR + SRC_M4_REG_OFFSET);
156
157         if (val & SRC_M4C_NON_SCLR_RST_MASK)
158                 return 0;  /* assert in reset */
159
160         return 1;
161 }
162 #endif
163 /*
164  * To i.MX6SX and i.MX7D, the image supported by bootaux needs
165  * the reset vector at the head for the image, with SP and PC
166  * as the first two words.
167  *
168  * Per the cortex-M reference manual, the reset vector of M4/M7 needs
169  * to exist at 0x0 (TCMUL/IDTCM). The PC and SP are the first two addresses
170  * of that vector.  So to boot M4/M7, the A core must build the M4/M7's reset
171  * vector with getting the PC and SP from image and filling them to
172  * TCMUL/IDTCM. When M4/M7 is kicked, it will load the PC and SP by itself.
173  * The TCMUL/IDTCM is mapped to (MCU_BOOTROM_BASE_ADDR) at A core side for
174  * accessing the M4/M7 TCMUL/IDTCM.
175  */
176 static int do_bootaux(struct cmd_tbl *cmdtp, int flag, int argc,
177                       char *const argv[])
178 {
179         ulong addr;
180         int ret, up;
181         u32 core = 0;
182
183         if (argc < 2)
184                 return CMD_RET_USAGE;
185
186         if (argc > 2)
187                 core = simple_strtoul(argv[2], NULL, 10);
188
189         up = arch_auxiliary_core_check_up(core);
190         if (up) {
191                 printf("## Auxiliary core is already up\n");
192                 return CMD_RET_SUCCESS;
193         }
194
195         addr = hextoul(argv[1], NULL);
196
197         if (!addr)
198                 return CMD_RET_FAILURE;
199
200         ret = arch_auxiliary_core_up(core, addr);
201         if (ret)
202                 return CMD_RET_FAILURE;
203
204         return CMD_RET_SUCCESS;
205 }
206
207 U_BOOT_CMD(
208         bootaux, CONFIG_SYS_MAXARGS, 1, do_bootaux,
209         "Start auxiliary core",
210         "<address> [<core>]\n"
211         "   - start auxiliary core [<core>] (default 0),\n"
212         "     at address <address>\n"
213 );