aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/pwm/pwm-samsung.txt43
-rw-r--r--arch/arm/boot/dts/Makefile1
-rw-r--r--arch/arm/boot/dts/exynos4.dtsi8
-rw-r--r--arch/arm/boot/dts/exynos4210-universal_c210.dts352
-rw-r--r--arch/arm/boot/dts/exynos4210.dtsi1
-rw-r--r--arch/arm/boot/dts/exynos4212.dtsi9
-rw-r--r--arch/arm/boot/dts/exynos4412.dtsi9
-rw-r--r--arch/arm/mach-exynos/common.c35
-rw-r--r--arch/arm/mach-exynos/common.h7
-rw-r--r--arch/arm/plat-samsung/Kconfig4
-rw-r--r--drivers/clk/samsung/clk-exynos4.c93
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c1
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c1
-rw-r--r--drivers/clk/samsung/clk.h2
-rw-r--r--drivers/clocksource/Kconfig9
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/exynos_mct.c21
-rw-r--r--drivers/clocksource/samsung_pwm_timer.c494
-rw-r--r--drivers/irqchip/exynos-combiner.c125
-rw-r--r--include/clocksource/samsung_pwm.h36
20 files changed, 1098 insertions, 154 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-samsung.txt b/Documentation/devicetree/bindings/pwm/pwm-samsung.txt
new file mode 100644
index 000000000000..ac67c687a327
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-samsung.txt
@@ -0,0 +1,43 @@
1* Samsung PWM timers
2
3Samsung SoCs contain PWM timer blocks which can be used for system clock source
4and clock event timers, as well as to drive SoC outputs with PWM signal. Each
5PWM timer block provides 5 PWM channels (not all of them can drive physical
6outputs - see SoC and board manual).
7
8Be aware that the clocksource driver supports only uniprocessor systems.
9
10Required properties:
11- compatible : should be one of following:
12 samsung,s3c2410-pwm - for 16-bit timers present on S3C24xx SoCs
13 samsung,s3c6400-pwm - for 32-bit timers present on S3C64xx SoCs
14 samsung,s5p6440-pwm - for 32-bit timers present on S5P64x0 SoCs
15 samsung,s5pc100-pwm - for 32-bit timers present on S5PC100, S5PV210,
16 Exynos4210 rev0 SoCs
17 samsung,exynos4210-pwm - for 32-bit timers present on Exynos4210,
18 Exynos4x12 and Exynos5250 SoCs
19- reg: base address and size of register area
20- interrupts: list of timer interrupts (one interrupt per timer, starting at
21 timer 0)
22- #pwm-cells: number of cells used for PWM specifier - must be 3
23 the specifier format is as follows:
24 - phandle to PWM controller node
25 - index of PWM channel (from 0 to 4)
26 - PWM signal period in nanoseconds
27 - bitmask of optional PWM flags:
28 0x1 - invert PWM signal
29
30Optional properties:
31- samsung,pwm-outputs: list of PWM channels used as PWM outputs on particular
32 platform - an array of up to 5 elements being indices of PWM channels
33 (from 0 to 4), the order does not matter.
34
35Example:
36 pwm@7f006000 {
37 compatible = "samsung,s3c6400-pwm";
38 reg = <0x7f006000 0x1000>;
39 interrupt-parent = <&vic0>;
40 interrupts = <23>, <24>, <25>, <27>, <28>;
41 samsung,pwm-outputs = <0>, <1>;
42 #pwm-cells = <3>;
43 }
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 5f1f4dfd706e..8562af4fe8fd 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -49,6 +49,7 @@ dtb-$(CONFIG_ARCH_DOVE) += dove-cm-a510.dtb \
49dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ 49dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
50 exynos4210-smdkv310.dtb \ 50 exynos4210-smdkv310.dtb \
51 exynos4210-trats.dtb \ 51 exynos4210-trats.dtb \
52 exynos4210-universal_c210.dtb \
52 exynos4412-odroidx.dtb \ 53 exynos4412-odroidx.dtb \
53 exynos4412-smdk4412.dtb \ 54 exynos4412-smdk4412.dtb \
54 exynos4412-origen.dtb \ 55 exynos4412-origen.dtb \
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 7cfbbd3b7732..359694c78918 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -336,6 +336,14 @@
336 status = "disabled"; 336 status = "disabled";
337 }; 337 };
338 338
339 pwm@139D0000 {
340 compatible = "samsung,exynos4210-pwm";
341 reg = <0x139D0000 0x1000>;
342 interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>;
343 #pwm-cells = <2>;
344 status = "disabled";
345 };
346
339 amba { 347 amba {
340 #address-cells = <1>; 348 #address-cells = <1>;
341 #size-cells = <1>; 349 #size-cells = <1>;
diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts
new file mode 100644
index 000000000000..345cdb51dcb7
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts
@@ -0,0 +1,352 @@
1/*
2 * Samsung's Exynos4210 based Universal C210 board device tree source
3 *
4 * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com
6 *
7 * Device tree source file for Samsung's Universal C210 board which is based on
8 * Samsung's Exynos4210 rev0 SoC.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15/dts-v1/;
16/include/ "exynos4210.dtsi"
17
18/ {
19 model = "Samsung Universal C210 based on Exynos4210 rev0";
20 compatible = "samsung,universal_c210", "samsung,exynos4210";
21
22 memory {
23 reg = <0x40000000 0x10000000
24 0x50000000 0x10000000>;
25 };
26
27 chosen {
28 bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
29 };
30
31 mct@10050000 {
32 compatible = "none";
33 };
34
35 fixed-rate-clocks {
36 xxti {
37 compatible = "samsung,clock-xxti";
38 clock-frequency = <0>;
39 };
40
41 xusbxti {
42 compatible = "samsung,clock-xusbxti";
43 clock-frequency = <24000000>;
44 };
45 };
46
47 vemmc_reg: voltage-regulator {
48 compatible = "regulator-fixed";
49 regulator-name = "VMEM_VDD_2_8V";
50 regulator-min-microvolt = <2800000>;
51 regulator-max-microvolt = <2800000>;
52 gpio = <&gpe1 3 0>;
53 enable-active-high;
54 };
55
56 sdhci_emmc: sdhci@12510000 {
57 bus-width = <8>;
58 non-removable;
59 pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus8>;
60 pinctrl-names = "default";
61 vmmc-supply = <&vemmc_reg>;
62 status = "okay";
63 };
64
65 serial@13800000 {
66 status = "okay";
67 };
68
69 serial@13810000 {
70 status = "okay";
71 };
72
73 serial@13820000 {
74 status = "okay";
75 };
76
77 serial@13830000 {
78 status = "okay";
79 };
80
81 gpio-keys {
82 compatible = "gpio-keys";
83
84 vol-up-key {
85 gpios = <&gpx2 0 1>;
86 linux,code = <115>;
87 label = "volume up";
88 debounce-interval = <1>;
89 };
90
91 vol-down-key {
92 gpios = <&gpx2 1 1>;
93 linux,code = <114>;
94 label = "volume down";
95 debounce-interval = <1>;
96 };
97
98 config-key {
99 gpios = <&gpx2 2 1>;
100 linux,code = <171>;
101 label = "config";
102 debounce-interval = <1>;
103 gpio-key,wakeup;
104 };
105
106 camera-key {
107 gpios = <&gpx2 3 1>;
108 linux,code = <212>;
109 label = "camera";
110 debounce-interval = <1>;
111 };
112
113 power-key {
114 gpios = <&gpx2 7 1>;
115 linux,code = <116>;
116 label = "power";
117 debounce-interval = <1>;
118 gpio-key,wakeup;
119 };
120
121 ok-key {
122 gpios = <&gpx3 5 1>;
123 linux,code = <352>;
124 label = "ok";
125 debounce-interval = <1>;
126 };
127 };
128
129 tsp_reg: voltage-regulator {
130 compatible = "regulator-fixed";
131 regulator-name = "TSP_2_8V";
132 regulator-min-microvolt = <2800000>;
133 regulator-max-microvolt = <2800000>;
134 gpio = <&gpe2 3 0>;
135 enable-active-high;
136 };
137
138 i2c@13890000 {
139 samsung,i2c-sda-delay = <100>;
140 samsung,i2c-slave-addr = <0x10>;
141 samsung,i2c-max-bus-freq = <100000>;
142 pinctrl-0 = <&i2c3_bus>;
143 pinctrl-names = "default";
144 status = "okay";
145
146 tsp@4a {
147 /* TBD: Atmel maXtouch touchscreen */
148 reg = <0x4a>;
149 };
150 };
151
152 i2c@138B0000 {
153 samsung,i2c-sda-delay = <100>;
154 samsung,i2c-slave-addr = <0x10>;
155 samsung,i2c-max-bus-freq = <100000>;
156 pinctrl-0 = <&i2c5_bus>;
157 pinctrl-names = "default";
158 status = "okay";
159
160 vdd_arm_reg: pmic@60 {
161 compatible = "maxim,max8952";
162 reg = <0x60>;
163
164 max8952,vid-gpios = <&gpx0 3 0>, <&gpx0 4 0>;
165 max8952,default-mode = <0>;
166 max8952,dvs-mode-microvolt = <1250000>, <1200000>,
167 <1050000>, <950000>;
168 max8952,sync-freq = <0>;
169 max8952,ramp-speed = <0>;
170
171 regulator-name = "vdd_arm";
172 regulator-min-microvolt = <770000>;
173 regulator-max-microvolt = <1400000>;
174 regulator-always-on;
175 regulator-boot-on;
176 };
177
178 pmic@66 {
179 compatible = "national,lp3974";
180 reg = <0x66>;
181
182 max8998,pmic-buck1-default-dvs-idx = <0>;
183 max8998,pmic-buck1-dvs-gpios = <&gpx0 5 0>,
184 <&gpx0 6 0>;
185 max8998,pmic-buck1-dvs-voltage = <1100000>, <1000000>,
186 <1100000>, <1000000>;
187
188 max8998,pmic-buck2-default-dvs-idx = <0>;
189 max8998,pmic-buck2-dvs-gpio = <&gpe2 0 0>;
190 max8998,pmic-buck2-dvs-voltage = <1200000>, <1100000>;
191
192 regulators {
193 ldo2_reg: LDO2 {
194 regulator-name = "VALIVE_1.2V";
195 regulator-min-microvolt = <1200000>;
196 regulator-max-microvolt = <1200000>;
197 regulator-always-on;
198 };
199
200 ldo3_reg: LDO3 {
201 regulator-name = "VUSB+MIPI_1.1V";
202 regulator-min-microvolt = <1100000>;
203 regulator-max-microvolt = <1100000>;
204 };
205
206 ldo4_reg: LDO4 {
207 regulator-name = "VADC_3.3V";
208 regulator-min-microvolt = <3300000>;
209 regulator-max-microvolt = <3300000>;
210 };
211
212 ldo5_reg: LDO5 {
213 regulator-name = "VTF_2.8V";
214 regulator-min-microvolt = <2800000>;
215 regulator-max-microvolt = <2800000>;
216 };
217
218 ldo6_reg: LDO6 {
219 regulator-name = "LDO6";
220 regulator-min-microvolt = <2000000>;
221 regulator-max-microvolt = <2000000>;
222 };
223
224 ldo7_reg: LDO7 {
225 regulator-name = "VLCD+VMIPI_1.8V";
226 regulator-min-microvolt = <1800000>;
227 regulator-max-microvolt = <1800000>;
228 };
229
230 ldo8_reg: LDO8 {
231 regulator-name = "VUSB+VDAC_3.3V";
232 regulator-min-microvolt = <3300000>;
233 regulator-max-microvolt = <3300000>;
234 };
235
236 ldo9_reg: LDO9 {
237 regulator-name = "VCC_2.8V";
238 regulator-min-microvolt = <2800000>;
239 regulator-max-microvolt = <2800000>;
240 regulator-always-on;
241 };
242
243 ldo10_reg: LDO10 {
244 regulator-name = "VPLL_1.1V";
245 regulator-min-microvolt = <1100000>;
246 regulator-max-microvolt = <1100000>;
247 regulator-boot-on;
248 regulator-always-on;
249 };
250
251 ldo11_reg: LDO11 {
252 regulator-name = "CAM_AF_3.3V";
253 regulator-min-microvolt = <3300000>;
254 regulator-max-microvolt = <3300000>;
255 };
256
257 ldo12_reg: LDO12 {
258 regulator-name = "PS_2.8V";
259 regulator-min-microvolt = <2800000>;
260 regulator-max-microvolt = <2800000>;
261 };
262
263 ldo13_reg: LDO13 {
264 regulator-name = "VHIC_1.2V";
265 regulator-min-microvolt = <1200000>;
266 regulator-max-microvolt = <1200000>;
267 };
268
269 ldo14_reg: LDO14 {
270 regulator-name = "CAM_I_HOST_1.8V";
271 regulator-min-microvolt = <1800000>;
272 regulator-max-microvolt = <1800000>;
273 };
274
275 ldo15_reg: LDO15 {
276 regulator-name = "CAM_S_DIG+FM33_CORE_1.2V";
277 regulator-min-microvolt = <1200000>;
278 regulator-max-microvolt = <1200000>;
279 };
280
281 ldo16_reg: LDO16 {
282 regulator-name = "CAM_S_ANA_2.8V";
283 regulator-min-microvolt = <2800000>;
284 regulator-max-microvolt = <2800000>;
285 };
286
287 ldo17_reg: LDO17 {
288 regulator-name = "VCC_3.0V_LCD";
289 regulator-min-microvolt = <3000000>;
290 regulator-max-microvolt = <3000000>;
291 };
292
293 buck1_reg: BUCK1 {
294 regulator-name = "VINT_1.1V";
295 regulator-min-microvolt = <750000>;
296 regulator-max-microvolt = <1500000>;
297 regulator-boot-on;
298 regulator-always-on;
299 };
300
301 buck2_reg: BUCK2 {
302 regulator-name = "VG3D_1.1V";
303 regulator-min-microvolt = <750000>;
304 regulator-max-microvolt = <1500000>;
305 regulator-boot-on;
306 };
307
308 buck3_reg: BUCK3 {
309 regulator-name = "VCC_1.8V";
310 regulator-min-microvolt = <1800000>;
311 regulator-max-microvolt = <1800000>;
312 regulator-always-on;
313 };
314
315 buck4_reg: BUCK4 {
316 regulator-name = "VMEM_1.2V";
317 regulator-min-microvolt = <1200000>;
318 regulator-max-microvolt = <1200000>;
319 regulator-always-on;
320 };
321
322 ap32khz_reg: EN32KHz-AP {
323 regulator-name = "32KHz AP";
324 regulator-always-on;
325 };
326
327 cp32khz_reg: EN32KHz-CP {
328 regulator-name = "32KHz CP";
329 };
330
331 vichg_reg: ENVICHG {
332 regulator-name = "VICHG";
333 };
334
335 safeout1_reg: ESAFEOUT1 {
336 regulator-name = "SAFEOUT1";
337 regulator-always-on;
338 };
339
340 safeout2_reg: ESAFEOUT2 {
341 regulator-name = "SAFEOUT2";
342 regulator-boot-on;
343 };
344 };
345 };
346 };
347
348 pwm@139D0000 {
349 compatible = "samsung,s5p6440-pwm";
350 status = "okay";
351 };
352};
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 66e6b03bf35e..54710de82908 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -41,6 +41,7 @@
41 }; 41 };
42 42
43 combiner:interrupt-controller@10440000 { 43 combiner:interrupt-controller@10440000 {
44 samsung,combiner-nr = <16>;
44 interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>, 45 interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
45 <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>, 46 <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
46 <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>, 47 <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
diff --git a/arch/arm/boot/dts/exynos4212.dtsi b/arch/arm/boot/dts/exynos4212.dtsi
index 36d4299789ef..c0f60f49cea6 100644
--- a/arch/arm/boot/dts/exynos4212.dtsi
+++ b/arch/arm/boot/dts/exynos4212.dtsi
@@ -26,6 +26,15 @@
26 cpu-offset = <0x8000>; 26 cpu-offset = <0x8000>;
27 }; 27 };
28 28
29 interrupt-controller@10440000 {
30 samsung,combiner-nr = <18>;
31 interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
32 <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
33 <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
34 <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
35 <0 107 0>, <0 108 0>;
36 };
37
29 mct@10050000 { 38 mct@10050000 {
30 compatible = "samsung,exynos4412-mct"; 39 compatible = "samsung,exynos4412-mct";
31 reg = <0x10050000 0x800>; 40 reg = <0x10050000 0x800>;
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 7f428272fee6..270b389e0a1b 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -26,6 +26,15 @@
26 cpu-offset = <0x4000>; 26 cpu-offset = <0x4000>;
27 }; 27 };
28 28
29 interrupt-controller@10440000 {
30 samsung,combiner-nr = <20>;
31 interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
32 <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
33 <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
34 <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
35 <0 107 0>, <0 108 0>, <0 48 0>, <0 42 0>;
36 };
37
29 mct@10050000 { 38 mct@10050000 {
30 compatible = "samsung,exynos4412-mct"; 39 compatible = "samsung,exynos4412-mct";
31 reg = <0x10050000 0x800>; 40 reg = <0x10050000 0x800>;
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index d126f26dbbf1..745e304ad0de 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -452,13 +452,26 @@ void __init exynos_init_time(void)
452 } else { 452 } else {
453 /* todo: remove after migrating legacy E4 platforms to dt */ 453 /* todo: remove after migrating legacy E4 platforms to dt */
454#ifdef CONFIG_ARCH_EXYNOS4 454#ifdef CONFIG_ARCH_EXYNOS4
455 exynos4_clk_init(NULL); 455 exynos4_clk_init(NULL, !soc_is_exynos4210(), S5P_VA_CMU, readl(S5P_VA_CHIPID + 8) & 1);
456 exynos4_clk_register_fixed_ext(xxti_f, xusbxti_f); 456 exynos4_clk_register_fixed_ext(xxti_f, xusbxti_f);
457#endif 457#endif
458 mct_init(); 458 mct_init(S5P_VA_SYSTIMER, EXYNOS4_IRQ_MCT_G0, EXYNOS4_IRQ_MCT_L0, EXYNOS4_IRQ_MCT_L1);
459 } 459 }
460} 460}
461 461
462static unsigned int max_combiner_nr(void)
463{
464 if (soc_is_exynos5250())
465 return EXYNOS5_MAX_COMBINER_NR;
466 else if (soc_is_exynos4412())
467 return EXYNOS4412_MAX_COMBINER_NR;
468 else if (soc_is_exynos4212())
469 return EXYNOS4212_MAX_COMBINER_NR;
470 else
471 return EXYNOS4210_MAX_COMBINER_NR;
472}
473
474
462void __init exynos4_init_irq(void) 475void __init exynos4_init_irq(void)
463{ 476{
464 unsigned int gic_bank_offset; 477 unsigned int gic_bank_offset;
@@ -473,14 +486,8 @@ void __init exynos4_init_irq(void)
473#endif 486#endif
474 487
475 if (!of_have_populated_dt()) 488 if (!of_have_populated_dt())
476 combiner_init(S5P_VA_COMBINER_BASE, NULL); 489 combiner_init(S5P_VA_COMBINER_BASE, NULL,
477 490 max_combiner_nr(), COMBINER_IRQ(0, 0));
478 /*
479 * The parameters of s5p_init_irq() are for VIC init.
480 * Theses parameters should be NULL and 0 because EXYNOS4
481 * uses GIC instead of VIC.
482 */
483 s5p_init_irq(NULL, 0);
484 491
485 gic_arch_extn.irq_set_wake = s3c_irq_wake; 492 gic_arch_extn.irq_set_wake = s3c_irq_wake;
486} 493}
@@ -490,14 +497,6 @@ void __init exynos5_init_irq(void)
490#ifdef CONFIG_OF 497#ifdef CONFIG_OF
491 irqchip_init(); 498 irqchip_init();
492#endif 499#endif
493 /*
494 * The parameters of s5p_init_irq() are for VIC init.
495 * Theses parameters should be NULL and 0 because EXYNOS4
496 * uses GIC instead of VIC.
497 */
498 if (!of_machine_is_compatible("samsung,exynos5440"))
499 s5p_init_irq(NULL, 0);
500
501 gic_arch_extn.irq_set_wake = s3c_irq_wake; 500 gic_arch_extn.irq_set_wake = s3c_irq_wake;
502} 501}
503 502
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index b17448c1a164..60dd35cc01a6 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -14,7 +14,7 @@
14 14
15#include <linux/of.h> 15#include <linux/of.h>
16 16
17extern void mct_init(void); 17void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1);
18void exynos_init_time(void); 18void exynos_init_time(void);
19extern unsigned long xxti_f, xusbxti_f; 19extern unsigned long xxti_f, xusbxti_f;
20 20
@@ -27,7 +27,7 @@ void exynos5_restart(char mode, const char *cmd);
27void exynos_init_late(void); 27void exynos_init_late(void);
28 28
29/* ToDo: remove these after migrating legacy exynos4 platforms to dt */ 29/* ToDo: remove these after migrating legacy exynos4 platforms to dt */
30void exynos4_clk_init(struct device_node *np); 30void exynos4_clk_init(struct device_node *np, int is_exynos4210, void __iomem *reg_base, unsigned long xom);
31void exynos4_clk_register_fixed_ext(unsigned long, unsigned long); 31void exynos4_clk_register_fixed_ext(unsigned long, unsigned long);
32 32
33void exynos_firmware_init(void); 33void exynos_firmware_init(void);
@@ -71,7 +71,8 @@ void exynos4212_register_clocks(void);
71#endif 71#endif
72 72
73struct device_node; 73struct device_node;
74void combiner_init(void __iomem *combiner_base, struct device_node *np); 74void combiner_init(void __iomem *combiner_base, struct device_node *np,
75 unsigned int max_nr, int irq_base);
75 76
76extern struct smp_operations exynos_smp_ops; 77extern struct smp_operations exynos_smp_ops;
77 78
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index 54d186106f9f..f8ed2de0a678 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -93,9 +93,9 @@ config SAMSUNG_IRQ_VIC_TIMER
93 Internal configuration to build the VIC timer interrupt code. 93 Internal configuration to build the VIC timer interrupt code.
94 94
95config S5P_IRQ 95config S5P_IRQ
96 def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS) 96 def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210)
97 help 97 help
98 Support common interrup part for ARCH_S5P and ARCH_EXYNOS SoCs 98 Support common interrupt part for ARCH_S5P SoCs
99 99
100config S5P_EXT_INT 100config S5P_EXT_INT
101 bool 101 bool
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 71046694d9dd..d0940e69d034 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -16,7 +16,6 @@
16#include <linux/of.h> 16#include <linux/of.h>
17#include <linux/of_address.h> 17#include <linux/of_address.h>
18 18
19#include <plat/cpu.h>
20#include "clk.h" 19#include "clk.h"
21#include "clk-pll.h" 20#include "clk-pll.h"
22 21
@@ -910,16 +909,6 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = {
910 CLK_IGNORE_UNUSED, 0), 909 CLK_IGNORE_UNUSED, 0),
911}; 910};
912 911
913#ifdef CONFIG_OF
914static struct of_device_id exynos4_clk_ids[] __initdata = {
915 { .compatible = "samsung,exynos4210-clock",
916 .data = (void *)EXYNOS4210, },
917 { .compatible = "samsung,exynos4412-clock",
918 .data = (void *)EXYNOS4X12, },
919 { },
920};
921#endif
922
923/* 912/*
924 * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit 913 * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit
925 * resides in chipid register space, outside of the clock controller memory 914 * resides in chipid register space, outside of the clock controller memory
@@ -927,33 +916,40 @@ static struct of_device_id exynos4_clk_ids[] __initdata = {
927 * controller is first remapped and the value of XOM[0] bit is read to 916 * controller is first remapped and the value of XOM[0] bit is read to
928 * determine the parent clock. 917 * determine the parent clock.
929 */ 918 */
930static void __init exynos4_clk_register_finpll(void) 919static unsigned long exynos4_get_xom(void)
931{ 920{
932 struct samsung_fixed_rate_clock fclk; 921 unsigned long xom = 0;
922 void __iomem *chipid_base;
933 struct device_node *np; 923 struct device_node *np;
934 struct clk *clk;
935 void __iomem *chipid_base = S5P_VA_CHIPID;
936 unsigned long xom, finpll_f = 24000000;
937 char *parent_name;
938 924
939 np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid"); 925 np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid");
940 if (np) 926 if (np) {
941 chipid_base = of_iomap(np, 0); 927 chipid_base = of_iomap(np, 0);
942 928
943 if (chipid_base) { 929 if (chipid_base)
944 xom = readl(chipid_base + 8); 930 xom = readl(chipid_base + 8);
945 parent_name = xom & 1 ? "xusbxti" : "xxti"; 931
946 clk = clk_get(NULL, parent_name); 932 iounmap(chipid_base);
947 if (IS_ERR(clk)) { 933 }
948 pr_err("%s: failed to lookup parent clock %s, assuming " 934
949 "fin_pll clock frequency is 24MHz\n", __func__, 935 return xom;
950 parent_name); 936}
951 } else { 937
952 finpll_f = clk_get_rate(clk); 938static void __init exynos4_clk_register_finpll(unsigned long xom)
953 } 939{
940 struct samsung_fixed_rate_clock fclk;
941 struct clk *clk;
942 unsigned long finpll_f = 24000000;
943 char *parent_name;
944
945 parent_name = xom & 1 ? "xusbxti" : "xxti";
946 clk = clk_get(NULL, parent_name);
947 if (IS_ERR(clk)) {
948 pr_err("%s: failed to lookup parent clock %s, assuming "
949 "fin_pll clock frequency is 24MHz\n", __func__,
950 parent_name);
954 } else { 951 } else {
955 pr_err("%s: failed to map chipid registers, assuming " 952 finpll_f = clk_get_rate(clk);
956 "fin_pll clock frequency is 24MHz\n", __func__);
957 } 953 }
958 954
959 fclk.id = fin_pll; 955 fclk.id = fin_pll;
@@ -963,8 +959,6 @@ static void __init exynos4_clk_register_finpll(void)
963 fclk.fixed_rate = finpll_f; 959 fclk.fixed_rate = finpll_f;
964 samsung_clk_register_fixed_rate(&fclk, 1); 960 samsung_clk_register_fixed_rate(&fclk, 1);
965 961
966 if (np)
967 iounmap(chipid_base);
968} 962}
969 963
970/* 964/*
@@ -988,28 +982,14 @@ static __initdata struct of_device_id ext_clk_match[] = {
988}; 982};
989 983
990/* register exynos4 clocks */ 984/* register exynos4 clocks */
991void __init exynos4_clk_init(struct device_node *np) 985void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom)
992{ 986{
993 void __iomem *reg_base;
994 struct clk *apll, *mpll, *epll, *vpll; 987 struct clk *apll, *mpll, *epll, *vpll;
995 u32 exynos4_soc;
996 988
997 if (np) { 989 if (np) {
998 const struct of_device_id *match;
999 match = of_match_node(exynos4_clk_ids, np);
1000 exynos4_soc = (u32)match->data;
1001
1002 reg_base = of_iomap(np, 0); 990 reg_base = of_iomap(np, 0);
1003 if (!reg_base) 991 if (!reg_base)
1004 panic("%s: failed to map registers\n", __func__); 992 panic("%s: failed to map registers\n", __func__);
1005 } else {
1006 reg_base = S5P_VA_CMU;
1007 if (soc_is_exynos4210())
1008 exynos4_soc = EXYNOS4210;
1009 else if (soc_is_exynos4212() || soc_is_exynos4412())
1010 exynos4_soc = EXYNOS4X12;
1011 else
1012 panic("%s: unable to determine soc\n", __func__);
1013 } 993 }
1014 994
1015 if (exynos4_soc == EXYNOS4210) 995 if (exynos4_soc == EXYNOS4210)
@@ -1026,7 +1006,7 @@ void __init exynos4_clk_init(struct device_node *np)
1026 ARRAY_SIZE(exynos4_fixed_rate_ext_clks), 1006 ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
1027 ext_clk_match); 1007 ext_clk_match);
1028 1008
1029 exynos4_clk_register_finpll(); 1009 exynos4_clk_register_finpll(xom);
1030 1010
1031 if (exynos4_soc == EXYNOS4210) { 1011 if (exynos4_soc == EXYNOS4210) {
1032 apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll", 1012 apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll",
@@ -1087,5 +1067,16 @@ void __init exynos4_clk_init(struct device_node *np)
1087 _get_rate("sclk_epll"), _get_rate("sclk_vpll"), 1067 _get_rate("sclk_epll"), _get_rate("sclk_vpll"),
1088 _get_rate("arm_clk")); 1068 _get_rate("arm_clk"));
1089} 1069}
1090CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4_clk_init); 1070
1091CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4_clk_init); 1071
1072static void __init exynos4210_clk_init(struct device_node *np)
1073{
1074 exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
1075}
1076CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
1077
1078static void __init exynos4412_clk_init(struct device_node *np)
1079{
1080 exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
1081}
1082CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index bb54606ff035..5c97e75924a8 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -16,7 +16,6 @@
16#include <linux/of.h> 16#include <linux/of.h>
17#include <linux/of_address.h> 17#include <linux/of_address.h>
18 18
19#include <plat/cpu.h>
20#include "clk.h" 19#include "clk.h"
21#include "clk-pll.h" 20#include "clk-pll.h"
22 21
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
index a0a094c06f19..7d5434167a96 100644
--- a/drivers/clk/samsung/clk-exynos5440.c
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -15,7 +15,6 @@
15#include <linux/of.h> 15#include <linux/of.h>
16#include <linux/of_address.h> 16#include <linux/of_address.h>
17 17
18#include <plat/cpu.h>
19#include "clk.h" 18#include "clk.h"
20#include "clk-pll.h" 19#include "clk-pll.h"
21 20
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 10b2111f0c0f..e4ad6ea9aa76 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -20,8 +20,6 @@
20#include <linux/of.h> 20#include <linux/of.h>
21#include <linux/of_address.h> 21#include <linux/of_address.h>
22 22
23#include <mach/map.h>
24
25/** 23/**
26 * struct samsung_clock_alias: information about mux clock 24 * struct samsung_clock_alias: information about mux clock
27 * @id: platform specific id of the clock. 25 * @id: platform specific id of the clock.
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index c20de4a85cbd..f151c6cf27c3 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -76,3 +76,12 @@ config CLKSRC_EXYNOS_MCT
76 def_bool y if ARCH_EXYNOS 76 def_bool y if ARCH_EXYNOS
77 help 77 help
78 Support for Multi Core Timer controller on Exynos SoCs. 78 Support for Multi Core Timer controller on Exynos SoCs.
79
80config CLKSRC_SAMSUNG_PWM
81 bool
82 select CLKSRC_MMIO
83 help
84 This is a new clocksource driver for the PWM timer found in
85 Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
86 for all devicetree enabled platforms. This driver will be
87 needed only on systems that do not have the Exynos MCT available.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index caacdb63aff9..8d979c72aa94 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
25obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o 25obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o 26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o 27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
28obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
28 29
29obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o 30obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
30obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o 31obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 13a9e4923a03..662fcc065821 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -25,11 +25,6 @@
25#include <linux/clocksource.h> 25#include <linux/clocksource.h>
26 26
27#include <asm/localtimer.h> 27#include <asm/localtimer.h>
28
29#include <plat/cpu.h>
30
31#include <mach/map.h>
32#include <mach/irqs.h>
33#include <asm/mach/time.h> 28#include <asm/mach/time.h>
34 29
35#define EXYNOS4_MCTREG(x) (x) 30#define EXYNOS4_MCTREG(x) (x)
@@ -510,18 +505,14 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
510#endif /* CONFIG_LOCAL_TIMERS */ 505#endif /* CONFIG_LOCAL_TIMERS */
511} 506}
512 507
513void __init mct_init(void) 508void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
514{ 509{
515 if (soc_is_exynos4210()) { 510 mct_irqs[MCT_G0_IRQ] = irq_g0;
516 mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0; 511 mct_irqs[MCT_L0_IRQ] = irq_l0;
517 mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0; 512 mct_irqs[MCT_L1_IRQ] = irq_l1;
518 mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1; 513 mct_int_type = MCT_INT_SPI;
519 mct_int_type = MCT_INT_SPI;
520 } else {
521 panic("unable to determine mct controller type\n");
522 }
523 514
524 exynos4_timer_resources(NULL, S5P_VA_SYSTIMER); 515 exynos4_timer_resources(NULL, base);
525 exynos4_clocksource_init(); 516 exynos4_clocksource_init();
526 exynos4_clockevent_init(); 517 exynos4_clockevent_init();
527} 518}
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
new file mode 100644
index 000000000000..0234c8d2c8f2
--- /dev/null
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -0,0 +1,494 @@
1/*
2 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com/
4 *
5 * samsung - Common hr-timer support (s3c and s5p)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12#include <linux/interrupt.h>
13#include <linux/irq.h>
14#include <linux/err.h>
15#include <linux/clk.h>
16#include <linux/clockchips.h>
17#include <linux/list.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_address.h>
21#include <linux/of_irq.h>
22#include <linux/platform_device.h>
23#include <linux/slab.h>
24
25#include <clocksource/samsung_pwm.h>
26
27#include <asm/sched_clock.h>
28
29/*
30 * Clocksource driver
31 */
32
33#define REG_TCFG0 0x00
34#define REG_TCFG1 0x04
35#define REG_TCON 0x08
36#define REG_TINT_CSTAT 0x44
37
38#define REG_TCNTB(chan) (0x0c + 12 * (chan))
39#define REG_TCMPB(chan) (0x10 + 12 * (chan))
40
41#define TCFG0_PRESCALER_MASK 0xff
42#define TCFG0_PRESCALER1_SHIFT 8
43
44#define TCFG1_SHIFT(x) ((x) * 4)
45#define TCFG1_MUX_MASK 0xf
46
47#define TCON_START(chan) (1 << (4 * (chan) + 0))
48#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))
49#define TCON_INVERT(chan) (1 << (4 * (chan) + 2))
50#define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))
51
52DEFINE_SPINLOCK(samsung_pwm_lock);
53EXPORT_SYMBOL(samsung_pwm_lock);
54
55struct samsung_pwm_clocksource {
56 void __iomem *base;
57 unsigned int irq[SAMSUNG_PWM_NUM];
58 struct samsung_pwm_variant variant;
59
60 struct clk *timerclk;
61
62 unsigned int event_id;
63 unsigned int source_id;
64 unsigned int tcnt_max;
65 unsigned int tscaler_div;
66 unsigned int tdiv;
67
68 unsigned long clock_count_per_tick;
69};
70
71static struct samsung_pwm_clocksource pwm;
72
73static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)
74{
75 unsigned long flags;
76 u8 shift = 0;
77 u32 reg;
78
79 if (channel >= 2)
80 shift = TCFG0_PRESCALER1_SHIFT;
81
82 spin_lock_irqsave(&samsung_pwm_lock, flags);
83
84 reg = readl(pwm.base + REG_TCFG0);
85 reg &= ~(TCFG0_PRESCALER_MASK << shift);
86 reg |= (prescale - 1) << shift;
87 writel(reg, pwm.base + REG_TCFG0);
88
89 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
90}
91
92static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)
93{
94 u8 shift = TCFG1_SHIFT(channel);
95 unsigned long flags;
96 u32 reg;
97 u8 bits;
98
99 bits = (fls(divisor) - 1) - pwm.variant.div_base;
100
101 spin_lock_irqsave(&samsung_pwm_lock, flags);
102
103 reg = readl(pwm.base + REG_TCFG1);
104 reg &= ~(TCFG1_MUX_MASK << shift);
105 reg |= bits << shift;
106 writel(reg, pwm.base + REG_TCFG1);
107
108 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
109}
110
111static void samsung_time_stop(unsigned int channel)
112{
113 unsigned long tcon;
114 unsigned long flags;
115
116 if (channel > 0)
117 ++channel;
118
119 spin_lock_irqsave(&samsung_pwm_lock, flags);
120
121 tcon = __raw_readl(pwm.base + REG_TCON);
122 tcon &= ~TCON_START(channel);
123 __raw_writel(tcon, pwm.base + REG_TCON);
124
125 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
126}
127
128static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
129{
130 unsigned long tcon;
131 unsigned long flags;
132 unsigned int tcon_chan = channel;
133
134 if (tcon_chan > 0)
135 ++tcon_chan;
136
137 spin_lock_irqsave(&samsung_pwm_lock, flags);
138
139 tcon = __raw_readl(pwm.base + REG_TCON);
140
141 tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));
142 tcon |= TCON_MANUALUPDATE(tcon_chan);
143
144 __raw_writel(tcnt, pwm.base + REG_TCNTB(channel));
145 __raw_writel(tcnt, pwm.base + REG_TCMPB(channel));
146 __raw_writel(tcon, pwm.base + REG_TCON);
147
148 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
149}
150
151static void samsung_time_start(unsigned int channel, bool periodic)
152{
153 unsigned long tcon;
154 unsigned long flags;
155
156 if (channel > 0)
157 ++channel;
158
159 spin_lock_irqsave(&samsung_pwm_lock, flags);
160
161 tcon = __raw_readl(pwm.base + REG_TCON);
162
163 tcon &= ~TCON_MANUALUPDATE(channel);
164 tcon |= TCON_START(channel);
165
166 if (periodic)
167 tcon |= TCON_AUTORELOAD(channel);
168 else
169 tcon &= ~TCON_AUTORELOAD(channel);
170
171 __raw_writel(tcon, pwm.base + REG_TCON);
172
173 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
174}
175
176static int samsung_set_next_event(unsigned long cycles,
177 struct clock_event_device *evt)
178{
179 /*
180 * This check is needed to account for internal rounding
181 * errors inside clockevents core, which might result in
182 * passing cycles = 0, which in turn would not generate any
183 * timer interrupt and hang the system.
184 *
185 * Another solution would be to set up the clockevent device
186 * with min_delta = 2, but this would unnecessarily increase
187 * the minimum sleep period.
188 */
189 if (!cycles)
190 cycles = 1;
191
192 samsung_time_setup(pwm.event_id, cycles);
193 samsung_time_start(pwm.event_id, false);
194
195 return 0;
196}
197
198static void samsung_timer_resume(void)
199{
200 /* event timer restart */
201 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
202 samsung_time_start(pwm.event_id, true);
203
204 /* source timer restart */
205 samsung_time_setup(pwm.source_id, pwm.tcnt_max);
206 samsung_time_start(pwm.source_id, true);
207}
208
209static void samsung_set_mode(enum clock_event_mode mode,
210 struct clock_event_device *evt)
211{
212 samsung_time_stop(pwm.event_id);
213
214 switch (mode) {
215 case CLOCK_EVT_MODE_PERIODIC:
216 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
217 samsung_time_start(pwm.event_id, true);
218 break;
219
220 case CLOCK_EVT_MODE_ONESHOT:
221 break;
222
223 case CLOCK_EVT_MODE_UNUSED:
224 case CLOCK_EVT_MODE_SHUTDOWN:
225 break;
226
227 case CLOCK_EVT_MODE_RESUME:
228 samsung_timer_resume();
229 break;
230 }
231}
232
233static struct clock_event_device time_event_device = {
234 .name = "samsung_event_timer",
235 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
236 .rating = 200,
237 .set_next_event = samsung_set_next_event,
238 .set_mode = samsung_set_mode,
239};
240
241static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)
242{
243 struct clock_event_device *evt = dev_id;
244
245 if (pwm.variant.has_tint_cstat) {
246 u32 mask = (1 << pwm.event_id);
247 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
248 }
249
250 evt->event_handler(evt);
251
252 return IRQ_HANDLED;
253}
254
255static struct irqaction samsung_clock_event_irq = {
256 .name = "samsung_time_irq",
257 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
258 .handler = samsung_clock_event_isr,
259 .dev_id = &time_event_device,
260};
261
262static void __init samsung_clockevent_init(void)
263{
264 unsigned long pclk;
265 unsigned long clock_rate;
266 unsigned int irq_number;
267
268 pclk = clk_get_rate(pwm.timerclk);
269
270 samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
271 samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
272
273 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
274 pwm.clock_count_per_tick = clock_rate / HZ;
275
276 time_event_device.cpumask = cpumask_of(0);
277 clockevents_config_and_register(&time_event_device,
278 clock_rate, 1, pwm.tcnt_max);
279
280 irq_number = pwm.irq[pwm.event_id];
281 setup_irq(irq_number, &samsung_clock_event_irq);
282
283 if (pwm.variant.has_tint_cstat) {
284 u32 mask = (1 << pwm.event_id);
285 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
286 }
287}
288
289static void __iomem *samsung_timer_reg(void)
290{
291 switch (pwm.source_id) {
292 case 0:
293 case 1:
294 case 2:
295 case 3:
296 return pwm.base + pwm.source_id * 0x0c + 0x14;
297
298 case 4:
299 return pwm.base + 0x40;
300
301 default:
302 BUG();
303 }
304}
305
306/*
307 * Override the global weak sched_clock symbol with this
308 * local implementation which uses the clocksource to get some
309 * better resolution when scheduling the kernel. We accept that
310 * this wraps around for now, since it is just a relative time
311 * stamp. (Inspired by U300 implementation.)
312 */
313static u32 notrace samsung_read_sched_clock(void)
314{
315 void __iomem *reg = samsung_timer_reg();
316
317 if (!reg)
318 return 0;
319
320 return ~__raw_readl(reg);
321}
322
323static void __init samsung_clocksource_init(void)
324{
325 void __iomem *reg = samsung_timer_reg();
326 unsigned long pclk;
327 unsigned long clock_rate;
328 int ret;
329
330 pclk = clk_get_rate(pwm.timerclk);
331
332 samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
333 samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
334
335 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
336
337 samsung_time_setup(pwm.source_id, pwm.tcnt_max);
338 samsung_time_start(pwm.source_id, true);
339
340 setup_sched_clock(samsung_read_sched_clock,
341 pwm.variant.bits, clock_rate);
342
343 ret = clocksource_mmio_init(reg, "samsung_clocksource_timer",
344 clock_rate, 250, pwm.variant.bits,
345 clocksource_mmio_readl_down);
346 if (ret)
347 panic("samsung_clocksource_timer: can't register clocksource\n");
348}
349
350static void __init samsung_timer_resources(void)
351{
352 pwm.timerclk = clk_get(NULL, "timers");
353 if (IS_ERR(pwm.timerclk))
354 panic("failed to get timers clock for timer");
355
356 clk_prepare_enable(pwm.timerclk);
357
358 pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;
359 if (pwm.variant.bits == 16) {
360 pwm.tscaler_div = 25;
361 pwm.tdiv = 2;
362 } else {
363 pwm.tscaler_div = 2;
364 pwm.tdiv = 1;
365 }
366}
367
368/*
369 * PWM master driver
370 */
371static void __init _samsung_pwm_clocksource_init(void)
372{
373 u8 mask;
374 int channel;
375
376 mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);
377 channel = fls(mask) - 1;
378 if (channel < 0)
379 panic("failed to find PWM channel for clocksource");
380 pwm.source_id = channel;
381
382 mask &= ~(1 << channel);
383 channel = fls(mask) - 1;
384 if (channel < 0)
385 panic("failed to find PWM channel for clock event");
386 pwm.event_id = channel;
387
388 samsung_timer_resources();
389 samsung_clockevent_init();
390 samsung_clocksource_init();
391}
392
393void __init samsung_pwm_clocksource_init(void __iomem *base,
394 unsigned int *irqs, struct samsung_pwm_variant *variant)
395{
396 pwm.base = base;
397 memcpy(&pwm.variant, variant, sizeof(pwm.variant));
398 memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));
399
400 _samsung_pwm_clocksource_init();
401}
402
403#ifdef CONFIG_CLKSRC_OF
404static void __init samsung_pwm_alloc(struct device_node *np,
405 const struct samsung_pwm_variant *variant)
406{
407 struct resource res;
408 struct property *prop;
409 const __be32 *cur;
410 u32 val;
411 int i;
412
413 memcpy(&pwm.variant, variant, sizeof(pwm.variant));
414 for (i = 0; i < SAMSUNG_PWM_NUM; ++i)
415 pwm.irq[i] = irq_of_parse_and_map(np, i);
416
417 of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
418 if (val >= SAMSUNG_PWM_NUM) {
419 pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
420 __func__);
421 continue;
422 }
423 pwm.variant.output_mask |= 1 << val;
424 }
425
426 of_address_to_resource(np, 0, &res);
427 if (!request_mem_region(res.start,
428 resource_size(&res), "samsung-pwm")) {
429 pr_err("%s: failed to request IO mem region\n", __func__);
430 return;
431 }
432
433 pwm.base = ioremap(res.start, resource_size(&res));
434 if (!pwm.base) {
435 pr_err("%s: failed to map PWM registers\n", __func__);
436 release_mem_region(res.start, resource_size(&res));
437 return;
438 }
439
440 _samsung_pwm_clocksource_init();
441}
442
443static const struct samsung_pwm_variant s3c24xx_variant = {
444 .bits = 16,
445 .div_base = 1,
446 .has_tint_cstat = false,
447 .tclk_mask = (1 << 4),
448};
449
450static void __init s3c2410_pwm_clocksource_init(struct device_node *np)
451{
452 samsung_pwm_alloc(np, &s3c24xx_variant);
453}
454CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
455
456static const struct samsung_pwm_variant s3c64xx_variant = {
457 .bits = 32,
458 .div_base = 0,
459 .has_tint_cstat = true,
460 .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5),
461};
462
463static void __init s3c64xx_pwm_clocksource_init(struct device_node *np)
464{
465 samsung_pwm_alloc(np, &s3c64xx_variant);
466}
467CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
468
469static const struct samsung_pwm_variant s5p64x0_variant = {
470 .bits = 32,
471 .div_base = 0,
472 .has_tint_cstat = true,
473 .tclk_mask = 0,
474};
475
476static void __init s5p64x0_pwm_clocksource_init(struct device_node *np)
477{
478 samsung_pwm_alloc(np, &s5p64x0_variant);
479}
480CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
481
482static const struct samsung_pwm_variant s5p_variant = {
483 .bits = 32,
484 .div_base = 0,
485 .has_tint_cstat = true,
486 .tclk_mask = (1 << 5),
487};
488
489static void __init s5p_pwm_clocksource_init(struct device_node *np)
490{
491 samsung_pwm_alloc(np, &s5p_variant);
492}
493CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
494#endif
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 02492ab20d22..a9d2b2fa4afd 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -12,13 +12,16 @@
12#include <linux/export.h> 12#include <linux/export.h>
13#include <linux/init.h> 13#include <linux/init.h>
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/slab.h>
15#include <linux/irqdomain.h> 16#include <linux/irqdomain.h>
16#include <linux/irqchip/chained_irq.h> 17#include <linux/irqchip/chained_irq.h>
17#include <linux/of_address.h> 18#include <linux/of_address.h>
18#include <linux/of_irq.h> 19#include <linux/of_irq.h>
19#include <asm/mach/irq.h> 20#include <asm/mach/irq.h>
20 21
22#ifdef CONFIG_EXYNOS_ATAGS
21#include <plat/cpu.h> 23#include <plat/cpu.h>
24#endif
22 25
23#include "irqchip.h" 26#include "irqchip.h"
24 27
@@ -26,17 +29,18 @@
26#define COMBINER_ENABLE_CLEAR 0x4 29#define COMBINER_ENABLE_CLEAR 0x4
27#define COMBINER_INT_STATUS 0xC 30#define COMBINER_INT_STATUS 0xC
28 31
32#define IRQ_IN_COMBINER 8
33
29static DEFINE_SPINLOCK(irq_controller_lock); 34static DEFINE_SPINLOCK(irq_controller_lock);
30 35
31struct combiner_chip_data { 36struct combiner_chip_data {
32 unsigned int irq_offset; 37 unsigned int hwirq_offset;
33 unsigned int irq_mask; 38 unsigned int irq_mask;
34 void __iomem *base; 39 void __iomem *base;
35 unsigned int parent_irq; 40 unsigned int parent_irq;
36}; 41};
37 42
38static struct irq_domain *combiner_irq_domain; 43static struct irq_domain *combiner_irq_domain;
39static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
40 44
41static inline void __iomem *combiner_base(struct irq_data *data) 45static inline void __iomem *combiner_base(struct irq_data *data)
42{ 46{
@@ -77,11 +81,11 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
77 if (status == 0) 81 if (status == 0)
78 goto out; 82 goto out;
79 83
80 combiner_irq = __ffs(status); 84 combiner_irq = chip_data->hwirq_offset + __ffs(status);
85 cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
81 86
82 cascade_irq = combiner_irq + (chip_data->irq_offset & ~31); 87 if (unlikely(!cascade_irq))
83 if (unlikely(cascade_irq >= NR_IRQS)) 88 do_bad_IRQ(irq, desc);
84 do_bad_IRQ(cascade_irq, desc);
85 else 89 else
86 generic_handle_irq(cascade_irq); 90 generic_handle_irq(cascade_irq);
87 91
@@ -113,40 +117,25 @@ static struct irq_chip combiner_chip = {
113#endif 117#endif
114}; 118};
115 119
116static unsigned int max_combiner_nr(void) 120static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
117{
118 if (soc_is_exynos5250())
119 return EXYNOS5_MAX_COMBINER_NR;
120 else if (soc_is_exynos4412())
121 return EXYNOS4412_MAX_COMBINER_NR;
122 else if (soc_is_exynos4212())
123 return EXYNOS4212_MAX_COMBINER_NR;
124 else
125 return EXYNOS4210_MAX_COMBINER_NR;
126}
127
128static void __init combiner_cascade_irq(unsigned int combiner_nr,
129 unsigned int irq) 121 unsigned int irq)
130{ 122{
131 if (combiner_nr >= max_combiner_nr()) 123 if (irq_set_handler_data(irq, combiner_data) != 0)
132 BUG();
133 if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
134 BUG(); 124 BUG();
135 irq_set_chained_handler(irq, combiner_handle_cascade_irq); 125 irq_set_chained_handler(irq, combiner_handle_cascade_irq);
136} 126}
137 127
138static void __init combiner_init_one(unsigned int combiner_nr, 128static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
129 unsigned int combiner_nr,
139 void __iomem *base, unsigned int irq) 130 void __iomem *base, unsigned int irq)
140{ 131{
141 combiner_data[combiner_nr].base = base; 132 combiner_data->base = base;
142 combiner_data[combiner_nr].irq_offset = irq_find_mapping( 133 combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
143 combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); 134 combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
144 combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); 135 combiner_data->parent_irq = irq;
145 combiner_data[combiner_nr].parent_irq = irq;
146 136
147 /* Disable all interrupts */ 137 /* Disable all interrupts */
148 __raw_writel(combiner_data[combiner_nr].irq_mask, 138 __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
149 base + COMBINER_ENABLE_CLEAR);
150} 139}
151 140
152#ifdef CONFIG_OF 141#ifdef CONFIG_OF
@@ -162,7 +151,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
162 if (intsize < 2) 151 if (intsize < 2)
163 return -EINVAL; 152 return -EINVAL;
164 153
165 *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1]; 154 *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1];
166 *out_type = 0; 155 *out_type = 0;
167 156
168 return 0; 157 return 0;
@@ -181,6 +170,8 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
181static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, 170static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
182 irq_hw_number_t hw) 171 irq_hw_number_t hw)
183{ 172{
173 struct combiner_chip_data *combiner_data = d->host_data;
174
184 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); 175 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
185 irq_set_chip_data(irq, &combiner_data[hw >> 3]); 176 irq_set_chip_data(irq, &combiner_data[hw >> 3]);
186 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 177 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@@ -193,8 +184,12 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
193 .map = combiner_irq_domain_map, 184 .map = combiner_irq_domain_map,
194}; 185};
195 186
196static unsigned int exynos4x12_combiner_extra_irq(int group) 187static unsigned int combiner_lookup_irq(int group)
197{ 188{
189#ifdef CONFIG_EXYNOS_ATAGS
190 if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
191 return IRQ_SPI(group);
192
198 switch (group) { 193 switch (group) {
199 case 16: 194 case 16:
200 return IRQ_SPI(107); 195 return IRQ_SPI(107);
@@ -204,53 +199,46 @@ static unsigned int exynos4x12_combiner_extra_irq(int group)
204 return IRQ_SPI(48); 199 return IRQ_SPI(48);
205 case 19: 200 case 19:
206 return IRQ_SPI(42); 201 return IRQ_SPI(42);
207 default:
208 return 0;
209 } 202 }
203#endif
204 return 0;
210} 205}
211 206
212void __init combiner_init(void __iomem *combiner_base, 207void __init combiner_init(void __iomem *combiner_base,
213 struct device_node *np) 208 struct device_node *np,
209 unsigned int max_nr,
210 int irq_base)
214{ 211{
215 int i, irq, irq_base; 212 int i, irq;
216 unsigned int max_nr, nr_irq; 213 unsigned int nr_irq;
214 struct combiner_chip_data *combiner_data;
217 215
218 max_nr = max_combiner_nr(); 216 nr_irq = max_nr * IRQ_IN_COMBINER;
219 217
220 if (np) { 218 combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
221 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { 219 if (!combiner_data) {
222 pr_info("%s: number of combiners not specified, " 220 pr_warning("%s: could not allocate combiner data\n", __func__);
223 "setting default as %d.\n", 221 return;
224 __func__, max_nr);
225 }
226 }
227
228 nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
229
230 irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
231 if (IS_ERR_VALUE(irq_base)) {
232 irq_base = COMBINER_IRQ(0, 0);
233 pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
234 } 222 }
235 223
236 combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0, 224 combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
237 &combiner_irq_domain_ops, &combiner_data); 225 &combiner_irq_domain_ops, combiner_data);
238 if (WARN_ON(!combiner_irq_domain)) { 226 if (WARN_ON(!combiner_irq_domain)) {
239 pr_warning("%s: irq domain init failed\n", __func__); 227 pr_warning("%s: irq domain init failed\n", __func__);
240 return; 228 return;
241 } 229 }
242 230
243 for (i = 0; i < max_nr; i++) { 231 for (i = 0; i < max_nr; i++) {
244 if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
245 irq = IRQ_SPI(i);
246 else
247 irq = exynos4x12_combiner_extra_irq(i);
248#ifdef CONFIG_OF 232#ifdef CONFIG_OF
249 if (np) 233 if (np)
250 irq = irq_of_parse_and_map(np, i); 234 irq = irq_of_parse_and_map(np, i);
235 else
251#endif 236#endif
252 combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq); 237 irq = combiner_lookup_irq(i);
253 combiner_cascade_irq(i, irq); 238
239 combiner_init_one(&combiner_data[i], i,
240 combiner_base + (i >> 2) * 0x10, irq);
241 combiner_cascade_irq(&combiner_data[i], irq);
254 } 242 }
255} 243}
256 244
@@ -259,6 +247,8 @@ static int __init combiner_of_init(struct device_node *np,
259 struct device_node *parent) 247 struct device_node *parent)
260{ 248{
261 void __iomem *combiner_base; 249 void __iomem *combiner_base;
250 unsigned int max_nr = 20;
251 int irq_base = -1;
262 252
263 combiner_base = of_iomap(np, 0); 253 combiner_base = of_iomap(np, 0);
264 if (!combiner_base) { 254 if (!combiner_base) {
@@ -266,7 +256,20 @@ static int __init combiner_of_init(struct device_node *np,
266 return -ENXIO; 256 return -ENXIO;
267 } 257 }
268 258
269 combiner_init(combiner_base, np); 259 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
260 pr_info("%s: number of combiners not specified, "
261 "setting default as %d.\n",
262 __func__, max_nr);
263 }
264
265 /*
266 * FIXME: This is a hardwired COMBINER_IRQ(0,0). Once all devices
267 * get their IRQ from DT, remove this in order to get dynamic
268 * allocation.
269 */
270 irq_base = 160;
271
272 combiner_init(combiner_base, np, max_nr, irq_base);
270 273
271 return 0; 274 return 0;
272} 275}
diff --git a/include/clocksource/samsung_pwm.h b/include/clocksource/samsung_pwm.h
new file mode 100644
index 000000000000..5c449c8199e9
--- /dev/null
+++ b/include/clocksource/samsung_pwm.h
@@ -0,0 +1,36 @@
1/*
2 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#ifndef __CLOCKSOURCE_SAMSUNG_PWM_H
17#define __CLOCKSOURCE_SAMSUNG_PWM_H
18
19#include <linux/spinlock.h>
20
21#define SAMSUNG_PWM_NUM 5
22
23extern spinlock_t samsung_pwm_lock;
24
25struct samsung_pwm_variant {
26 u8 bits;
27 u8 div_base;
28 u8 tclk_mask;
29 u8 output_mask;
30 bool has_tint_cstat;
31};
32
33void samsung_pwm_clocksource_init(void __iomem *base,
34 unsigned int *irqs, struct samsung_pwm_variant *variant);
35
36#endif /* __CLOCKSOURCE_SAMSUNG_PWM_H */