1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * arch/arm/mach-orion5x/ts78xx-setup.c |
4 | * |
5 | * Maintainer: Alexander Clouter <alex@digriz.org.uk> |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/init.h> |
12 | #include <linux/sysfs.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/mv643xx_eth.h> |
15 | #include <linux/ata_platform.h> |
16 | #include <linux/mtd/platnand.h> |
17 | #include <linux/timeriomem-rng.h> |
18 | #include <asm/mach-types.h> |
19 | #include <asm/mach/arch.h> |
20 | #include <asm/mach/map.h> |
21 | #include "common.h" |
22 | #include "mpp.h" |
23 | #include "orion5x.h" |
24 | #include "ts78xx-fpga.h" |
25 | |
26 | /***************************************************************************** |
27 | * TS-78xx Info |
28 | ****************************************************************************/ |
29 | |
30 | /* |
31 | * FPGA - lives where the PCI bus would be at ORION5X_PCI_MEM_PHYS_BASE |
32 | */ |
33 | #define TS78XX_FPGA_REGS_PHYS_BASE 0xe8000000 |
34 | #define TS78XX_FPGA_REGS_VIRT_BASE IOMEM(0xff900000) |
35 | #define TS78XX_FPGA_REGS_SIZE SZ_1M |
36 | |
37 | static struct ts78xx_fpga_data ts78xx_fpga = { |
38 | .id = 0, |
39 | .state = 1, |
40 | /* .supports = ... - populated by ts78xx_fpga_supports() */ |
41 | }; |
42 | |
43 | /***************************************************************************** |
44 | * I/O Address Mapping |
45 | ****************************************************************************/ |
46 | static struct map_desc ts78xx_io_desc[] __initdata = { |
47 | { |
48 | .virtual = (unsigned long)TS78XX_FPGA_REGS_VIRT_BASE, |
49 | .pfn = __phys_to_pfn(TS78XX_FPGA_REGS_PHYS_BASE), |
50 | .length = TS78XX_FPGA_REGS_SIZE, |
51 | .type = MT_DEVICE, |
52 | }, |
53 | }; |
54 | |
55 | static void __init ts78xx_map_io(void) |
56 | { |
57 | orion5x_map_io(); |
58 | iotable_init(ts78xx_io_desc, ARRAY_SIZE(ts78xx_io_desc)); |
59 | } |
60 | |
61 | /***************************************************************************** |
62 | * Ethernet |
63 | ****************************************************************************/ |
64 | static struct mv643xx_eth_platform_data ts78xx_eth_data = { |
65 | .phy_addr = MV643XX_ETH_PHY_ADDR(0), |
66 | }; |
67 | |
68 | /***************************************************************************** |
69 | * SATA |
70 | ****************************************************************************/ |
71 | static struct mv_sata_platform_data ts78xx_sata_data = { |
72 | .n_ports = 2, |
73 | }; |
74 | |
75 | /***************************************************************************** |
76 | * RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c |
77 | ****************************************************************************/ |
78 | #define TS_RTC_CTRL (TS78XX_FPGA_REGS_PHYS_BASE + 0x808) |
79 | #define TS_RTC_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x80c) |
80 | |
81 | static struct resource ts78xx_ts_rtc_resources[] = { |
82 | DEFINE_RES_MEM(TS_RTC_CTRL, 0x01), |
83 | DEFINE_RES_MEM(TS_RTC_DATA, 0x01), |
84 | }; |
85 | |
86 | static struct platform_device ts78xx_ts_rtc_device = { |
87 | .name = "rtc-m48t86" , |
88 | .id = -1, |
89 | .resource = ts78xx_ts_rtc_resources, |
90 | .num_resources = ARRAY_SIZE(ts78xx_ts_rtc_resources), |
91 | }; |
92 | |
93 | static int ts78xx_ts_rtc_load(void) |
94 | { |
95 | int rc; |
96 | |
97 | if (ts78xx_fpga.supports.ts_rtc.init == 0) { |
98 | rc = platform_device_register(&ts78xx_ts_rtc_device); |
99 | if (!rc) |
100 | ts78xx_fpga.supports.ts_rtc.init = 1; |
101 | } else { |
102 | rc = platform_device_add(pdev: &ts78xx_ts_rtc_device); |
103 | } |
104 | |
105 | if (rc) |
106 | pr_info("RTC could not be registered: %d\n" , rc); |
107 | |
108 | return rc; |
109 | } |
110 | |
111 | static void ts78xx_ts_rtc_unload(void) |
112 | { |
113 | platform_device_del(pdev: &ts78xx_ts_rtc_device); |
114 | } |
115 | |
116 | /***************************************************************************** |
117 | * NAND Flash |
118 | ****************************************************************************/ |
119 | #define TS_NAND_CTRL (TS78XX_FPGA_REGS_VIRT_BASE + 0x800) /* VIRT */ |
120 | #define TS_NAND_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x804) /* PHYS */ |
121 | |
122 | /* |
123 | * hardware specific access to control-lines |
124 | * |
125 | * ctrl: |
126 | * NAND_NCE: bit 0 -> bit 2 |
127 | * NAND_CLE: bit 1 -> bit 1 |
128 | * NAND_ALE: bit 2 -> bit 0 |
129 | */ |
130 | static void ts78xx_ts_nand_cmd_ctrl(struct nand_chip *this, int cmd, |
131 | unsigned int ctrl) |
132 | { |
133 | if (ctrl & NAND_CTRL_CHANGE) { |
134 | unsigned char bits; |
135 | |
136 | bits = (ctrl & NAND_NCE) << 2; |
137 | bits |= ctrl & NAND_CLE; |
138 | bits |= (ctrl & NAND_ALE) >> 2; |
139 | |
140 | writeb(val: (readb(TS_NAND_CTRL) & ~0x7) | bits, TS_NAND_CTRL); |
141 | } |
142 | |
143 | if (cmd != NAND_CMD_NONE) |
144 | writeb(val: cmd, addr: this->legacy.IO_ADDR_W); |
145 | } |
146 | |
147 | static int ts78xx_ts_nand_dev_ready(struct nand_chip *chip) |
148 | { |
149 | return readb(TS_NAND_CTRL) & 0x20; |
150 | } |
151 | |
152 | static void ts78xx_ts_nand_write_buf(struct nand_chip *chip, |
153 | const uint8_t *buf, int len) |
154 | { |
155 | void __iomem *io_base = chip->legacy.IO_ADDR_W; |
156 | unsigned long off = ((unsigned long)buf & 3); |
157 | int sz; |
158 | |
159 | if (off) { |
160 | sz = min_t(int, 4 - off, len); |
161 | writesb(addr: io_base, buffer: buf, count: sz); |
162 | buf += sz; |
163 | len -= sz; |
164 | } |
165 | |
166 | sz = len >> 2; |
167 | if (sz) { |
168 | u32 *buf32 = (u32 *)buf; |
169 | writesl(addr: io_base, buffer: buf32, count: sz); |
170 | buf += sz << 2; |
171 | len -= sz << 2; |
172 | } |
173 | |
174 | if (len) |
175 | writesb(addr: io_base, buffer: buf, count: len); |
176 | } |
177 | |
178 | static void ts78xx_ts_nand_read_buf(struct nand_chip *chip, |
179 | uint8_t *buf, int len) |
180 | { |
181 | void __iomem *io_base = chip->legacy.IO_ADDR_R; |
182 | unsigned long off = ((unsigned long)buf & 3); |
183 | int sz; |
184 | |
185 | if (off) { |
186 | sz = min_t(int, 4 - off, len); |
187 | readsb(addr: io_base, buffer: buf, count: sz); |
188 | buf += sz; |
189 | len -= sz; |
190 | } |
191 | |
192 | sz = len >> 2; |
193 | if (sz) { |
194 | u32 *buf32 = (u32 *)buf; |
195 | readsl(addr: io_base, buffer: buf32, count: sz); |
196 | buf += sz << 2; |
197 | len -= sz << 2; |
198 | } |
199 | |
200 | if (len) |
201 | readsb(addr: io_base, buffer: buf, count: len); |
202 | } |
203 | |
204 | static struct mtd_partition ts78xx_ts_nand_parts[] = { |
205 | { |
206 | .name = "mbr" , |
207 | .offset = 0, |
208 | .size = SZ_128K, |
209 | .mask_flags = MTD_WRITEABLE, |
210 | }, { |
211 | .name = "kernel" , |
212 | .offset = MTDPART_OFS_APPEND, |
213 | .size = SZ_4M, |
214 | }, { |
215 | .name = "initrd" , |
216 | .offset = MTDPART_OFS_APPEND, |
217 | .size = SZ_4M, |
218 | }, { |
219 | .name = "rootfs" , |
220 | .offset = MTDPART_OFS_APPEND, |
221 | .size = MTDPART_SIZ_FULL, |
222 | } |
223 | }; |
224 | |
225 | static struct platform_nand_data ts78xx_ts_nand_data = { |
226 | .chip = { |
227 | .nr_chips = 1, |
228 | .partitions = ts78xx_ts_nand_parts, |
229 | .nr_partitions = ARRAY_SIZE(ts78xx_ts_nand_parts), |
230 | .chip_delay = 15, |
231 | .bbt_options = NAND_BBT_USE_FLASH, |
232 | }, |
233 | .ctrl = { |
234 | /* |
235 | * The HW ECC offloading functions, used to give about a 9% |
236 | * performance increase for 'dd if=/dev/mtdblockX' and 5% for |
237 | * nanddump. This all however was changed by git commit |
238 | * e6cf5df1838c28bb060ac45b5585e48e71bbc740 so now there is |
239 | * no performance advantage to be had so we no longer bother |
240 | */ |
241 | .cmd_ctrl = ts78xx_ts_nand_cmd_ctrl, |
242 | .dev_ready = ts78xx_ts_nand_dev_ready, |
243 | .write_buf = ts78xx_ts_nand_write_buf, |
244 | .read_buf = ts78xx_ts_nand_read_buf, |
245 | }, |
246 | }; |
247 | |
248 | static struct resource ts78xx_ts_nand_resources |
249 | = DEFINE_RES_MEM(TS_NAND_DATA, 4); |
250 | |
251 | static struct platform_device ts78xx_ts_nand_device = { |
252 | .name = "gen_nand" , |
253 | .id = -1, |
254 | .dev = { |
255 | .platform_data = &ts78xx_ts_nand_data, |
256 | }, |
257 | .resource = &ts78xx_ts_nand_resources, |
258 | .num_resources = 1, |
259 | }; |
260 | |
261 | static int ts78xx_ts_nand_load(void) |
262 | { |
263 | int rc; |
264 | |
265 | if (ts78xx_fpga.supports.ts_nand.init == 0) { |
266 | rc = platform_device_register(&ts78xx_ts_nand_device); |
267 | if (!rc) |
268 | ts78xx_fpga.supports.ts_nand.init = 1; |
269 | } else |
270 | rc = platform_device_add(pdev: &ts78xx_ts_nand_device); |
271 | |
272 | if (rc) |
273 | pr_info("NAND could not be registered: %d\n" , rc); |
274 | return rc; |
275 | }; |
276 | |
277 | static void ts78xx_ts_nand_unload(void) |
278 | { |
279 | platform_device_del(pdev: &ts78xx_ts_nand_device); |
280 | } |
281 | |
282 | /***************************************************************************** |
283 | * HW RNG |
284 | ****************************************************************************/ |
285 | #define TS_RNG_DATA (TS78XX_FPGA_REGS_PHYS_BASE | 0x044) |
286 | |
287 | static struct resource ts78xx_ts_rng_resource |
288 | = DEFINE_RES_MEM(TS_RNG_DATA, 4); |
289 | |
290 | static struct timeriomem_rng_data ts78xx_ts_rng_data = { |
291 | .period = 1000000, /* one second */ |
292 | }; |
293 | |
294 | static struct platform_device ts78xx_ts_rng_device = { |
295 | .name = "timeriomem_rng" , |
296 | .id = -1, |
297 | .dev = { |
298 | .platform_data = &ts78xx_ts_rng_data, |
299 | }, |
300 | .resource = &ts78xx_ts_rng_resource, |
301 | .num_resources = 1, |
302 | }; |
303 | |
304 | static int ts78xx_ts_rng_load(void) |
305 | { |
306 | int rc; |
307 | |
308 | if (ts78xx_fpga.supports.ts_rng.init == 0) { |
309 | rc = platform_device_register(&ts78xx_ts_rng_device); |
310 | if (!rc) |
311 | ts78xx_fpga.supports.ts_rng.init = 1; |
312 | } else |
313 | rc = platform_device_add(pdev: &ts78xx_ts_rng_device); |
314 | |
315 | if (rc) |
316 | pr_info("RNG could not be registered: %d\n" , rc); |
317 | return rc; |
318 | }; |
319 | |
320 | static void ts78xx_ts_rng_unload(void) |
321 | { |
322 | platform_device_del(pdev: &ts78xx_ts_rng_device); |
323 | } |
324 | |
325 | /***************************************************************************** |
326 | * FPGA 'hotplug' support code |
327 | ****************************************************************************/ |
328 | static void ts78xx_fpga_devices_zero_init(void) |
329 | { |
330 | ts78xx_fpga.supports.ts_rtc.init = 0; |
331 | ts78xx_fpga.supports.ts_nand.init = 0; |
332 | ts78xx_fpga.supports.ts_rng.init = 0; |
333 | } |
334 | |
335 | static void ts78xx_fpga_supports(void) |
336 | { |
337 | /* TODO: put this 'table' into ts78xx-fpga.h */ |
338 | switch (ts78xx_fpga.id) { |
339 | case TS7800_REV_1: |
340 | case TS7800_REV_2: |
341 | case TS7800_REV_3: |
342 | case TS7800_REV_4: |
343 | case TS7800_REV_5: |
344 | case TS7800_REV_6: |
345 | case TS7800_REV_7: |
346 | case TS7800_REV_8: |
347 | case TS7800_REV_9: |
348 | ts78xx_fpga.supports.ts_rtc.present = 1; |
349 | ts78xx_fpga.supports.ts_nand.present = 1; |
350 | ts78xx_fpga.supports.ts_rng.present = 1; |
351 | break; |
352 | default: |
353 | /* enable devices if magic matches */ |
354 | switch ((ts78xx_fpga.id >> 8) & 0xffffff) { |
355 | case TS7800_FPGA_MAGIC: |
356 | pr_warn("unrecognised FPGA revision 0x%.2x\n" , |
357 | ts78xx_fpga.id & 0xff); |
358 | ts78xx_fpga.supports.ts_rtc.present = 1; |
359 | ts78xx_fpga.supports.ts_nand.present = 1; |
360 | ts78xx_fpga.supports.ts_rng.present = 1; |
361 | break; |
362 | default: |
363 | ts78xx_fpga.supports.ts_rtc.present = 0; |
364 | ts78xx_fpga.supports.ts_nand.present = 0; |
365 | ts78xx_fpga.supports.ts_rng.present = 0; |
366 | } |
367 | } |
368 | } |
369 | |
370 | static int ts78xx_fpga_load_devices(void) |
371 | { |
372 | int tmp, ret = 0; |
373 | |
374 | if (ts78xx_fpga.supports.ts_rtc.present == 1) { |
375 | tmp = ts78xx_ts_rtc_load(); |
376 | if (tmp) |
377 | ts78xx_fpga.supports.ts_rtc.present = 0; |
378 | ret |= tmp; |
379 | } |
380 | if (ts78xx_fpga.supports.ts_nand.present == 1) { |
381 | tmp = ts78xx_ts_nand_load(); |
382 | if (tmp) |
383 | ts78xx_fpga.supports.ts_nand.present = 0; |
384 | ret |= tmp; |
385 | } |
386 | if (ts78xx_fpga.supports.ts_rng.present == 1) { |
387 | tmp = ts78xx_ts_rng_load(); |
388 | if (tmp) |
389 | ts78xx_fpga.supports.ts_rng.present = 0; |
390 | ret |= tmp; |
391 | } |
392 | |
393 | return ret; |
394 | } |
395 | |
396 | static int ts78xx_fpga_unload_devices(void) |
397 | { |
398 | |
399 | if (ts78xx_fpga.supports.ts_rtc.present == 1) |
400 | ts78xx_ts_rtc_unload(); |
401 | if (ts78xx_fpga.supports.ts_nand.present == 1) |
402 | ts78xx_ts_nand_unload(); |
403 | if (ts78xx_fpga.supports.ts_rng.present == 1) |
404 | ts78xx_ts_rng_unload(); |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | static int ts78xx_fpga_load(void) |
410 | { |
411 | ts78xx_fpga.id = readl(TS78XX_FPGA_REGS_VIRT_BASE); |
412 | |
413 | pr_info("FPGA magic=0x%.6x, rev=0x%.2x\n" , |
414 | (ts78xx_fpga.id >> 8) & 0xffffff, |
415 | ts78xx_fpga.id & 0xff); |
416 | |
417 | ts78xx_fpga_supports(); |
418 | |
419 | if (ts78xx_fpga_load_devices()) { |
420 | ts78xx_fpga.state = -1; |
421 | return -EBUSY; |
422 | } |
423 | |
424 | return 0; |
425 | }; |
426 | |
427 | static int ts78xx_fpga_unload(void) |
428 | { |
429 | unsigned int fpga_id; |
430 | |
431 | fpga_id = readl(TS78XX_FPGA_REGS_VIRT_BASE); |
432 | |
433 | /* |
434 | * There does not seem to be a feasible way to block access to the GPIO |
435 | * pins from userspace (/dev/mem). This if clause should hopefully warn |
436 | * those foolish enough not to follow 'policy' :) |
437 | * |
438 | * UrJTAG SVN since r1381 can be used to reprogram the FPGA |
439 | */ |
440 | if (ts78xx_fpga.id != fpga_id) { |
441 | pr_err("FPGA magic/rev mismatch\n" |
442 | "TS-78xx FPGA: was 0x%.6x/%.2x but now 0x%.6x/%.2x\n" , |
443 | (ts78xx_fpga.id >> 8) & 0xffffff, ts78xx_fpga.id & 0xff, |
444 | (fpga_id >> 8) & 0xffffff, fpga_id & 0xff); |
445 | ts78xx_fpga.state = -1; |
446 | return -EBUSY; |
447 | } |
448 | |
449 | if (ts78xx_fpga_unload_devices()) { |
450 | ts78xx_fpga.state = -1; |
451 | return -EBUSY; |
452 | } |
453 | |
454 | return 0; |
455 | }; |
456 | |
457 | static ssize_t ts78xx_fpga_show(struct kobject *kobj, |
458 | struct kobj_attribute *attr, char *buf) |
459 | { |
460 | if (ts78xx_fpga.state < 0) |
461 | return sprintf(buf, fmt: "borked\n" ); |
462 | |
463 | return sprintf(buf, fmt: "%s\n" , (ts78xx_fpga.state) ? "online" : "offline" ); |
464 | } |
465 | |
466 | static ssize_t ts78xx_fpga_store(struct kobject *kobj, |
467 | struct kobj_attribute *attr, const char *buf, size_t n) |
468 | { |
469 | int value, ret; |
470 | |
471 | if (ts78xx_fpga.state < 0) { |
472 | pr_err("FPGA borked, you must powercycle ASAP\n" ); |
473 | return -EBUSY; |
474 | } |
475 | |
476 | if (strncmp(buf, "online" , sizeof("online" ) - 1) == 0) |
477 | value = 1; |
478 | else if (strncmp(buf, "offline" , sizeof("offline" ) - 1) == 0) |
479 | value = 0; |
480 | else |
481 | return -EINVAL; |
482 | |
483 | if (ts78xx_fpga.state == value) |
484 | return n; |
485 | |
486 | ret = (ts78xx_fpga.state == 0) |
487 | ? ts78xx_fpga_load() |
488 | : ts78xx_fpga_unload(); |
489 | |
490 | if (!(ret < 0)) |
491 | ts78xx_fpga.state = value; |
492 | |
493 | return n; |
494 | } |
495 | |
496 | static struct kobj_attribute ts78xx_fpga_attr = |
497 | __ATTR(ts78xx_fpga, 0644, ts78xx_fpga_show, ts78xx_fpga_store); |
498 | |
499 | /***************************************************************************** |
500 | * General Setup |
501 | ****************************************************************************/ |
502 | static unsigned int ts78xx_mpp_modes[] __initdata = { |
503 | MPP0_UNUSED, |
504 | MPP1_GPIO, /* JTAG Clock */ |
505 | MPP2_GPIO, /* JTAG Data In */ |
506 | MPP3_GPIO, /* Lat ECP2 256 FPGA - PB2B */ |
507 | MPP4_GPIO, /* JTAG Data Out */ |
508 | MPP5_GPIO, /* JTAG TMS */ |
509 | MPP6_GPIO, /* Lat ECP2 256 FPGA - PB31A_CLK4+ */ |
510 | MPP7_GPIO, /* Lat ECP2 256 FPGA - PB22B */ |
511 | MPP8_UNUSED, |
512 | MPP9_UNUSED, |
513 | MPP10_UNUSED, |
514 | MPP11_UNUSED, |
515 | MPP12_UNUSED, |
516 | MPP13_UNUSED, |
517 | MPP14_UNUSED, |
518 | MPP15_UNUSED, |
519 | MPP16_UART, |
520 | MPP17_UART, |
521 | MPP18_UART, |
522 | MPP19_UART, |
523 | /* |
524 | * MPP[20] PCI Clock Out 1 |
525 | * MPP[21] PCI Clock Out 0 |
526 | * MPP[22] Unused |
527 | * MPP[23] Unused |
528 | * MPP[24] Unused |
529 | * MPP[25] Unused |
530 | */ |
531 | 0, |
532 | }; |
533 | |
534 | static void __init ts78xx_init(void) |
535 | { |
536 | int ret; |
537 | |
538 | /* |
539 | * Setup basic Orion functions. Need to be called early. |
540 | */ |
541 | orion5x_init(); |
542 | |
543 | orion5x_mpp_conf(mpp_list: ts78xx_mpp_modes); |
544 | |
545 | /* |
546 | * Configure peripherals. |
547 | */ |
548 | orion5x_ehci0_init(); |
549 | orion5x_ehci1_init(); |
550 | orion5x_eth_init(eth_data: &ts78xx_eth_data); |
551 | orion5x_sata_init(sata_data: &ts78xx_sata_data); |
552 | orion5x_uart0_init(); |
553 | orion5x_uart1_init(); |
554 | orion5x_xor_init(); |
555 | |
556 | /* FPGA init */ |
557 | ts78xx_fpga_devices_zero_init(); |
558 | ret = ts78xx_fpga_load(); |
559 | ret = sysfs_create_file(kobj: firmware_kobj, attr: &ts78xx_fpga_attr.attr); |
560 | if (ret) |
561 | pr_err("sysfs_create_file failed: %d\n" , ret); |
562 | } |
563 | |
564 | MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC" ) |
565 | /* Maintainer: Alexander Clouter <alex@digriz.org.uk> */ |
566 | .atag_offset = 0x100, |
567 | .nr_irqs = ORION5X_NR_IRQS, |
568 | .init_machine = ts78xx_init, |
569 | .map_io = ts78xx_map_io, |
570 | .init_early = orion5x_init_early, |
571 | .init_irq = orion5x_init_irq, |
572 | .init_time = orion5x_timer_init, |
573 | .restart = orion5x_restart, |
574 | MACHINE_END |
575 | |