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