diff options
-rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-samsung.txt | 43 | ||||
-rw-r--r-- | arch/arm/boot/dts/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4.dtsi | 8 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4210-universal_c210.dts | 352 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4210.dtsi | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4212.dtsi | 9 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4412.dtsi | 9 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.c | 35 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.h | 7 | ||||
-rw-r--r-- | arch/arm/plat-samsung/Kconfig | 4 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-exynos4.c | 93 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-exynos5250.c | 1 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-exynos5440.c | 1 | ||||
-rw-r--r-- | drivers/clk/samsung/clk.h | 2 | ||||
-rw-r--r-- | drivers/clocksource/Kconfig | 9 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/exynos_mct.c | 21 | ||||
-rw-r--r-- | drivers/clocksource/samsung_pwm_timer.c | 494 | ||||
-rw-r--r-- | drivers/irqchip/exynos-combiner.c | 125 | ||||
-rw-r--r-- | include/clocksource/samsung_pwm.h | 36 |
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 | |||
3 | Samsung SoCs contain PWM timer blocks which can be used for system clock source | ||
4 | and clock event timers, as well as to drive SoC outputs with PWM signal. Each | ||
5 | PWM timer block provides 5 PWM channels (not all of them can drive physical | ||
6 | outputs - see SoC and board manual). | ||
7 | |||
8 | Be aware that the clocksource driver supports only uniprocessor systems. | ||
9 | |||
10 | Required 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 | |||
30 | Optional 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 | |||
35 | Example: | ||
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 \ | |||
49 | dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ | 49 | dtb-$(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 | ||
462 | static 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 | |||
462 | void __init exynos4_init_irq(void) | 475 | void __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 | ||
17 | extern void mct_init(void); | 17 | void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1); |
18 | void exynos_init_time(void); | 18 | void exynos_init_time(void); |
19 | extern unsigned long xxti_f, xusbxti_f; | 19 | extern unsigned long xxti_f, xusbxti_f; |
20 | 20 | ||
@@ -27,7 +27,7 @@ void exynos5_restart(char mode, const char *cmd); | |||
27 | void exynos_init_late(void); | 27 | void 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 */ |
30 | void exynos4_clk_init(struct device_node *np); | 30 | void exynos4_clk_init(struct device_node *np, int is_exynos4210, void __iomem *reg_base, unsigned long xom); |
31 | void exynos4_clk_register_fixed_ext(unsigned long, unsigned long); | 31 | void exynos4_clk_register_fixed_ext(unsigned long, unsigned long); |
32 | 32 | ||
33 | void exynos_firmware_init(void); | 33 | void exynos_firmware_init(void); |
@@ -71,7 +71,8 @@ void exynos4212_register_clocks(void); | |||
71 | #endif | 71 | #endif |
72 | 72 | ||
73 | struct device_node; | 73 | struct device_node; |
74 | void combiner_init(void __iomem *combiner_base, struct device_node *np); | 74 | void combiner_init(void __iomem *combiner_base, struct device_node *np, |
75 | unsigned int max_nr, int irq_base); | ||
75 | 76 | ||
76 | extern struct smp_operations exynos_smp_ops; | 77 | extern 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 | ||
95 | config S5P_IRQ | 95 | config 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 | ||
100 | config S5P_EXT_INT | 100 | config 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 | ||
914 | static 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 | */ |
930 | static void __init exynos4_clk_register_finpll(void) | 919 | static 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); | 938 | static 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 */ |
991 | void __init exynos4_clk_init(struct device_node *np) | 985 | void __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 | } |
1090 | CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4_clk_init); | 1070 | |
1091 | CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4_clk_init); | 1071 | |
1072 | static void __init exynos4210_clk_init(struct device_node *np) | ||
1073 | { | ||
1074 | exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom()); | ||
1075 | } | ||
1076 | CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init); | ||
1077 | |||
1078 | static void __init exynos4412_clk_init(struct device_node *np) | ||
1079 | { | ||
1080 | exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom()); | ||
1081 | } | ||
1082 | CLK_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 | |||
80 | config 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 | |||
25 | obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o | 25 | obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o |
26 | obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o | 26 | obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o |
27 | obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o | 27 | obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o |
28 | obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o | ||
28 | 29 | ||
29 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o | 30 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o |
30 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o | 31 | obj-$(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 | ||
513 | void __init mct_init(void) | 508 | void __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 | |||
52 | DEFINE_SPINLOCK(samsung_pwm_lock); | ||
53 | EXPORT_SYMBOL(samsung_pwm_lock); | ||
54 | |||
55 | struct 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 | |||
71 | static struct samsung_pwm_clocksource pwm; | ||
72 | |||
73 | static 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 | |||
92 | static 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 | |||
111 | static 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 | |||
128 | static 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 | |||
151 | static 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 | |||
176 | static 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 | |||
198 | static 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 | |||
209 | static 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 | |||
233 | static 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 | |||
241 | static 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 | |||
255 | static 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 | |||
262 | static 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 | |||
289 | static 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 | */ | ||
313 | static 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 | |||
323 | static 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 | |||
350 | static 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 | */ | ||
371 | static 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 | |||
393 | void __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 | ||
404 | static 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 | |||
443 | static 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 | |||
450 | static void __init s3c2410_pwm_clocksource_init(struct device_node *np) | ||
451 | { | ||
452 | samsung_pwm_alloc(np, &s3c24xx_variant); | ||
453 | } | ||
454 | CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init); | ||
455 | |||
456 | static 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 | |||
463 | static void __init s3c64xx_pwm_clocksource_init(struct device_node *np) | ||
464 | { | ||
465 | samsung_pwm_alloc(np, &s3c64xx_variant); | ||
466 | } | ||
467 | CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init); | ||
468 | |||
469 | static 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 | |||
476 | static void __init s5p64x0_pwm_clocksource_init(struct device_node *np) | ||
477 | { | ||
478 | samsung_pwm_alloc(np, &s5p64x0_variant); | ||
479 | } | ||
480 | CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init); | ||
481 | |||
482 | static 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 | |||
489 | static void __init s5p_pwm_clocksource_init(struct device_node *np) | ||
490 | { | ||
491 | samsung_pwm_alloc(np, &s5p_variant); | ||
492 | } | ||
493 | CLOCKSOURCE_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 | |||
29 | static DEFINE_SPINLOCK(irq_controller_lock); | 34 | static DEFINE_SPINLOCK(irq_controller_lock); |
30 | 35 | ||
31 | struct combiner_chip_data { | 36 | struct 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 | ||
38 | static struct irq_domain *combiner_irq_domain; | 43 | static struct irq_domain *combiner_irq_domain; |
39 | static struct combiner_chip_data combiner_data[MAX_COMBINER_NR]; | ||
40 | 44 | ||
41 | static inline void __iomem *combiner_base(struct irq_data *data) | 45 | static 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 | ||
116 | static unsigned int max_combiner_nr(void) | 120 | static 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 | |||
128 | static 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 | ||
138 | static void __init combiner_init_one(unsigned int combiner_nr, | 128 | static 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, | |||
181 | static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, | 170 | static 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 | ||
196 | static unsigned int exynos4x12_combiner_extra_irq(int group) | 187 | static 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 | ||
212 | void __init combiner_init(void __iomem *combiner_base, | 207 | void __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 | |||
23 | extern spinlock_t samsung_pwm_lock; | ||
24 | |||
25 | struct 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 | |||
33 | void samsung_pwm_clocksource_init(void __iomem *base, | ||
34 | unsigned int *irqs, struct samsung_pwm_variant *variant); | ||
35 | |||
36 | #endif /* __CLOCKSOURCE_SAMSUNG_PWM_H */ | ||