aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/samsung/clk-exynos4.c93
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c1
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c1
-rw-r--r--drivers/clk/samsung/clk.h2
-rw-r--r--drivers/clocksource/Kconfig9
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/exynos_mct.c21
-rw-r--r--drivers/clocksource/samsung_pwm_timer.c494
-rw-r--r--drivers/irqchip/exynos-combiner.c125
9 files changed, 616 insertions, 131 deletions
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 71046694d9dd..d0940e69d034 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -16,7 +16,6 @@
16#include <linux/of.h> 16#include <linux/of.h>
17#include <linux/of_address.h> 17#include <linux/of_address.h>
18 18
19#include <plat/cpu.h>
20#include "clk.h" 19#include "clk.h"
21#include "clk-pll.h" 20#include "clk-pll.h"
22 21
@@ -910,16 +909,6 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = {
910 CLK_IGNORE_UNUSED, 0), 909 CLK_IGNORE_UNUSED, 0),
911}; 910};
912 911
913#ifdef CONFIG_OF
914static struct of_device_id exynos4_clk_ids[] __initdata = {
915 { .compatible = "samsung,exynos4210-clock",
916 .data = (void *)EXYNOS4210, },
917 { .compatible = "samsung,exynos4412-clock",
918 .data = (void *)EXYNOS4X12, },
919 { },
920};
921#endif
922
923/* 912/*
924 * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit 913 * The parent of the fin_pll clock is selected by the XOM[0] bit. This bit
925 * resides in chipid register space, outside of the clock controller memory 914 * resides in chipid register space, outside of the clock controller memory
@@ -927,33 +916,40 @@ static struct of_device_id exynos4_clk_ids[] __initdata = {
927 * controller is first remapped and the value of XOM[0] bit is read to 916 * controller is first remapped and the value of XOM[0] bit is read to
928 * determine the parent clock. 917 * determine the parent clock.
929 */ 918 */
930static void __init exynos4_clk_register_finpll(void) 919static unsigned long exynos4_get_xom(void)
931{ 920{
932 struct samsung_fixed_rate_clock fclk; 921 unsigned long xom = 0;
922 void __iomem *chipid_base;
933 struct device_node *np; 923 struct device_node *np;
934 struct clk *clk;
935 void __iomem *chipid_base = S5P_VA_CHIPID;
936 unsigned long xom, finpll_f = 24000000;
937 char *parent_name;
938 924
939 np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid"); 925 np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid");
940 if (np) 926 if (np) {
941 chipid_base = of_iomap(np, 0); 927 chipid_base = of_iomap(np, 0);
942 928
943 if (chipid_base) { 929 if (chipid_base)
944 xom = readl(chipid_base + 8); 930 xom = readl(chipid_base + 8);
945 parent_name = xom & 1 ? "xusbxti" : "xxti"; 931
946 clk = clk_get(NULL, parent_name); 932 iounmap(chipid_base);
947 if (IS_ERR(clk)) { 933 }
948 pr_err("%s: failed to lookup parent clock %s, assuming " 934
949 "fin_pll clock frequency is 24MHz\n", __func__, 935 return xom;
950 parent_name); 936}
951 } else { 937
952 finpll_f = clk_get_rate(clk); 938static void __init exynos4_clk_register_finpll(unsigned long xom)
953 } 939{
940 struct samsung_fixed_rate_clock fclk;
941 struct clk *clk;
942 unsigned long finpll_f = 24000000;
943 char *parent_name;
944
945 parent_name = xom & 1 ? "xusbxti" : "xxti";
946 clk = clk_get(NULL, parent_name);
947 if (IS_ERR(clk)) {
948 pr_err("%s: failed to lookup parent clock %s, assuming "
949 "fin_pll clock frequency is 24MHz\n", __func__,
950 parent_name);
954 } else { 951 } else {
955 pr_err("%s: failed to map chipid registers, assuming " 952 finpll_f = clk_get_rate(clk);
956 "fin_pll clock frequency is 24MHz\n", __func__);
957 } 953 }
958 954
959 fclk.id = fin_pll; 955 fclk.id = fin_pll;
@@ -963,8 +959,6 @@ static void __init exynos4_clk_register_finpll(void)
963 fclk.fixed_rate = finpll_f; 959 fclk.fixed_rate = finpll_f;
964 samsung_clk_register_fixed_rate(&fclk, 1); 960 samsung_clk_register_fixed_rate(&fclk, 1);
965 961
966 if (np)
967 iounmap(chipid_base);
968} 962}
969 963
970/* 964/*
@@ -988,28 +982,14 @@ static __initdata struct of_device_id ext_clk_match[] = {
988}; 982};
989 983
990/* register exynos4 clocks */ 984/* register exynos4 clocks */
991void __init exynos4_clk_init(struct device_node *np) 985void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom)
992{ 986{
993 void __iomem *reg_base;
994 struct clk *apll, *mpll, *epll, *vpll; 987 struct clk *apll, *mpll, *epll, *vpll;
995 u32 exynos4_soc;
996 988
997 if (np) { 989 if (np) {
998 const struct of_device_id *match;
999 match = of_match_node(exynos4_clk_ids, np);
1000 exynos4_soc = (u32)match->data;
1001
1002 reg_base = of_iomap(np, 0); 990 reg_base = of_iomap(np, 0);
1003 if (!reg_base) 991 if (!reg_base)
1004 panic("%s: failed to map registers\n", __func__); 992 panic("%s: failed to map registers\n", __func__);
1005 } else {
1006 reg_base = S5P_VA_CMU;
1007 if (soc_is_exynos4210())
1008 exynos4_soc = EXYNOS4210;
1009 else if (soc_is_exynos4212() || soc_is_exynos4412())
1010 exynos4_soc = EXYNOS4X12;
1011 else
1012 panic("%s: unable to determine soc\n", __func__);
1013 } 993 }
1014 994
1015 if (exynos4_soc == EXYNOS4210) 995 if (exynos4_soc == EXYNOS4210)
@@ -1026,7 +1006,7 @@ void __init exynos4_clk_init(struct device_node *np)
1026 ARRAY_SIZE(exynos4_fixed_rate_ext_clks), 1006 ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
1027 ext_clk_match); 1007 ext_clk_match);
1028 1008
1029 exynos4_clk_register_finpll(); 1009 exynos4_clk_register_finpll(xom);
1030 1010
1031 if (exynos4_soc == EXYNOS4210) { 1011 if (exynos4_soc == EXYNOS4210) {
1032 apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll", 1012 apll = samsung_clk_register_pll45xx("fout_apll", "fin_pll",
@@ -1087,5 +1067,16 @@ void __init exynos4_clk_init(struct device_node *np)
1087 _get_rate("sclk_epll"), _get_rate("sclk_vpll"), 1067 _get_rate("sclk_epll"), _get_rate("sclk_vpll"),
1088 _get_rate("arm_clk")); 1068 _get_rate("arm_clk"));
1089} 1069}
1090CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4_clk_init); 1070
1091CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4_clk_init); 1071
1072static void __init exynos4210_clk_init(struct device_node *np)
1073{
1074 exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
1075}
1076CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
1077
1078static void __init exynos4412_clk_init(struct device_node *np)
1079{
1080 exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
1081}
1082CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index bb54606ff035..5c97e75924a8 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -16,7 +16,6 @@
16#include <linux/of.h> 16#include <linux/of.h>
17#include <linux/of_address.h> 17#include <linux/of_address.h>
18 18
19#include <plat/cpu.h>
20#include "clk.h" 19#include "clk.h"
21#include "clk-pll.h" 20#include "clk-pll.h"
22 21
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
index a0a094c06f19..7d5434167a96 100644
--- a/drivers/clk/samsung/clk-exynos5440.c
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -15,7 +15,6 @@
15#include <linux/of.h> 15#include <linux/of.h>
16#include <linux/of_address.h> 16#include <linux/of_address.h>
17 17
18#include <plat/cpu.h>
19#include "clk.h" 18#include "clk.h"
20#include "clk-pll.h" 19#include "clk-pll.h"
21 20
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 10b2111f0c0f..e4ad6ea9aa76 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -20,8 +20,6 @@
20#include <linux/of.h> 20#include <linux/of.h>
21#include <linux/of_address.h> 21#include <linux/of_address.h>
22 22
23#include <mach/map.h>
24
25/** 23/**
26 * struct samsung_clock_alias: information about mux clock 24 * struct samsung_clock_alias: information about mux clock
27 * @id: platform specific id of the clock. 25 * @id: platform specific id of the clock.
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index c20de4a85cbd..f151c6cf27c3 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -76,3 +76,12 @@ config CLKSRC_EXYNOS_MCT
76 def_bool y if ARCH_EXYNOS 76 def_bool y if ARCH_EXYNOS
77 help 77 help
78 Support for Multi Core Timer controller on Exynos SoCs. 78 Support for Multi Core Timer controller on Exynos SoCs.
79
80config CLKSRC_SAMSUNG_PWM
81 bool
82 select CLKSRC_MMIO
83 help
84 This is a new clocksource driver for the PWM timer found in
85 Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
86 for all devicetree enabled platforms. This driver will be
87 needed only on systems that do not have the Exynos MCT available.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index caacdb63aff9..8d979c72aa94 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
25obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o 25obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o 26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o 27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
28obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
28 29
29obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o 30obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
30obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o 31obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 13a9e4923a03..662fcc065821 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -25,11 +25,6 @@
25#include <linux/clocksource.h> 25#include <linux/clocksource.h>
26 26
27#include <asm/localtimer.h> 27#include <asm/localtimer.h>
28
29#include <plat/cpu.h>
30
31#include <mach/map.h>
32#include <mach/irqs.h>
33#include <asm/mach/time.h> 28#include <asm/mach/time.h>
34 29
35#define EXYNOS4_MCTREG(x) (x) 30#define EXYNOS4_MCTREG(x) (x)
@@ -510,18 +505,14 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
510#endif /* CONFIG_LOCAL_TIMERS */ 505#endif /* CONFIG_LOCAL_TIMERS */
511} 506}
512 507
513void __init mct_init(void) 508void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
514{ 509{
515 if (soc_is_exynos4210()) { 510 mct_irqs[MCT_G0_IRQ] = irq_g0;
516 mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0; 511 mct_irqs[MCT_L0_IRQ] = irq_l0;
517 mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0; 512 mct_irqs[MCT_L1_IRQ] = irq_l1;
518 mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1; 513 mct_int_type = MCT_INT_SPI;
519 mct_int_type = MCT_INT_SPI;
520 } else {
521 panic("unable to determine mct controller type\n");
522 }
523 514
524 exynos4_timer_resources(NULL, S5P_VA_SYSTIMER); 515 exynos4_timer_resources(NULL, base);
525 exynos4_clocksource_init(); 516 exynos4_clocksource_init();
526 exynos4_clockevent_init(); 517 exynos4_clockevent_init();
527} 518}
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
new file mode 100644
index 000000000000..0234c8d2c8f2
--- /dev/null
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -0,0 +1,494 @@
1/*
2 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com/
4 *
5 * samsung - Common hr-timer support (s3c and s5p)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12#include <linux/interrupt.h>
13#include <linux/irq.h>
14#include <linux/err.h>
15#include <linux/clk.h>
16#include <linux/clockchips.h>
17#include <linux/list.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_address.h>
21#include <linux/of_irq.h>
22#include <linux/platform_device.h>
23#include <linux/slab.h>
24
25#include <clocksource/samsung_pwm.h>
26
27#include <asm/sched_clock.h>
28
29/*
30 * Clocksource driver
31 */
32
33#define REG_TCFG0 0x00
34#define REG_TCFG1 0x04
35#define REG_TCON 0x08
36#define REG_TINT_CSTAT 0x44
37
38#define REG_TCNTB(chan) (0x0c + 12 * (chan))
39#define REG_TCMPB(chan) (0x10 + 12 * (chan))
40
41#define TCFG0_PRESCALER_MASK 0xff
42#define TCFG0_PRESCALER1_SHIFT 8
43
44#define TCFG1_SHIFT(x) ((x) * 4)
45#define TCFG1_MUX_MASK 0xf
46
47#define TCON_START(chan) (1 << (4 * (chan) + 0))
48#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))
49#define TCON_INVERT(chan) (1 << (4 * (chan) + 2))
50#define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))
51
52DEFINE_SPINLOCK(samsung_pwm_lock);
53EXPORT_SYMBOL(samsung_pwm_lock);
54
55struct samsung_pwm_clocksource {
56 void __iomem *base;
57 unsigned int irq[SAMSUNG_PWM_NUM];
58 struct samsung_pwm_variant variant;
59
60 struct clk *timerclk;
61
62 unsigned int event_id;
63 unsigned int source_id;
64 unsigned int tcnt_max;
65 unsigned int tscaler_div;
66 unsigned int tdiv;
67
68 unsigned long clock_count_per_tick;
69};
70
71static struct samsung_pwm_clocksource pwm;
72
73static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)
74{
75 unsigned long flags;
76 u8 shift = 0;
77 u32 reg;
78
79 if (channel >= 2)
80 shift = TCFG0_PRESCALER1_SHIFT;
81
82 spin_lock_irqsave(&samsung_pwm_lock, flags);
83
84 reg = readl(pwm.base + REG_TCFG0);
85 reg &= ~(TCFG0_PRESCALER_MASK << shift);
86 reg |= (prescale - 1) << shift;
87 writel(reg, pwm.base + REG_TCFG0);
88
89 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
90}
91
92static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)
93{
94 u8 shift = TCFG1_SHIFT(channel);
95 unsigned long flags;
96 u32 reg;
97 u8 bits;
98
99 bits = (fls(divisor) - 1) - pwm.variant.div_base;
100
101 spin_lock_irqsave(&samsung_pwm_lock, flags);
102
103 reg = readl(pwm.base + REG_TCFG1);
104 reg &= ~(TCFG1_MUX_MASK << shift);
105 reg |= bits << shift;
106 writel(reg, pwm.base + REG_TCFG1);
107
108 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
109}
110
111static void samsung_time_stop(unsigned int channel)
112{
113 unsigned long tcon;
114 unsigned long flags;
115
116 if (channel > 0)
117 ++channel;
118
119 spin_lock_irqsave(&samsung_pwm_lock, flags);
120
121 tcon = __raw_readl(pwm.base + REG_TCON);
122 tcon &= ~TCON_START(channel);
123 __raw_writel(tcon, pwm.base + REG_TCON);
124
125 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
126}
127
128static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
129{
130 unsigned long tcon;
131 unsigned long flags;
132 unsigned int tcon_chan = channel;
133
134 if (tcon_chan > 0)
135 ++tcon_chan;
136
137 spin_lock_irqsave(&samsung_pwm_lock, flags);
138
139 tcon = __raw_readl(pwm.base + REG_TCON);
140
141 tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));
142 tcon |= TCON_MANUALUPDATE(tcon_chan);
143
144 __raw_writel(tcnt, pwm.base + REG_TCNTB(channel));
145 __raw_writel(tcnt, pwm.base + REG_TCMPB(channel));
146 __raw_writel(tcon, pwm.base + REG_TCON);
147
148 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
149}
150
151static void samsung_time_start(unsigned int channel, bool periodic)
152{
153 unsigned long tcon;
154 unsigned long flags;
155
156 if (channel > 0)
157 ++channel;
158
159 spin_lock_irqsave(&samsung_pwm_lock, flags);
160
161 tcon = __raw_readl(pwm.base + REG_TCON);
162
163 tcon &= ~TCON_MANUALUPDATE(channel);
164 tcon |= TCON_START(channel);
165
166 if (periodic)
167 tcon |= TCON_AUTORELOAD(channel);
168 else
169 tcon &= ~TCON_AUTORELOAD(channel);
170
171 __raw_writel(tcon, pwm.base + REG_TCON);
172
173 spin_unlock_irqrestore(&samsung_pwm_lock, flags);
174}
175
176static int samsung_set_next_event(unsigned long cycles,
177 struct clock_event_device *evt)
178{
179 /*
180 * This check is needed to account for internal rounding
181 * errors inside clockevents core, which might result in
182 * passing cycles = 0, which in turn would not generate any
183 * timer interrupt and hang the system.
184 *
185 * Another solution would be to set up the clockevent device
186 * with min_delta = 2, but this would unnecessarily increase
187 * the minimum sleep period.
188 */
189 if (!cycles)
190 cycles = 1;
191
192 samsung_time_setup(pwm.event_id, cycles);
193 samsung_time_start(pwm.event_id, false);
194
195 return 0;
196}
197
198static void samsung_timer_resume(void)
199{
200 /* event timer restart */
201 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
202 samsung_time_start(pwm.event_id, true);
203
204 /* source timer restart */
205 samsung_time_setup(pwm.source_id, pwm.tcnt_max);
206 samsung_time_start(pwm.source_id, true);
207}
208
209static void samsung_set_mode(enum clock_event_mode mode,
210 struct clock_event_device *evt)
211{
212 samsung_time_stop(pwm.event_id);
213
214 switch (mode) {
215 case CLOCK_EVT_MODE_PERIODIC:
216 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
217 samsung_time_start(pwm.event_id, true);
218 break;
219
220 case CLOCK_EVT_MODE_ONESHOT:
221 break;
222
223 case CLOCK_EVT_MODE_UNUSED:
224 case CLOCK_EVT_MODE_SHUTDOWN:
225 break;
226
227 case CLOCK_EVT_MODE_RESUME:
228 samsung_timer_resume();
229 break;
230 }
231}
232
233static struct clock_event_device time_event_device = {
234 .name = "samsung_event_timer",
235 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
236 .rating = 200,
237 .set_next_event = samsung_set_next_event,
238 .set_mode = samsung_set_mode,
239};
240
241static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)
242{
243 struct clock_event_device *evt = dev_id;
244
245 if (pwm.variant.has_tint_cstat) {
246 u32 mask = (1 << pwm.event_id);
247 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
248 }
249
250 evt->event_handler(evt);
251
252 return IRQ_HANDLED;
253}
254
255static struct irqaction samsung_clock_event_irq = {
256 .name = "samsung_time_irq",
257 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
258 .handler = samsung_clock_event_isr,
259 .dev_id = &time_event_device,
260};
261
262static void __init samsung_clockevent_init(void)
263{
264 unsigned long pclk;
265 unsigned long clock_rate;
266 unsigned int irq_number;
267
268 pclk = clk_get_rate(pwm.timerclk);
269
270 samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
271 samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
272
273 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
274 pwm.clock_count_per_tick = clock_rate / HZ;
275
276 time_event_device.cpumask = cpumask_of(0);
277 clockevents_config_and_register(&time_event_device,
278 clock_rate, 1, pwm.tcnt_max);
279
280 irq_number = pwm.irq[pwm.event_id];
281 setup_irq(irq_number, &samsung_clock_event_irq);
282
283 if (pwm.variant.has_tint_cstat) {
284 u32 mask = (1 << pwm.event_id);
285 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
286 }
287}
288
289static void __iomem *samsung_timer_reg(void)
290{
291 switch (pwm.source_id) {
292 case 0:
293 case 1:
294 case 2:
295 case 3:
296 return pwm.base + pwm.source_id * 0x0c + 0x14;
297
298 case 4:
299 return pwm.base + 0x40;
300
301 default:
302 BUG();
303 }
304}
305
306/*
307 * Override the global weak sched_clock symbol with this
308 * local implementation which uses the clocksource to get some
309 * better resolution when scheduling the kernel. We accept that
310 * this wraps around for now, since it is just a relative time
311 * stamp. (Inspired by U300 implementation.)
312 */
313static u32 notrace samsung_read_sched_clock(void)
314{
315 void __iomem *reg = samsung_timer_reg();
316
317 if (!reg)
318 return 0;
319
320 return ~__raw_readl(reg);
321}
322
323static void __init samsung_clocksource_init(void)
324{
325 void __iomem *reg = samsung_timer_reg();
326 unsigned long pclk;
327 unsigned long clock_rate;
328 int ret;
329
330 pclk = clk_get_rate(pwm.timerclk);
331
332 samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
333 samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
334
335 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
336
337 samsung_time_setup(pwm.source_id, pwm.tcnt_max);
338 samsung_time_start(pwm.source_id, true);
339
340 setup_sched_clock(samsung_read_sched_clock,
341 pwm.variant.bits, clock_rate);
342
343 ret = clocksource_mmio_init(reg, "samsung_clocksource_timer",
344 clock_rate, 250, pwm.variant.bits,
345 clocksource_mmio_readl_down);
346 if (ret)
347 panic("samsung_clocksource_timer: can't register clocksource\n");
348}
349
350static void __init samsung_timer_resources(void)
351{
352 pwm.timerclk = clk_get(NULL, "timers");
353 if (IS_ERR(pwm.timerclk))
354 panic("failed to get timers clock for timer");
355
356 clk_prepare_enable(pwm.timerclk);
357
358 pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;
359 if (pwm.variant.bits == 16) {
360 pwm.tscaler_div = 25;
361 pwm.tdiv = 2;
362 } else {
363 pwm.tscaler_div = 2;
364 pwm.tdiv = 1;
365 }
366}
367
368/*
369 * PWM master driver
370 */
371static void __init _samsung_pwm_clocksource_init(void)
372{
373 u8 mask;
374 int channel;
375
376 mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);
377 channel = fls(mask) - 1;
378 if (channel < 0)
379 panic("failed to find PWM channel for clocksource");
380 pwm.source_id = channel;
381
382 mask &= ~(1 << channel);
383 channel = fls(mask) - 1;
384 if (channel < 0)
385 panic("failed to find PWM channel for clock event");
386 pwm.event_id = channel;
387
388 samsung_timer_resources();
389 samsung_clockevent_init();
390 samsung_clocksource_init();
391}
392
393void __init samsung_pwm_clocksource_init(void __iomem *base,
394 unsigned int *irqs, struct samsung_pwm_variant *variant)
395{
396 pwm.base = base;
397 memcpy(&pwm.variant, variant, sizeof(pwm.variant));
398 memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));
399
400 _samsung_pwm_clocksource_init();
401}
402
403#ifdef CONFIG_CLKSRC_OF
404static void __init samsung_pwm_alloc(struct device_node *np,
405 const struct samsung_pwm_variant *variant)
406{
407 struct resource res;
408 struct property *prop;
409 const __be32 *cur;
410 u32 val;
411 int i;
412
413 memcpy(&pwm.variant, variant, sizeof(pwm.variant));
414 for (i = 0; i < SAMSUNG_PWM_NUM; ++i)
415 pwm.irq[i] = irq_of_parse_and_map(np, i);
416
417 of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
418 if (val >= SAMSUNG_PWM_NUM) {
419 pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
420 __func__);
421 continue;
422 }
423 pwm.variant.output_mask |= 1 << val;
424 }
425
426 of_address_to_resource(np, 0, &res);
427 if (!request_mem_region(res.start,
428 resource_size(&res), "samsung-pwm")) {
429 pr_err("%s: failed to request IO mem region\n", __func__);
430 return;
431 }
432
433 pwm.base = ioremap(res.start, resource_size(&res));
434 if (!pwm.base) {
435 pr_err("%s: failed to map PWM registers\n", __func__);
436 release_mem_region(res.start, resource_size(&res));
437 return;
438 }
439
440 _samsung_pwm_clocksource_init();
441}
442
443static const struct samsung_pwm_variant s3c24xx_variant = {
444 .bits = 16,
445 .div_base = 1,
446 .has_tint_cstat = false,
447 .tclk_mask = (1 << 4),
448};
449
450static void __init s3c2410_pwm_clocksource_init(struct device_node *np)
451{
452 samsung_pwm_alloc(np, &s3c24xx_variant);
453}
454CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
455
456static const struct samsung_pwm_variant s3c64xx_variant = {
457 .bits = 32,
458 .div_base = 0,
459 .has_tint_cstat = true,
460 .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5),
461};
462
463static void __init s3c64xx_pwm_clocksource_init(struct device_node *np)
464{
465 samsung_pwm_alloc(np, &s3c64xx_variant);
466}
467CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
468
469static const struct samsung_pwm_variant s5p64x0_variant = {
470 .bits = 32,
471 .div_base = 0,
472 .has_tint_cstat = true,
473 .tclk_mask = 0,
474};
475
476static void __init s5p64x0_pwm_clocksource_init(struct device_node *np)
477{
478 samsung_pwm_alloc(np, &s5p64x0_variant);
479}
480CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
481
482static const struct samsung_pwm_variant s5p_variant = {
483 .bits = 32,
484 .div_base = 0,
485 .has_tint_cstat = true,
486 .tclk_mask = (1 << 5),
487};
488
489static void __init s5p_pwm_clocksource_init(struct device_node *np)
490{
491 samsung_pwm_alloc(np, &s5p_variant);
492}
493CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
494#endif
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 02492ab20d22..a9d2b2fa4afd 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -12,13 +12,16 @@
12#include <linux/export.h> 12#include <linux/export.h>
13#include <linux/init.h> 13#include <linux/init.h>
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/slab.h>
15#include <linux/irqdomain.h> 16#include <linux/irqdomain.h>
16#include <linux/irqchip/chained_irq.h> 17#include <linux/irqchip/chained_irq.h>
17#include <linux/of_address.h> 18#include <linux/of_address.h>
18#include <linux/of_irq.h> 19#include <linux/of_irq.h>
19#include <asm/mach/irq.h> 20#include <asm/mach/irq.h>
20 21
22#ifdef CONFIG_EXYNOS_ATAGS
21#include <plat/cpu.h> 23#include <plat/cpu.h>
24#endif
22 25
23#include "irqchip.h" 26#include "irqchip.h"
24 27
@@ -26,17 +29,18 @@
26#define COMBINER_ENABLE_CLEAR 0x4 29#define COMBINER_ENABLE_CLEAR 0x4
27#define COMBINER_INT_STATUS 0xC 30#define COMBINER_INT_STATUS 0xC
28 31
32#define IRQ_IN_COMBINER 8
33
29static DEFINE_SPINLOCK(irq_controller_lock); 34static DEFINE_SPINLOCK(irq_controller_lock);
30 35
31struct combiner_chip_data { 36struct combiner_chip_data {
32 unsigned int irq_offset; 37 unsigned int hwirq_offset;
33 unsigned int irq_mask; 38 unsigned int irq_mask;
34 void __iomem *base; 39 void __iomem *base;
35 unsigned int parent_irq; 40 unsigned int parent_irq;
36}; 41};
37 42
38static struct irq_domain *combiner_irq_domain; 43static struct irq_domain *combiner_irq_domain;
39static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
40 44
41static inline void __iomem *combiner_base(struct irq_data *data) 45static inline void __iomem *combiner_base(struct irq_data *data)
42{ 46{
@@ -77,11 +81,11 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
77 if (status == 0) 81 if (status == 0)
78 goto out; 82 goto out;
79 83
80 combiner_irq = __ffs(status); 84 combiner_irq = chip_data->hwirq_offset + __ffs(status);
85 cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
81 86
82 cascade_irq = combiner_irq + (chip_data->irq_offset & ~31); 87 if (unlikely(!cascade_irq))
83 if (unlikely(cascade_irq >= NR_IRQS)) 88 do_bad_IRQ(irq, desc);
84 do_bad_IRQ(cascade_irq, desc);
85 else 89 else
86 generic_handle_irq(cascade_irq); 90 generic_handle_irq(cascade_irq);
87 91
@@ -113,40 +117,25 @@ static struct irq_chip combiner_chip = {
113#endif 117#endif
114}; 118};
115 119
116static unsigned int max_combiner_nr(void) 120static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
117{
118 if (soc_is_exynos5250())
119 return EXYNOS5_MAX_COMBINER_NR;
120 else if (soc_is_exynos4412())
121 return EXYNOS4412_MAX_COMBINER_NR;
122 else if (soc_is_exynos4212())
123 return EXYNOS4212_MAX_COMBINER_NR;
124 else
125 return EXYNOS4210_MAX_COMBINER_NR;
126}
127
128static void __init combiner_cascade_irq(unsigned int combiner_nr,
129 unsigned int irq) 121 unsigned int irq)
130{ 122{
131 if (combiner_nr >= max_combiner_nr()) 123 if (irq_set_handler_data(irq, combiner_data) != 0)
132 BUG();
133 if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
134 BUG(); 124 BUG();
135 irq_set_chained_handler(irq, combiner_handle_cascade_irq); 125 irq_set_chained_handler(irq, combiner_handle_cascade_irq);
136} 126}
137 127
138static void __init combiner_init_one(unsigned int combiner_nr, 128static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
129 unsigned int combiner_nr,
139 void __iomem *base, unsigned int irq) 130 void __iomem *base, unsigned int irq)
140{ 131{
141 combiner_data[combiner_nr].base = base; 132 combiner_data->base = base;
142 combiner_data[combiner_nr].irq_offset = irq_find_mapping( 133 combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
143 combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); 134 combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
144 combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); 135 combiner_data->parent_irq = irq;
145 combiner_data[combiner_nr].parent_irq = irq;
146 136
147 /* Disable all interrupts */ 137 /* Disable all interrupts */
148 __raw_writel(combiner_data[combiner_nr].irq_mask, 138 __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
149 base + COMBINER_ENABLE_CLEAR);
150} 139}
151 140
152#ifdef CONFIG_OF 141#ifdef CONFIG_OF
@@ -162,7 +151,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
162 if (intsize < 2) 151 if (intsize < 2)
163 return -EINVAL; 152 return -EINVAL;
164 153
165 *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1]; 154 *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1];
166 *out_type = 0; 155 *out_type = 0;
167 156
168 return 0; 157 return 0;
@@ -181,6 +170,8 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
181static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, 170static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
182 irq_hw_number_t hw) 171 irq_hw_number_t hw)
183{ 172{
173 struct combiner_chip_data *combiner_data = d->host_data;
174
184 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); 175 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
185 irq_set_chip_data(irq, &combiner_data[hw >> 3]); 176 irq_set_chip_data(irq, &combiner_data[hw >> 3]);
186 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 177 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@@ -193,8 +184,12 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
193 .map = combiner_irq_domain_map, 184 .map = combiner_irq_domain_map,
194}; 185};
195 186
196static unsigned int exynos4x12_combiner_extra_irq(int group) 187static unsigned int combiner_lookup_irq(int group)
197{ 188{
189#ifdef CONFIG_EXYNOS_ATAGS
190 if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
191 return IRQ_SPI(group);
192
198 switch (group) { 193 switch (group) {
199 case 16: 194 case 16:
200 return IRQ_SPI(107); 195 return IRQ_SPI(107);
@@ -204,53 +199,46 @@ static unsigned int exynos4x12_combiner_extra_irq(int group)
204 return IRQ_SPI(48); 199 return IRQ_SPI(48);
205 case 19: 200 case 19:
206 return IRQ_SPI(42); 201 return IRQ_SPI(42);
207 default:
208 return 0;
209 } 202 }
203#endif
204 return 0;
210} 205}
211 206
212void __init combiner_init(void __iomem *combiner_base, 207void __init combiner_init(void __iomem *combiner_base,
213 struct device_node *np) 208 struct device_node *np,
209 unsigned int max_nr,
210 int irq_base)
214{ 211{
215 int i, irq, irq_base; 212 int i, irq;
216 unsigned int max_nr, nr_irq; 213 unsigned int nr_irq;
214 struct combiner_chip_data *combiner_data;
217 215
218 max_nr = max_combiner_nr(); 216 nr_irq = max_nr * IRQ_IN_COMBINER;
219 217
220 if (np) { 218 combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
221 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { 219 if (!combiner_data) {
222 pr_info("%s: number of combiners not specified, " 220 pr_warning("%s: could not allocate combiner data\n", __func__);
223 "setting default as %d.\n", 221 return;
224 __func__, max_nr);
225 }
226 }
227
228 nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
229
230 irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
231 if (IS_ERR_VALUE(irq_base)) {
232 irq_base = COMBINER_IRQ(0, 0);
233 pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
234 } 222 }
235 223
236 combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0, 224 combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
237 &combiner_irq_domain_ops, &combiner_data); 225 &combiner_irq_domain_ops, combiner_data);
238 if (WARN_ON(!combiner_irq_domain)) { 226 if (WARN_ON(!combiner_irq_domain)) {
239 pr_warning("%s: irq domain init failed\n", __func__); 227 pr_warning("%s: irq domain init failed\n", __func__);
240 return; 228 return;
241 } 229 }
242 230
243 for (i = 0; i < max_nr; i++) { 231 for (i = 0; i < max_nr; i++) {
244 if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
245 irq = IRQ_SPI(i);
246 else
247 irq = exynos4x12_combiner_extra_irq(i);
248#ifdef CONFIG_OF 232#ifdef CONFIG_OF
249 if (np) 233 if (np)
250 irq = irq_of_parse_and_map(np, i); 234 irq = irq_of_parse_and_map(np, i);
235 else
251#endif 236#endif
252 combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq); 237 irq = combiner_lookup_irq(i);
253 combiner_cascade_irq(i, irq); 238
239 combiner_init_one(&combiner_data[i], i,
240 combiner_base + (i >> 2) * 0x10, irq);
241 combiner_cascade_irq(&combiner_data[i], irq);
254 } 242 }
255} 243}
256 244
@@ -259,6 +247,8 @@ static int __init combiner_of_init(struct device_node *np,
259 struct device_node *parent) 247 struct device_node *parent)
260{ 248{
261 void __iomem *combiner_base; 249 void __iomem *combiner_base;
250 unsigned int max_nr = 20;
251 int irq_base = -1;
262 252
263 combiner_base = of_iomap(np, 0); 253 combiner_base = of_iomap(np, 0);
264 if (!combiner_base) { 254 if (!combiner_base) {
@@ -266,7 +256,20 @@ static int __init combiner_of_init(struct device_node *np,
266 return -ENXIO; 256 return -ENXIO;
267 } 257 }
268 258
269 combiner_init(combiner_base, np); 259 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
260 pr_info("%s: number of combiners not specified, "
261 "setting default as %d.\n",
262 __func__, max_nr);
263 }
264
265 /*
266 * FIXME: This is a hardwired COMBINER_IRQ(0,0). Once all devices
267 * get their IRQ from DT, remove this in order to get dynamic
268 * allocation.
269 */
270 irq_base = 160;
271
272 combiner_init(combiner_base, np, max_nr, irq_base);
270 273
271 return 0; 274 return 0;
272} 275}