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