diff options
author | Olof Johansson <olof@lixom.net> | 2013-06-12 15:32:01 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2013-06-12 15:32:01 -0400 |
commit | 7d428ce284fce4aacb215ca15d0274e7261864a3 (patch) | |
tree | 2eef7a50a2a31989bf0af1858a46c3de2dbd9648 | |
parent | 6f39ef575df368ed77fcaa2f1d0f28191c7414fc (diff) | |
parent | 10021488997317d1121505a7ac659124c058efed (diff) |
Merge tag 'dw_apb_timer_of' of git://github.com/mmind/linux-rockchip into next/drivers
From Heiko Stuebner, enhancements for dw_apb_timer:
- use DECLARE_CLOCKSOURCE_OF and convert its users
- handle the sptimer not being present as sched_clock
- add optional handling of timer clocks
* tag 'dw_apb_timer_of' of git://github.com/mmind/linux-rockchip:
clocksource: dw_apb_timer_of: use clocksource_of_init
clocksource: dw_apb_timer_of: select DW_APB_TIMER
clocksource: dw_apb_timer_of: add clock-handling
clocksource: dw_apb_timer_of: enable the use the clocksource as sched clock
-rw-r--r-- | Documentation/devicetree/bindings/rtc/dw-apb.txt | 19 | ||||
-rw-r--r-- | arch/arm/mach-picoxcell/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-picoxcell/common.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-socfpga/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-socfpga/socfpga.c | 2 | ||||
-rw-r--r-- | drivers/clocksource/Kconfig | 2 | ||||
-rw-r--r-- | drivers/clocksource/dw_apb_timer_of.c | 95 | ||||
-rw-r--r-- | include/linux/dw_apb_timer.h | 1 |
8 files changed, 84 insertions, 39 deletions
diff --git a/Documentation/devicetree/bindings/rtc/dw-apb.txt b/Documentation/devicetree/bindings/rtc/dw-apb.txt index 93e2b0f048e6..eb2327b2bdb3 100644 --- a/Documentation/devicetree/bindings/rtc/dw-apb.txt +++ b/Documentation/devicetree/bindings/rtc/dw-apb.txt | |||
@@ -5,9 +5,20 @@ Required properties: | |||
5 | - reg: physical base address of the controller and length of memory mapped | 5 | - reg: physical base address of the controller and length of memory mapped |
6 | region. | 6 | region. |
7 | - interrupts: IRQ line for the timer. | 7 | - interrupts: IRQ line for the timer. |
8 | - either clocks+clock-names or clock-frequency properties | ||
9 | |||
10 | Optional properties: | ||
11 | - clocks : list of clock specifiers, corresponding to entries in | ||
12 | the clock-names property; | ||
13 | - clock-names : should contain "timer" and "pclk" entries, matching entries | ||
14 | in the clocks property. | ||
8 | - clock-frequency: The frequency in HZ of the timer. | 15 | - clock-frequency: The frequency in HZ of the timer. |
9 | - clock-freq: For backwards compatibility with picoxcell | 16 | - clock-freq: For backwards compatibility with picoxcell |
10 | 17 | ||
18 | If using the clock specifiers, the pclk clock is optional, as not all | ||
19 | systems may use one. | ||
20 | |||
21 | |||
11 | Example: | 22 | Example: |
12 | 23 | ||
13 | timer1: timer@ffc09000 { | 24 | timer1: timer@ffc09000 { |
@@ -23,3 +34,11 @@ Example: | |||
23 | clock-frequency = <200000000>; | 34 | clock-frequency = <200000000>; |
24 | reg = <0xffd00000 0x1000>; | 35 | reg = <0xffd00000 0x1000>; |
25 | }; | 36 | }; |
37 | |||
38 | timer3: timer@ffe00000 { | ||
39 | compatible = "snps,dw-apb-timer-osc"; | ||
40 | interrupts = <0 170 4>; | ||
41 | reg = <0xffe00000 0x1000>; | ||
42 | clocks = <&timer_clk>, <&timer_pclk>; | ||
43 | clock-names = "timer", "pclk"; | ||
44 | }; | ||
diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig index 13bae78b215a..b1022f4315f7 100644 --- a/arch/arm/mach-picoxcell/Kconfig +++ b/arch/arm/mach-picoxcell/Kconfig | |||
@@ -4,7 +4,6 @@ config ARCH_PICOXCELL | |||
4 | select ARM_PATCH_PHYS_VIRT | 4 | select ARM_PATCH_PHYS_VIRT |
5 | select ARM_VIC | 5 | select ARM_VIC |
6 | select CPU_V6K | 6 | select CPU_V6K |
7 | select DW_APB_TIMER | ||
8 | select DW_APB_TIMER_OF | 7 | select DW_APB_TIMER_OF |
9 | select GENERIC_CLOCKEVENTS | 8 | select GENERIC_CLOCKEVENTS |
10 | select HAVE_TCM | 9 | select HAVE_TCM |
diff --git a/arch/arm/mach-picoxcell/common.c b/arch/arm/mach-picoxcell/common.c index 70b441ad1d18..7cde0424d33c 100644 --- a/arch/arm/mach-picoxcell/common.c +++ b/arch/arm/mach-picoxcell/common.c | |||
@@ -15,7 +15,6 @@ | |||
15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
16 | #include <linux/of_irq.h> | 16 | #include <linux/of_irq.h> |
17 | #include <linux/of_platform.h> | 17 | #include <linux/of_platform.h> |
18 | #include <linux/dw_apb_timer.h> | ||
19 | 18 | ||
20 | #include <asm/mach/arch.h> | 19 | #include <asm/mach/arch.h> |
21 | #include <asm/mach/map.h> | 20 | #include <asm/mach/map.h> |
@@ -88,7 +87,6 @@ DT_MACHINE_START(PICOXCELL, "Picochip picoXcell") | |||
88 | .map_io = picoxcell_map_io, | 87 | .map_io = picoxcell_map_io, |
89 | .nr_irqs = NR_IRQS_LEGACY, | 88 | .nr_irqs = NR_IRQS_LEGACY, |
90 | .init_irq = irqchip_init, | 89 | .init_irq = irqchip_init, |
91 | .init_time = dw_apb_timer_init, | ||
92 | .init_machine = picoxcell_init_machine, | 90 | .init_machine = picoxcell_init_machine, |
93 | .dt_compat = picoxcell_dt_match, | 91 | .dt_compat = picoxcell_dt_match, |
94 | .restart = picoxcell_wdt_restart, | 92 | .restart = picoxcell_wdt_restart, |
diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 566e804d4036..a279fb315069 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig | |||
@@ -7,7 +7,6 @@ config ARCH_SOCFPGA | |||
7 | select CLKDEV_LOOKUP | 7 | select CLKDEV_LOOKUP |
8 | select COMMON_CLK | 8 | select COMMON_CLK |
9 | select CPU_V7 | 9 | select CPU_V7 |
10 | select DW_APB_TIMER | ||
11 | select DW_APB_TIMER_OF | 10 | select DW_APB_TIMER_OF |
12 | select GENERIC_CLOCKEVENTS | 11 | select GENERIC_CLOCKEVENTS |
13 | select GPIO_PL061 if GPIOLIB | 12 | select GPIO_PL061 if GPIOLIB |
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 46a051359f02..8ea11b472b91 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c | |||
@@ -14,7 +14,6 @@ | |||
14 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | */ | 16 | */ |
17 | #include <linux/dw_apb_timer.h> | ||
18 | #include <linux/clk-provider.h> | 17 | #include <linux/clk-provider.h> |
19 | #include <linux/irqchip.h> | 18 | #include <linux/irqchip.h> |
20 | #include <linux/of_address.h> | 19 | #include <linux/of_address.h> |
@@ -120,7 +119,6 @@ DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") | |||
120 | .smp = smp_ops(socfpga_smp_ops), | 119 | .smp = smp_ops(socfpga_smp_ops), |
121 | .map_io = socfpga_map_io, | 120 | .map_io = socfpga_map_io, |
122 | .init_irq = socfpga_init_irq, | 121 | .init_irq = socfpga_init_irq, |
123 | .init_time = dw_apb_timer_init, | ||
124 | .init_machine = socfpga_cyclone5_init, | 122 | .init_machine = socfpga_cyclone5_init, |
125 | .restart = socfpga_cyclone5_restart, | 123 | .restart = socfpga_cyclone5_restart, |
126 | .dt_compat = altera_dt_match, | 124 | .dt_compat = altera_dt_match, |
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index f151c6cf27c3..5871933c4e51 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -21,6 +21,8 @@ config DW_APB_TIMER | |||
21 | 21 | ||
22 | config DW_APB_TIMER_OF | 22 | config DW_APB_TIMER_OF |
23 | bool | 23 | bool |
24 | select DW_APB_TIMER | ||
25 | select CLKSRC_OF | ||
24 | 26 | ||
25 | config ARMADA_370_XP_TIMER | 27 | config ARMADA_370_XP_TIMER |
26 | bool | 28 | bool |
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index ab09ed3742ee..cef554432a33 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
23 | #include <linux/clk.h> | ||
23 | 24 | ||
24 | #include <asm/mach/time.h> | 25 | #include <asm/mach/time.h> |
25 | #include <asm/sched_clock.h> | 26 | #include <asm/sched_clock.h> |
@@ -27,14 +28,37 @@ | |||
27 | static void timer_get_base_and_rate(struct device_node *np, | 28 | static void timer_get_base_and_rate(struct device_node *np, |
28 | void __iomem **base, u32 *rate) | 29 | void __iomem **base, u32 *rate) |
29 | { | 30 | { |
31 | struct clk *timer_clk; | ||
32 | struct clk *pclk; | ||
33 | |||
30 | *base = of_iomap(np, 0); | 34 | *base = of_iomap(np, 0); |
31 | 35 | ||
32 | if (!*base) | 36 | if (!*base) |
33 | panic("Unable to map regs for %s", np->name); | 37 | panic("Unable to map regs for %s", np->name); |
34 | 38 | ||
39 | /* | ||
40 | * Not all implementations use a periphal clock, so don't panic | ||
41 | * if it's not present | ||
42 | */ | ||
43 | pclk = of_clk_get_by_name(np, "pclk"); | ||
44 | if (!IS_ERR(pclk)) | ||
45 | if (clk_prepare_enable(pclk)) | ||
46 | pr_warn("pclk for %s is present, but could not be activated\n", | ||
47 | np->name); | ||
48 | |||
49 | timer_clk = of_clk_get_by_name(np, "timer"); | ||
50 | if (IS_ERR(timer_clk)) | ||
51 | goto try_clock_freq; | ||
52 | |||
53 | if (!clk_prepare_enable(timer_clk)) { | ||
54 | *rate = clk_get_rate(timer_clk); | ||
55 | return; | ||
56 | } | ||
57 | |||
58 | try_clock_freq: | ||
35 | if (of_property_read_u32(np, "clock-freq", rate) && | 59 | if (of_property_read_u32(np, "clock-freq", rate) && |
36 | of_property_read_u32(np, "clock-frequency", rate)) | 60 | of_property_read_u32(np, "clock-frequency", rate)) |
37 | panic("No clock-frequency property for %s", np->name); | 61 | panic("No clock nor clock-frequency property for %s", np->name); |
38 | } | 62 | } |
39 | 63 | ||
40 | static void add_clockevent(struct device_node *event_timer) | 64 | static void add_clockevent(struct device_node *event_timer) |
@@ -57,6 +81,9 @@ static void add_clockevent(struct device_node *event_timer) | |||
57 | dw_apb_clockevent_register(ced); | 81 | dw_apb_clockevent_register(ced); |
58 | } | 82 | } |
59 | 83 | ||
84 | static void __iomem *sched_io_base; | ||
85 | static u32 sched_rate; | ||
86 | |||
60 | static void add_clocksource(struct device_node *source_timer) | 87 | static void add_clocksource(struct device_node *source_timer) |
61 | { | 88 | { |
62 | void __iomem *iobase; | 89 | void __iomem *iobase; |
@@ -71,9 +98,15 @@ static void add_clocksource(struct device_node *source_timer) | |||
71 | 98 | ||
72 | dw_apb_clocksource_start(cs); | 99 | dw_apb_clocksource_start(cs); |
73 | dw_apb_clocksource_register(cs); | 100 | dw_apb_clocksource_register(cs); |
74 | } | ||
75 | 101 | ||
76 | static void __iomem *sched_io_base; | 102 | /* |
103 | * Fallback to use the clocksource as sched_clock if no separate | ||
104 | * timer is found. sched_io_base then points to the current_value | ||
105 | * register of the clocksource timer. | ||
106 | */ | ||
107 | sched_io_base = iobase + 0x04; | ||
108 | sched_rate = rate; | ||
109 | } | ||
77 | 110 | ||
78 | static u32 read_sched_clock(void) | 111 | static u32 read_sched_clock(void) |
79 | { | 112 | { |
@@ -89,39 +122,37 @@ static const struct of_device_id sptimer_ids[] __initconst = { | |||
89 | static void init_sched_clock(void) | 122 | static void init_sched_clock(void) |
90 | { | 123 | { |
91 | struct device_node *sched_timer; | 124 | struct device_node *sched_timer; |
92 | u32 rate; | ||
93 | 125 | ||
94 | sched_timer = of_find_matching_node(NULL, sptimer_ids); | 126 | sched_timer = of_find_matching_node(NULL, sptimer_ids); |
95 | if (!sched_timer) | 127 | if (sched_timer) { |
96 | panic("No RTC for sched clock to use"); | 128 | timer_get_base_and_rate(sched_timer, &sched_io_base, |
129 | &sched_rate); | ||
130 | of_node_put(sched_timer); | ||
131 | } | ||
97 | 132 | ||
98 | timer_get_base_and_rate(sched_timer, &sched_io_base, &rate); | 133 | setup_sched_clock(read_sched_clock, 32, sched_rate); |
99 | of_node_put(sched_timer); | ||
100 | |||
101 | setup_sched_clock(read_sched_clock, 32, rate); | ||
102 | } | 134 | } |
103 | 135 | ||
104 | static const struct of_device_id osctimer_ids[] __initconst = { | 136 | static int num_called; |
105 | { .compatible = "picochip,pc3x2-timer" }, | 137 | static void __init dw_apb_timer_init(struct device_node *timer) |
106 | { .compatible = "snps,dw-apb-timer-osc" }, | ||
107 | {}, | ||
108 | }; | ||
109 | |||
110 | void __init dw_apb_timer_init(void) | ||
111 | { | 138 | { |
112 | struct device_node *event_timer, *source_timer; | 139 | switch (num_called) { |
113 | 140 | case 0: | |
114 | event_timer = of_find_matching_node(NULL, osctimer_ids); | 141 | pr_debug("%s: found clockevent timer\n", __func__); |
115 | if (!event_timer) | 142 | add_clockevent(timer); |
116 | panic("No timer for clockevent"); | 143 | of_node_put(timer); |
117 | add_clockevent(event_timer); | 144 | break; |
118 | 145 | case 1: | |
119 | source_timer = of_find_matching_node(event_timer, osctimer_ids); | 146 | pr_debug("%s: found clocksource timer\n", __func__); |
120 | if (!source_timer) | 147 | add_clocksource(timer); |
121 | panic("No timer for clocksource"); | 148 | of_node_put(timer); |
122 | add_clocksource(source_timer); | 149 | init_sched_clock(); |
123 | 150 | break; | |
124 | of_node_put(source_timer); | 151 | default: |
125 | 152 | break; | |
126 | init_sched_clock(); | 153 | } |
154 | |||
155 | num_called++; | ||
127 | } | 156 | } |
157 | CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); | ||
158 | CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer-osc", dw_apb_timer_init); | ||
diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h index dd755ce2a5eb..07261d52a6df 100644 --- a/include/linux/dw_apb_timer.h +++ b/include/linux/dw_apb_timer.h | |||
@@ -53,5 +53,4 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs); | |||
53 | cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs); | 53 | cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs); |
54 | void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs); | 54 | void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs); |
55 | 55 | ||
56 | extern void dw_apb_timer_init(void); | ||
57 | #endif /* __DW_APB_TIMER_H__ */ | 56 | #endif /* __DW_APB_TIMER_H__ */ |