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 */ | ||
