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