2013-07-29 13:10:31

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 0/6] ARM: rockchip: add smp functionality

This series enables the use of the additional cores on Rockchip
Cortex-A9 SoCs.

To achieve this, add the scu, the needed sram and power-management-unit.

Tested on a rk3066a (dual core) by me and on a rk3188 (quad core)
by Ulrich Prinz.

(Note: rk3188 support in general needs additional upcoming patchsets)

changes since v3:
- address comments from Rob Herring:
- split the gathering of the reserve-data into a separate loop
- spelling and style fixes
- first patch only included for reference, already part of the
char-misc git tree

changes since v2:
- rework the sram allocation following the suggestion from Philipp Zabel

changes since v1:
- add reserved block feature for mmio-sram, to not use two logical
sram nodes
- the sram content is kept intact while the device is running, so
copying the trampoline is only needed once

Heiko Stuebner (6):
misc: sram: fix error path in sram_probe
misc: sram: add ability to mark sram sections as reserved
ARM: rockchip: add snoop-control-unit
ARM: rockchip: add sram dt nodes and documentation
ARM: rockchip: add power-management-unit dt node
ARM: rockchip: add smp bringup code

.../devicetree/bindings/arm/rockchip/pmu.txt | 16 ++
.../devicetree/bindings/arm/rockchip/smp-sram.txt | 23 +++
Documentation/devicetree/bindings/misc/sram.txt | 8 +
arch/arm/boot/dts/rk3066a.dtsi | 16 ++
arch/arm/mach-rockchip/Kconfig | 1 +
arch/arm/mach-rockchip/Makefile | 1 +
arch/arm/mach-rockchip/core.h | 22 +++
arch/arm/mach-rockchip/headsmp.S | 32 +++
arch/arm/mach-rockchip/platsmp.c | 203 ++++++++++++++++++++
arch/arm/mach-rockchip/rockchip.c | 2 +
drivers/misc/sram.c | 100 +++++++++-
11 files changed, 417 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/rockchip/pmu.txt
create mode 100644 Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt
create mode 100644 arch/arm/mach-rockchip/core.h
create mode 100644 arch/arm/mach-rockchip/headsmp.S
create mode 100644 arch/arm/mach-rockchip/platsmp.c

--
1.7.10.4


2013-07-29 13:11:22

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 1/6] misc: sram: fix error path in sram_probe

The pool is created thru devm_gen_pool_create, so the call to
gen_pool_destroy is not necessary.
Instead the sram-clock must be turned off again if it exists.

Signed-off-by: Heiko Stuebner <[email protected]>
Tested-by: Ulrich Prinz <[email protected]>
Acked-by: Philipp Zabel <[email protected]>
---
Only included for reference and completeness, as it is already
added to the char-misc git tree.

drivers/misc/sram.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index d87cc91..afe66571 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -68,7 +68,8 @@ static int sram_probe(struct platform_device *pdev)
ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base,
res->start, size, -1);
if (ret < 0) {
- gen_pool_destroy(sram->pool);
+ if (sram->clk)
+ clk_disable_unprepare(sram->clk);
return ret;
}

--
1.7.10.4

2013-07-29 13:12:12

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 2/6] misc: sram: add ability to mark sram sections as reserved

Some SoCs need parts of their sram for special purposes. So while being part
of the peripheral, it should not be part of the genpool controlling the sram.

Therefore add an option mmio-sram-reserved to keep arbitrary portions of the
sram from being part of the pool.

Suggested-by: Rob Herring <[email protected]>
Signed-off-by: Heiko Stuebner <[email protected]>
Tested-by: Ulrich Prinz <[email protected]>
---
Philipp: I didn't carry the ack, because the loop changed significantly again.
So if it looks ok, could you re-ack it please?

Documentation/devicetree/bindings/misc/sram.txt | 8 ++
drivers/misc/sram.c | 101 +++++++++++++++++++++--
2 files changed, 101 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/misc/sram.txt b/Documentation/devicetree/bindings/misc/sram.txt
index 4d0a00e..3b7a5c1 100644
--- a/Documentation/devicetree/bindings/misc/sram.txt
+++ b/Documentation/devicetree/bindings/misc/sram.txt
@@ -8,9 +8,17 @@ Required properties:

- reg : SRAM iomem address range

+Optional properties:
+
+- mmio-sram-reserved: ordered list of reserved chunks inside the sram that
+ should not be used by the OS.
+ Format is <base size>, <base size>, ...; with base being relative to the
+ reg property base.
+
Example:

sram: sram@5c000000 {
compatible = "mmio-sram";
reg = <0x5c000000 0x40000>; /* 256 KiB SRAM at address 0x5c000000 */
+ mmio-sram-reserved = <0x0 0x100>; /* reserve 0x5c000000-0x5c000100 */
};
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index afe66571..b3ceefb 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -36,13 +36,23 @@ struct sram_dev {
struct clk *clk;
};

+struct sram_reserve {
+ unsigned long start;
+ unsigned long size;
+};
+
static int sram_probe(struct platform_device *pdev)
{
void __iomem *virt_base;
struct sram_dev *sram;
struct resource *res;
- unsigned long size;
- int ret;
+ unsigned long size, cur_start, cur_size;
+ const __be32 *reserved_list = NULL;
+ int reserved_size = 0;
+ struct sram_reserve *rblocks;
+ unsigned int nblocks;
+ int ret = 0;
+ int i;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
virt_base = devm_ioremap_resource(&pdev->dev, res);
@@ -65,19 +75,94 @@ static int sram_probe(struct platform_device *pdev)
if (!sram->pool)
return -ENOMEM;

- ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base,
- res->start, size, -1);
- if (ret < 0) {
- if (sram->clk)
- clk_disable_unprepare(sram->clk);
- return ret;
+ if (pdev->dev.of_node) {
+ reserved_list = of_get_property(pdev->dev.of_node,
+ "mmio-sram-reserved",
+ &reserved_size);
+ if (reserved_list) {
+ reserved_size /= sizeof(*reserved_list);
+ if (!reserved_size || reserved_size % 2) {
+ dev_warn(&pdev->dev, "wrong number of arguments in mmio-sram-reserved\n");
+ reserved_list = NULL;
+ reserved_size = 0;
+ }
+ }
}

+ /*
+ * We need an additional block to mark the end of the memory region
+ * after the reserved blocks from the dt are processed.
+ */
+ nblocks = reserved_size / 2 + 1;
+ rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL);
+ if (!rblocks) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ for (i = 0; i < nblocks - 1; i++) {
+ rblocks[i].start = be32_to_cpu(*reserved_list++);
+ rblocks[i].size = be32_to_cpu(*reserved_list++);
+
+ dev_dbg(&pdev->dev, "found reserved block 0x%lx-0x%lx\n",
+ rblocks[i].start,
+ rblocks[i].start + rblocks[i].size);
+ }
+
+ /* the last chunk marks the end of the region */
+ rblocks[nblocks - 1].start = size;
+ rblocks[nblocks - 1].size = 0;
+
+ cur_start = 0;
+ for (i = 0; i < nblocks; i++) {
+ /* catch unsorted list entries */
+ if (rblocks[i].start < cur_start) {
+ dev_err(&pdev->dev,
+ "unsorted reserved list (0x%lx before current 0x%lx)\n",
+ rblocks[i].start, cur_start);
+ ret = -EINVAL;
+ goto err_chunks;
+ }
+
+ /* current start is in a reserved block, so continue after it */
+ if (rblocks[i].start == cur_start) {
+ cur_start = rblocks[i].start + rblocks[i].size;
+ continue;
+ }
+
+ /*
+ * allocate the space between the current starting
+ * address and the following reserved block, or the
+ * end of the region.
+ */
+ cur_size = rblocks[i].start - cur_start;
+
+ dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n",
+ cur_start, cur_start + cur_size);
+ ret = gen_pool_add_virt(sram->pool,
+ (unsigned long)virt_base + cur_start,
+ res->start + cur_start, cur_size, -1);
+ if (ret < 0)
+ goto err_chunks;
+
+ /* next allocation after this reserved block */
+ cur_start = rblocks[i].start + rblocks[i].size;
+ }
+
+ kfree(rblocks);
+
platform_set_drvdata(pdev, sram);

dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base);

return 0;
+
+err_chunks:
+ kfree(rblocks);
+err_alloc:
+ if (sram->clk)
+ clk_disable_unprepare(sram->clk);
+ return ret;
}

static int sram_remove(struct platform_device *pdev)
--
1.7.10.4

2013-07-29 13:12:51

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 3/6] ARM: rockchip: add snoop-control-unit

This adds the device-node and config select to enable the
scu in all Rockchip Cortex-A9 SoCs.

Signed-off-by: Heiko Stuebner <[email protected]>
Tested-by: Ulrich Prinz <[email protected]>
---
arch/arm/boot/dts/rk3066a.dtsi | 5 +++++
arch/arm/mach-rockchip/Kconfig | 1 +
2 files changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index 56bfac9..26c4311 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -48,6 +48,11 @@
compatible = "simple-bus";
ranges;

+ scu@1013c000 {
+ compatible = "arm,cortex-a9-scu";
+ reg = <0x1013c000 0x100>;
+ };
+
gic: interrupt-controller@1013d000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index 25ee12b..0f5484c 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -5,6 +5,7 @@ config ARCH_ROCKCHIP
select ARCH_REQUIRE_GPIOLIB
select ARM_GIC
select CACHE_L2X0
+ select HAVE_ARM_SCU
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_SMP
select LOCAL_TIMERS if SMP
--
1.7.10.4

2013-07-29 13:13:36

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 4/6] ARM: rockchip: add sram dt nodes and documentation

The Rockchip SoCs need a special part of their sram for bringup
of additional cores. Therefore also add a reserved section when adding the
mmio-sram node to keep the sram driver from using this space.

Signed-off-by: Heiko Stuebner <[email protected]>
Tested-by: Ulrich Prinz <[email protected]>
---
.../devicetree/bindings/arm/rockchip/smp-sram.txt | 23 ++++++++++++++++++++
arch/arm/boot/dts/rk3066a.dtsi | 6 +++++
2 files changed, 29 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt

diff --git a/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt b/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt
new file mode 100644
index 0000000..80c878e
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt
@@ -0,0 +1,23 @@
+Rockchip SRAM for smp bringup:
+------------------------------
+
+Rockchip's smp-capable SoCs use the first part of the sram for the bringup
+of the cores. Once the core gets powered up it executes the code that is
+residing at the very beginning of the sram.
+
+Therefore a reserved section has to be added to the mmio-sram declaration.
+
+Required node properties:
+- compatible : should contain both "rockchip,rk3066-sram", "mmio-sram"
+ so that the smp code can select the correct sram node.
+
+The rest of the properties should follow the generic mmio-sram discription
+found in ../../misc/sram.txt
+
+Example:
+
+ sram: sram@10080000 {
+ compatible = "rockchip,rk3066-sram", "mmio-sram";
+ reg = <0x10080000 0x10000>;
+ mmio-sram-reserved = <0x0 0x50>;
+ };
diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index 26c4311..24d1941 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -53,6 +53,12 @@
reg = <0x1013c000 0x100>;
};

+ sram: sram@10080000 {
+ compatible = "rockchip,rk3066-sram", "mmio-sram";
+ reg = <0x10080000 0x10000>;
+ mmio-sram-reserved = <0x0 0x50>;
+ };
+
gic: interrupt-controller@1013d000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
--
1.7.10.4

2013-07-29 13:14:12

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 5/6] ARM: rockchip: add power-management-unit dt node

The pmu is needed to bring up the cores during smp operations.
Therefore add a node and documentation for it.

Signed-off-by: Heiko Stuebner <[email protected]>
Tested-by: Ulrich Prinz <[email protected]>
---
Documentation/devicetree/bindings/arm/rockchip/pmu.txt | 16 ++++++++++++++++
arch/arm/boot/dts/rk3066a.dtsi | 5 +++++
2 files changed, 21 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/rockchip/pmu.txt

diff --git a/Documentation/devicetree/bindings/arm/rockchip/pmu.txt b/Documentation/devicetree/bindings/arm/rockchip/pmu.txt
new file mode 100644
index 0000000..3ee9b42
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/rockchip/pmu.txt
@@ -0,0 +1,16 @@
+Rockchip power-management-unit:
+-------------------------------
+
+The pmu is used to turn off and on different power domains of the SoCs
+This includes the power to the CPU cores.
+
+Required node properties:
+- compatible value : = "rockchip,rk3066-pmu";
+- reg : physical base address and the size of the registers window
+
+Example:
+
+ pmu@20004000 {
+ compatible = "rockchip,rk3066-pmu";
+ reg = <0x20004000 0x100>;
+ };
diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index 24d1941..43ac7c8 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -59,6 +59,11 @@
mmio-sram-reserved = <0x0 0x50>;
};

+ pmu@20004000 {
+ compatible = "rockchip,rk3066-pmu";
+ reg = <0x20004000 0x100>;
+ };
+
gic: interrupt-controller@1013d000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
--
1.7.10.4

2013-07-29 13:15:15

by Heiko Stuebner

[permalink] [raw]
Subject: [PATCH v4 6/6] ARM: rockchip: add smp bringup code

This adds the necessary smp-operations and startup code to use
additional cores on Rockchip SoCs.

We currently hog the power management unit in the smp code, as it is
necessary to control the power to the cpu core and nothing else is
currently using it, so a generic implementation can be done later.

Signed-off-by: Heiko Stuebner <[email protected]>
Tested-by: Ulrich Prinz <[email protected]>
---
arch/arm/mach-rockchip/Makefile | 1 +
arch/arm/mach-rockchip/core.h | 22 ++++
arch/arm/mach-rockchip/headsmp.S | 32 ++++++
arch/arm/mach-rockchip/platsmp.c | 203 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-rockchip/rockchip.c | 2 +
5 files changed, 260 insertions(+)
create mode 100644 arch/arm/mach-rockchip/core.h
create mode 100644 arch/arm/mach-rockchip/headsmp.S
create mode 100644 arch/arm/mach-rockchip/platsmp.c

diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 1547d4f..4377a14 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
+obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-rockchip/core.h b/arch/arm/mach-rockchip/core.h
new file mode 100644
index 0000000..e2e7c9d
--- /dev/null
+++ b/arch/arm/mach-rockchip/core.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+extern char rockchip_secondary_trampoline;
+extern char rockchip_secondary_trampoline_end;
+
+extern unsigned long rockchip_boot_fn;
+extern void rockchip_secondary_startup(void);
+
+extern struct smp_operations rockchip_smp_ops;
diff --git a/arch/arm/mach-rockchip/headsmp.S b/arch/arm/mach-rockchip/headsmp.S
new file mode 100644
index 0000000..3dd72f7
--- /dev/null
+++ b/arch/arm/mach-rockchip/headsmp.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+ __CPUINIT
+
+ENTRY(rockchip_secondary_startup)
+ bl v7_invalidate_l1
+ b secondary_startup
+ENDPROC(rockchip_secondary_startup)
+
+ENTRY(rockchip_secondary_trampoline)
+ ldr pc, 1f
+ENDPROC(rockchip_secondary_trampoline)
+ .globl rockchip_boot_fn
+rockchip_boot_fn:
+1: .space 4
+
+ENTRY(rockchip_secondary_trampoline_end)
diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c
new file mode 100644
index 0000000..7849c39
--- /dev/null
+++ b/arch/arm/mach-rockchip/platsmp.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_scu.h>
+#include <asm/smp_plat.h>
+#include <asm/mach/map.h>
+
+#include "core.h"
+
+static void __iomem *scu_base_addr;
+static void __iomem *sram_base_addr;
+static int ncores;
+
+/*
+ * temporary PMU handling
+ */
+
+#define PMU_PWRDN_CON 0x08
+#define PMU_PWRDN_ST 0x0c
+
+static void __iomem *pmu_base_addr;
+
+static inline bool pmu_power_domain_is_on(int pd)
+{
+ return !(readl_relaxed(pmu_base_addr + PMU_PWRDN_ST) & BIT(pd));
+}
+
+static void pmu_set_power_domain(int pd, bool on)
+{
+ u32 val = readl_relaxed(pmu_base_addr + PMU_PWRDN_CON);
+ if (on)
+ val &= ~BIT(pd);
+ else
+ val |= BIT(pd);
+ writel(val, pmu_base_addr + PMU_PWRDN_CON);
+
+ while (pmu_power_domain_is_on(pd) != on) { }
+}
+
+/*
+ * Handling of CPU cores
+ */
+
+static int __cpuinit rockchip_boot_secondary(unsigned int cpu,
+ struct task_struct *idle)
+{
+ if (!sram_base_addr || !pmu_base_addr) {
+ pr_err("%s: sram or pmu missing for cpu boot\n", __func__);
+ return -ENXIO;
+ }
+
+ if (cpu >= ncores) {
+ pr_err("%s: cpu %d outside maximum number of cpus %d\n",
+ __func__, cpu, ncores);
+ return -ENXIO;
+ }
+
+ /* start the core */
+ pmu_set_power_domain(0 + cpu, true);
+
+ return 0;
+}
+
+/**
+ * rockchip_smp_prepare_sram - populate necessary sram block
+ * Starting cores execute the code residing at the start of the on-chip sram
+ * after power-on. Therefore make sure, this sram region is reserved and
+ * big enough. After this check, copy the trampoline code that directs the
+ * core to the real startup code in ram into the sram-region.
+ * @node: mmio-sram device node
+ */
+static int __init rockchip_smp_prepare_sram(struct device_node *node)
+{
+ unsigned int trampoline_sz = &rockchip_secondary_trampoline_end -
+ &rockchip_secondary_trampoline;
+ const __be32 *reserved_list = NULL;
+ int reserved_size;
+ int rstart = -1;
+ unsigned int rsize;
+ unsigned int i;
+
+ reserved_list = of_get_property(node, "mmio-sram-reserved",
+ &reserved_size);
+ if (!reserved_list) {
+ pr_err("%s: wrong number of arguments in mmio-sram-reserved\n",
+ __func__);
+ return -ENOENT;
+ }
+
+ reserved_size /= sizeof(*reserved_list);
+ if (!reserved_size || reserved_size % 2) {
+ pr_err("%s: wrong number of arguments in mmio-sram-reserved\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < reserved_size; i += 2) {
+ /* get the next reserved block */
+ rstart = be32_to_cpu(*reserved_list++);
+ rsize = be32_to_cpu(*reserved_list++);
+
+ if (!rstart)
+ break;
+ }
+
+ if (rstart) {
+ pr_err("%s: start of sram is not reserved from mmio-sram\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (rsize < trampoline_sz) {
+ pr_err("%s: reserved block with size 0x%x is to small for trampoline size 0x%x\n",
+ __func__, rsize, trampoline_sz);
+ return -EINVAL;
+ }
+
+ sram_base_addr = of_iomap(node, 0);
+
+ /* set the boot function for the sram code */
+ rockchip_boot_fn = virt_to_phys(rockchip_secondary_startup);
+
+ /* copy the trampoline to sram, that runs during startup of the core */
+ memcpy(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz);
+ flush_cache_all();
+ outer_clean_range(0, trampoline_sz);
+
+ dsb_sev();
+
+ return 0;
+}
+
+static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *node;
+ unsigned int i;
+
+ node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
+ if (!node) {
+ pr_err("%s: missing scu\n", __func__);
+ return;
+ }
+
+ scu_base_addr = of_iomap(node, 0);
+ if (!scu_base_addr) {
+ pr_err("%s: could not map scu registers\n", __func__);
+ return;
+ }
+
+ node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-sram");
+ if (!node) {
+ pr_err("%s: could not find sram dt node\n", __func__);
+ return;
+ }
+
+ if (rockchip_smp_prepare_sram(node))
+ return;
+
+ node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu");
+ if (!node) {
+ pr_err("%s: could not find sram dt node\n", __func__);
+ return;
+ }
+
+ pmu_base_addr = of_iomap(node, 0);
+
+ /*
+ * While the number of cpus is gathered from dt, also get the number
+ * of cores from the scu to verify this value when booting the cores.
+ */
+ ncores = scu_get_core_count(scu_base_addr);
+
+ scu_enable(scu_base_addr);
+
+ /* Make sure that all cores except the first are really off */
+ for (i = 1; i < ncores; i++)
+ pmu_set_power_domain(0 + i, false);
+}
+
+struct smp_operations rockchip_smp_ops __initdata = {
+ .smp_prepare_cpus = rockchip_smp_prepare_cpus,
+ .smp_boot_secondary = rockchip_boot_secondary,
+};
diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c
index 724d2d8..a0c1f19 100644
--- a/arch/arm/mach-rockchip/rockchip.c
+++ b/arch/arm/mach-rockchip/rockchip.c
@@ -24,6 +24,7 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/hardware/cache-l2x0.h>
+#include "core.h"

static void __init rockchip_timer_init(void)
{
@@ -46,6 +47,7 @@ static const char * const rockchip_board_dt_compat[] = {
};

DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
+ .smp = smp_ops(rockchip_smp_ops),
.init_machine = rockchip_dt_init,
.init_time = rockchip_timer_init,
.dt_compat = rockchip_board_dt_compat,
--
1.7.10.4

2013-07-29 14:03:11

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] misc: sram: add ability to mark sram sections as reserved

Hi Heiko,

Am Montag, den 29.07.2013, 15:12 +0200 schrieb Heiko Stübner:
> Some SoCs need parts of their sram for special purposes. So while being part
> of the peripheral, it should not be part of the genpool controlling the sram.
>
> Therefore add an option mmio-sram-reserved to keep arbitrary portions of the
> sram from being part of the pool.
>
> Suggested-by: Rob Herring <[email protected]>
> Signed-off-by: Heiko Stuebner <[email protected]>
> Tested-by: Ulrich Prinz <[email protected]>
> ---
> Philipp: I didn't carry the ack, because the loop changed significantly again.
> So if it looks ok, could you re-ack it please?

I'd prefer the first loop to contain the magic and produce a list of
useable chunks, instead of a list of reserved blocks. The second loop
could then iterate over the array and just call gen_pool_add_virt
repeatedly.

regards
Philipp

> Documentation/devicetree/bindings/misc/sram.txt | 8 ++
> drivers/misc/sram.c | 101 +++++++++++++++++++++--
> 2 files changed, 101 insertions(+), 8 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/misc/sram.txt b/Documentation/devicetree/bindings/misc/sram.txt
> index 4d0a00e..3b7a5c1 100644
> --- a/Documentation/devicetree/bindings/misc/sram.txt
> +++ b/Documentation/devicetree/bindings/misc/sram.txt
> @@ -8,9 +8,17 @@ Required properties:
>
> - reg : SRAM iomem address range
>
> +Optional properties:
> +
> +- mmio-sram-reserved: ordered list of reserved chunks inside the sram that
> + should not be used by the OS.
> + Format is <base size>, <base size>, ...; with base being relative to the
> + reg property base.
> +
> Example:
>
> sram: sram@5c000000 {
> compatible = "mmio-sram";
> reg = <0x5c000000 0x40000>; /* 256 KiB SRAM at address 0x5c000000 */
> + mmio-sram-reserved = <0x0 0x100>; /* reserve 0x5c000000-0x5c000100 */
> };
> diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
> index afe66571..b3ceefb 100644
> --- a/drivers/misc/sram.c
> +++ b/drivers/misc/sram.c
> @@ -36,13 +36,23 @@ struct sram_dev {
> struct clk *clk;
> };
>
> +struct sram_reserve {
> + unsigned long start;
> + unsigned long size;
> +};
> +
> static int sram_probe(struct platform_device *pdev)
> {
> void __iomem *virt_base;
> struct sram_dev *sram;
> struct resource *res;
> - unsigned long size;
> - int ret;
> + unsigned long size, cur_start, cur_size;
> + const __be32 *reserved_list = NULL;
> + int reserved_size = 0;
> + struct sram_reserve *rblocks;
> + unsigned int nblocks;
> + int ret = 0;
> + int i;
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> virt_base = devm_ioremap_resource(&pdev->dev, res);
> @@ -65,19 +75,94 @@ static int sram_probe(struct platform_device *pdev)
> if (!sram->pool)
> return -ENOMEM;
>
> - ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base,
> - res->start, size, -1);
> - if (ret < 0) {
> - if (sram->clk)
> - clk_disable_unprepare(sram->clk);
> - return ret;
> + if (pdev->dev.of_node) {
> + reserved_list = of_get_property(pdev->dev.of_node,
> + "mmio-sram-reserved",
> + &reserved_size);
> + if (reserved_list) {
> + reserved_size /= sizeof(*reserved_list);
> + if (!reserved_size || reserved_size % 2) {
> + dev_warn(&pdev->dev, "wrong number of arguments in mmio-sram-reserved\n");
> + reserved_list = NULL;
> + reserved_size = 0;
> + }
> + }
> }
>
> + /*
> + * We need an additional block to mark the end of the memory region
> + * after the reserved blocks from the dt are processed.
> + */
> + nblocks = reserved_size / 2 + 1;
> + rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL);
> + if (!rblocks) {
> + ret = -ENOMEM;
> + goto err_alloc;
> + }
> +
> + for (i = 0; i < nblocks - 1; i++) {
> + rblocks[i].start = be32_to_cpu(*reserved_list++);
> + rblocks[i].size = be32_to_cpu(*reserved_list++);
> +
> + dev_dbg(&pdev->dev, "found reserved block 0x%lx-0x%lx\n",
> + rblocks[i].start,
> + rblocks[i].start + rblocks[i].size);
> + }
> +
> + /* the last chunk marks the end of the region */
> + rblocks[nblocks - 1].start = size;
> + rblocks[nblocks - 1].size = 0;
> +
> + cur_start = 0;
> + for (i = 0; i < nblocks; i++) {
> + /* catch unsorted list entries */
> + if (rblocks[i].start < cur_start) {
> + dev_err(&pdev->dev,
> + "unsorted reserved list (0x%lx before current 0x%lx)\n",
> + rblocks[i].start, cur_start);
> + ret = -EINVAL;
> + goto err_chunks;
> + }
> +
> + /* current start is in a reserved block, so continue after it */
> + if (rblocks[i].start == cur_start) {
> + cur_start = rblocks[i].start + rblocks[i].size;
> + continue;
> + }
> +
> + /*
> + * allocate the space between the current starting
> + * address and the following reserved block, or the
> + * end of the region.
> + */
> + cur_size = rblocks[i].start - cur_start;
> +
> + dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n",
> + cur_start, cur_start + cur_size);
> + ret = gen_pool_add_virt(sram->pool,
> + (unsigned long)virt_base + cur_start,
> + res->start + cur_start, cur_size, -1);
> + if (ret < 0)
> + goto err_chunks;
> +
> + /* next allocation after this reserved block */
> + cur_start = rblocks[i].start + rblocks[i].size;
> + }
> +
> + kfree(rblocks);
> +
> platform_set_drvdata(pdev, sram);
>
> dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base);
>
> return 0;
> +
> +err_chunks:
> + kfree(rblocks);
> +err_alloc:
> + if (sram->clk)
> + clk_disable_unprepare(sram->clk);
> + return ret;
> }
>
> static int sram_remove(struct platform_device *pdev)

2013-07-29 21:39:47

by Matt Sealey

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] misc: sram: add ability to mark sram sections as reserved

On Mon, Jul 29, 2013 at 9:02 AM, Philipp Zabel <[email protected]> wrote:
> Hi Heiko,
>
> Am Montag, den 29.07.2013, 15:12 +0200 schrieb Heiko St?bner:
>> Some SoCs need parts of their sram for special purposes. So while being part
>> of the peripheral, it should not be part of the genpool controlling the sram.
>>
>> Therefore add an option mmio-sram-reserved to keep arbitrary portions of the
>> sram from being part of the pool.
>>
>> Suggested-by: Rob Herring <[email protected]>
>> Signed-off-by: Heiko Stuebner <[email protected]>
>> Tested-by: Ulrich Prinz <[email protected]>
>> ---
>> Philipp: I didn't carry the ack, because the loop changed significantly again.
>> So if it looks ok, could you re-ack it please?
>
> I'd prefer the first loop to contain the magic and produce a list of
> useable chunks, instead of a list of reserved blocks. The second loop
> could then iterate over the array and just call gen_pool_add_virt
> repeatedly.
>
> regards
> Philipp

Agreed, however specifying chunks of memory should probably match the
format of the standard memory@ node "available" property - mostly
because it would be the same syntax and definition as defining any
other chunk of memory, as OpenFirmware and device trees have been
doing since the dark ages. In this case, why not re-use the
"available" property name instead of creating a new one? Standard OF
memory parsing code is then free for you to use to pull the chunks
out.

--
Matt Sealey <[email protected]>

2013-08-01 16:35:24

by Heiko Stuebner

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] misc: sram: add ability to mark sram sections as reserved

Am Montag, 29. Juli 2013, 23:39:45 schrieb Matt Sealey:
> On Mon, Jul 29, 2013 at 9:02 AM, Philipp Zabel <[email protected]>
wrote:
> > Hi Heiko,
> >
> > Am Montag, den 29.07.2013, 15:12 +0200 schrieb Heiko St?bner:
> >> Some SoCs need parts of their sram for special purposes. So while being
> >> part of the peripheral, it should not be part of the genpool
> >> controlling the sram.
> >>
> >> Therefore add an option mmio-sram-reserved to keep arbitrary portions of
> >> the sram from being part of the pool.
> >>
> >> Suggested-by: Rob Herring <[email protected]>
> >> Signed-off-by: Heiko Stuebner <[email protected]>
> >> Tested-by: Ulrich Prinz <[email protected]>
> >> ---
> >> Philipp: I didn't carry the ack, because the loop changed significantly
> >> again. So if it looks ok, could you re-ack it please?
> >
> > I'd prefer the first loop to contain the magic and produce a list of
> > useable chunks, instead of a list of reserved blocks. The second loop
> > could then iterate over the array and just call gen_pool_add_virt
> > repeatedly.
> >
> > regards
> > Philipp
>
> Agreed, however specifying chunks of memory should probably match the
> format of the standard memory@ node "available" property - mostly
> because it would be the same syntax and definition as defining any
> other chunk of memory, as OpenFirmware and device trees have been
> doing since the dark ages. In this case, why not re-use the
> "available" property name instead of creating a new one? Standard OF
> memory parsing code is then free for you to use to pull the chunks
> out.

Sound interesting, but could you give me a pointer to some sort of docs about
this "available" property in memory nodes?

Documentation/devicetree/booting-without-of.txt seems to be the only file
describing the memory node at all but only required properties, and not any
"available" property.

I've found a document on power.org describing the memory node, but also not
mentioning any "available" property.

And devicetree.org does not seem to handle the memory node at all.


Thanks for any hints
Heiko

2013-08-01 17:07:46

by Matt Sealey

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] misc: sram: add ability to mark sram sections as reserved

IEEE1275 (Open Firmware) specification has details on it in the MMU
(section 3.6.5), Memory (section 3.7.6) node specifications and the
glossary defines how the property should look. How you find that
document is.. up to you (I have a PDF of it I got hold of around 2004,
it used to be linked at http://www.openfirmware.org but I've not checked). I
think it's missed from the devicetree.org spec since it's already very
well defined and there's no reason to reinvent (or redefine) the
wheel.

Essentially it follows the same format as "reg" - phys.hi phys.lo
length - which define regions which are ACTUALLY available to the
system. The intent is that it would be used to define the memory as
going from an address for a certain size, but mark sections of it the
device tree user could use (further intent being that you would want
the OS to know that there is, for example, 4GiB of memory there and
set up any mappings to suit, but the last 256MiB is where you've put
some important data and you would rather it didn't just allocate it to
something else..).

Linux doesn't actually parse and use the information in the available
property (or care about "existing" property in an MMU node since it
replaces page tables super early and has different ideas about mapping
than most bootloaders), but other operating systems possibly do. It
would not be a significant hardship for Linux to actually parse the
property if present and use that information after a bit of sanity
checking rather than just trusting reg. And in that sense, it would
not be a significant hardship to use existing specifications to define
nodes that describe memory, since if there IS parsing code available,
it will be re-used, which is good all round.

--
Matt

On Thu, Aug 1, 2013 at 11:35 AM, Heiko St?bner <[email protected]> wrote:
> Am Montag, 29. Juli 2013, 23:39:45 schrieb Matt Sealey:
>> On Mon, Jul 29, 2013 at 9:02 AM, Philipp Zabel <[email protected]>
> wrote:
>> > Hi Heiko,
>> >
>> > Am Montag, den 29.07.2013, 15:12 +0200 schrieb Heiko St?bner:
>> >> Some SoCs need parts of their sram for special purposes. So while being
>> >> part of the peripheral, it should not be part of the genpool
>> >> controlling the sram.
>> >>
>> >> Therefore add an option mmio-sram-reserved to keep arbitrary portions of
>> >> the sram from being part of the pool.
>> >>
>> >> Suggested-by: Rob Herring <[email protected]>
>> >> Signed-off-by: Heiko Stuebner <[email protected]>
>> >> Tested-by: Ulrich Prinz <[email protected]>
>> >> ---
>> >> Philipp: I didn't carry the ack, because the loop changed significantly
>> >> again. So if it looks ok, could you re-ack it please?
>> >
>> > I'd prefer the first loop to contain the magic and produce a list of
>> > useable chunks, instead of a list of reserved blocks. The second loop
>> > could then iterate over the array and just call gen_pool_add_virt
>> > repeatedly.
>> >
>> > regards
>> > Philipp
>>
>> Agreed, however specifying chunks of memory should probably match the
>> format of the standard memory@ node "available" property - mostly
>> because it would be the same syntax and definition as defining any
>> other chunk of memory, as OpenFirmware and device trees have been
>> doing since the dark ages. In this case, why not re-use the
>> "available" property name instead of creating a new one? Standard OF
>> memory parsing code is then free for you to use to pull the chunks
>> out.
>
> Sound interesting, but could you give me a pointer to some sort of docs about
> this "available" property in memory nodes?
>
> Documentation/devicetree/booting-without-of.txt seems to be the only file
> describing the memory node at all but only required properties, and not any
> "available" property.
>
> I've found a document on power.org describing the memory node, but also not
> mentioning any "available" property.
>
> And devicetree.org does not seem to handle the memory node at all.
>
>
> Thanks for any hints
> Heiko