diff options
author | Joseph Chen <chenjh@rock-chips.com> | 2019-03-21 18:22:13 +0800 |
---|---|---|
committer | Jianhong Chen <chenjh@rock-chips.com> | 2019-04-08 20:53:44 +0800 |
commit | 4388deca723e78cda09c928f33f3a4d299e414c9 (patch) | |
tree | 98a2e688f5ff1faf85b9c95839fb6299ea569a57 | |
parent | 5ae28c899a83ad1e62e928d6e833cb3f8916e13e (diff) |
dm: add amp uclass and rockchip amp driver support
- add amp uclass;
- add a simple rockchip amp driver.
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!
load = <0x800000>;
entry = <0x800000>;
memory = <0x800000 0x400000>;
};
amp@1 {
......
};
......
};
U-Boot loads "mcu-os1" firmware to "0x800000" address from partiton
"mcu1" for cpu[1], the cpu[1] entry address is 0x800000. And
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
Change-Id: I127d5d9f460ec0c1812a76fb4c3702e82f21c9a6
Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | drivers/cpu/Kconfig | 12 | ||||
-rw-r--r-- | drivers/cpu/Makefile | 2 | ||||
-rw-r--r-- | drivers/cpu/amp-uclass.c | 162 | ||||
-rw-r--r-- | drivers/cpu/rockchip_amp.c | 130 | ||||
-rw-r--r-- | include/amp.h | 29 | ||||
-rw-r--r-- | include/dm/uclass-id.h | 1 |
7 files changed, 337 insertions, 0 deletions
@@ -654,6 +654,7 @@ libs-y += fs/ libs-y += net/ libs-y += disk/ libs-y += drivers/ +libs-y += drivers/cpu/ libs-y += drivers/dma/ libs-y += drivers/gpio/ libs-y += drivers/i2c/ diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig index 0d1424d38e..6dfced4f4f 100644 --- a/drivers/cpu/Kconfig +++ b/drivers/cpu/Kconfig @@ -6,3 +6,15 @@ config CPU multiple CPUs, then normally have to be set up in U-Boot so that they can work correctly in the OS. This provides a framework for finding out information about available CPUs and making changes. + +config AMP + bool "Enable AMP drivers using Driver Model" + help + This support Asymmetric Multi-Processing, cpus can run on different + firmware. + +config ROCKCHIP_AMP + bool "Enable Rockchip AMP driver" + depends on AMP && ROCKCHIP_SMCCC && RKIMG_BOOTLOADER + help + This enable Rockchip AMP driver support. diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile index db515f6f17..d50912ffe5 100644 --- a/drivers/cpu/Makefile +++ b/drivers/cpu/Makefile @@ -5,5 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # obj-$(CONFIG_CPU) += cpu-uclass.o +obj-$(CONFIG_AMP) += amp-uclass.o obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o +obj-$(CONFIG_ROCKCHIP_AMP) += rockchip_amp.o diff --git a/drivers/cpu/amp-uclass.c b/drivers/cpu/amp-uclass.c new file mode 100644 index 0000000000..7d52722a2d --- /dev/null +++ b/drivers/cpu/amp-uclass.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd + */ + +#include <common.h> +#include <amp.h> +#include <dm.h> +#include <dm/lists.h> + +int amp_cpu_on(u32 cpu) +{ + struct dm_amp_uclass_platdata *uc_pdata; + const struct dm_amp_ops *ops; + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_AMP, &uc); + if (ret) + return ret; + + for (uclass_first_device(UCLASS_AMP, &dev); + dev; + uclass_next_device(&dev)) { + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata || uc_pdata->cpu != cpu) + continue; + + ops = dev_get_driver_ops(dev); + if (!ops || !ops->cpu_on) + return -ENOSYS; + + return ops->cpu_on(dev); + } + + return -ENODEV; +} + +int amp_cpus_on(void) +{ + const struct dm_amp_ops *ops; + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_AMP, &uc); + if (ret) + return ret; + + for (uclass_first_device(UCLASS_AMP, &dev); + dev; + uclass_next_device(&dev)) { + ops = dev_get_driver_ops(dev); + if (!ops || !ops->cpu_on) + continue; + ret = ops->cpu_on(dev); + } + + return ret; +} + +int amp_bind_children(struct udevice *dev, const char *drv_name) +{ + const char *name; + ofnode node; + int ret; + + dev_for_each_subnode(node, dev) { + /* + * If this node has "compatible" property, this is not + * a amp subnode, but a normal device. skip. + */ + ofnode_get_property(node, "compatible", &ret); + if (ret >= 0) + continue; + + if (ret != -FDT_ERR_NOTFOUND) + return ret; + + name = ofnode_get_name(node); + if (!name) + return -EINVAL; + ret = device_bind_driver_to_node(dev, drv_name, name, + node, NULL); + if (ret) + return ret; + } + + return 0; +} + +static int amp_pre_probe(struct udevice *dev) +{ + struct dm_amp_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + uc_pdata->desc = dev_read_string(dev, "description"); + uc_pdata->partition = dev_read_string(dev, "partition"); + uc_pdata->cpu = dev_read_u32_default(dev, "cpu", -ENODATA); +#ifdef CONFIG_ARM64 + uc_pdata->aarch = dev_read_u32_default(dev, "aarch", 64); +#else + uc_pdata->aarch = dev_read_u32_default(dev, "aarch", 32); +#endif + uc_pdata->load = dev_read_u32_default(dev, "load", -ENODATA); + uc_pdata->entry = dev_read_u32_default(dev, "entry", -ENODATA); + + dev_read_u32_array(dev, "memory", + uc_pdata->reserved_mem, + ARRAY_SIZE(uc_pdata->reserved_mem)); + + if (!uc_pdata->desc || !uc_pdata->partition || + uc_pdata->cpu == -ENODATA || uc_pdata->load == -ENODATA || + uc_pdata->entry == -ENODATA || !uc_pdata->reserved_mem[0] || + !uc_pdata->reserved_mem[1] || + (uc_pdata->aarch != 64 && uc_pdata->aarch != 32)) { + printf("AMP: \"%s\" is not complete\n", dev->name); + return -EINVAL; + } + +#ifdef DEBUG + printf("[%s]:\n", dev_read_name(dev)); + printf(" descrption: %s\n", uc_pdata->desc); + printf(" partition: %s\n", uc_pdata->partition); + printf(" cpu: 0x%x\n", uc_pdata->cpu); + printf(" aarch: %d\n", uc_pdata->aarch); + printf(" load: 0x%08x\n", uc_pdata->load); + printf(" entry: 0x%08x\n", uc_pdata->entry); + printf(" reserved_mem: 0x%08x - 0x%08x\n\n", + uc_pdata->reserved_mem[0], + uc_pdata->reserved_mem[0] + uc_pdata->reserved_mem[1]); +#endif + + return 0; +} + +UCLASS_DRIVER(amp) = { + .id = UCLASS_AMP, + .name = "amp", + .pre_probe = amp_pre_probe, + .per_device_platdata_auto_alloc_size = + sizeof(struct dm_amp_uclass_platdata), +}; + +#ifdef DEBUG +static int do_amp_cpus_on(cmd_tbl_t *cmdtp, int flag, + int argc, char *const argv[]) +{ + amp_cpus_on(); + return 0; +} + +U_BOOT_CMD( + amp_cpus_on, 1, 1, do_amp_cpus_on, + "Brought up all amp cpus", + "" +); +#endif diff --git a/drivers/cpu/rockchip_amp.c b/drivers/cpu/rockchip_amp.c new file mode 100644 index 0000000000..9408c8c1b6 --- /dev/null +++ b/drivers/cpu/rockchip_amp.c @@ -0,0 +1,130 @@ +// 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) + +/* + * 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! + * load = <0x800000>; + * entry = <0x800000>; + * memory = <0x800000 0x400000>; + * }; + * + * amp@1 { + * ...... + * }; + * + * ...... + * }; + * + * U-Boot loads "mcu-os1" firmware to "0x800000" address from partiton + * "mcu1" for cpu[1], the cpu[1] entry address is 0x800000. And + * 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; + + 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; + } + + 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)); + + AMP_I("Brought up cpu[%x] on \"%s\" entry 0x%08x ...", + uc_pdata->cpu, uc_pdata->desc, uc_pdata->entry); + + 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, +}; diff --git a/include/amp.h b/include/amp.h new file mode 100644 index 0000000000..629dcdcd04 --- /dev/null +++ b/include/amp.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 Rockchip Electronics Co., Ltd + */ + +#ifndef _AMP_H_ +#define _AMP_H_ + +#include <dm.h> + +struct dm_amp_ops { + int (*cpu_on)(struct udevice *dev); +}; + +struct dm_amp_uclass_platdata { + const char *desc; + const char *partition; + u32 cpu; /* cpu mpidr */ + u32 aarch; + u32 load; + u32 entry; + u32 reserved_mem[2]; /* [0]: start, [1]: size */ +}; + +int amp_bind_children(struct udevice *dev, const char *drv_name); +int amp_cpus_on(void); +int amp_cpu_on(u32 cpu); + +#endif /* _AMP_H_ */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 70910f319e..b51b9873f2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -31,6 +31,7 @@ enum uclass_id { UCLASS_BLK, /* Block device */ UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CPU, /* CPU, typically part of an SoC */ + UCLASS_AMP, /* Asymmetric Multi-Processing */ UCLASS_CODEC, /* audio codec */ UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_DISPLAY, /* Display (e.g. DisplayPort, HDMI) */ |