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/mach-exynos/common.c | 15 | ||||
| -rw-r--r-- | arch/arm/plat-samsung/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/clocksource/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clocksource/samsung_pwm_timer.c | 494 | ||||
| -rw-r--r-- | include/clocksource/samsung_pwm.h | 36 |
10 files changed, 946 insertions, 17 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 d3cd880d70b3..2d870f4e4e2b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile | |||
| @@ -42,6 +42,7 @@ dtb-$(CONFIG_ARCH_DOVE) += dove-cm-a510.dtb \ | |||
| 42 | dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ | 42 | dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ |
| 43 | exynos4210-smdkv310.dtb \ | 43 | exynos4210-smdkv310.dtb \ |
| 44 | exynos4210-trats.dtb \ | 44 | exynos4210-trats.dtb \ |
| 45 | exynos4210-universal_c210.dtb \ | ||
| 45 | exynos4412-odroidx.dtb \ | 46 | exynos4412-odroidx.dtb \ |
| 46 | exynos4412-smdk4412.dtb \ | 47 | exynos4412-smdk4412.dtb \ |
| 47 | exynos4412-origen.dtb \ | 48 | exynos4412-origen.dtb \ |
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 9ac47d51c407..b620e34fbd92 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi | |||
| @@ -316,6 +316,14 @@ | |||
| 316 | status = "disabled"; | 316 | status = "disabled"; |
| 317 | }; | 317 | }; |
| 318 | 318 | ||
| 319 | pwm@139D0000 { | ||
| 320 | compatible = "samsung,exynos4210-pwm"; | ||
| 321 | reg = <0x139D0000 0x1000>; | ||
| 322 | interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, <0 41 0>; | ||
| 323 | #pwm-cells = <2>; | ||
| 324 | status = "disabled"; | ||
| 325 | }; | ||
| 326 | |||
| 319 | amba { | 327 | amba { |
| 320 | #address-cells = <1>; | 328 | #address-cells = <1>; |
| 321 | #size-cells = <1>; | 329 | #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/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 9208079d5d52..4bc1c49c69f1 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c | |||
| @@ -449,13 +449,6 @@ void __init exynos4_init_irq(void) | |||
| 449 | if (!of_have_populated_dt()) | 449 | if (!of_have_populated_dt()) |
| 450 | combiner_init(S5P_VA_COMBINER_BASE, NULL, | 450 | combiner_init(S5P_VA_COMBINER_BASE, NULL, |
| 451 | max_combiner_nr(), COMBINER_IRQ(0, 0)); | 451 | max_combiner_nr(), COMBINER_IRQ(0, 0)); |
| 452 | |||
| 453 | /* | ||
| 454 | * The parameters of s5p_init_irq() are for VIC init. | ||
| 455 | * Theses parameters should be NULL and 0 because EXYNOS4 | ||
| 456 | * uses GIC instead of VIC. | ||
| 457 | */ | ||
| 458 | s5p_init_irq(NULL, 0); | ||
| 459 | } | 452 | } |
| 460 | 453 | ||
| 461 | void __init exynos5_init_irq(void) | 454 | void __init exynos5_init_irq(void) |
| @@ -463,14 +456,6 @@ void __init exynos5_init_irq(void) | |||
| 463 | #ifdef CONFIG_OF | 456 | #ifdef CONFIG_OF |
| 464 | irqchip_init(); | 457 | irqchip_init(); |
| 465 | #endif | 458 | #endif |
| 466 | /* | ||
| 467 | * The parameters of s5p_init_irq() are for VIC init. | ||
| 468 | * Theses parameters should be NULL and 0 because EXYNOS4 | ||
| 469 | * uses GIC instead of VIC. | ||
| 470 | */ | ||
| 471 | if (!of_machine_is_compatible("samsung,exynos5440")) | ||
| 472 | s5p_init_irq(NULL, 0); | ||
| 473 | |||
| 474 | gic_arch_extn.irq_set_wake = s3c_irq_wake; | 459 | gic_arch_extn.irq_set_wake = s3c_irq_wake; |
| 475 | } | 460 | } |
| 476 | 461 | ||
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 77dd30af32f5..53b2d59ca252 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig | |||
| @@ -105,9 +105,9 @@ config SAMSUNG_IRQ_VIC_TIMER | |||
| 105 | Internal configuration to build the VIC timer interrupt code. | 105 | Internal configuration to build the VIC timer interrupt code. |
| 106 | 106 | ||
| 107 | config S5P_IRQ | 107 | config S5P_IRQ |
| 108 | def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS) | 108 | def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210) |
| 109 | help | 109 | help |
| 110 | Support common interrup part for ARCH_S5P and ARCH_EXYNOS SoCs | 110 | Support common interrupt part for ARCH_S5P SoCs |
| 111 | 111 | ||
| 112 | config S5P_EXT_INT | 112 | config S5P_EXT_INT |
| 113 | bool | 113 | bool |
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 73fcddb8314d..5d9bab2b5012 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
| @@ -75,3 +75,12 @@ config CLKSRC_EXYNOS_MCT | |||
| 75 | def_bool y if ARCH_EXYNOS | 75 | def_bool y if ARCH_EXYNOS |
| 76 | help | 76 | help |
| 77 | Support for Multi Core Timer controller on Exynos SoCs. | 77 | Support for Multi Core Timer controller on Exynos SoCs. |
| 78 | |||
| 79 | config CLKSRC_SAMSUNG_PWM | ||
| 80 | bool | ||
| 81 | select CLKSRC_MMIO | ||
| 82 | help | ||
| 83 | This is a new clocksource driver for the PWM timer found in | ||
| 84 | Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver | ||
| 85 | for all devicetree enabled platforms. This driver will be | ||
| 86 | needed only on systems that do not have the Exynos MCT available. | ||
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index cd1f09cbd61a..2289f0cb4c4c 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
| @@ -21,6 +21,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o | |||
| 21 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o | 21 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o |
| 22 | obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o | 22 | obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o |
| 23 | obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o | 23 | obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o |
| 24 | obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o | ||
| 24 | 25 | ||
| 25 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o | 26 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o |
| 26 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o | 27 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o |
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/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 */ | ||
