1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Copyright (c) 2011 Samsung Electronics Co., Ltd. |
4 | // http://www.samsung.com |
5 | // |
6 | // Copyright 2008 Openmoko, Inc. |
7 | // Copyright 2008 Simtec Electronics |
8 | // Ben Dooks <ben@simtec.co.uk> |
9 | // http://armlinux.simtec.co.uk/ |
10 | // |
11 | // Common Codes for S3C64XX machines |
12 | |
13 | /* |
14 | * NOTE: Code in this file is not used when booting with Device Tree support. |
15 | */ |
16 | |
17 | #include <linux/kernel.h> |
18 | #include <linux/init.h> |
19 | #include <linux/module.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/ioport.h> |
22 | #include <linux/serial_core.h> |
23 | #include <linux/serial_s3c.h> |
24 | #include <linux/of.h> |
25 | #include <linux/platform_device.h> |
26 | #include <linux/reboot.h> |
27 | #include <linux/io.h> |
28 | #include <linux/clk/samsung.h> |
29 | #include <linux/dma-mapping.h> |
30 | #include <linux/irq.h> |
31 | #include <linux/irqchip/arm-vic.h> |
32 | #include <clocksource/samsung_pwm.h> |
33 | |
34 | #include <asm/mach/arch.h> |
35 | #include <asm/mach/map.h> |
36 | #include <asm/system_misc.h> |
37 | |
38 | #include "map.h" |
39 | #include "irqs.h" |
40 | #include "regs-gpio.h" |
41 | #include "gpio-samsung.h" |
42 | |
43 | #include "cpu.h" |
44 | #include "devs.h" |
45 | #include "pm.h" |
46 | #include "gpio-cfg.h" |
47 | #include "pwm-core.h" |
48 | #include "regs-irqtype.h" |
49 | #include "s3c64xx.h" |
50 | #include "irq-uart-s3c64xx.h" |
51 | |
52 | /* External clock frequency */ |
53 | static unsigned long xtal_f __ro_after_init = 12000000; |
54 | static unsigned long xusbxti_f __ro_after_init = 48000000; |
55 | |
56 | void __init s3c64xx_set_xtal_freq(unsigned long freq) |
57 | { |
58 | xtal_f = freq; |
59 | } |
60 | |
61 | void __init s3c64xx_set_xusbxti_freq(unsigned long freq) |
62 | { |
63 | xusbxti_f = freq; |
64 | } |
65 | |
66 | /* uart registration process */ |
67 | |
68 | static void __init s3c64xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) |
69 | { |
70 | s3c24xx_init_uartdevs(name: "s3c6400-uart" , res: s3c64xx_uart_resources, cfg, no); |
71 | } |
72 | |
73 | /* table of supported CPUs */ |
74 | |
75 | static const char name_s3c6410[] = "S3C6410" ; |
76 | |
77 | static struct cpu_table cpu_ids[] __initdata = { |
78 | { |
79 | .idcode = S3C6410_CPU_ID, |
80 | .idmask = S3C64XX_CPU_MASK, |
81 | .map_io = s3c6410_map_io, |
82 | .init_uarts = s3c64xx_init_uarts, |
83 | .init = s3c6410_init, |
84 | .name = name_s3c6410, |
85 | }, |
86 | }; |
87 | |
88 | /* minimal IO mapping */ |
89 | |
90 | /* |
91 | * note, for the boot process to work we have to keep the UART |
92 | * virtual address aligned to an 1MiB boundary for the L1 |
93 | * mapping the head code makes. We keep the UART virtual address |
94 | * aligned and add in the offset when we load the value here. |
95 | */ |
96 | #define UART_OFFS (S3C_PA_UART & 0xfffff) |
97 | |
98 | static struct map_desc s3c_iodesc[] __initdata = { |
99 | { |
100 | .virtual = (unsigned long)S3C_VA_SYS, |
101 | .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON), |
102 | .length = SZ_4K, |
103 | .type = MT_DEVICE, |
104 | }, { |
105 | .virtual = (unsigned long)S3C_VA_MEM, |
106 | .pfn = __phys_to_pfn(S3C64XX_PA_SROM), |
107 | .length = SZ_4K, |
108 | .type = MT_DEVICE, |
109 | }, { |
110 | .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS), |
111 | .pfn = __phys_to_pfn(S3C_PA_UART), |
112 | .length = SZ_4K, |
113 | .type = MT_DEVICE, |
114 | }, { |
115 | .virtual = (unsigned long)VA_VIC0, |
116 | .pfn = __phys_to_pfn(S3C64XX_PA_VIC0), |
117 | .length = SZ_16K, |
118 | .type = MT_DEVICE, |
119 | }, { |
120 | .virtual = (unsigned long)VA_VIC1, |
121 | .pfn = __phys_to_pfn(S3C64XX_PA_VIC1), |
122 | .length = SZ_16K, |
123 | .type = MT_DEVICE, |
124 | }, { |
125 | .virtual = (unsigned long)S3C_VA_TIMER, |
126 | .pfn = __phys_to_pfn(S3C_PA_TIMER), |
127 | .length = SZ_16K, |
128 | .type = MT_DEVICE, |
129 | }, { |
130 | .virtual = (unsigned long)S3C64XX_VA_GPIO, |
131 | .pfn = __phys_to_pfn(S3C64XX_PA_GPIO), |
132 | .length = SZ_4K, |
133 | .type = MT_DEVICE, |
134 | }, { |
135 | .virtual = (unsigned long)S3C64XX_VA_MODEM, |
136 | .pfn = __phys_to_pfn(S3C64XX_PA_MODEM), |
137 | .length = SZ_4K, |
138 | .type = MT_DEVICE, |
139 | }, { |
140 | .virtual = (unsigned long)S3C_VA_WATCHDOG, |
141 | .pfn = __phys_to_pfn(S3C64XX_PA_WATCHDOG), |
142 | .length = SZ_4K, |
143 | .type = MT_DEVICE, |
144 | }, { |
145 | .virtual = (unsigned long)S3C_VA_USB_HSPHY, |
146 | .pfn = __phys_to_pfn(S3C64XX_PA_USB_HSPHY), |
147 | .length = SZ_1K, |
148 | .type = MT_DEVICE, |
149 | }, |
150 | }; |
151 | |
152 | static const struct bus_type s3c64xx_subsys = { |
153 | .name = "s3c64xx-core" , |
154 | .dev_name = "s3c64xx-core" , |
155 | }; |
156 | |
157 | static struct device s3c64xx_dev = { |
158 | .bus = &s3c64xx_subsys, |
159 | }; |
160 | |
161 | static struct samsung_pwm_variant s3c64xx_pwm_variant = { |
162 | .bits = 32, |
163 | .div_base = 0, |
164 | .has_tint_cstat = true, |
165 | .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5), |
166 | }; |
167 | |
168 | void __init s3c64xx_set_timer_source(enum s3c64xx_timer_mode event, |
169 | enum s3c64xx_timer_mode source) |
170 | { |
171 | s3c64xx_pwm_variant.output_mask = BIT(SAMSUNG_PWM_NUM) - 1; |
172 | s3c64xx_pwm_variant.output_mask &= ~(BIT(event) | BIT(source)); |
173 | } |
174 | |
175 | void __init s3c64xx_timer_init(void) |
176 | { |
177 | unsigned int timer_irqs[SAMSUNG_PWM_NUM] = { |
178 | IRQ_TIMER0_VIC, IRQ_TIMER1_VIC, IRQ_TIMER2_VIC, |
179 | IRQ_TIMER3_VIC, IRQ_TIMER4_VIC, |
180 | }; |
181 | |
182 | samsung_pwm_clocksource_init(S3C_VA_TIMER, |
183 | irqs: timer_irqs, variant: &s3c64xx_pwm_variant); |
184 | } |
185 | |
186 | /* read cpu identification code */ |
187 | |
188 | void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) |
189 | { |
190 | /* initialise the io descriptors we need for initialisation */ |
191 | iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); |
192 | iotable_init(mach_desc, size); |
193 | |
194 | /* detect cpu id */ |
195 | s3c64xx_init_cpu(); |
196 | |
197 | s3c_init_cpu(idcode: samsung_cpu_id, cpus: cpu_ids, ARRAY_SIZE(cpu_ids)); |
198 | |
199 | samsung_pwm_set_platdata(pd: &s3c64xx_pwm_variant); |
200 | } |
201 | |
202 | static __init int s3c64xx_dev_init(void) |
203 | { |
204 | /* Not applicable when using DT. */ |
205 | if (of_have_populated_dt() || !soc_is_s3c64xx()) |
206 | return 0; |
207 | |
208 | subsys_system_register(subsys: &s3c64xx_subsys, NULL); |
209 | return device_register(dev: &s3c64xx_dev); |
210 | } |
211 | core_initcall(s3c64xx_dev_init); |
212 | |
213 | /* |
214 | * setup the sources the vic should advertise resume |
215 | * for, even though it is not doing the wake |
216 | * (set_irq_wake needs to be valid) |
217 | */ |
218 | #define IRQ_VIC0_RESUME (1 << (IRQ_RTC_TIC - IRQ_VIC0_BASE)) |
219 | #define IRQ_VIC1_RESUME (1 << (IRQ_RTC_ALARM - IRQ_VIC1_BASE) | \ |
220 | 1 << (IRQ_PENDN - IRQ_VIC1_BASE) | \ |
221 | 1 << (IRQ_HSMMC0 - IRQ_VIC1_BASE) | \ |
222 | 1 << (IRQ_HSMMC1 - IRQ_VIC1_BASE) | \ |
223 | 1 << (IRQ_HSMMC2 - IRQ_VIC1_BASE)) |
224 | |
225 | void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) |
226 | { |
227 | s3c64xx_clk_init(NULL, xtal_f, xusbxti_f, soc_is_s3c6400(), S3C_VA_SYS); |
228 | |
229 | printk(KERN_DEBUG "%s: initialising interrupts\n" , __func__); |
230 | |
231 | /* initialise the pair of VICs */ |
232 | vic_init(VA_VIC0, IRQ_VIC0_BASE, vic_sources: vic0_valid, IRQ_VIC0_RESUME); |
233 | vic_init(VA_VIC1, IRQ_VIC1_BASE, vic_sources: vic1_valid, IRQ_VIC1_RESUME); |
234 | } |
235 | |
236 | #define eint_offset(irq) ((irq) - IRQ_EINT(0)) |
237 | #define eint_irq_to_bit(irq) ((u32)(1 << eint_offset(irq))) |
238 | |
239 | static inline void s3c_irq_eint_mask(struct irq_data *data) |
240 | { |
241 | u32 mask; |
242 | |
243 | mask = __raw_readl(S3C64XX_EINT0MASK); |
244 | mask |= (u32)data->chip_data; |
245 | __raw_writel(val: mask, S3C64XX_EINT0MASK); |
246 | } |
247 | |
248 | static void s3c_irq_eint_unmask(struct irq_data *data) |
249 | { |
250 | u32 mask; |
251 | |
252 | mask = __raw_readl(S3C64XX_EINT0MASK); |
253 | mask &= ~((u32)data->chip_data); |
254 | __raw_writel(val: mask, S3C64XX_EINT0MASK); |
255 | } |
256 | |
257 | static inline void s3c_irq_eint_ack(struct irq_data *data) |
258 | { |
259 | __raw_writel(val: (u32)data->chip_data, S3C64XX_EINT0PEND); |
260 | } |
261 | |
262 | static void s3c_irq_eint_maskack(struct irq_data *data) |
263 | { |
264 | /* compiler should in-line these */ |
265 | s3c_irq_eint_mask(data); |
266 | s3c_irq_eint_ack(data); |
267 | } |
268 | |
269 | static int s3c_irq_eint_set_type(struct irq_data *data, unsigned int type) |
270 | { |
271 | int offs = eint_offset(data->irq); |
272 | int pin, pin_val; |
273 | int shift; |
274 | u32 ctrl, mask; |
275 | u32 newvalue = 0; |
276 | void __iomem *reg; |
277 | |
278 | if (offs > 27) |
279 | return -EINVAL; |
280 | |
281 | if (offs <= 15) |
282 | reg = S3C64XX_EINT0CON0; |
283 | else |
284 | reg = S3C64XX_EINT0CON1; |
285 | |
286 | switch (type) { |
287 | case IRQ_TYPE_NONE: |
288 | printk(KERN_WARNING "No edge setting!\n" ); |
289 | break; |
290 | |
291 | case IRQ_TYPE_EDGE_RISING: |
292 | newvalue = S3C2410_EXTINT_RISEEDGE; |
293 | break; |
294 | |
295 | case IRQ_TYPE_EDGE_FALLING: |
296 | newvalue = S3C2410_EXTINT_FALLEDGE; |
297 | break; |
298 | |
299 | case IRQ_TYPE_EDGE_BOTH: |
300 | newvalue = S3C2410_EXTINT_BOTHEDGE; |
301 | break; |
302 | |
303 | case IRQ_TYPE_LEVEL_LOW: |
304 | newvalue = S3C2410_EXTINT_LOWLEV; |
305 | break; |
306 | |
307 | case IRQ_TYPE_LEVEL_HIGH: |
308 | newvalue = S3C2410_EXTINT_HILEV; |
309 | break; |
310 | |
311 | default: |
312 | printk(KERN_ERR "No such irq type %d" , type); |
313 | return -1; |
314 | } |
315 | |
316 | if (offs <= 15) |
317 | shift = (offs / 2) * 4; |
318 | else |
319 | shift = ((offs - 16) / 2) * 4; |
320 | mask = 0x7 << shift; |
321 | |
322 | ctrl = __raw_readl(addr: reg); |
323 | ctrl &= ~mask; |
324 | ctrl |= newvalue << shift; |
325 | __raw_writel(val: ctrl, addr: reg); |
326 | |
327 | /* set the GPIO pin appropriately */ |
328 | |
329 | if (offs < 16) { |
330 | pin = S3C64XX_GPN(offs); |
331 | pin_val = S3C_GPIO_SFN(2); |
332 | } else if (offs < 23) { |
333 | pin = S3C64XX_GPL(offs + 8 - 16); |
334 | pin_val = S3C_GPIO_SFN(3); |
335 | } else { |
336 | pin = S3C64XX_GPM(offs - 23); |
337 | pin_val = S3C_GPIO_SFN(3); |
338 | } |
339 | |
340 | s3c_gpio_cfgpin(pin, to: pin_val); |
341 | |
342 | return 0; |
343 | } |
344 | |
345 | static struct irq_chip s3c_irq_eint = { |
346 | .name = "s3c-eint" , |
347 | .irq_mask = s3c_irq_eint_mask, |
348 | .irq_unmask = s3c_irq_eint_unmask, |
349 | .irq_mask_ack = s3c_irq_eint_maskack, |
350 | .irq_ack = s3c_irq_eint_ack, |
351 | .irq_set_type = s3c_irq_eint_set_type, |
352 | .irq_set_wake = s3c_irqext_wake, |
353 | }; |
354 | |
355 | /* s3c_irq_demux_eint |
356 | * |
357 | * This function demuxes the IRQ from the group0 external interrupts, |
358 | * from IRQ_EINT(0) to IRQ_EINT(27). It is designed to be inlined into |
359 | * the specific handlers s3c_irq_demux_eintX_Y. |
360 | */ |
361 | static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end) |
362 | { |
363 | u32 status = __raw_readl(S3C64XX_EINT0PEND); |
364 | u32 mask = __raw_readl(S3C64XX_EINT0MASK); |
365 | unsigned int irq; |
366 | |
367 | status &= ~mask; |
368 | status >>= start; |
369 | status &= (1 << (end - start + 1)) - 1; |
370 | |
371 | for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) { |
372 | if (status & 1) |
373 | generic_handle_irq(irq); |
374 | |
375 | status >>= 1; |
376 | } |
377 | } |
378 | |
379 | static void s3c_irq_demux_eint0_3(struct irq_desc *desc) |
380 | { |
381 | s3c_irq_demux_eint(start: 0, end: 3); |
382 | } |
383 | |
384 | static void s3c_irq_demux_eint4_11(struct irq_desc *desc) |
385 | { |
386 | s3c_irq_demux_eint(start: 4, end: 11); |
387 | } |
388 | |
389 | static void s3c_irq_demux_eint12_19(struct irq_desc *desc) |
390 | { |
391 | s3c_irq_demux_eint(start: 12, end: 19); |
392 | } |
393 | |
394 | static void s3c_irq_demux_eint20_27(struct irq_desc *desc) |
395 | { |
396 | s3c_irq_demux_eint(start: 20, end: 27); |
397 | } |
398 | |
399 | static int __init s3c64xx_init_irq_eint(void) |
400 | { |
401 | int irq; |
402 | |
403 | /* On DT-enabled systems EINTs are handled by pinctrl-s3c64xx driver. */ |
404 | if (of_have_populated_dt() || !soc_is_s3c64xx()) |
405 | return -ENODEV; |
406 | |
407 | for (irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) { |
408 | irq_set_chip_and_handler(irq, chip: &s3c_irq_eint, handle: handle_level_irq); |
409 | irq_set_chip_data(irq, data: (void *)eint_irq_to_bit(irq)); |
410 | irq_clear_status_flags(irq, clr: IRQ_NOREQUEST); |
411 | } |
412 | |
413 | irq_set_chained_handler(IRQ_EINT0_3, handle: s3c_irq_demux_eint0_3); |
414 | irq_set_chained_handler(IRQ_EINT4_11, handle: s3c_irq_demux_eint4_11); |
415 | irq_set_chained_handler(IRQ_EINT12_19, handle: s3c_irq_demux_eint12_19); |
416 | irq_set_chained_handler(IRQ_EINT20_27, handle: s3c_irq_demux_eint20_27); |
417 | |
418 | return 0; |
419 | } |
420 | arch_initcall(s3c64xx_init_irq_eint); |
421 | |
422 | #ifndef CONFIG_COMPILE_TEST |
423 | #pragma message "The platform is deprecated and scheduled for removal. " \ |
424 | "Please reach to the maintainers of the platform " \ |
425 | "and linux-samsung-soc@vger.kernel.org if you still use it." \ |
426 | "Without such feedback, the platform will be removed after 2024." |
427 | #endif |
428 | |