Commit | Line | Data |
---|---|---|
1c953bda LW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Intel IXP4xx Expansion Bus Controller | |
4 | * Copyright (C) 2021 Linaro Ltd. | |
5 | * | |
6 | * Author: Linus Walleij <linus.walleij@linaro.org> | |
7 | */ | |
8 | ||
9 | #include <linux/bitfield.h> | |
10 | #include <linux/bits.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/log2.h> | |
14 | #include <linux/mfd/syscon.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/of.h> | |
17 | #include <linux/of_platform.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/regmap.h> | |
20 | ||
21 | #define IXP4XX_EXP_NUM_CS 8 | |
22 | ||
23 | #define IXP4XX_EXP_TIMING_CS0 0x00 | |
24 | #define IXP4XX_EXP_TIMING_CS1 0x04 | |
25 | #define IXP4XX_EXP_TIMING_CS2 0x08 | |
26 | #define IXP4XX_EXP_TIMING_CS3 0x0c | |
27 | #define IXP4XX_EXP_TIMING_CS4 0x10 | |
28 | #define IXP4XX_EXP_TIMING_CS5 0x14 | |
29 | #define IXP4XX_EXP_TIMING_CS6 0x18 | |
30 | #define IXP4XX_EXP_TIMING_CS7 0x1c | |
31 | ||
32 | /* Bits inside each CS timing register */ | |
33 | #define IXP4XX_EXP_TIMING_STRIDE 0x04 | |
34 | #define IXP4XX_EXP_CS_EN BIT(31) | |
35 | #define IXP456_EXP_PAR_EN BIT(30) /* Only on IXP45x and IXP46x */ | |
36 | #define IXP4XX_EXP_T1_MASK GENMASK(28, 27) | |
37 | #define IXP4XX_EXP_T1_SHIFT 28 | |
38 | #define IXP4XX_EXP_T2_MASK GENMASK(27, 26) | |
39 | #define IXP4XX_EXP_T2_SHIFT 26 | |
40 | #define IXP4XX_EXP_T3_MASK GENMASK(25, 22) | |
41 | #define IXP4XX_EXP_T3_SHIFT 22 | |
42 | #define IXP4XX_EXP_T4_MASK GENMASK(21, 20) | |
43 | #define IXP4XX_EXP_T4_SHIFT 20 | |
44 | #define IXP4XX_EXP_T5_MASK GENMASK(19, 16) | |
45 | #define IXP4XX_EXP_T5_SHIFT 16 | |
46 | #define IXP4XX_EXP_CYC_TYPE_MASK GENMASK(15, 14) | |
47 | #define IXP4XX_EXP_CYC_TYPE_SHIFT 14 | |
48 | #define IXP4XX_EXP_SIZE_MASK GENMASK(13, 10) | |
49 | #define IXP4XX_EXP_SIZE_SHIFT 10 | |
50 | #define IXP4XX_EXP_CNFG_0 BIT(9) /* Always zero */ | |
51 | #define IXP43X_EXP_SYNC_INTEL BIT(8) /* Only on IXP43x */ | |
ff5a1990 | 52 | #define IXP43X_EXP_EXP_CHIP BIT(7) /* Only on IXP43x, dangerous to touch on IXP42x */ |
1c953bda LW |
53 | #define IXP4XX_EXP_BYTE_RD16 BIT(6) |
54 | #define IXP4XX_EXP_HRDY_POL BIT(5) /* Only on IXP42x */ | |
55 | #define IXP4XX_EXP_MUX_EN BIT(4) | |
56 | #define IXP4XX_EXP_SPLT_EN BIT(3) | |
57 | #define IXP4XX_EXP_WORD BIT(2) /* Always zero */ | |
58 | #define IXP4XX_EXP_WR_EN BIT(1) | |
59 | #define IXP4XX_EXP_BYTE_EN BIT(0) | |
1c953bda LW |
60 | |
61 | #define IXP4XX_EXP_CNFG0 0x20 | |
62 | #define IXP4XX_EXP_CNFG0_MEM_MAP BIT(31) | |
63 | #define IXP4XX_EXP_CNFG1 0x24 | |
64 | ||
65 | #define IXP4XX_EXP_BOOT_BASE 0x00000000 | |
66 | #define IXP4XX_EXP_NORMAL_BASE 0x50000000 | |
67 | #define IXP4XX_EXP_STRIDE 0x01000000 | |
68 | ||
69 | /* Fuses on the IXP43x */ | |
70 | #define IXP43X_EXP_UNIT_FUSE_RESET 0x28 | |
71 | #define IXP43x_EXP_FUSE_SPEED_MASK GENMASK(23, 22) | |
72 | ||
73 | /* Number of device tree values in "reg" */ | |
74 | #define IXP4XX_OF_REG_SIZE 3 | |
75 | ||
76 | struct ixp4xx_eb { | |
77 | struct device *dev; | |
78 | struct regmap *rmap; | |
79 | u32 bus_base; | |
80 | bool is_42x; | |
81 | bool is_43x; | |
82 | }; | |
83 | ||
84 | struct ixp4xx_exp_tim_prop { | |
85 | const char *prop; | |
86 | u32 max; | |
87 | u32 mask; | |
88 | u16 shift; | |
89 | }; | |
90 | ||
91 | static const struct ixp4xx_exp_tim_prop ixp4xx_exp_tim_props[] = { | |
92 | { | |
93 | .prop = "intel,ixp4xx-eb-t1", | |
94 | .max = 3, | |
95 | .mask = IXP4XX_EXP_T1_MASK, | |
96 | .shift = IXP4XX_EXP_T1_SHIFT, | |
97 | }, | |
98 | { | |
99 | .prop = "intel,ixp4xx-eb-t2", | |
100 | .max = 3, | |
101 | .mask = IXP4XX_EXP_T2_MASK, | |
102 | .shift = IXP4XX_EXP_T2_SHIFT, | |
103 | }, | |
104 | { | |
105 | .prop = "intel,ixp4xx-eb-t3", | |
106 | .max = 15, | |
107 | .mask = IXP4XX_EXP_T3_MASK, | |
108 | .shift = IXP4XX_EXP_T3_SHIFT, | |
109 | }, | |
110 | { | |
111 | .prop = "intel,ixp4xx-eb-t4", | |
112 | .max = 3, | |
113 | .mask = IXP4XX_EXP_T4_MASK, | |
114 | .shift = IXP4XX_EXP_T4_SHIFT, | |
115 | }, | |
116 | { | |
117 | .prop = "intel,ixp4xx-eb-t5", | |
118 | .max = 15, | |
119 | .mask = IXP4XX_EXP_T5_MASK, | |
120 | .shift = IXP4XX_EXP_T5_SHIFT, | |
121 | }, | |
122 | { | |
123 | .prop = "intel,ixp4xx-eb-byte-access-on-halfword", | |
124 | .max = 1, | |
125 | .mask = IXP4XX_EXP_BYTE_RD16, | |
126 | }, | |
127 | { | |
128 | .prop = "intel,ixp4xx-eb-hpi-hrdy-pol-high", | |
129 | .max = 1, | |
130 | .mask = IXP4XX_EXP_HRDY_POL, | |
131 | }, | |
132 | { | |
133 | .prop = "intel,ixp4xx-eb-mux-address-and-data", | |
134 | .max = 1, | |
135 | .mask = IXP4XX_EXP_MUX_EN, | |
136 | }, | |
137 | { | |
138 | .prop = "intel,ixp4xx-eb-ahb-split-transfers", | |
139 | .max = 1, | |
140 | .mask = IXP4XX_EXP_SPLT_EN, | |
141 | }, | |
142 | { | |
143 | .prop = "intel,ixp4xx-eb-write-enable", | |
144 | .max = 1, | |
145 | .mask = IXP4XX_EXP_WR_EN, | |
146 | }, | |
147 | { | |
148 | .prop = "intel,ixp4xx-eb-byte-access", | |
149 | .max = 1, | |
150 | .mask = IXP4XX_EXP_BYTE_EN, | |
151 | }, | |
152 | }; | |
153 | ||
154 | static void ixp4xx_exp_setup_chipselect(struct ixp4xx_eb *eb, | |
155 | struct device_node *np, | |
156 | u32 cs_index, | |
157 | u32 cs_size) | |
158 | { | |
159 | u32 cs_cfg; | |
160 | u32 val; | |
161 | u32 cur_cssize; | |
162 | u32 cs_order; | |
163 | int ret; | |
164 | int i; | |
165 | ||
166 | if (eb->is_42x && (cs_index > 7)) { | |
167 | dev_err(eb->dev, | |
168 | "invalid chipselect %u, we only support 0-7\n", | |
169 | cs_index); | |
170 | return; | |
171 | } | |
172 | if (eb->is_43x && (cs_index > 3)) { | |
173 | dev_err(eb->dev, | |
174 | "invalid chipselect %u, we only support 0-3\n", | |
175 | cs_index); | |
176 | return; | |
177 | } | |
178 | ||
179 | /* Several chip selects can be joined into one device */ | |
180 | if (cs_size > IXP4XX_EXP_STRIDE) | |
181 | cur_cssize = IXP4XX_EXP_STRIDE; | |
182 | else | |
183 | cur_cssize = cs_size; | |
184 | ||
185 | ||
186 | /* | |
187 | * The following will read/modify/write the configuration for one | |
188 | * chipselect, attempting to leave the boot defaults in place unless | |
189 | * something is explicitly defined. | |
190 | */ | |
191 | regmap_read(eb->rmap, IXP4XX_EXP_TIMING_CS0 + | |
192 | IXP4XX_EXP_TIMING_STRIDE * cs_index, &cs_cfg); | |
193 | dev_info(eb->dev, "CS%d at %#08x, size %#08x, config before: %#08x\n", | |
194 | cs_index, eb->bus_base + IXP4XX_EXP_STRIDE * cs_index, | |
195 | cur_cssize, cs_cfg); | |
196 | ||
197 | /* Size set-up first align to 2^9 .. 2^24 */ | |
198 | cur_cssize = roundup_pow_of_two(cur_cssize); | |
199 | if (cur_cssize < 512) | |
200 | cur_cssize = 512; | |
201 | cs_order = ilog2(cur_cssize); | |
202 | if (cs_order < 9 || cs_order > 24) { | |
203 | dev_err(eb->dev, "illegal size order %d\n", cs_order); | |
204 | return; | |
205 | } | |
206 | dev_dbg(eb->dev, "CS%d size order: %d\n", cs_index, cs_order); | |
207 | cs_cfg &= ~(IXP4XX_EXP_SIZE_MASK); | |
208 | cs_cfg |= ((cs_order - 9) << IXP4XX_EXP_SIZE_SHIFT); | |
209 | ||
210 | for (i = 0; i < ARRAY_SIZE(ixp4xx_exp_tim_props); i++) { | |
211 | const struct ixp4xx_exp_tim_prop *ip = &ixp4xx_exp_tim_props[i]; | |
212 | ||
213 | /* All are regular u32 values */ | |
214 | ret = of_property_read_u32(np, ip->prop, &val); | |
215 | if (ret) | |
216 | continue; | |
217 | ||
218 | /* Handle bools (single bits) first */ | |
219 | if (ip->max == 1) { | |
220 | if (val) | |
221 | cs_cfg |= ip->mask; | |
222 | else | |
223 | cs_cfg &= ~ip->mask; | |
224 | dev_info(eb->dev, "CS%d %s %s\n", cs_index, | |
225 | val ? "enabled" : "disabled", | |
226 | ip->prop); | |
227 | continue; | |
228 | } | |
229 | ||
230 | if (val > ip->max) { | |
231 | dev_err(eb->dev, | |
232 | "CS%d too high value for %s: %u, capped at %u\n", | |
233 | cs_index, ip->prop, val, ip->max); | |
234 | val = ip->max; | |
235 | } | |
236 | /* This assumes max value fills all the assigned bits (and it does) */ | |
237 | cs_cfg &= ~ip->mask; | |
238 | cs_cfg |= (val << ip->shift); | |
239 | dev_info(eb->dev, "CS%d set %s to %u\n", cs_index, ip->prop, val); | |
240 | } | |
241 | ||
242 | ret = of_property_read_u32(np, "intel,ixp4xx-eb-cycle-type", &val); | |
243 | if (!ret) { | |
244 | if (val > 3) { | |
245 | dev_err(eb->dev, "illegal cycle type %d\n", val); | |
246 | return; | |
247 | } | |
248 | dev_info(eb->dev, "CS%d set cycle type %d\n", cs_index, val); | |
249 | cs_cfg &= ~IXP4XX_EXP_CYC_TYPE_MASK; | |
250 | cs_cfg |= val << IXP4XX_EXP_CYC_TYPE_SHIFT; | |
251 | } | |
252 | ||
1c953bda | 253 | if (eb->is_43x) { |
ff5a1990 LW |
254 | /* Should always be zero */ |
255 | cs_cfg &= ~IXP4XX_EXP_WORD; | |
1c953bda LW |
256 | /* |
257 | * This bit for Intel strata flash is currently unused, but let's | |
258 | * report it if we find one. | |
259 | */ | |
260 | if (cs_cfg & IXP43X_EXP_SYNC_INTEL) | |
261 | dev_info(eb->dev, "claims to be Intel strata flash\n"); | |
262 | } | |
263 | cs_cfg |= IXP4XX_EXP_CS_EN; | |
264 | ||
265 | regmap_write(eb->rmap, | |
266 | IXP4XX_EXP_TIMING_CS0 + IXP4XX_EXP_TIMING_STRIDE * cs_index, | |
267 | cs_cfg); | |
268 | dev_info(eb->dev, "CS%d wrote %#08x into CS config\n", cs_index, cs_cfg); | |
269 | ||
270 | /* | |
271 | * If several chip selects are joined together into one big | |
272 | * device area, we call ourselves recursively for each successive | |
273 | * chip select. For a 32MB flash chip this results in two calls | |
274 | * for example. | |
275 | */ | |
276 | if (cs_size > IXP4XX_EXP_STRIDE) | |
277 | ixp4xx_exp_setup_chipselect(eb, np, | |
278 | cs_index + 1, | |
279 | cs_size - IXP4XX_EXP_STRIDE); | |
280 | } | |
281 | ||
282 | static void ixp4xx_exp_setup_child(struct ixp4xx_eb *eb, | |
283 | struct device_node *np) | |
284 | { | |
285 | u32 cs_sizes[IXP4XX_EXP_NUM_CS]; | |
286 | int num_regs; | |
287 | u32 csindex; | |
288 | u32 cssize; | |
289 | int ret; | |
290 | int i; | |
291 | ||
292 | num_regs = of_property_count_elems_of_size(np, "reg", IXP4XX_OF_REG_SIZE); | |
293 | if (num_regs <= 0) | |
294 | return; | |
295 | dev_dbg(eb->dev, "child %s has %d register sets\n", | |
296 | of_node_full_name(np), num_regs); | |
297 | ||
298 | for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) | |
299 | cs_sizes[csindex] = 0; | |
300 | ||
301 | for (i = 0; i < num_regs; i++) { | |
302 | u32 rbase, rsize; | |
303 | ||
304 | ret = of_property_read_u32_index(np, "reg", | |
305 | i * IXP4XX_OF_REG_SIZE, &csindex); | |
306 | if (ret) | |
307 | break; | |
308 | ret = of_property_read_u32_index(np, "reg", | |
309 | i * IXP4XX_OF_REG_SIZE + 1, &rbase); | |
310 | if (ret) | |
311 | break; | |
312 | ret = of_property_read_u32_index(np, "reg", | |
313 | i * IXP4XX_OF_REG_SIZE + 2, &rsize); | |
314 | if (ret) | |
315 | break; | |
316 | ||
317 | if (csindex >= IXP4XX_EXP_NUM_CS) { | |
318 | dev_err(eb->dev, "illegal CS %d\n", csindex); | |
319 | continue; | |
320 | } | |
321 | /* | |
322 | * The memory window always starts from CS base so we need to add | |
323 | * the start and size to get to the size from the start of the CS | |
324 | * base. For example if CS0 is at 0x50000000 and the reg is | |
325 | * <0 0xe40000 0x40000> the size is e80000. | |
326 | * | |
327 | * Roof this if we have several regs setting the same CS. | |
328 | */ | |
329 | cssize = rbase + rsize; | |
330 | dev_dbg(eb->dev, "CS%d size %#08x\n", csindex, cssize); | |
331 | if (cs_sizes[csindex] < cssize) | |
332 | cs_sizes[csindex] = cssize; | |
333 | } | |
334 | ||
335 | for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) { | |
336 | cssize = cs_sizes[csindex]; | |
337 | if (!cssize) | |
338 | continue; | |
339 | /* Just this one, so set it up and return */ | |
340 | ixp4xx_exp_setup_chipselect(eb, np, csindex, cssize); | |
341 | } | |
342 | } | |
343 | ||
344 | static int ixp4xx_exp_probe(struct platform_device *pdev) | |
345 | { | |
346 | struct device *dev = &pdev->dev; | |
347 | struct device_node *np = dev->of_node; | |
348 | struct ixp4xx_eb *eb; | |
349 | struct device_node *child; | |
350 | bool have_children = false; | |
351 | u32 val; | |
352 | int ret; | |
353 | ||
354 | eb = devm_kzalloc(dev, sizeof(*eb), GFP_KERNEL); | |
355 | if (!eb) | |
356 | return -ENOMEM; | |
357 | ||
358 | eb->dev = dev; | |
359 | eb->is_42x = of_device_is_compatible(np, "intel,ixp42x-expansion-bus-controller"); | |
360 | eb->is_43x = of_device_is_compatible(np, "intel,ixp43x-expansion-bus-controller"); | |
361 | ||
362 | eb->rmap = syscon_node_to_regmap(np); | |
363 | if (IS_ERR(eb->rmap)) | |
364 | return dev_err_probe(dev, PTR_ERR(eb->rmap), "no regmap\n"); | |
365 | ||
366 | /* We check that the regmap work only on first read */ | |
367 | ret = regmap_read(eb->rmap, IXP4XX_EXP_CNFG0, &val); | |
368 | if (ret) | |
d2c334f4 | 369 | return dev_err_probe(dev, ret, "cannot read regmap\n"); |
1c953bda LW |
370 | if (val & IXP4XX_EXP_CNFG0_MEM_MAP) |
371 | eb->bus_base = IXP4XX_EXP_BOOT_BASE; | |
372 | else | |
373 | eb->bus_base = IXP4XX_EXP_NORMAL_BASE; | |
374 | dev_info(dev, "expansion bus at %08x\n", eb->bus_base); | |
375 | ||
376 | if (eb->is_43x) { | |
377 | /* Check some fuses */ | |
378 | regmap_read(eb->rmap, IXP43X_EXP_UNIT_FUSE_RESET, &val); | |
379 | switch (FIELD_GET(IXP43x_EXP_FUSE_SPEED_MASK, val)) { | |
380 | case 0: | |
381 | dev_info(dev, "IXP43x at 533 MHz\n"); | |
382 | break; | |
383 | case 1: | |
384 | dev_info(dev, "IXP43x at 400 MHz\n"); | |
385 | break; | |
386 | case 2: | |
387 | dev_info(dev, "IXP43x at 667 MHz\n"); | |
388 | break; | |
389 | default: | |
390 | dev_info(dev, "IXP43x unknown speed\n"); | |
391 | break; | |
392 | } | |
393 | } | |
394 | ||
395 | /* Walk over the child nodes and see what chipselects we use */ | |
396 | for_each_available_child_of_node(np, child) { | |
397 | ixp4xx_exp_setup_child(eb, child); | |
398 | /* We have at least one child */ | |
399 | have_children = true; | |
400 | } | |
401 | ||
402 | if (have_children) | |
403 | return of_platform_default_populate(np, NULL, dev); | |
404 | ||
405 | return 0; | |
406 | } | |
407 | ||
408 | static const struct of_device_id ixp4xx_exp_of_match[] = { | |
409 | { .compatible = "intel,ixp42x-expansion-bus-controller", }, | |
410 | { .compatible = "intel,ixp43x-expansion-bus-controller", }, | |
411 | { .compatible = "intel,ixp45x-expansion-bus-controller", }, | |
412 | { .compatible = "intel,ixp46x-expansion-bus-controller", }, | |
413 | { } | |
414 | }; | |
415 | ||
416 | static struct platform_driver ixp4xx_exp_driver = { | |
417 | .probe = ixp4xx_exp_probe, | |
418 | .driver = { | |
419 | .name = "intel-extbus", | |
420 | .of_match_table = ixp4xx_exp_of_match, | |
421 | }, | |
422 | }; | |
423 | module_platform_driver(ixp4xx_exp_driver); | |
424 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); | |
425 | MODULE_DESCRIPTION("Intel IXP4xx external bus driver"); | |
426 | MODULE_LICENSE("GPL"); |