From a1198f83407ae3421f3f58355a0f296d5ea6249c Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 4 Jun 2013 11:37:02 +0200 Subject: clocksource: dw_apb_timer_of: enable the use the clocksource as sched clock Currently the dw_apb_timer always expects a separate special timer to be availbable for the sched_clock. Some devices using dw_apb_timers do not have this sptimer but can use the clocksource as sched_clock instead. Therefore enable the driver to distiguish between devices with and without sptimer based on the devicetree data and select the correct timer as sched_clock. Signed-off-by: Heiko Stuebner Acked-by: Linus Walleij Acked-by: Jamie Iles --- drivers/clocksource/dw_apb_timer_of.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/clocksource/dw_apb_timer_of.c') diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index ab09ed3742ee..d6c0fda76a7e 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -57,6 +57,9 @@ static void add_clockevent(struct device_node *event_timer) dw_apb_clockevent_register(ced); } +static void __iomem *sched_io_base; +static u32 sched_rate; + static void add_clocksource(struct device_node *source_timer) { void __iomem *iobase; @@ -71,9 +74,15 @@ static void add_clocksource(struct device_node *source_timer) dw_apb_clocksource_start(cs); dw_apb_clocksource_register(cs); -} -static void __iomem *sched_io_base; + /* + * Fallback to use the clocksource as sched_clock if no separate + * timer is found. sched_io_base then points to the current_value + * register of the clocksource timer. + */ + sched_io_base = iobase + 0x04; + sched_rate = rate; +} static u32 read_sched_clock(void) { @@ -89,16 +98,15 @@ static const struct of_device_id sptimer_ids[] __initconst = { static void init_sched_clock(void) { struct device_node *sched_timer; - u32 rate; sched_timer = of_find_matching_node(NULL, sptimer_ids); - if (!sched_timer) - panic("No RTC for sched clock to use"); - - timer_get_base_and_rate(sched_timer, &sched_io_base, &rate); - of_node_put(sched_timer); + if (sched_timer) { + timer_get_base_and_rate(sched_timer, &sched_io_base, + &sched_rate); + of_node_put(sched_timer); + } - setup_sched_clock(read_sched_clock, 32, rate); + setup_sched_clock(read_sched_clock, 32, sched_rate); } static const struct of_device_id osctimer_ids[] __initconst = { -- cgit v1.2.3-70-g09d2 From a8b447f2bbbba737ff4478f498d7f83c75a9461b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 4 Jun 2013 11:37:36 +0200 Subject: clocksource: dw_apb_timer_of: add clock-handling Add the possibility to get the clock-frequency from a timer clock instead of specifying it as dt property. Additionally also add the possibility to also define a controlling periphal clock for the timer block. The clock-frequency property is kept to act as fallback if no clocks are specified. Signed-off-by: Heiko Stuebner Acked-by: Jamie Iles --- Documentation/devicetree/bindings/rtc/dw-apb.txt | 19 +++++++++++++++++ drivers/clocksource/dw_apb_timer_of.c | 26 +++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'drivers/clocksource/dw_apb_timer_of.c') 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: - reg: physical base address of the controller and length of memory mapped region. - interrupts: IRQ line for the timer. +- either clocks+clock-names or clock-frequency properties + +Optional properties: +- clocks : list of clock specifiers, corresponding to entries in + the clock-names property; +- clock-names : should contain "timer" and "pclk" entries, matching entries + in the clocks property. - clock-frequency: The frequency in HZ of the timer. - clock-freq: For backwards compatibility with picoxcell +If using the clock specifiers, the pclk clock is optional, as not all +systems may use one. + + Example: timer1: timer@ffc09000 { @@ -23,3 +34,11 @@ Example: clock-frequency = <200000000>; reg = <0xffd00000 0x1000>; }; + + timer3: timer@ffe00000 { + compatible = "snps,dw-apb-timer-osc"; + interrupts = <0 170 4>; + reg = <0xffe00000 0x1000>; + clocks = <&timer_clk>, <&timer_pclk>; + clock-names = "timer", "pclk"; + }; diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index d6c0fda76a7e..1964f8716966 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -27,14 +28,37 @@ static void timer_get_base_and_rate(struct device_node *np, void __iomem **base, u32 *rate) { + struct clk *timer_clk; + struct clk *pclk; + *base = of_iomap(np, 0); if (!*base) panic("Unable to map regs for %s", np->name); + /* + * Not all implementations use a periphal clock, so don't panic + * if it's not present + */ + pclk = of_clk_get_by_name(np, "pclk"); + if (!IS_ERR(pclk)) + if (clk_prepare_enable(pclk)) + pr_warn("pclk for %s is present, but could not be activated\n", + np->name); + + timer_clk = of_clk_get_by_name(np, "timer"); + if (IS_ERR(timer_clk)) + goto try_clock_freq; + + if (!clk_prepare_enable(timer_clk)) { + *rate = clk_get_rate(timer_clk); + return; + } + +try_clock_freq: if (of_property_read_u32(np, "clock-freq", rate) && of_property_read_u32(np, "clock-frequency", rate)) - panic("No clock-frequency property for %s", np->name); + panic("No clock nor clock-frequency property for %s", np->name); } static void add_clockevent(struct device_node *event_timer) -- cgit v1.2.3-70-g09d2 From 10021488997317d1121505a7ac659124c058efed Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 4 Jun 2013 11:38:42 +0200 Subject: clocksource: dw_apb_timer_of: use clocksource_of_init dw_apb_timer_init used to search the devicetree for matching timer devices, making calls to it from board files necessary. Change the dw_apb_timer_init to work with CLOCKSOURCE_OF_DECLARE. With this change the function gets called once for each timer node and tracks these number of calls to attach clockevent and clocksource devices to the nodes. Also remove the calls to dw_apb_timer_init from all previous users, as clocksource_of_init is the default for init_time now. Tested on the upcoming rk3066 code. Signed-off-by: Heiko Stuebner Acked-by: Rob Herring Acked-by: Arnd Bergmann Acked-by: Jamie Iles Acked-by: Dinh Nguyen --- arch/arm/mach-picoxcell/common.c | 2 -- arch/arm/mach-socfpga/socfpga.c | 2 -- drivers/clocksource/Kconfig | 1 + drivers/clocksource/dw_apb_timer_of.c | 41 +++++++++++++++++------------------ include/linux/dw_apb_timer.h | 1 - 5 files changed, 21 insertions(+), 26 deletions(-) (limited to 'drivers/clocksource/dw_apb_timer_of.c') 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 @@ #include #include #include -#include #include #include @@ -88,7 +87,6 @@ DT_MACHINE_START(PICOXCELL, "Picochip picoXcell") .map_io = picoxcell_map_io, .nr_irqs = NR_IRQS_LEGACY, .init_irq = irqchip_init, - .init_time = dw_apb_timer_init, .init_machine = picoxcell_init_machine, .dt_compat = picoxcell_dt_match, .restart = picoxcell_wdt_restart, 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 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include #include #include #include @@ -120,7 +119,6 @@ DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") .smp = smp_ops(socfpga_smp_ops), .map_io = socfpga_map_io, .init_irq = socfpga_init_irq, - .init_time = dw_apb_timer_init, .init_machine = socfpga_cyclone5_init, .restart = socfpga_cyclone5_restart, .dt_compat = altera_dt_match, diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 48a0f2e1dd78..5871933c4e51 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -22,6 +22,7 @@ config DW_APB_TIMER config DW_APB_TIMER_OF bool select DW_APB_TIMER + select CLKSRC_OF config ARMADA_370_XP_TIMER bool diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 1964f8716966..cef554432a33 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -133,27 +133,26 @@ static void init_sched_clock(void) setup_sched_clock(read_sched_clock, 32, sched_rate); } -static const struct of_device_id osctimer_ids[] __initconst = { - { .compatible = "picochip,pc3x2-timer" }, - { .compatible = "snps,dw-apb-timer-osc" }, - {}, -}; - -void __init dw_apb_timer_init(void) +static int num_called; +static void __init dw_apb_timer_init(struct device_node *timer) { - struct device_node *event_timer, *source_timer; - - event_timer = of_find_matching_node(NULL, osctimer_ids); - if (!event_timer) - panic("No timer for clockevent"); - add_clockevent(event_timer); - - source_timer = of_find_matching_node(event_timer, osctimer_ids); - if (!source_timer) - panic("No timer for clocksource"); - add_clocksource(source_timer); - - of_node_put(source_timer); + switch (num_called) { + case 0: + pr_debug("%s: found clockevent timer\n", __func__); + add_clockevent(timer); + of_node_put(timer); + break; + case 1: + pr_debug("%s: found clocksource timer\n", __func__); + add_clocksource(timer); + of_node_put(timer); + init_sched_clock(); + break; + default: + break; + } - init_sched_clock(); + num_called++; } +CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init); +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); cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs); void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs); -extern void dw_apb_timer_init(void); #endif /* __DW_APB_TIMER_H__ */ -- cgit v1.2.3-70-g09d2