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