aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Hilman <khilman@linaro.org>2014-01-14 18:23:00 -0500
committerKevin Hilman <khilman@linaro.org>2014-01-14 18:25:32 -0500
commit3e0a79695cf8600322c519b33df76b8bc2f1aaca (patch)
tree322c96e36f819368150ccfc154f97f353abf6948
parenteb90217750136e4712f1d87a33e1951b80a1a365 (diff)
parent75cac6abe756fde529a000e725dceed2b908f61a (diff)
Merge tag 'sunxi-core-for-3.14-2' of https://github.com/mripard/linux into next/soc
From Maxime Ripard: Second round of core additions for the Allwinner SoCs Fixes to select missing configuration options, and update of the maintainer file. * tag 'sunxi-core-for-3.14-2' of https://github.com/mripard/linux: ARM: sunxi: select ARM_PSCI MAINTAINERS: Update Allwinner sunXi maintainer files ARM: sunxi: Select RESET_CONTROLLER ARM: sun6i: Add SMP support for the Allwinner A31 dt-bindings: fix example of allwinner interrupt controller ARM: sunxi: Register the A31 reset IP in init_time ARM: sunxi: Select ARCH_HAS_RESET_CONTROLLER reset: Add Allwinner SoCs Reset Controller Driver Signed-off-by: Kevin Hilman <khilman@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt2
-rw-r--r--MAINTAINERS7
-rw-r--r--arch/arm/mach-sunxi/Kconfig3
-rw-r--r--arch/arm/mach-sunxi/Makefile1
-rw-r--r--arch/arm/mach-sunxi/common.h19
-rw-r--r--arch/arm/mach-sunxi/headsmp.S9
-rw-r--r--arch/arm/mach-sunxi/platsmp.c124
-rw-r--r--arch/arm/mach-sunxi/sunxi.c14
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-sunxi.c175
10 files changed, 353 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt
index 3d3b2b91e333..32cec4b26cd0 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-ic.txt
@@ -14,5 +14,5 @@ intc: interrupt-controller {
14 compatible = "allwinner,sun4i-ic"; 14 compatible = "allwinner,sun4i-ic";
15 reg = <0x01c20400 0x400>; 15 reg = <0x01c20400 0x400>;
16 interrupt-controller; 16 interrupt-controller;
17 #interrupt-cells = <2>; 17 #interrupt-cells = <1>;
18}; 18};
diff --git a/MAINTAINERS b/MAINTAINERS
index 6ea1fb247a8c..b6d1afbf9920 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -766,7 +766,12 @@ ARM/Allwinner A1X SoC support
766M: Maxime Ripard <maxime.ripard@free-electrons.com> 766M: Maxime Ripard <maxime.ripard@free-electrons.com>
767L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) 767L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
768S: Maintained 768S: Maintained
769F: arch/arm/mach-sunxi/ 769N: sun[x4567]i
770
771ARM/Allwinner SoC Clock Support
772M: Emilio López <emilio@elopez.com.ar>
773S: Maintained
774F: drivers/clk/sunxi/
770 775
771ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES 776ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES
772M: Andrew Victor <linux@maxim.org.za> 777M: Andrew Victor <linux@maxim.org.za>
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index c9e72c89066a..8053b1befc5e 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1,7 +1,9 @@
1config ARCH_SUNXI 1config ARCH_SUNXI
2 bool "Allwinner A1X SOCs" if ARCH_MULTI_V7 2 bool "Allwinner A1X SOCs" if ARCH_MULTI_V7
3 select ARCH_HAS_RESET_CONTROLLER
3 select ARCH_REQUIRE_GPIOLIB 4 select ARCH_REQUIRE_GPIOLIB
4 select ARM_GIC 5 select ARM_GIC
6 select ARM_PSCI
5 select CLKSRC_MMIO 7 select CLKSRC_MMIO
6 select CLKSRC_OF 8 select CLKSRC_OF
7 select COMMON_CLK 9 select COMMON_CLK
@@ -10,5 +12,6 @@ config ARCH_SUNXI
10 select HAVE_SMP 12 select HAVE_SMP
11 select PINCTRL 13 select PINCTRL
12 select PINCTRL_SUNXI 14 select PINCTRL_SUNXI
15 select RESET_CONTROLLER
13 select SPARSE_IRQ 16 select SPARSE_IRQ
14 select SUN4I_TIMER 17 select SUN4I_TIMER
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 93bebfc3ff9f..d9397202d6ec 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_ARCH_SUNXI) += sunxi.o 1obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
2obj-$(CONFIG_SMP) += platsmp.o headsmp.o
diff --git a/arch/arm/mach-sunxi/common.h b/arch/arm/mach-sunxi/common.h
new file mode 100644
index 000000000000..9e5ac4756cbb
--- /dev/null
+++ b/arch/arm/mach-sunxi/common.h
@@ -0,0 +1,19 @@
1/*
2 * Core functions for Allwinner SoCs
3 *
4 * Copyright (C) 2013 Maxime Ripard
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#ifndef __ARCH_SUNXI_COMMON_H_
14#define __ARCH_SUNXI_COMMON_H_
15
16void sun6i_secondary_startup(void);
17extern struct smp_operations sun6i_smp_ops;
18
19#endif /* __ARCH_SUNXI_COMMON_H_ */
diff --git a/arch/arm/mach-sunxi/headsmp.S b/arch/arm/mach-sunxi/headsmp.S
new file mode 100644
index 000000000000..a10d494fb37b
--- /dev/null
+++ b/arch/arm/mach-sunxi/headsmp.S
@@ -0,0 +1,9 @@
1#include <linux/linkage.h>
2#include <linux/init.h>
3
4 .section ".text.head", "ax"
5
6ENTRY(sun6i_secondary_startup)
7 msr cpsr_fsxc, #0xd3
8 b secondary_startup
9ENDPROC(sun6i_secondary_startup)
diff --git a/arch/arm/mach-sunxi/platsmp.c b/arch/arm/mach-sunxi/platsmp.c
new file mode 100644
index 000000000000..7b141d8342a1
--- /dev/null
+++ b/arch/arm/mach-sunxi/platsmp.c
@@ -0,0 +1,124 @@
1/*
2 * SMP support for Allwinner SoCs
3 *
4 * Copyright (C) 2013 Maxime Ripard
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * Based on code
9 * Copyright (C) 2012-2013 Allwinner Ltd.
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#include <linux/delay.h>
17#include <linux/init.h>
18#include <linux/io.h>
19#include <linux/memory.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/smp.h>
23
24#include "common.h"
25
26#define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu) ((cpu) * 0x40 + 0x64)
27#define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40)
28#define CPUCFG_CPU_CTRL_REG(cpu) (((cpu) + 1) * 0x40 + 0x04)
29#define CPUCFG_CPU_STATUS_REG(cpu) (((cpu) + 1) * 0x40 + 0x08)
30#define CPUCFG_GEN_CTRL_REG 0x184
31#define CPUCFG_PRIVATE0_REG 0x1a4
32#define CPUCFG_PRIVATE1_REG 0x1a8
33#define CPUCFG_DBG_CTL0_REG 0x1e0
34#define CPUCFG_DBG_CTL1_REG 0x1e4
35
36#define PRCM_CPU_PWROFF_REG 0x100
37#define PRCM_CPU_PWR_CLAMP_REG(cpu) (((cpu) * 4) + 0x140)
38
39static void __iomem *cpucfg_membase;
40static void __iomem *prcm_membase;
41
42static DEFINE_SPINLOCK(cpu_lock);
43
44static void __init sun6i_smp_prepare_cpus(unsigned int max_cpus)
45{
46 struct device_node *node;
47
48 node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm");
49 if (!node) {
50 pr_err("Missing A31 PRCM node in the device tree\n");
51 return;
52 }
53
54 prcm_membase = of_iomap(node, 0);
55 if (!prcm_membase) {
56 pr_err("Couldn't map A31 PRCM registers\n");
57 return;
58 }
59
60 node = of_find_compatible_node(NULL, NULL,
61 "allwinner,sun6i-a31-cpuconfig");
62 if (!node) {
63 pr_err("Missing A31 CPU config node in the device tree\n");
64 return;
65 }
66
67 cpucfg_membase = of_iomap(node, 0);
68 if (!cpucfg_membase)
69 pr_err("Couldn't map A31 CPU config registers\n");
70
71}
72
73static int sun6i_smp_boot_secondary(unsigned int cpu,
74 struct task_struct *idle)
75{
76 u32 reg;
77 int i;
78
79 if (!(prcm_membase && cpucfg_membase))
80 return -EFAULT;
81
82 spin_lock(&cpu_lock);
83
84 /* Set CPU boot address */
85 writel(virt_to_phys(sun6i_secondary_startup),
86 cpucfg_membase + CPUCFG_PRIVATE0_REG);
87
88 /* Assert the CPU core in reset */
89 writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
90
91 /* Assert the L1 cache in reset */
92 reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
93 writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
94
95 /* Disable external debug access */
96 reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
97 writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
98
99 /* Power up the CPU */
100 for (i = 0; i <= 8; i++)
101 writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu));
102 mdelay(10);
103
104 /* Clear CPU power-off gating */
105 reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
106 writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
107 mdelay(1);
108
109 /* Deassert the CPU core reset */
110 writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
111
112 /* Enable back the external debug accesses */
113 reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
114 writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
115
116 spin_unlock(&cpu_lock);
117
118 return 0;
119}
120
121struct smp_operations sun6i_smp_ops __initdata = {
122 .smp_prepare_cpus = sun6i_smp_prepare_cpus,
123 .smp_boot_secondary = sun6i_smp_boot_secondary,
124};
diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
index 61d3a387f01c..aeea6ceea725 100644
--- a/arch/arm/mach-sunxi/sunxi.c
+++ b/arch/arm/mach-sunxi/sunxi.c
@@ -10,6 +10,8 @@
10 * warranty of any kind, whether express or implied. 10 * warranty of any kind, whether express or implied.
11 */ 11 */
12 12
13#include <linux/clk-provider.h>
14#include <linux/clocksource.h>
13#include <linux/delay.h> 15#include <linux/delay.h>
14#include <linux/kernel.h> 16#include <linux/kernel.h>
15#include <linux/init.h> 17#include <linux/init.h>
@@ -23,6 +25,8 @@
23#include <asm/mach/map.h> 25#include <asm/mach/map.h>
24#include <asm/system_misc.h> 26#include <asm/system_misc.h>
25 27
28#include "common.h"
29
26#define SUN4I_WATCHDOG_CTRL_REG 0x00 30#define SUN4I_WATCHDOG_CTRL_REG 0x00
27#define SUN4I_WATCHDOG_CTRL_RESTART BIT(0) 31#define SUN4I_WATCHDOG_CTRL_RESTART BIT(0)
28#define SUN4I_WATCHDOG_MODE_REG 0x04 32#define SUN4I_WATCHDOG_MODE_REG 0x04
@@ -132,10 +136,20 @@ static const char * const sun6i_board_dt_compat[] = {
132 NULL, 136 NULL,
133}; 137};
134 138
139extern void __init sun6i_reset_init(void);
140static void __init sun6i_timer_init(void)
141{
142 of_clk_init(NULL);
143 sun6i_reset_init();
144 clocksource_of_init();
145}
146
135DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family") 147DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family")
136 .init_machine = sunxi_dt_init, 148 .init_machine = sunxi_dt_init,
149 .init_time = sun6i_timer_init,
137 .dt_compat = sun6i_board_dt_compat, 150 .dt_compat = sun6i_board_dt_compat,
138 .restart = sun6i_restart, 151 .restart = sun6i_restart,
152 .smp = smp_ops(sun6i_smp_ops),
139MACHINE_END 153MACHINE_END
140 154
141static const char * const sun7i_board_dt_compat[] = { 155static const char * const sun7i_board_dt_compat[] = {
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 1e2d83f2b995..cc29832c9638 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_RESET_CONTROLLER) += core.o 1obj-$(CONFIG_RESET_CONTROLLER) += core.o
2obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
new file mode 100644
index 000000000000..695bd3496eba
--- /dev/null
+++ b/drivers/reset/reset-sunxi.c
@@ -0,0 +1,175 @@
1/*
2 * Allwinner SoCs Reset Controller driver
3 *
4 * Copyright 2013 Maxime Ripard
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/err.h>
15#include <linux/io.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/of_address.h>
19#include <linux/platform_device.h>
20#include <linux/reset-controller.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/types.h>
24
25struct sunxi_reset_data {
26 spinlock_t lock;
27 void __iomem *membase;
28 struct reset_controller_dev rcdev;
29};
30
31static int sunxi_reset_assert(struct reset_controller_dev *rcdev,
32 unsigned long id)
33{
34 struct sunxi_reset_data *data = container_of(rcdev,
35 struct sunxi_reset_data,
36 rcdev);
37 int bank = id / BITS_PER_LONG;
38 int offset = id % BITS_PER_LONG;
39 unsigned long flags;
40 u32 reg;
41
42 spin_lock_irqsave(&data->lock, flags);
43
44 reg = readl(data->membase + (bank * 4));
45 writel(reg & ~BIT(offset), data->membase + (bank * 4));
46
47 spin_unlock_irqrestore(&data->lock, flags);
48
49 return 0;
50}
51
52static int sunxi_reset_deassert(struct reset_controller_dev *rcdev,
53 unsigned long id)
54{
55 struct sunxi_reset_data *data = container_of(rcdev,
56 struct sunxi_reset_data,
57 rcdev);
58 int bank = id / BITS_PER_LONG;
59 int offset = id % BITS_PER_LONG;
60 unsigned long flags;
61 u32 reg;
62
63 spin_lock_irqsave(&data->lock, flags);
64
65 reg = readl(data->membase + (bank * 4));
66 writel(reg | BIT(offset), data->membase + (bank * 4));
67
68 spin_unlock_irqrestore(&data->lock, flags);
69
70 return 0;
71}
72
73static struct reset_control_ops sunxi_reset_ops = {
74 .assert = sunxi_reset_assert,
75 .deassert = sunxi_reset_deassert,
76};
77
78static int sunxi_reset_init(struct device_node *np)
79{
80 struct sunxi_reset_data *data;
81 struct resource res;
82 resource_size_t size;
83 int ret;
84
85 data = kzalloc(sizeof(*data), GFP_KERNEL);
86 if (!data)
87 return -ENOMEM;
88
89 ret = of_address_to_resource(np, 0, &res);
90 if (ret)
91 goto err_alloc;
92
93 size = resource_size(&res);
94 if (!request_mem_region(res.start, size, np->name)) {
95 ret = -EBUSY;
96 goto err_alloc;
97 }
98
99 data->membase = ioremap(res.start, size);
100 if (!data->membase) {
101 ret = -ENOMEM;
102 goto err_alloc;
103 }
104
105 data->rcdev.owner = THIS_MODULE;
106 data->rcdev.nr_resets = size * 32;
107 data->rcdev.ops = &sunxi_reset_ops;
108 data->rcdev.of_node = np;
109 reset_controller_register(&data->rcdev);
110
111 return 0;
112
113err_alloc:
114 kfree(data);
115 return ret;
116};
117
118/*
119 * These are the reset controller we need to initialize early on in
120 * our system, before we can even think of using a regular device
121 * driver for it.
122 */
123static const struct of_device_id sunxi_early_reset_dt_ids[] __initdata = {
124 { .compatible = "allwinner,sun6i-a31-ahb1-reset", },
125 { /* sentinel */ },
126};
127
128void __init sun6i_reset_init(void)
129{
130 struct device_node *np;
131
132 for_each_matching_node(np, sunxi_early_reset_dt_ids)
133 sunxi_reset_init(np);
134}
135
136/*
137 * And these are the controllers we can register through the regular
138 * device model.
139 */
140static const struct of_device_id sunxi_reset_dt_ids[] = {
141 { .compatible = "allwinner,sun6i-a31-clock-reset", },
142 { /* sentinel */ },
143};
144MODULE_DEVICE_TABLE(of, sunxi_reset_dt_ids);
145
146static int sunxi_reset_probe(struct platform_device *pdev)
147{
148 return sunxi_reset_init(pdev->dev.of_node);
149}
150
151static int sunxi_reset_remove(struct platform_device *pdev)
152{
153 struct sunxi_reset_data *data = platform_get_drvdata(pdev);
154
155 reset_controller_unregister(&data->rcdev);
156 iounmap(data->membase);
157 kfree(data);
158
159 return 0;
160}
161
162static struct platform_driver sunxi_reset_driver = {
163 .probe = sunxi_reset_probe,
164 .remove = sunxi_reset_remove,
165 .driver = {
166 .name = "sunxi-reset",
167 .owner = THIS_MODULE,
168 .of_match_table = sunxi_reset_dt_ids,
169 },
170};
171module_platform_driver(sunxi_reset_driver);
172
173MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
174MODULE_DESCRIPTION("Allwinner SoCs Reset Controller Driver");
175MODULE_LICENSE("GPL");