xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision 39be8a089b3adec6a9a16f79b9faecd03dd8ea68)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 #include <common.h>
6 #include <amp.h>
7 #include <boot_rkimg.h>
8 #include <bidram.h>
9 #include <dm.h>
10 #include <sysmem.h>
11 #include <asm/arch/rockchip_smccc.h>
12 #include <asm/u-boot-arm.h>
13 
14 #define AMP_I(fmt, args...)	printf("AMP: "fmt, ##args)
15 #define AMP_E(fmt, args...)	printf("AMP Error: "fmt, ##args)
16 
17 /*
18  * non-OTA packaged kernel.img & boot.img return the image size on success,
19  * and a negative value on error.
20  */
21 static int read_rockchip_image(struct blk_desc *dev_desc,
22 			       disk_partition_t *part, void *dst)
23 {
24 	struct rockchip_image *img;
25 	int header_len = 8;
26 	int cnt, ret;
27 #ifdef CONFIG_ROCKCHIP_CRC
28 	u32 crc32;
29 #endif
30 
31 	img = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE);
32 	if (!img)
33 		return -ENOMEM;
34 
35 	/* read first block with header imformation */
36 	ret = blk_dread(dev_desc, part->start, 1, img);
37 	if (ret != 1) {
38 		ret = -EIO;
39 		goto err;
40 	}
41 
42 	if (img->tag != TAG_KERNEL) {
43 		printf("Invalid %s image tag(0x%x)\n", part->name, img->tag);
44 		ret = -EINVAL;
45 		goto err;
46 	}
47 
48 	/*
49 	 * read the rest blks
50 	 * total size = image size + 8 bytes header + 4 bytes crc32
51 	 */
52 	cnt = DIV_ROUND_UP(img->size + 8 + 4, RK_BLK_SIZE);
53 	if (!sysmem_alloc_base_by_name((const char *)part->name,
54 				       (phys_addr_t)dst,
55 				       cnt * dev_desc->blksz)) {
56 		ret = -ENXIO;
57 		goto err;
58 	}
59 
60 	memcpy(dst, img->image, RK_BLK_SIZE - header_len);
61 	ret = blk_dread(dev_desc, part->start + 1, cnt - 1,
62 			dst + RK_BLK_SIZE - header_len);
63 	if (ret != (cnt - 1)) {
64 		printf("Failed to read %s part, ret=%d\n", part->name, ret);
65 		ret = -EIO;
66 	} else {
67 		ret = img->size;
68 	}
69 
70 #ifdef CONFIG_ROCKCHIP_CRC
71 	printf("%s image rk crc32 verify... ", part->name);
72 	crc32 = crc32_verify((uchar *)(ulong)dst, img->size + 4);
73 	if (!crc32) {
74 		printf("fail!\n");
75 		ret = -EINVAL;
76 	} else {
77 		printf("okay.\n");
78 	}
79 #endif
80 
81 err:
82 	free(img);
83 	return ret;
84 }
85 
86 /*
87  * An example for amps dts node configure:
88  *
89  * amps {
90  *	compatible = "uboot,rockchip-amp";
91  *	status = "okay";
92  *
93  *	amp@0 {
94  *		description  = "mcu-os1";
95  *		partition    = "mcu1";
96  *		cpu          = <0x1>;		// this is mpidr!
97  *		aarch64	     = <1>;	     	// 0: aarch32, 1: aarch64
98  *		thumb	     = <0>;		// 0: arm, 1: thumb
99  *		hyp	     = <1>;	 	// 0: el1/svc, 1: el2/hyp
100  *		secure	     = <0>;		// 0: non-secure, 1: secure
101  *		load         = <0x800000>;
102  *		entry        = <0x800000>;
103  *		memory       = <0x800000 0x400000>;
104  *	};
105  *
106  *	amp@1 {
107  *		......
108  *	};
109  *
110  *	......
111  * };
112  *
113  * U-Boot load "mcu-os1" firmware to "0x800000" address from partiton
114  * "mcu1" for cpu[1] with ARM, aarch64, hyp(el2) and non-secure state,
115  * running on 0x800000.
116  *
117  * U-Boot reserve memory from 0x800000 with 0x400000 size in order
118  * to make it invisible for kernel.
119  *
120  * Please use rockchip tool "mkkrnlimg" to pack firmware binary, example:
121  * ./scripts/mkkrnlimg mcu-os1.bin mcu-os1.img
122  */
123 
124 static int rockchip_amp_cpu_on(struct udevice *dev, bool this_cpu)
125 {
126 	struct dm_amp_uclass_platdata *uc_pdata;
127 	struct blk_desc *dev_desc;
128 	disk_partition_t part_info;
129 	int ret, size;
130 	u32 pe_state;
131 	u32 es_to_aarch;
132 
133 	uc_pdata = dev_get_uclass_platdata(dev);
134 	if (!uc_pdata)
135 		return -ENXIO;
136 
137 	dev_desc = rockchip_get_bootdev();
138 	if (!dev_desc)
139 		return -EEXIST;
140 
141 	ret = part_get_info_by_name(dev_desc, uc_pdata->partition, &part_info);
142 	if (ret < 0) {
143 		AMP_E("\"%s\" find partition \"%s\" failed\n",
144 		      uc_pdata->desc, uc_pdata->partition);
145 		return ret;
146 	}
147 
148 	if (uc_pdata->reserved_mem[1]) {
149 		ret = bidram_reserve_by_name(uc_pdata->partition,
150 					     uc_pdata->reserved_mem[0],
151 					     uc_pdata->reserved_mem[1]);
152 		if (ret) {
153 			AMP_E("Reserve \"%s\" region at 0x%08x - 0x%08x failed, ret=%d\n",
154 			      uc_pdata->desc, uc_pdata->reserved_mem[0],
155 			      uc_pdata->reserved_mem[0] +
156 			      uc_pdata->reserved_mem[1], ret);
157 			return -ENOMEM;
158 		}
159 	}
160 
161 	size = read_rockchip_image(dev_desc, &part_info,
162 				   (void *)(ulong)uc_pdata->load);
163 	if (size < 0) {
164 		AMP_E("\"%s\" load at 0x%08x failed\n",
165 		      uc_pdata->desc, uc_pdata->load);
166 		return size;
167 	}
168 
169 	flush_dcache_range(uc_pdata->load,
170 			   uc_pdata->load + ALIGN(size, ARCH_DMA_MINALIGN));
171 
172 	pe_state = PE_STATE(uc_pdata->aarch64, uc_pdata->hyp,
173 			    uc_pdata->thumb, uc_pdata->secure);
174 
175 	if (this_cpu) {
176 		AMP_I("Brought up cpu[%x] from \"%s\" with state 0x%x, entry 0x%08x ...",
177 		      uc_pdata->cpu, uc_pdata->desc, pe_state, uc_pdata->entry);
178 
179 		cleanup_before_linux();
180 		es_to_aarch = uc_pdata->aarch64 ? ES_TO_AARCH64 : ES_TO_AARCH32;
181 		printf("OK\n");
182 		armv8_switch_to_el2(0, 0, 0, pe_state, (u64)uc_pdata->entry, es_to_aarch);
183 	} else {
184 		AMP_I("Brought up cpu[%x] from \"%s\" with state 0x%x, entry 0x%08x ...",
185 		      uc_pdata->cpu, uc_pdata->desc, pe_state, uc_pdata->entry);
186 
187 		ret = sip_smc_amp_cfg(AMP_PE_STATE, uc_pdata->cpu, pe_state);
188 		if (ret) {
189 			printf("amp cfg failed, ret=%d\n", ret);
190 			return ret;
191 		}
192 		ret = psci_cpu_on(uc_pdata->cpu, uc_pdata->entry);
193 		if (ret) {
194 			printf("cpu up failed, ret=%d\n", ret);
195 			return ret;
196 		}
197 		printf("OK\n");
198 	}
199 
200 	return 0;
201 }
202 
203 static const struct dm_amp_ops rockchip_amp_ops = {
204 	.cpu_on = rockchip_amp_cpu_on,
205 };
206 
207 U_BOOT_DRIVER(rockchip_amp) = {
208 	.name	   = "rockchip_amp",
209 	.id	   = UCLASS_AMP,
210 	.ops	   = &rockchip_amp_ops,
211 };
212 
213 /* AMP bus driver as all amp parent */
214 static int rockchip_amp_bus_bind(struct udevice *dev)
215 {
216 	return amp_bind_children(dev, "rockchip_amp");
217 }
218 
219 static const struct udevice_id rockchip_amp_bus_match[] = {
220 	{ .compatible = "uboot,rockchip-amp", },
221 	{},
222 };
223 
224 U_BOOT_DRIVER(rockchip_amp_bus) = {
225 	.name	   = "rockchip_amp_bus",
226 	.id	   = UCLASS_SIMPLE_BUS,
227 	.of_match  = rockchip_amp_bus_match,
228 	.bind	   = rockchip_amp_bus_bind,
229 };
230