Commit bb9055b2744ada735a2fe555c4196ad39a83ef2a

Authored by Linus Torvalds

Merge tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull late ARM Exynos multiplatform changes from Arnd Bergmann:
 "These continue the multiplatform support for exynos, adding support
  for building most of the essential drivers (clocksource, clk, irqchip)
  when combined with other platforms.  As a result, it should become
  really easy to add full multiplatform exynos support in 3.11, although
  we don't yet enable it for 3.10.

  The changes were not included in the earlier multiplatform series in
  order to avoid clashes with the other Exynos updates.

  This also includes work from Tomasz Figa to fix the pwm clocksource
  code on Exynos, which is not strictly required for multiplatform, but
  related to the other patches in this set and needed as a bug fix for
  at least one board."

* tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (22 commits)
  ARM: dts: exynops4210: really add universal_c210 dts
  ARM: dts: exynos4210: Add basic dts file for universal_c210 board
  ARM: dts: exynos4: Add node for PWM device
  ARM: SAMSUNG: Do not register legacy timer interrupts on Exynos
  clocksource: samsung_pwm_timer: Work around rounding errors in clockevents core
  clocksource: samsung_pwm_timer: Correct programming of clock events
  clocksource: samsung_pwm_timer: Use proper clockevents max_delta
  clocksource: samsung_pwm_timer: Add support for non-DT platforms
  clocksource: samsung_pwm_timer: Drop unused samsung_pwm struct
  clocksource: samsung_pwm_timer: Keep all driver data in a structure
  clocksource: samsung_pwm_timer: Make PWM spinlock global
  clocksource: samsung_pwm_timer: Let platforms select the driver
  Documentation: Add device tree bindings for Samsung PWM timers
  clocksource: add samsung pwm timer driver
  irqchip: exynos: look up irq using irq_find_mapping
  irqchip: exynos: pass irq_base from platform
  irqchip: exynos: localize irq lookup for ATAGS
  irqchip: exynos: allocate combiner_data dynamically
  irqchip: exynos: pass max combiner number to combiner_init
  ARM: exynos: add missing properties for combiner IRQs
  ...

Showing 20 changed files Side-by-side Diff

Documentation/devicetree/bindings/pwm/pwm-samsung.txt
  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 + }
arch/arm/boot/dts/Makefile
... ... @@ -49,6 +49,7 @@
49 49 dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
50 50 exynos4210-smdkv310.dtb \
51 51 exynos4210-trats.dtb \
  52 + exynos4210-universal_c210.dtb \
52 53 exynos4412-odroidx.dtb \
53 54 exynos4412-smdk4412.dtb \
54 55 exynos4412-origen.dtb \
arch/arm/boot/dts/exynos4.dtsi
... ... @@ -336,6 +336,14 @@
336 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 347 amba {
340 348 #address-cells = <1>;
341 349 #size-cells = <1>;
arch/arm/boot/dts/exynos4210-universal_c210.dts
  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 +};
arch/arm/boot/dts/exynos4210.dtsi
... ... @@ -41,6 +41,7 @@
41 41 };
42 42  
43 43 combiner:interrupt-controller@10440000 {
  44 + samsung,combiner-nr = <16>;
44 45 interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
45 46 <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
46 47 <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
arch/arm/boot/dts/exynos4212.dtsi
... ... @@ -26,6 +26,15 @@
26 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 38 mct@10050000 {
30 39 compatible = "samsung,exynos4412-mct";
31 40 reg = <0x10050000 0x800>;
arch/arm/boot/dts/exynos4412.dtsi
... ... @@ -26,6 +26,15 @@
26 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 38 mct@10050000 {
30 39 compatible = "samsung,exynos4412-mct";
31 40 reg = <0x10050000 0x800>;
arch/arm/mach-exynos/common.c
... ... @@ -452,13 +452,26 @@
452 452 } else {
453 453 /* todo: remove after migrating legacy E4 platforms to dt */
454 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 456 exynos4_clk_register_fixed_ext(xxti_f, xusbxti_f);
457 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 475 void __init exynos4_init_irq(void)
463 476 {
464 477 unsigned int gic_bank_offset;
465 478  
... ... @@ -473,15 +486,9 @@
473 486 #endif
474 487  
475 488 if (!of_have_populated_dt())
476   - combiner_init(S5P_VA_COMBINER_BASE, NULL);
  489 + combiner_init(S5P_VA_COMBINER_BASE, NULL,
  490 + max_combiner_nr(), COMBINER_IRQ(0, 0));
477 491  
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   -
485 492 gic_arch_extn.irq_set_wake = s3c_irq_wake;
486 493 }
487 494  
... ... @@ -490,14 +497,6 @@
490 497 #ifdef CONFIG_OF
491 498 irqchip_init();
492 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 500 gic_arch_extn.irq_set_wake = s3c_irq_wake;
502 501 }
503 502  
arch/arm/mach-exynos/common.h
... ... @@ -14,7 +14,7 @@
14 14  
15 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 18 void exynos_init_time(void);
19 19 extern unsigned long xxti_f, xusbxti_f;
20 20  
... ... @@ -27,7 +27,7 @@
27 27 void exynos_init_late(void);
28 28  
29 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 31 void exynos4_clk_register_fixed_ext(unsigned long, unsigned long);
32 32  
33 33 void exynos_firmware_init(void);
... ... @@ -71,7 +71,8 @@
71 71 #endif
72 72  
73 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 77 extern struct smp_operations exynos_smp_ops;
77 78  
arch/arm/plat-samsung/Kconfig
... ... @@ -93,9 +93,9 @@
93 93 Internal configuration to build the VIC timer interrupt code.
94 94  
95 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 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 100 config S5P_EXT_INT
101 101 bool
drivers/clk/samsung/clk-exynos4.c
... ... @@ -16,7 +16,6 @@
16 16 #include <linux/of.h>
17 17 #include <linux/of_address.h>
18 18  
19   -#include <plat/cpu.h>
20 19 #include "clk.h"
21 20 #include "clk-pll.h"
22 21  
... ... @@ -910,16 +909,6 @@
910 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 913 * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit
925 914 * resides in chipid register space, outside of the clock controller memory
926 915  
927 916  
928 917  
929 918  
930 919  
... ... @@ -927,33 +916,40 @@
927 916 * controller is first remapped and the value of XOM[0] bit is read to
928 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 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 925 np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid");
940   - if (np)
  926 + if (np) {
941 927 chipid_base = of_iomap(np, 0);
942 928  
943   - if (chipid_base) {
944   - xom = readl(chipid_base + 8);
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);
951   - } else {
952   - finpll_f = clk_get_rate(clk);
953   - }
  929 + if (chipid_base)
  930 + xom = readl(chipid_base + 8);
  931 +
  932 + iounmap(chipid_base);
  933 + }
  934 +
  935 + return xom;
  936 +}
  937 +
  938 +static void __init exynos4_clk_register_finpll(unsigned long xom)
  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 951 } else {
955   - pr_err("%s: failed to map chipid registers, assuming "
956   - "fin_pll clock frequency is 24MHz\n", __func__);
  952 + finpll_f = clk_get_rate(clk);
957 953 }
958 954  
959 955 fclk.id = fin_pll;
... ... @@ -963,8 +959,6 @@
963 959 fclk.fixed_rate = finpll_f;
964 960 samsung_clk_register_fixed_rate(&fclk, 1);
965 961  
966   - if (np)
967   - iounmap(chipid_base);
968 962 }
969 963  
970 964 /*
971 965  
972 966  
973 967  
974 968  
... ... @@ -988,28 +982,14 @@
988 982 };
989 983  
990 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 987 struct clk *apll, *mpll, *epll, *vpll;
995   - u32 exynos4_soc;
996 988  
997 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 990 reg_base = of_iomap(np, 0);
1003 991 if (!reg_base)
1004 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 995 if (exynos4_soc == EXYNOS4210)
... ... @@ -1026,7 +1006,7 @@
1026 1006 ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
1027 1007 ext_clk_match);
1028 1008  
1029   - exynos4_clk_register_finpll();
  1009 + exynos4_clk_register_finpll(xom);
1030 1010  
1031 1011 if (exynos4_soc == EXYNOS4210) {
1032 1012 apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll",
... ... @@ -1087,6 +1067,17 @@
1087 1067 _get_rate("sclk_epll"), _get_rate("sclk_vpll"),
1088 1068 _get_rate("arm_clk"));
1089 1069 }
1090   -CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4_clk_init);
1091   -CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4_clk_init);
  1070 +
  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);
drivers/clk/samsung/clk-exynos5250.c
... ... @@ -16,7 +16,6 @@
16 16 #include <linux/of.h>
17 17 #include <linux/of_address.h>
18 18  
19   -#include <plat/cpu.h>
20 19 #include "clk.h"
21 20 #include "clk-pll.h"
22 21  
drivers/clk/samsung/clk-exynos5440.c
... ... @@ -15,7 +15,6 @@
15 15 #include <linux/of.h>
16 16 #include <linux/of_address.h>
17 17  
18   -#include <plat/cpu.h>
19 18 #include "clk.h"
20 19 #include "clk-pll.h"
21 20  
drivers/clk/samsung/clk.h
... ... @@ -20,8 +20,6 @@
20 20 #include <linux/of.h>
21 21 #include <linux/of_address.h>
22 22  
23   -#include <mach/map.h>
24   -
25 23 /**
26 24 * struct samsung_clock_alias: information about mux clock
27 25 * @id: platform specific id of the clock.
drivers/clocksource/Kconfig
... ... @@ -76,4 +76,13 @@
76 76 def_bool y if ARCH_EXYNOS
77 77 help
78 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.
drivers/clocksource/Makefile
... ... @@ -25,6 +25,7 @@
25 25 obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
26 26 obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
27 27 obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
  28 +obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
28 29  
29 30 obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
30 31 obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
drivers/clocksource/exynos_mct.c
... ... @@ -25,11 +25,6 @@
25 25 #include <linux/clocksource.h>
26 26  
27 27 #include <asm/localtimer.h>
28   -
29   -#include <plat/cpu.h>
30   -
31   -#include <mach/map.h>
32   -#include <mach/irqs.h>
33 28 #include <asm/mach/time.h>
34 29  
35 30 #define EXYNOS4_MCTREG(x) (x)
36 31  
37 32  
... ... @@ -510,18 +505,14 @@
510 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()) {
516   - mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0;
517   - mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0;
518   - mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1;
519   - mct_int_type = MCT_INT_SPI;
520   - } else {
521   - panic("unable to determine mct controller type\n");
522   - }
  510 + mct_irqs[MCT_G0_IRQ] = irq_g0;
  511 + mct_irqs[MCT_L0_IRQ] = irq_l0;
  512 + mct_irqs[MCT_L1_IRQ] = irq_l1;
  513 + mct_int_type = MCT_INT_SPI;
523 514  
524   - exynos4_timer_resources(NULL, S5P_VA_SYSTIMER);
  515 + exynos4_timer_resources(NULL, base);
525 516 exynos4_clocksource_init();
526 517 exynos4_clockevent_init();
527 518 }
drivers/clocksource/samsung_pwm_timer.c
  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
drivers/irqchip/exynos-combiner.c
... ... @@ -12,13 +12,16 @@
12 12 #include <linux/export.h>
13 13 #include <linux/init.h>
14 14 #include <linux/io.h>
  15 +#include <linux/slab.h>
15 16 #include <linux/irqdomain.h>
16 17 #include <linux/irqchip/chained_irq.h>
17 18 #include <linux/of_address.h>
18 19 #include <linux/of_irq.h>
19 20 #include <asm/mach/irq.h>
20 21  
  22 +#ifdef CONFIG_EXYNOS_ATAGS
21 23 #include <plat/cpu.h>
  24 +#endif
22 25  
23 26 #include "irqchip.h"
24 27  
25 28  
26 29  
... ... @@ -26,17 +29,18 @@
26 29 #define COMBINER_ENABLE_CLEAR 0x4
27 30 #define COMBINER_INT_STATUS 0xC
28 31  
  32 +#define IRQ_IN_COMBINER 8
  33 +
29 34 static DEFINE_SPINLOCK(irq_controller_lock);
30 35  
31 36 struct combiner_chip_data {
32   - unsigned int irq_offset;
  37 + unsigned int hwirq_offset;
33 38 unsigned int irq_mask;
34 39 void __iomem *base;
35 40 unsigned int parent_irq;
36 41 };
37 42  
38 43 static struct irq_domain *combiner_irq_domain;
39   -static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
40 44  
41 45 static inline void __iomem *combiner_base(struct irq_data *data)
42 46 {
43 47  
... ... @@ -77,11 +81,11 @@
77 81 if (status == 0)
78 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);
83   - if (unlikely(cascade_irq >= NR_IRQS))
84   - do_bad_IRQ(cascade_irq, desc);
  87 + if (unlikely(!cascade_irq))
  88 + do_bad_IRQ(irq, desc);
85 89 else
86 90 generic_handle_irq(cascade_irq);
87 91  
88 92  
89 93  
90 94  
91 95  
92 96  
... ... @@ -113,40 +117,25 @@
113 117 #endif
114 118 };
115 119  
116   -static unsigned int max_combiner_nr(void)
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,
  120 +static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
129 121 unsigned int irq)
130 122 {
131   - if (combiner_nr >= max_combiner_nr())
  123 + if (irq_set_handler_data(irq, combiner_data) != 0)
132 124 BUG();
133   - if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
134   - BUG();
135 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 130 void __iomem *base, unsigned int irq)
140 131 {
141   - combiner_data[combiner_nr].base = base;
142   - combiner_data[combiner_nr].irq_offset = irq_find_mapping(
143   - combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER);
144   - combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3);
145   - combiner_data[combiner_nr].parent_irq = irq;
  132 + combiner_data->base = base;
  133 + combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
  134 + combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
  135 + combiner_data->parent_irq = irq;
146 136  
147 137 /* Disable all interrupts */
148   - __raw_writel(combiner_data[combiner_nr].irq_mask,
149   - base + COMBINER_ENABLE_CLEAR);
  138 + __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
150 139 }
151 140  
152 141 #ifdef CONFIG_OF
... ... @@ -162,7 +151,7 @@
162 151 if (intsize < 2)
163 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 155 *out_type = 0;
167 156  
168 157 return 0;
... ... @@ -181,6 +170,8 @@
181 170 static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
182 171 irq_hw_number_t hw)
183 172 {
  173 + struct combiner_chip_data *combiner_data = d->host_data;
  174 +
184 175 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
185 176 irq_set_chip_data(irq, &combiner_data[hw >> 3]);
186 177 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
187 178  
... ... @@ -193,8 +184,12 @@
193 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 193 switch (group) {
199 194 case 16:
200 195 return IRQ_SPI(107);
201 196  
202 197  
203 198  
204 199  
205 200  
206 201  
207 202  
208 203  
209 204  
... ... @@ -204,53 +199,46 @@
204 199 return IRQ_SPI(48);
205 200 case 19:
206 201 return IRQ_SPI(42);
207   - default:
208   - return 0;
209 202 }
  203 +#endif
  204 + return 0;
210 205 }
211 206  
212 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;
216   - unsigned int max_nr, nr_irq;
  212 + int i, 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) {
221   - if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
222   - pr_info("%s: number of combiners not specified, "
223   - "setting default as %d.\n",
224   - __func__, max_nr);
225   - }
  218 + combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
  219 + if (!combiner_data) {
  220 + pr_warning("%s: could not allocate combiner data\n", __func__);
  221 + return;
226 222 }
227 223  
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   - }
235   -
236   - combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0,
237   - &combiner_irq_domain_ops, &combiner_data);
  224 + combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
  225 + &combiner_irq_domain_ops, combiner_data);
238 226 if (WARN_ON(!combiner_irq_domain)) {
239 227 pr_warning("%s: irq domain init failed\n", __func__);
240 228 return;
241 229 }
242 230  
243 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 232 #ifdef CONFIG_OF
249 233 if (np)
250 234 irq = irq_of_parse_and_map(np, i);
  235 + else
251 236 #endif
252   - combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq);
253   - combiner_cascade_irq(i, irq);
  237 + irq = combiner_lookup_irq(i);
  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 @@
259 247 struct device_node *parent)
260 248 {
261 249 void __iomem *combiner_base;
  250 + unsigned int max_nr = 20;
  251 + int irq_base = -1;
262 252  
263 253 combiner_base = of_iomap(np, 0);
264 254 if (!combiner_base) {
... ... @@ -266,7 +256,20 @@
266 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 274 return 0;
272 275 }
include/clocksource/samsung_pwm.h
  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 */