1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Versatile OF physmap driver add-on |
4 | * |
5 | * Copyright (c) 2016, Linaro Limited |
6 | * Author: Linus Walleij <linus.walleij@linaro.org> |
7 | */ |
8 | #include <linux/export.h> |
9 | #include <linux/io.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/mtd/map.h> |
13 | #include <linux/mfd/syscon.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/bitops.h> |
17 | #include "physmap-versatile.h" |
18 | |
19 | static struct regmap *syscon_regmap; |
20 | |
21 | enum versatile_flashprot { |
22 | INTEGRATOR_AP_FLASHPROT, |
23 | INTEGRATOR_CP_FLASHPROT, |
24 | VERSATILE_FLASHPROT, |
25 | REALVIEW_FLASHPROT, |
26 | }; |
27 | |
28 | static const struct of_device_id syscon_match[] = { |
29 | { |
30 | .compatible = "arm,integrator-ap-syscon" , |
31 | .data = (void *)INTEGRATOR_AP_FLASHPROT, |
32 | }, |
33 | { |
34 | .compatible = "arm,integrator-cp-syscon" , |
35 | .data = (void *)INTEGRATOR_CP_FLASHPROT, |
36 | }, |
37 | { |
38 | .compatible = "arm,core-module-versatile" , |
39 | .data = (void *)VERSATILE_FLASHPROT, |
40 | }, |
41 | { |
42 | .compatible = "arm,realview-eb-syscon" , |
43 | .data = (void *)REALVIEW_FLASHPROT, |
44 | }, |
45 | { |
46 | .compatible = "arm,realview-pb1176-syscon" , |
47 | .data = (void *)REALVIEW_FLASHPROT, |
48 | }, |
49 | { |
50 | .compatible = "arm,realview-pb11mp-syscon" , |
51 | .data = (void *)REALVIEW_FLASHPROT, |
52 | }, |
53 | { |
54 | .compatible = "arm,realview-pba8-syscon" , |
55 | .data = (void *)REALVIEW_FLASHPROT, |
56 | }, |
57 | { |
58 | .compatible = "arm,realview-pbx-syscon" , |
59 | .data = (void *)REALVIEW_FLASHPROT, |
60 | }, |
61 | {}, |
62 | }; |
63 | |
64 | /* |
65 | * Flash protection handling for the Integrator/AP |
66 | */ |
67 | #define INTEGRATOR_SC_CTRLS_OFFSET 0x08 |
68 | #define INTEGRATOR_SC_CTRLC_OFFSET 0x0C |
69 | #define INTEGRATOR_SC_CTRL_FLVPPEN BIT(1) |
70 | #define INTEGRATOR_SC_CTRL_FLWP BIT(2) |
71 | |
72 | #define INTEGRATOR_EBI_CSR1_OFFSET 0x04 |
73 | /* The manual says bit 2, the code says bit 3, trust the code */ |
74 | #define INTEGRATOR_EBI_WRITE_ENABLE BIT(3) |
75 | #define INTEGRATOR_EBI_LOCK_OFFSET 0x20 |
76 | #define INTEGRATOR_EBI_LOCK_VAL 0xA05F |
77 | |
78 | static const struct of_device_id ebi_match[] = { |
79 | { .compatible = "arm,external-bus-interface" }, |
80 | { }, |
81 | }; |
82 | |
83 | static int ap_flash_init(struct platform_device *pdev) |
84 | { |
85 | struct device_node *ebi; |
86 | void __iomem *ebi_base; |
87 | u32 val; |
88 | int ret; |
89 | |
90 | /* Look up the EBI */ |
91 | ebi = of_find_matching_node(NULL, matches: ebi_match); |
92 | if (!ebi) { |
93 | return -ENODEV; |
94 | } |
95 | ebi_base = of_iomap(node: ebi, index: 0); |
96 | of_node_put(node: ebi); |
97 | if (!ebi_base) |
98 | return -ENODEV; |
99 | |
100 | /* Clear VPP and write protection bits */ |
101 | ret = regmap_write(map: syscon_regmap, |
102 | INTEGRATOR_SC_CTRLC_OFFSET, |
103 | INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); |
104 | if (ret) |
105 | dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n" ); |
106 | |
107 | /* Unlock the EBI */ |
108 | writel(INTEGRATOR_EBI_LOCK_VAL, addr: ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); |
109 | |
110 | /* Enable write cycles on the EBI, CSR1 (flash) */ |
111 | val = readl(addr: ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); |
112 | val |= INTEGRATOR_EBI_WRITE_ENABLE; |
113 | writel(val, addr: ebi_base + INTEGRATOR_EBI_CSR1_OFFSET); |
114 | |
115 | /* Lock the EBI again */ |
116 | writel(val: 0, addr: ebi_base + INTEGRATOR_EBI_LOCK_OFFSET); |
117 | iounmap(addr: ebi_base); |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static void ap_flash_set_vpp(struct map_info *map, int on) |
123 | { |
124 | int ret; |
125 | |
126 | if (on) { |
127 | ret = regmap_write(map: syscon_regmap, |
128 | INTEGRATOR_SC_CTRLS_OFFSET, |
129 | INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); |
130 | if (ret) |
131 | pr_err("error enabling AP VPP\n" ); |
132 | } else { |
133 | ret = regmap_write(map: syscon_regmap, |
134 | INTEGRATOR_SC_CTRLC_OFFSET, |
135 | INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP); |
136 | if (ret) |
137 | pr_err("error disabling AP VPP\n" ); |
138 | } |
139 | } |
140 | |
141 | /* |
142 | * Flash protection handling for the Integrator/CP |
143 | */ |
144 | |
145 | #define INTCP_FLASHPROG_OFFSET 0x04 |
146 | #define CINTEGRATOR_FLVPPEN BIT(0) |
147 | #define CINTEGRATOR_FLWREN BIT(1) |
148 | #define CINTEGRATOR_FLMASK BIT(0)|BIT(1) |
149 | |
150 | static void cp_flash_set_vpp(struct map_info *map, int on) |
151 | { |
152 | int ret; |
153 | |
154 | if (on) { |
155 | ret = regmap_update_bits(map: syscon_regmap, |
156 | INTCP_FLASHPROG_OFFSET, |
157 | CINTEGRATOR_FLMASK, |
158 | CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN); |
159 | if (ret) |
160 | pr_err("error setting CP VPP\n" ); |
161 | } else { |
162 | ret = regmap_update_bits(map: syscon_regmap, |
163 | INTCP_FLASHPROG_OFFSET, |
164 | CINTEGRATOR_FLMASK, |
165 | val: 0); |
166 | if (ret) |
167 | pr_err("error setting CP VPP\n" ); |
168 | } |
169 | } |
170 | |
171 | /* |
172 | * Flash protection handling for the Versatiles and RealViews |
173 | */ |
174 | |
175 | #define VERSATILE_SYS_FLASH_OFFSET 0x4C |
176 | |
177 | static void versatile_flash_set_vpp(struct map_info *map, int on) |
178 | { |
179 | int ret; |
180 | |
181 | ret = regmap_update_bits(map: syscon_regmap, VERSATILE_SYS_FLASH_OFFSET, |
182 | mask: 0x01, val: !!on); |
183 | if (ret) |
184 | pr_err("error setting Versatile VPP\n" ); |
185 | } |
186 | |
187 | int of_flash_probe_versatile(struct platform_device *pdev, |
188 | struct device_node *np, |
189 | struct map_info *map) |
190 | { |
191 | struct device_node *sysnp; |
192 | const struct of_device_id *devid; |
193 | struct regmap *rmap; |
194 | static enum versatile_flashprot versatile_flashprot; |
195 | int ret; |
196 | |
197 | /* Not all flash chips use this protection line */ |
198 | if (!of_device_is_compatible(device: np, "arm,versatile-flash" )) |
199 | return 0; |
200 | |
201 | /* For first chip probed, look up the syscon regmap */ |
202 | if (!syscon_regmap) { |
203 | sysnp = of_find_matching_node_and_match(NULL, |
204 | matches: syscon_match, |
205 | match: &devid); |
206 | if (!sysnp) |
207 | return -ENODEV; |
208 | |
209 | versatile_flashprot = (uintptr_t)devid->data; |
210 | rmap = syscon_node_to_regmap(np: sysnp); |
211 | of_node_put(node: sysnp); |
212 | if (IS_ERR(ptr: rmap)) |
213 | return PTR_ERR(ptr: rmap); |
214 | |
215 | syscon_regmap = rmap; |
216 | } |
217 | |
218 | switch (versatile_flashprot) { |
219 | case INTEGRATOR_AP_FLASHPROT: |
220 | ret = ap_flash_init(pdev); |
221 | if (ret) |
222 | return ret; |
223 | map->set_vpp = ap_flash_set_vpp; |
224 | dev_info(&pdev->dev, "Integrator/AP flash protection\n" ); |
225 | break; |
226 | case INTEGRATOR_CP_FLASHPROT: |
227 | map->set_vpp = cp_flash_set_vpp; |
228 | dev_info(&pdev->dev, "Integrator/CP flash protection\n" ); |
229 | break; |
230 | case VERSATILE_FLASHPROT: |
231 | case REALVIEW_FLASHPROT: |
232 | map->set_vpp = versatile_flash_set_vpp; |
233 | dev_info(&pdev->dev, "versatile/realview flash protection\n" ); |
234 | break; |
235 | default: |
236 | dev_info(&pdev->dev, "device marked as Versatile flash " |
237 | "but no system controller was found\n" ); |
238 | break; |
239 | } |
240 | |
241 | return 0; |
242 | } |
243 | |