summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Chen <chenjh@rock-chips.com>2019-03-21 18:22:13 +0800
committerJianhong Chen <chenjh@rock-chips.com>2019-04-08 20:53:44 +0800
commit4388deca723e78cda09c928f33f3a4d299e414c9 (patch)
tree98a2e688f5ff1faf85b9c95839fb6299ea569a57
parent5ae28c899a83ad1e62e928d6e833cb3f8916e13e (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--Makefile1
-rw-r--r--drivers/cpu/Kconfig12
-rw-r--r--drivers/cpu/Makefile2
-rw-r--r--drivers/cpu/amp-uclass.c162
-rw-r--r--drivers/cpu/rockchip_amp.c130
-rw-r--r--include/amp.h29
-rw-r--r--include/dm/uclass-id.h1
7 files changed, 337 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index efedc288d4..dc812e2d97 100644
--- a/Makefile
+++ b/Makefile
@@ -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) */