Commit | Line | Data |
---|---|---|
ccb7cc74 KM |
1 | /* |
2 | * r8a7778 processor support | |
3 | * | |
4 | * Copyright (C) 2013 Renesas Solutions Corp. | |
5 | * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
52421914 | 6 | * Copyright (C) 2013 Cogent Embedded, Inc. |
ccb7cc74 KM |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; version 2 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
22 | #include <linux/kernel.h> | |
23 | #include <linux/io.h> | |
24 | #include <linux/irqchip/arm-gic.h> | |
25 | #include <linux/of.h> | |
26 | #include <linux/of_platform.h> | |
39ca2283 | 27 | #include <linux/platform_data/gpio-rcar.h> |
3a42fa20 | 28 | #include <linux/platform_data/irq-renesas-intc-irqpin.h> |
ccb7cc74 KM |
29 | #include <linux/platform_device.h> |
30 | #include <linux/irqchip.h> | |
db331fc8 | 31 | #include <linux/serial_sci.h> |
ccb7cc74 | 32 | #include <linux/sh_timer.h> |
02474a41 SS |
33 | #include <linux/pm_runtime.h> |
34 | #include <linux/usb/phy.h> | |
35 | #include <linux/usb/hcd.h> | |
36 | #include <linux/usb/ehci_pdriver.h> | |
37 | #include <linux/usb/ohci_pdriver.h> | |
38 | #include <linux/dma-mapping.h> | |
ccb7cc74 KM |
39 | #include <mach/irqs.h> |
40 | #include <mach/r8a7778.h> | |
41 | #include <mach/common.h> | |
42 | #include <asm/mach/arch.h> | |
43 | #include <asm/hardware/cache-l2x0.h> | |
44 | ||
db331fc8 KM |
45 | /* SCIF */ |
46 | #define SCIF_INFO(baseaddr, irq) \ | |
47 | { \ | |
48 | .mapbase = baseaddr, \ | |
49 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, \ | |
50 | .scscr = SCSCR_RE | SCSCR_TE | SCSCR_CKE1, \ | |
51 | .scbrr_algo_id = SCBRR_ALGO_2, \ | |
52 | .type = PORT_SCIF, \ | |
53 | .irqs = SCIx_IRQ_MUXED(irq), \ | |
54 | } | |
55 | ||
56 | static struct plat_sci_port scif_platform_data[] = { | |
57 | SCIF_INFO(0xffe40000, gic_iid(0x66)), | |
58 | SCIF_INFO(0xffe41000, gic_iid(0x67)), | |
59 | SCIF_INFO(0xffe42000, gic_iid(0x68)), | |
60 | SCIF_INFO(0xffe43000, gic_iid(0x69)), | |
61 | SCIF_INFO(0xffe44000, gic_iid(0x6a)), | |
62 | SCIF_INFO(0xffe45000, gic_iid(0x6b)), | |
63 | }; | |
64 | ||
ccb7cc74 KM |
65 | /* TMU */ |
66 | static struct resource sh_tmu0_resources[] = { | |
67 | DEFINE_RES_MEM(0xffd80008, 12), | |
68 | DEFINE_RES_IRQ(gic_iid(0x40)), | |
69 | }; | |
70 | ||
71 | static struct sh_timer_config sh_tmu0_platform_data = { | |
72 | .name = "TMU00", | |
73 | .channel_offset = 0x4, | |
74 | .timer_bit = 0, | |
75 | .clockevent_rating = 200, | |
76 | }; | |
77 | ||
78 | static struct resource sh_tmu1_resources[] = { | |
79 | DEFINE_RES_MEM(0xffd80014, 12), | |
80 | DEFINE_RES_IRQ(gic_iid(0x41)), | |
81 | }; | |
82 | ||
83 | static struct sh_timer_config sh_tmu1_platform_data = { | |
84 | .name = "TMU01", | |
85 | .channel_offset = 0x10, | |
86 | .timer_bit = 1, | |
87 | .clocksource_rating = 200, | |
88 | }; | |
89 | ||
81484487 KM |
90 | #define r8a7778_register_tmu(idx) \ |
91 | platform_device_register_resndata( \ | |
92 | &platform_bus, "sh_tmu", idx, \ | |
93 | sh_tmu##idx##_resources, \ | |
94 | ARRAY_SIZE(sh_tmu##idx##_resources), \ | |
95 | &sh_tmu##idx##_platform_data, \ | |
96 | sizeof(sh_tmu##idx##_platform_data)) | |
ccb7cc74 | 97 | |
02474a41 SS |
98 | /* USB PHY */ |
99 | static struct resource usb_phy_resources[] __initdata = { | |
100 | DEFINE_RES_MEM(0xffe70800, 0x100), | |
101 | DEFINE_RES_MEM(0xffe76000, 0x100), | |
102 | }; | |
103 | ||
104 | void __init r8a7778_add_usb_phy_device(struct rcar_phy_platform_data *pdata) | |
105 | { | |
106 | platform_device_register_resndata(&platform_bus, "rcar_usb_phy", -1, | |
107 | usb_phy_resources, | |
108 | ARRAY_SIZE(usb_phy_resources), | |
109 | pdata, sizeof(*pdata)); | |
110 | } | |
111 | ||
112 | /* USB */ | |
113 | static struct usb_phy *phy; | |
114 | ||
115 | static int usb_power_on(struct platform_device *pdev) | |
116 | { | |
117 | if (IS_ERR(phy)) | |
118 | return PTR_ERR(phy); | |
119 | ||
120 | pm_runtime_enable(&pdev->dev); | |
121 | pm_runtime_get_sync(&pdev->dev); | |
122 | ||
123 | usb_phy_init(phy); | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static void usb_power_off(struct platform_device *pdev) | |
129 | { | |
130 | if (IS_ERR(phy)) | |
131 | return; | |
132 | ||
133 | usb_phy_shutdown(phy); | |
134 | ||
135 | pm_runtime_put_sync(&pdev->dev); | |
136 | pm_runtime_disable(&pdev->dev); | |
137 | } | |
138 | ||
139 | static int ehci_init_internal_buffer(struct usb_hcd *hcd) | |
140 | { | |
141 | /* | |
142 | * Below are recommended values from the datasheet; | |
143 | * see [USB :: Setting of EHCI Internal Buffer]. | |
144 | */ | |
145 | /* EHCI IP internal buffer setting */ | |
146 | iowrite32(0x00ff0040, hcd->regs + 0x0094); | |
147 | /* EHCI IP internal buffer enable */ | |
148 | iowrite32(0x00000001, hcd->regs + 0x009C); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static struct usb_ehci_pdata ehci_pdata __initdata = { | |
154 | .power_on = usb_power_on, | |
155 | .power_off = usb_power_off, | |
156 | .power_suspend = usb_power_off, | |
157 | .pre_setup = ehci_init_internal_buffer, | |
158 | }; | |
159 | ||
160 | static struct resource ehci_resources[] __initdata = { | |
161 | DEFINE_RES_MEM(0xffe70000, 0x400), | |
162 | DEFINE_RES_IRQ(gic_iid(0x4c)), | |
163 | }; | |
164 | ||
165 | static struct usb_ohci_pdata ohci_pdata __initdata = { | |
166 | .power_on = usb_power_on, | |
167 | .power_off = usb_power_off, | |
168 | .power_suspend = usb_power_off, | |
169 | }; | |
170 | ||
171 | static struct resource ohci_resources[] __initdata = { | |
172 | DEFINE_RES_MEM(0xffe70400, 0x400), | |
173 | DEFINE_RES_IRQ(gic_iid(0x4c)), | |
174 | }; | |
175 | ||
176 | #define USB_PLATFORM_INFO(hci) \ | |
177 | static struct platform_device_info hci##_info __initdata = { \ | |
178 | .parent = &platform_bus, \ | |
179 | .name = #hci "-platform", \ | |
180 | .id = -1, \ | |
181 | .res = hci##_resources, \ | |
182 | .num_res = ARRAY_SIZE(hci##_resources), \ | |
183 | .data = &hci##_pdata, \ | |
184 | .size_data = sizeof(hci##_pdata), \ | |
185 | .dma_mask = DMA_BIT_MASK(32), \ | |
186 | } | |
187 | ||
188 | USB_PLATFORM_INFO(ehci); | |
189 | USB_PLATFORM_INFO(ohci); | |
190 | ||
734e02f8 KM |
191 | /* Ether */ |
192 | static struct resource ether_resources[] = { | |
193 | DEFINE_RES_MEM(0xfde00000, 0x400), | |
194 | DEFINE_RES_IRQ(gic_iid(0x89)), | |
195 | }; | |
196 | ||
197 | void __init r8a7778_add_ether_device(struct sh_eth_plat_data *pdata) | |
198 | { | |
c02f8469 | 199 | platform_device_register_resndata(&platform_bus, "r8a777x-ether", -1, |
734e02f8 KM |
200 | ether_resources, |
201 | ARRAY_SIZE(ether_resources), | |
202 | pdata, sizeof(*pdata)); | |
203 | } | |
204 | ||
39ca2283 | 205 | /* PFC/GPIO */ |
369b00bb KM |
206 | static struct resource pfc_resources[] = { |
207 | DEFINE_RES_MEM(0xfffc0000, 0x118), | |
208 | }; | |
209 | ||
39ca2283 KM |
210 | #define R8A7778_GPIO(idx) \ |
211 | static struct resource r8a7778_gpio##idx##_resources[] = { \ | |
212 | DEFINE_RES_MEM(0xffc40000 + 0x1000 * (idx), 0x30), \ | |
213 | DEFINE_RES_IRQ(gic_iid(0x87)), \ | |
214 | }; \ | |
215 | \ | |
216 | static struct gpio_rcar_config r8a7778_gpio##idx##_platform_data = { \ | |
217 | .gpio_base = 32 * (idx), \ | |
218 | .irq_base = GPIO_IRQ_BASE(idx), \ | |
219 | .number_of_pins = 32, \ | |
220 | .pctl_name = "pfc-r8a7778", \ | |
221 | } | |
222 | ||
223 | R8A7778_GPIO(0); | |
224 | R8A7778_GPIO(1); | |
225 | R8A7778_GPIO(2); | |
226 | R8A7778_GPIO(3); | |
227 | R8A7778_GPIO(4); | |
228 | ||
229 | #define r8a7778_register_gpio(idx) \ | |
230 | platform_device_register_resndata( \ | |
231 | &platform_bus, "gpio_rcar", idx, \ | |
232 | r8a7778_gpio##idx##_resources, \ | |
233 | ARRAY_SIZE(r8a7778_gpio##idx##_resources), \ | |
234 | &r8a7778_gpio##idx##_platform_data, \ | |
235 | sizeof(r8a7778_gpio##idx##_platform_data)) | |
236 | ||
369b00bb KM |
237 | void __init r8a7778_pinmux_init(void) |
238 | { | |
239 | platform_device_register_simple( | |
240 | "pfc-r8a7778", -1, | |
241 | pfc_resources, | |
242 | ARRAY_SIZE(pfc_resources)); | |
39ca2283 KM |
243 | |
244 | r8a7778_register_gpio(0); | |
245 | r8a7778_register_gpio(1); | |
246 | r8a7778_register_gpio(2); | |
247 | r8a7778_register_gpio(3); | |
248 | r8a7778_register_gpio(4); | |
ae8b378f SH |
249 | }; |
250 | ||
dab58113 KM |
251 | /* SDHI */ |
252 | static struct resource sdhi_resources[] = { | |
253 | /* SDHI0 */ | |
254 | DEFINE_RES_MEM(0xFFE4C000, 0x100), | |
255 | DEFINE_RES_IRQ(gic_iid(0x77)), | |
256 | /* SDHI1 */ | |
257 | DEFINE_RES_MEM(0xFFE4D000, 0x100), | |
258 | DEFINE_RES_IRQ(gic_iid(0x78)), | |
259 | /* SDHI2 */ | |
260 | DEFINE_RES_MEM(0xFFE4F000, 0x100), | |
261 | DEFINE_RES_IRQ(gic_iid(0x76)), | |
262 | }; | |
263 | ||
264 | void __init r8a7778_sdhi_init(int id, | |
265 | struct sh_mobile_sdhi_info *info) | |
266 | { | |
267 | BUG_ON(id < 0 || id > 2); | |
268 | ||
269 | platform_device_register_resndata( | |
270 | &platform_bus, "sh_mobile_sdhi", id, | |
271 | sdhi_resources + (2 * id), 2, | |
272 | info, sizeof(*info)); | |
369b00bb KM |
273 | } |
274 | ||
46b9a092 KM |
275 | /* I2C */ |
276 | static struct resource i2c_resources[] __initdata = { | |
277 | /* I2C0 */ | |
278 | DEFINE_RES_MEM(0xffc70000, 0x1000), | |
279 | DEFINE_RES_IRQ(gic_iid(0x63)), | |
280 | /* I2C1 */ | |
281 | DEFINE_RES_MEM(0xffc71000, 0x1000), | |
282 | DEFINE_RES_IRQ(gic_iid(0x6e)), | |
283 | /* I2C2 */ | |
284 | DEFINE_RES_MEM(0xffc72000, 0x1000), | |
285 | DEFINE_RES_IRQ(gic_iid(0x6c)), | |
286 | /* I2C3 */ | |
287 | DEFINE_RES_MEM(0xffc73000, 0x1000), | |
288 | DEFINE_RES_IRQ(gic_iid(0x6d)), | |
289 | }; | |
290 | ||
291 | void __init r8a7778_add_i2c_device(int id) | |
292 | { | |
293 | BUG_ON(id < 0 || id > 3); | |
294 | ||
295 | platform_device_register_simple( | |
296 | "i2c-rcar", id, | |
297 | i2c_resources + (2 * id), 2); | |
298 | } | |
299 | ||
8b89797f KM |
300 | /* HSPI */ |
301 | static struct resource hspi_resources[] __initdata = { | |
302 | /* HSPI0 */ | |
303 | DEFINE_RES_MEM(0xfffc7000, 0x18), | |
304 | DEFINE_RES_IRQ(gic_iid(0x5f)), | |
305 | /* HSPI1 */ | |
306 | DEFINE_RES_MEM(0xfffc8000, 0x18), | |
307 | DEFINE_RES_IRQ(gic_iid(0x74)), | |
308 | /* HSPI2 */ | |
309 | DEFINE_RES_MEM(0xfffc6000, 0x18), | |
310 | DEFINE_RES_IRQ(gic_iid(0x75)), | |
311 | }; | |
312 | ||
313 | void __init r8a7778_add_hspi_device(int id) | |
314 | { | |
315 | BUG_ON(id < 0 || id > 2); | |
316 | ||
317 | platform_device_register_simple( | |
318 | "sh-hspi", id, | |
319 | hspi_resources + (2 * id), 2); | |
320 | } | |
321 | ||
31b2eacc KM |
322 | /* MMC */ |
323 | static struct resource mmc_resources[] __initdata = { | |
324 | DEFINE_RES_MEM(0xffe4e000, 0x100), | |
325 | DEFINE_RES_IRQ(gic_iid(0x5d)), | |
326 | }; | |
327 | ||
328 | void __init r8a7778_add_mmc_device(struct sh_mmcif_plat_data *info) | |
329 | { | |
330 | platform_device_register_resndata( | |
331 | &platform_bus, "sh_mmcif", -1, | |
332 | mmc_resources, ARRAY_SIZE(mmc_resources), | |
333 | info, sizeof(*info)); | |
334 | } | |
8b89797f | 335 | |
ccb7cc74 KM |
336 | void __init r8a7778_add_standard_devices(void) |
337 | { | |
338 | int i; | |
339 | ||
340 | #ifdef CONFIG_CACHE_L2X0 | |
341 | void __iomem *base = ioremap_nocache(0xf0100000, 0x1000); | |
342 | if (base) { | |
343 | /* | |
344 | * Early BRESP enable, Shared attribute override enable, 64K*16way | |
345 | * don't call iounmap(base) | |
346 | */ | |
347 | l2x0_init(base, 0x40470000, 0x82000fff); | |
348 | } | |
349 | #endif | |
350 | ||
db331fc8 KM |
351 | for (i = 0; i < ARRAY_SIZE(scif_platform_data); i++) |
352 | platform_device_register_data(&platform_bus, "sh-sci", i, | |
353 | &scif_platform_data[i], | |
354 | sizeof(struct plat_sci_port)); | |
355 | ||
81484487 KM |
356 | r8a7778_register_tmu(0); |
357 | r8a7778_register_tmu(1); | |
ccb7cc74 KM |
358 | } |
359 | ||
02474a41 SS |
360 | void __init r8a7778_init_late(void) |
361 | { | |
362 | phy = usb_get_phy(USB_PHY_TYPE_USB2); | |
363 | ||
364 | platform_device_register_full(&ehci_info); | |
365 | platform_device_register_full(&ohci_info); | |
366 | } | |
367 | ||
3a42fa20 KM |
368 | static struct renesas_intc_irqpin_config irqpin_platform_data = { |
369 | .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */ | |
370 | .sense_bitfield_width = 2, | |
371 | }; | |
372 | ||
373 | static struct resource irqpin_resources[] = { | |
374 | DEFINE_RES_MEM(0xfe78001c, 4), /* ICR1 */ | |
375 | DEFINE_RES_MEM(0xfe780010, 4), /* INTPRI */ | |
376 | DEFINE_RES_MEM(0xfe780024, 4), /* INTREQ */ | |
377 | DEFINE_RES_MEM(0xfe780044, 4), /* INTMSK0 */ | |
378 | DEFINE_RES_MEM(0xfe780064, 4), /* INTMSKCLR0 */ | |
379 | DEFINE_RES_IRQ(gic_iid(0x3b)), /* IRQ0 */ | |
380 | DEFINE_RES_IRQ(gic_iid(0x3c)), /* IRQ1 */ | |
381 | DEFINE_RES_IRQ(gic_iid(0x3d)), /* IRQ2 */ | |
382 | DEFINE_RES_IRQ(gic_iid(0x3e)), /* IRQ3 */ | |
383 | }; | |
384 | ||
385 | void __init r8a7778_init_irq_extpin(int irlm) | |
386 | { | |
387 | void __iomem *icr0 = ioremap_nocache(0xfe780000, PAGE_SIZE); | |
388 | unsigned long tmp; | |
389 | ||
390 | if (!icr0) { | |
391 | pr_warn("r8a7778: unable to setup external irq pin mode\n"); | |
392 | return; | |
393 | } | |
394 | ||
395 | tmp = ioread32(icr0); | |
396 | if (irlm) | |
397 | tmp |= 1 << 23; /* IRQ0 -> IRQ3 as individual pins */ | |
398 | else | |
399 | tmp &= ~(1 << 23); /* IRL mode - not supported */ | |
400 | tmp |= (1 << 21); /* LVLMODE = 1 */ | |
401 | iowrite32(tmp, icr0); | |
402 | iounmap(icr0); | |
403 | ||
404 | if (irlm) | |
405 | platform_device_register_resndata( | |
406 | &platform_bus, "renesas_intc_irqpin", -1, | |
407 | irqpin_resources, ARRAY_SIZE(irqpin_resources), | |
408 | &irqpin_platform_data, sizeof(irqpin_platform_data)); | |
409 | } | |
410 | ||
ccb7cc74 KM |
411 | #define INT2SMSKCR0 0x82288 /* 0xfe782288 */ |
412 | #define INT2SMSKCR1 0x8228c /* 0xfe78228c */ | |
413 | ||
414 | #define INT2NTSR0 0x00018 /* 0xfe700018 */ | |
415 | #define INT2NTSR1 0x0002c /* 0xfe70002c */ | |
416 | static void __init r8a7778_init_irq_common(void) | |
417 | { | |
418 | void __iomem *base = ioremap_nocache(0xfe700000, 0x00100000); | |
419 | ||
420 | BUG_ON(!base); | |
421 | ||
422 | /* route all interrupts to ARM */ | |
423 | __raw_writel(0x73ffffff, base + INT2NTSR0); | |
424 | __raw_writel(0xffffffff, base + INT2NTSR1); | |
425 | ||
426 | /* unmask all known interrupts in INTCS2 */ | |
427 | __raw_writel(0x08330773, base + INT2SMSKCR0); | |
428 | __raw_writel(0x00311110, base + INT2SMSKCR1); | |
429 | ||
430 | iounmap(base); | |
431 | } | |
432 | ||
433 | void __init r8a7778_init_irq(void) | |
434 | { | |
435 | void __iomem *gic_dist_base; | |
436 | void __iomem *gic_cpu_base; | |
437 | ||
438 | gic_dist_base = ioremap_nocache(0xfe438000, PAGE_SIZE); | |
439 | gic_cpu_base = ioremap_nocache(0xfe430000, PAGE_SIZE); | |
440 | BUG_ON(!gic_dist_base || !gic_cpu_base); | |
441 | ||
442 | /* use GIC to handle interrupts */ | |
443 | gic_init(0, 29, gic_dist_base, gic_cpu_base); | |
444 | ||
445 | r8a7778_init_irq_common(); | |
446 | } | |
447 | ||
448 | void __init r8a7778_init_delay(void) | |
449 | { | |
450 | shmobile_setup_delay(800, 1, 3); /* Cortex-A9 @ 800MHz */ | |
451 | } | |
452 | ||
453 | #ifdef CONFIG_USE_OF | |
454 | void __init r8a7778_init_irq_dt(void) | |
455 | { | |
456 | irqchip_init(); | |
457 | r8a7778_init_irq_common(); | |
458 | } | |
459 | ||
460 | static const struct of_dev_auxdata r8a7778_auxdata_lookup[] __initconst = { | |
461 | {}, | |
462 | }; | |
463 | ||
464 | void __init r8a7778_add_standard_devices_dt(void) | |
465 | { | |
466 | of_platform_populate(NULL, of_default_bus_match_table, | |
467 | r8a7778_auxdata_lookup, NULL); | |
468 | } | |
469 | ||
470 | static const char *r8a7778_compat_dt[] __initdata = { | |
471 | "renesas,r8a7778", | |
472 | NULL, | |
473 | }; | |
474 | ||
475 | DT_MACHINE_START(R8A7778_DT, "Generic R8A7778 (Flattened Device Tree)") | |
476 | .init_early = r8a7778_init_delay, | |
477 | .init_irq = r8a7778_init_irq_dt, | |
478 | .init_machine = r8a7778_add_standard_devices_dt, | |
479 | .init_time = shmobile_timer_init, | |
480 | .dt_compat = r8a7778_compat_dt, | |
02474a41 | 481 | .init_late = r8a7778_init_late, |
ccb7cc74 KM |
482 | MACHINE_END |
483 | ||
484 | #endif /* CONFIG_USE_OF */ |