rv1126-uboot/drivers/cpu/rockchip_amp.c

214 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
*/
#include <common.h>
#include <amp.h>
#include <boot_rkimg.h>
#include <bidram.h>
#include <dm.h>
#include <sysmem.h>
#include <asm/arch/rockchip_smccc.h>
#define AMP_I(fmt, args...) printf("AMP: "fmt, ##args)
#define AMP_E(fmt, args...) printf("AMP Error: "fmt, ##args)
/*
* non-OTA packaged kernel.img & boot.img return the image size on success,
* and a negative value on error.
*/
static int read_rockchip_image(struct blk_desc *dev_desc,
disk_partition_t *part, void *dst)
{
struct rockchip_image *img;
int header_len = 8;
int cnt, ret;
#ifdef CONFIG_ROCKCHIP_CRC
u32 crc32;
#endif
img = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE);
if (!img)
return -ENOMEM;
/* read first block with header imformation */
ret = blk_dread(dev_desc, part->start, 1, img);
if (ret != 1) {
ret = -EIO;
goto err;
}
if (img->tag != TAG_KERNEL) {
printf("Invalid %s image tag(0x%x)\n", part->name, img->tag);
ret = -EINVAL;
goto err;
}
/*
* read the rest blks
* total size = image size + 8 bytes header + 4 bytes crc32
*/
cnt = DIV_ROUND_UP(img->size + 8 + 4, RK_BLK_SIZE);
if (!sysmem_alloc_base_by_name((const char *)part->name,
(phys_addr_t)dst,
cnt * dev_desc->blksz)) {
ret = -ENXIO;
goto err;
}
memcpy(dst, img->image, RK_BLK_SIZE - header_len);
ret = blk_dread(dev_desc, part->start + 1, cnt - 1,
dst + RK_BLK_SIZE - header_len);
if (ret != (cnt - 1)) {
printf("Failed to read %s part, ret=%d\n", part->name, ret);
ret = -EIO;
} else {
ret = img->size;
}
#ifdef CONFIG_ROCKCHIP_CRC
printf("%s image rk crc32 verify... ", part->name);
crc32 = crc32_verify((uchar *)(ulong)dst, img->size + 4);
if (!crc32) {
printf("fail!\n");
ret = -EINVAL;
} else {
printf("okay.\n");
}
#endif
err:
free(img);
return ret;
}
/*
* An example for amps dts node configure:
*
* amps {
* compatible = "uboot,rockchip-amp";
* status = "okay";
*
* amp@0 {
* description = "mcu-os1";
* partition = "mcu1";
* cpu = <0x1>; // this is mpidr!
* aarch64 = <1>; // 0: aarch32, 1: aarch64
* thumb = <0>; // 0: arm, 1: thumb
* hyp = <1>; // 0: el1/svc, 1: el2/hyp
* secure = <0>; // 0: non-secure, 1: secure
* load = <0x800000>;
* entry = <0x800000>;
* memory = <0x800000 0x400000>;
* };
*
* amp@1 {
* ......
* };
*
* ......
* };
*
* U-Boot load "mcu-os1" firmware to "0x800000" address from partiton
* "mcu1" for cpu[1] with ARM, aarch64, hyp(el2) and non-secure state,
* running on 0x800000.
*
* U-Boot reserve memory from 0x800000 with 0x400000 size in order
* to make it invisible for kernel.
*
* Please use rockchip tool "mkkrnlimg" to pack firmware binary, example:
* ./scripts/mkkrnlimg mcu-os1.bin mcu-os1.img
*/
static int rockchip_amp_cpu_on(struct udevice *dev)
{
struct dm_amp_uclass_platdata *uc_pdata;
struct blk_desc *dev_desc;
disk_partition_t part_info;
int ret, size;
u32 pe_state;
uc_pdata = dev_get_uclass_platdata(dev);
if (!uc_pdata)
return -ENXIO;
dev_desc = rockchip_get_bootdev();
if (!dev_desc)
return -EEXIST;
ret = part_get_info_by_name(dev_desc, uc_pdata->partition, &part_info);
if (ret < 0) {
AMP_E("\"%s\" find partition \"%s\" failed\n",
uc_pdata->desc, uc_pdata->partition);
return ret;
}
if (uc_pdata->reserved_mem[1]) {
ret = bidram_reserve_by_name(uc_pdata->partition,
uc_pdata->reserved_mem[0],
uc_pdata->reserved_mem[1]);
if (ret) {
AMP_E("Reserve \"%s\" region at 0x%08x - 0x%08x failed, ret=%d\n",
uc_pdata->desc, uc_pdata->reserved_mem[0],
uc_pdata->reserved_mem[0] +
uc_pdata->reserved_mem[1], ret);
return -ENOMEM;
}
}
size = read_rockchip_image(dev_desc, &part_info,
(void *)(ulong)uc_pdata->load);
if (size < 0) {
AMP_E("\"%s\" load at 0x%08x failed\n",
uc_pdata->desc, uc_pdata->load);
return size;
}
flush_dcache_range(uc_pdata->load,
uc_pdata->load + ALIGN(size, ARCH_DMA_MINALIGN));
pe_state = PE_STATE(uc_pdata->aarch64, uc_pdata->hyp,
uc_pdata->thumb, uc_pdata->secure);
AMP_I("Brought up cpu[%x] with state(%x) from \"%s\" entry 0x%08x ...",
uc_pdata->cpu, pe_state, uc_pdata->desc, uc_pdata->entry);
sip_smc_amp_cfg(AMP_PE_STATE, uc_pdata->cpu, pe_state);
ret = psci_cpu_on(uc_pdata->cpu, uc_pdata->entry);
if (ret) {
printf("failed\n");
return ret;
}
printf("OK\n");
return 0;
}
static const struct dm_amp_ops rockchip_amp_ops = {
.cpu_on = rockchip_amp_cpu_on,
};
U_BOOT_DRIVER(rockchip_amp) = {
.name = "rockchip_amp",
.id = UCLASS_AMP,
.ops = &rockchip_amp_ops,
};
/* AMP bus driver as all amp parent */
static int rockchip_amp_bus_bind(struct udevice *dev)
{
return amp_bind_children(dev, "rockchip_amp");
}
static const struct udevice_id rockchip_amp_bus_match[] = {
{ .compatible = "uboot,rockchip-amp", },
{},
};
U_BOOT_DRIVER(rockchip_amp_bus) = {
.name = "rockchip_amp_bus",
.id = UCLASS_SIMPLE_BUS,
.of_match = rockchip_amp_bus_match,
.bind = rockchip_amp_bus_bind,
};