Commit | Line | Data |
---|---|---|
3f20fb11 TP |
1 | /* |
2 | * Copyright (C) 2014 Marvell | |
3 | * | |
4 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | |
5 | * | |
6 | * This file is licensed under the terms of the GNU General Public | |
7 | * License version 2. This program is licensed "as is" without any | |
8 | * warranty of any kind, whether express or implied. | |
9 | */ | |
10 | ||
11 | #define pr_fmt(fmt) "mvebu-cpureset: " fmt | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/resource.h> | |
3f20fb11 TP |
18 | |
19 | static void __iomem *cpu_reset_base; | |
20 | static size_t cpu_reset_size; | |
21 | ||
22 | #define CPU_RESET_OFFSET(cpu) (cpu * 0x8) | |
23 | #define CPU_RESET_ASSERT BIT(0) | |
24 | ||
25 | int mvebu_cpu_reset_deassert(int cpu) | |
26 | { | |
27 | u32 reg; | |
28 | ||
29 | if (!cpu_reset_base) | |
30 | return -ENODEV; | |
31 | ||
32 | if (CPU_RESET_OFFSET(cpu) >= cpu_reset_size) | |
33 | return -EINVAL; | |
34 | ||
35 | reg = readl(cpu_reset_base + CPU_RESET_OFFSET(cpu)); | |
36 | reg &= ~CPU_RESET_ASSERT; | |
37 | writel(reg, cpu_reset_base + CPU_RESET_OFFSET(cpu)); | |
38 | ||
39 | return 0; | |
40 | } | |
41 | ||
49754ffe | 42 | static int mvebu_cpu_reset_map(struct device_node *np, int res_idx) |
3f20fb11 | 43 | { |
3f20fb11 | 44 | struct resource res; |
3f20fb11 | 45 | |
49754ffe | 46 | if (of_address_to_resource(np, res_idx, &res)) { |
3f20fb11 | 47 | pr_err("unable to get resource\n"); |
49754ffe | 48 | return -ENOENT; |
3f20fb11 TP |
49 | } |
50 | ||
51 | if (!request_mem_region(res.start, resource_size(&res), | |
52 | np->full_name)) { | |
53 | pr_err("unable to request region\n"); | |
49754ffe | 54 | return -EBUSY; |
3f20fb11 TP |
55 | } |
56 | ||
57 | cpu_reset_base = ioremap(res.start, resource_size(&res)); | |
58 | if (!cpu_reset_base) { | |
59 | pr_err("unable to map registers\n"); | |
60 | release_mem_region(res.start, resource_size(&res)); | |
49754ffe | 61 | return -ENOMEM; |
3f20fb11 TP |
62 | } |
63 | ||
64 | cpu_reset_size = resource_size(&res); | |
65 | ||
49754ffe TP |
66 | return 0; |
67 | } | |
68 | ||
e6571474 | 69 | static int __init mvebu_cpu_reset_init(void) |
49754ffe TP |
70 | { |
71 | struct device_node *np; | |
72 | int res_idx; | |
73 | int ret; | |
74 | ||
75 | np = of_find_compatible_node(NULL, NULL, | |
76 | "marvell,armada-370-cpu-reset"); | |
77 | if (np) { | |
78 | res_idx = 0; | |
79 | } else { | |
80 | /* | |
81 | * This code is kept for backward compatibility with | |
82 | * old Device Trees. | |
83 | */ | |
84 | np = of_find_compatible_node(NULL, NULL, | |
85 | "marvell,armada-370-xp-pmsu"); | |
86 | if (np) { | |
87 | pr_warn(FW_WARN "deprecated pmsu binding\n"); | |
88 | res_idx = 1; | |
89 | } | |
90 | } | |
91 | ||
92 | /* No reset node found */ | |
93 | if (!np) | |
94 | return -ENODEV; | |
95 | ||
96 | ret = mvebu_cpu_reset_map(np, res_idx); | |
3f20fb11 | 97 | of_node_put(np); |
49754ffe | 98 | |
3f20fb11 TP |
99 | return ret; |
100 | } | |
101 | ||
102 | early_initcall(mvebu_cpu_reset_init); |