xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision 39be8a089b3adec6a9a16f79b9faecd03dd8ea68)
14388decaSJoseph Chen // SPDX-License-Identifier: GPL-2.0
24388decaSJoseph Chen /*
34388decaSJoseph Chen  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
44388decaSJoseph Chen  */
54388decaSJoseph Chen #include <common.h>
64388decaSJoseph Chen #include <amp.h>
74388decaSJoseph Chen #include <boot_rkimg.h>
84388decaSJoseph Chen #include <bidram.h>
94388decaSJoseph Chen #include <dm.h>
104388decaSJoseph Chen #include <sysmem.h>
114388decaSJoseph Chen #include <asm/arch/rockchip_smccc.h>
12*39be8a08SJoseph Chen #include <asm/u-boot-arm.h>
134388decaSJoseph Chen 
144388decaSJoseph Chen #define AMP_I(fmt, args...)	printf("AMP: "fmt, ##args)
154388decaSJoseph Chen #define AMP_E(fmt, args...)	printf("AMP Error: "fmt, ##args)
164388decaSJoseph Chen 
174388decaSJoseph Chen /*
180df2c3dfSJoseph Chen  * non-OTA packaged kernel.img & boot.img return the image size on success,
190df2c3dfSJoseph Chen  * and a negative value on error.
200df2c3dfSJoseph Chen  */
210df2c3dfSJoseph Chen static int read_rockchip_image(struct blk_desc *dev_desc,
220df2c3dfSJoseph Chen 			       disk_partition_t *part, void *dst)
230df2c3dfSJoseph Chen {
240df2c3dfSJoseph Chen 	struct rockchip_image *img;
250df2c3dfSJoseph Chen 	int header_len = 8;
260df2c3dfSJoseph Chen 	int cnt, ret;
270df2c3dfSJoseph Chen #ifdef CONFIG_ROCKCHIP_CRC
280df2c3dfSJoseph Chen 	u32 crc32;
290df2c3dfSJoseph Chen #endif
300df2c3dfSJoseph Chen 
310df2c3dfSJoseph Chen 	img = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE);
320df2c3dfSJoseph Chen 	if (!img)
330df2c3dfSJoseph Chen 		return -ENOMEM;
340df2c3dfSJoseph Chen 
350df2c3dfSJoseph Chen 	/* read first block with header imformation */
360df2c3dfSJoseph Chen 	ret = blk_dread(dev_desc, part->start, 1, img);
370df2c3dfSJoseph Chen 	if (ret != 1) {
380df2c3dfSJoseph Chen 		ret = -EIO;
390df2c3dfSJoseph Chen 		goto err;
400df2c3dfSJoseph Chen 	}
410df2c3dfSJoseph Chen 
420df2c3dfSJoseph Chen 	if (img->tag != TAG_KERNEL) {
430df2c3dfSJoseph Chen 		printf("Invalid %s image tag(0x%x)\n", part->name, img->tag);
440df2c3dfSJoseph Chen 		ret = -EINVAL;
450df2c3dfSJoseph Chen 		goto err;
460df2c3dfSJoseph Chen 	}
470df2c3dfSJoseph Chen 
480df2c3dfSJoseph Chen 	/*
490df2c3dfSJoseph Chen 	 * read the rest blks
500df2c3dfSJoseph Chen 	 * total size = image size + 8 bytes header + 4 bytes crc32
510df2c3dfSJoseph Chen 	 */
520df2c3dfSJoseph Chen 	cnt = DIV_ROUND_UP(img->size + 8 + 4, RK_BLK_SIZE);
530df2c3dfSJoseph Chen 	if (!sysmem_alloc_base_by_name((const char *)part->name,
540df2c3dfSJoseph Chen 				       (phys_addr_t)dst,
550df2c3dfSJoseph Chen 				       cnt * dev_desc->blksz)) {
560df2c3dfSJoseph Chen 		ret = -ENXIO;
570df2c3dfSJoseph Chen 		goto err;
580df2c3dfSJoseph Chen 	}
590df2c3dfSJoseph Chen 
600df2c3dfSJoseph Chen 	memcpy(dst, img->image, RK_BLK_SIZE - header_len);
610df2c3dfSJoseph Chen 	ret = blk_dread(dev_desc, part->start + 1, cnt - 1,
620df2c3dfSJoseph Chen 			dst + RK_BLK_SIZE - header_len);
630df2c3dfSJoseph Chen 	if (ret != (cnt - 1)) {
640df2c3dfSJoseph Chen 		printf("Failed to read %s part, ret=%d\n", part->name, ret);
650df2c3dfSJoseph Chen 		ret = -EIO;
660df2c3dfSJoseph Chen 	} else {
670df2c3dfSJoseph Chen 		ret = img->size;
680df2c3dfSJoseph Chen 	}
690df2c3dfSJoseph Chen 
700df2c3dfSJoseph Chen #ifdef CONFIG_ROCKCHIP_CRC
710df2c3dfSJoseph Chen 	printf("%s image rk crc32 verify... ", part->name);
720df2c3dfSJoseph Chen 	crc32 = crc32_verify((uchar *)(ulong)dst, img->size + 4);
730df2c3dfSJoseph Chen 	if (!crc32) {
740df2c3dfSJoseph Chen 		printf("fail!\n");
750df2c3dfSJoseph Chen 		ret = -EINVAL;
760df2c3dfSJoseph Chen 	} else {
770df2c3dfSJoseph Chen 		printf("okay.\n");
780df2c3dfSJoseph Chen 	}
790df2c3dfSJoseph Chen #endif
800df2c3dfSJoseph Chen 
810df2c3dfSJoseph Chen err:
820df2c3dfSJoseph Chen 	free(img);
830df2c3dfSJoseph Chen 	return ret;
840df2c3dfSJoseph Chen }
850df2c3dfSJoseph Chen 
860df2c3dfSJoseph Chen /*
874388decaSJoseph Chen  * An example for amps dts node configure:
884388decaSJoseph Chen  *
894388decaSJoseph Chen  * amps {
904388decaSJoseph Chen  *	compatible = "uboot,rockchip-amp";
914388decaSJoseph Chen  *	status = "okay";
924388decaSJoseph Chen  *
934388decaSJoseph Chen  *	amp@0 {
944388decaSJoseph Chen  *		description  = "mcu-os1";
954388decaSJoseph Chen  *		partition    = "mcu1";
964388decaSJoseph Chen  *		cpu          = <0x1>;		// this is mpidr!
97f06413e4SJoseph Chen  *		aarch64	     = <1>;	     	// 0: aarch32, 1: aarch64
98f06413e4SJoseph Chen  *		thumb	     = <0>;		// 0: arm, 1: thumb
99f06413e4SJoseph Chen  *		hyp	     = <1>;	 	// 0: el1/svc, 1: el2/hyp
100f06413e4SJoseph Chen  *		secure	     = <0>;		// 0: non-secure, 1: secure
1014388decaSJoseph Chen  *		load         = <0x800000>;
1024388decaSJoseph Chen  *		entry        = <0x800000>;
1034388decaSJoseph Chen  *		memory       = <0x800000 0x400000>;
1044388decaSJoseph Chen  *	};
1054388decaSJoseph Chen  *
1064388decaSJoseph Chen  *	amp@1 {
1074388decaSJoseph Chen  *		......
1084388decaSJoseph Chen  *	};
1094388decaSJoseph Chen  *
1104388decaSJoseph Chen  *	......
1114388decaSJoseph Chen  * };
1124388decaSJoseph Chen  *
113f06413e4SJoseph Chen  * U-Boot load "mcu-os1" firmware to "0x800000" address from partiton
114f06413e4SJoseph Chen  * "mcu1" for cpu[1] with ARM, aarch64, hyp(el2) and non-secure state,
115f06413e4SJoseph Chen  * running on 0x800000.
116f06413e4SJoseph Chen  *
1174388decaSJoseph Chen  * U-Boot reserve memory from 0x800000 with 0x400000 size in order
1184388decaSJoseph Chen  * to make it invisible for kernel.
1194388decaSJoseph Chen  *
1204388decaSJoseph Chen  * Please use rockchip tool "mkkrnlimg" to pack firmware binary, example:
1214388decaSJoseph Chen  * ./scripts/mkkrnlimg mcu-os1.bin mcu-os1.img
1224388decaSJoseph Chen  */
1234388decaSJoseph Chen 
124*39be8a08SJoseph Chen static int rockchip_amp_cpu_on(struct udevice *dev, bool this_cpu)
1254388decaSJoseph Chen {
1264388decaSJoseph Chen 	struct dm_amp_uclass_platdata *uc_pdata;
1274388decaSJoseph Chen 	struct blk_desc *dev_desc;
1284388decaSJoseph Chen 	disk_partition_t part_info;
1294388decaSJoseph Chen 	int ret, size;
130f06413e4SJoseph Chen 	u32 pe_state;
131*39be8a08SJoseph Chen 	u32 es_to_aarch;
1324388decaSJoseph Chen 
1334388decaSJoseph Chen 	uc_pdata = dev_get_uclass_platdata(dev);
1344388decaSJoseph Chen 	if (!uc_pdata)
1354388decaSJoseph Chen 		return -ENXIO;
1364388decaSJoseph Chen 
1374388decaSJoseph Chen 	dev_desc = rockchip_get_bootdev();
1384388decaSJoseph Chen 	if (!dev_desc)
1394388decaSJoseph Chen 		return -EEXIST;
1404388decaSJoseph Chen 
1414388decaSJoseph Chen 	ret = part_get_info_by_name(dev_desc, uc_pdata->partition, &part_info);
1424388decaSJoseph Chen 	if (ret < 0) {
1434388decaSJoseph Chen 		AMP_E("\"%s\" find partition \"%s\" failed\n",
1444388decaSJoseph Chen 		      uc_pdata->desc, uc_pdata->partition);
1454388decaSJoseph Chen 		return ret;
1464388decaSJoseph Chen 	}
1474388decaSJoseph Chen 
148f06413e4SJoseph Chen 	if (uc_pdata->reserved_mem[1]) {
1494388decaSJoseph Chen 		ret = bidram_reserve_by_name(uc_pdata->partition,
1504388decaSJoseph Chen 					     uc_pdata->reserved_mem[0],
1514388decaSJoseph Chen 					     uc_pdata->reserved_mem[1]);
1524388decaSJoseph Chen 		if (ret) {
1534388decaSJoseph Chen 			AMP_E("Reserve \"%s\" region at 0x%08x - 0x%08x failed, ret=%d\n",
1544388decaSJoseph Chen 			      uc_pdata->desc, uc_pdata->reserved_mem[0],
155f06413e4SJoseph Chen 			      uc_pdata->reserved_mem[0] +
156f06413e4SJoseph Chen 			      uc_pdata->reserved_mem[1], ret);
1574388decaSJoseph Chen 			return -ENOMEM;
1584388decaSJoseph Chen 		}
159f06413e4SJoseph Chen 	}
1604388decaSJoseph Chen 
1614388decaSJoseph Chen 	size = read_rockchip_image(dev_desc, &part_info,
1624388decaSJoseph Chen 				   (void *)(ulong)uc_pdata->load);
1634388decaSJoseph Chen 	if (size < 0) {
1644388decaSJoseph Chen 		AMP_E("\"%s\" load at 0x%08x failed\n",
1654388decaSJoseph Chen 		      uc_pdata->desc, uc_pdata->load);
1664388decaSJoseph Chen 		return size;
1674388decaSJoseph Chen 	}
1684388decaSJoseph Chen 
1694388decaSJoseph Chen 	flush_dcache_range(uc_pdata->load,
1704388decaSJoseph Chen 			   uc_pdata->load + ALIGN(size, ARCH_DMA_MINALIGN));
1714388decaSJoseph Chen 
172f06413e4SJoseph Chen 	pe_state = PE_STATE(uc_pdata->aarch64, uc_pdata->hyp,
173f06413e4SJoseph Chen 			    uc_pdata->thumb, uc_pdata->secure);
1744388decaSJoseph Chen 
175*39be8a08SJoseph Chen 	if (this_cpu) {
176*39be8a08SJoseph Chen 		AMP_I("Brought up cpu[%x] from \"%s\" with state 0x%x, entry 0x%08x ...",
177*39be8a08SJoseph Chen 		      uc_pdata->cpu, uc_pdata->desc, pe_state, uc_pdata->entry);
178f06413e4SJoseph Chen 
179*39be8a08SJoseph Chen 		cleanup_before_linux();
180*39be8a08SJoseph Chen 		es_to_aarch = uc_pdata->aarch64 ? ES_TO_AARCH64 : ES_TO_AARCH32;
181*39be8a08SJoseph Chen 		printf("OK\n");
182*39be8a08SJoseph Chen 		armv8_switch_to_el2(0, 0, 0, pe_state, (u64)uc_pdata->entry, es_to_aarch);
183*39be8a08SJoseph Chen 	} else {
184*39be8a08SJoseph Chen 		AMP_I("Brought up cpu[%x] from \"%s\" with state 0x%x, entry 0x%08x ...",
185*39be8a08SJoseph Chen 		      uc_pdata->cpu, uc_pdata->desc, pe_state, uc_pdata->entry);
186*39be8a08SJoseph Chen 
187*39be8a08SJoseph Chen 		ret = sip_smc_amp_cfg(AMP_PE_STATE, uc_pdata->cpu, pe_state);
188*39be8a08SJoseph Chen 		if (ret) {
189*39be8a08SJoseph Chen 			printf("amp cfg failed, ret=%d\n", ret);
190*39be8a08SJoseph Chen 			return ret;
191*39be8a08SJoseph Chen 		}
1924388decaSJoseph Chen 		ret = psci_cpu_on(uc_pdata->cpu, uc_pdata->entry);
1934388decaSJoseph Chen 		if (ret) {
194*39be8a08SJoseph Chen 			printf("cpu up failed, ret=%d\n", ret);
1954388decaSJoseph Chen 			return ret;
1964388decaSJoseph Chen 		}
1974388decaSJoseph Chen 		printf("OK\n");
198*39be8a08SJoseph Chen 	}
1994388decaSJoseph Chen 
2004388decaSJoseph Chen 	return 0;
2014388decaSJoseph Chen }
2024388decaSJoseph Chen 
2034388decaSJoseph Chen static const struct dm_amp_ops rockchip_amp_ops = {
2044388decaSJoseph Chen 	.cpu_on = rockchip_amp_cpu_on,
2054388decaSJoseph Chen };
2064388decaSJoseph Chen 
2074388decaSJoseph Chen U_BOOT_DRIVER(rockchip_amp) = {
2084388decaSJoseph Chen 	.name	   = "rockchip_amp",
2094388decaSJoseph Chen 	.id	   = UCLASS_AMP,
2104388decaSJoseph Chen 	.ops	   = &rockchip_amp_ops,
2114388decaSJoseph Chen };
2124388decaSJoseph Chen 
2134388decaSJoseph Chen /* AMP bus driver as all amp parent */
2144388decaSJoseph Chen static int rockchip_amp_bus_bind(struct udevice *dev)
2154388decaSJoseph Chen {
2164388decaSJoseph Chen 	return amp_bind_children(dev, "rockchip_amp");
2174388decaSJoseph Chen }
2184388decaSJoseph Chen 
2194388decaSJoseph Chen static const struct udevice_id rockchip_amp_bus_match[] = {
2204388decaSJoseph Chen 	{ .compatible = "uboot,rockchip-amp", },
2214388decaSJoseph Chen 	{},
2224388decaSJoseph Chen };
2234388decaSJoseph Chen 
2244388decaSJoseph Chen U_BOOT_DRIVER(rockchip_amp_bus) = {
2254388decaSJoseph Chen 	.name	   = "rockchip_amp_bus",
2264388decaSJoseph Chen 	.id	   = UCLASS_SIMPLE_BUS,
2274388decaSJoseph Chen 	.of_match  = rockchip_amp_bus_match,
2284388decaSJoseph Chen 	.bind	   = rockchip_amp_bus_bind,
2294388decaSJoseph Chen };
230