xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision 008ec9b4bc06f98dd7efdc7d2f44eb066be036e6)
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  *		load         = <0x800000>;
97  *		entry        = <0x800000>;
98  *		memory       = <0x800000 0x400000>;
99  *	};
100  *
101  *	amp@1 {
102  *		......
103  *	};
104  *
105  *	......
106  * };
107  *
108  * U-Boot loads "mcu-os1" firmware to "0x800000" address from partiton
109  * "mcu1" for cpu[1], the cpu[1] entry address is 0x800000. And
110  * U-Boot reserve memory from 0x800000 with 0x400000 size in order
111  * to make it invisible for kernel.
112  *
113  * Please use rockchip tool "mkkrnlimg" to pack firmware binary, example:
114  * ./scripts/mkkrnlimg mcu-os1.bin mcu-os1.img
115  */
116 
117 static int rockchip_amp_cpu_on(struct udevice *dev)
118 {
119 	struct dm_amp_uclass_platdata *uc_pdata;
120 	struct blk_desc *dev_desc;
121 	disk_partition_t part_info;
122 	int ret, size;
123 
124 	uc_pdata = dev_get_uclass_platdata(dev);
125 	if (!uc_pdata)
126 		return -ENXIO;
127 
128 	dev_desc = rockchip_get_bootdev();
129 	if (!dev_desc)
130 		return -EEXIST;
131 
132 	ret = part_get_info_by_name(dev_desc, uc_pdata->partition, &part_info);
133 	if (ret < 0) {
134 		AMP_E("\"%s\" find partition \"%s\" failed\n",
135 		      uc_pdata->desc, uc_pdata->partition);
136 		return ret;
137 	}
138 
139 	ret = bidram_reserve_by_name(uc_pdata->partition,
140 				     uc_pdata->reserved_mem[0],
141 				     uc_pdata->reserved_mem[1]);
142 	if (ret) {
143 		AMP_E("Reserve \"%s\" region at 0x%08x - 0x%08x failed, ret=%d\n",
144 		      uc_pdata->desc, uc_pdata->reserved_mem[0],
145 		      uc_pdata->reserved_mem[0] + uc_pdata->reserved_mem[1], ret);
146 		return -ENOMEM;
147 	}
148 
149 	size = read_rockchip_image(dev_desc, &part_info,
150 				   (void *)(ulong)uc_pdata->load);
151 	if (size < 0) {
152 		AMP_E("\"%s\" load at 0x%08x failed\n",
153 		      uc_pdata->desc, uc_pdata->load);
154 		return size;
155 	}
156 
157 	flush_dcache_range(uc_pdata->load,
158 			   uc_pdata->load + ALIGN(size, ARCH_DMA_MINALIGN));
159 
160 	AMP_I("Brought up cpu[%x] on \"%s\" entry 0x%08x ...",
161 	      uc_pdata->cpu, uc_pdata->desc, uc_pdata->entry);
162 
163 	ret = psci_cpu_on(uc_pdata->cpu, uc_pdata->entry);
164 	if (ret) {
165 		printf("failed\n");
166 		return ret;
167 	}
168 	printf("OK\n");
169 
170 	return 0;
171 }
172 
173 static const struct dm_amp_ops rockchip_amp_ops = {
174 	.cpu_on = rockchip_amp_cpu_on,
175 };
176 
177 U_BOOT_DRIVER(rockchip_amp) = {
178 	.name	   = "rockchip_amp",
179 	.id	   = UCLASS_AMP,
180 	.ops	   = &rockchip_amp_ops,
181 };
182 
183 /* AMP bus driver as all amp parent */
184 static int rockchip_amp_bus_bind(struct udevice *dev)
185 {
186 	return amp_bind_children(dev, "rockchip_amp");
187 }
188 
189 static const struct udevice_id rockchip_amp_bus_match[] = {
190 	{ .compatible = "uboot,rockchip-amp", },
191 	{},
192 };
193 
194 U_BOOT_DRIVER(rockchip_amp_bus) = {
195 	.name	   = "rockchip_amp_bus",
196 	.id	   = UCLASS_SIMPLE_BUS,
197 	.of_match  = rockchip_amp_bus_match,
198 	.bind	   = rockchip_amp_bus_bind,
199 };
200