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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQEcBAABCAAGBQJRuGStAAoJEPOmecmc0R2BLWcH/iHYyHg0IimZFFngD5H50F+S WO+R3xVUz9Y1/Hlsh9IQK9R2f/z2QUje1sQOVZhx4WYMAarUTAsX/oDs7TnPyoZ6 MaFJHKsAysXukiWWrrb1MaG8NGUqh4nt4YwCVsBHkG02i9dA3qy55+3BoMg3YiKP afWyXbJ/9IP1q9U9/tIjYO7fHDUmHtKRIo1MTx7erCm3gBSMUw7fWvs0Gc0QqUAn cfCnwilK8/BAnCTOi8RiTdUmQaI5CqGrNcBTw47E88HmoQVkp5W6BCY0brw4CfE8 XeqchZH6wEH89hRmf/0+zGuLT4qQ+jBL8bM88+KEbnsXREiIKFU/oZYygqskY60= =geqn -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
7d428ce284
8 changed files with 83 additions and 38 deletions
|
@ -5,9 +5,20 @@ Required properties:
|
||||||
- reg: physical base address of the controller and length of memory mapped
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
region.
|
region.
|
||||||
- interrupts: IRQ line for the timer.
|
- 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-frequency: The frequency in HZ of the timer.
|
||||||
- clock-freq: For backwards compatibility with picoxcell
|
- 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:
|
Example:
|
||||||
|
|
||||||
timer1: timer@ffc09000 {
|
timer1: timer@ffc09000 {
|
||||||
|
@ -23,3 +34,11 @@ Example:
|
||||||
clock-frequency = <200000000>;
|
clock-frequency = <200000000>;
|
||||||
reg = <0xffd00000 0x1000>;
|
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";
|
||||||
|
};
|
||||||
|
|
|
@ -4,7 +4,6 @@ config ARCH_PICOXCELL
|
||||||
select ARM_PATCH_PHYS_VIRT
|
select ARM_PATCH_PHYS_VIRT
|
||||||
select ARM_VIC
|
select ARM_VIC
|
||||||
select CPU_V6K
|
select CPU_V6K
|
||||||
select DW_APB_TIMER
|
|
||||||
select DW_APB_TIMER_OF
|
select DW_APB_TIMER_OF
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select HAVE_TCM
|
select HAVE_TCM
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/dw_apb_timer.h>
|
|
||||||
|
|
||||||
#include <asm/mach/arch.h>
|
#include <asm/mach/arch.h>
|
||||||
#include <asm/mach/map.h>
|
#include <asm/mach/map.h>
|
||||||
|
@ -88,7 +87,6 @@ DT_MACHINE_START(PICOXCELL, "Picochip picoXcell")
|
||||||
.map_io = picoxcell_map_io,
|
.map_io = picoxcell_map_io,
|
||||||
.nr_irqs = NR_IRQS_LEGACY,
|
.nr_irqs = NR_IRQS_LEGACY,
|
||||||
.init_irq = irqchip_init,
|
.init_irq = irqchip_init,
|
||||||
.init_time = dw_apb_timer_init,
|
|
||||||
.init_machine = picoxcell_init_machine,
|
.init_machine = picoxcell_init_machine,
|
||||||
.dt_compat = picoxcell_dt_match,
|
.dt_compat = picoxcell_dt_match,
|
||||||
.restart = picoxcell_wdt_restart,
|
.restart = picoxcell_wdt_restart,
|
||||||
|
|
|
@ -7,7 +7,6 @@ config ARCH_SOCFPGA
|
||||||
select CLKDEV_LOOKUP
|
select CLKDEV_LOOKUP
|
||||||
select COMMON_CLK
|
select COMMON_CLK
|
||||||
select CPU_V7
|
select CPU_V7
|
||||||
select DW_APB_TIMER
|
|
||||||
select DW_APB_TIMER_OF
|
select DW_APB_TIMER_OF
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select GPIO_PL061 if GPIOLIB
|
select GPIO_PL061 if GPIOLIB
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <linux/dw_apb_timer.h>
|
|
||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
@ -120,7 +119,6 @@ DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA")
|
||||||
.smp = smp_ops(socfpga_smp_ops),
|
.smp = smp_ops(socfpga_smp_ops),
|
||||||
.map_io = socfpga_map_io,
|
.map_io = socfpga_map_io,
|
||||||
.init_irq = socfpga_init_irq,
|
.init_irq = socfpga_init_irq,
|
||||||
.init_time = dw_apb_timer_init,
|
|
||||||
.init_machine = socfpga_cyclone5_init,
|
.init_machine = socfpga_cyclone5_init,
|
||||||
.restart = socfpga_cyclone5_restart,
|
.restart = socfpga_cyclone5_restart,
|
||||||
.dt_compat = altera_dt_match,
|
.dt_compat = altera_dt_match,
|
||||||
|
|
|
@ -21,6 +21,8 @@ config DW_APB_TIMER
|
||||||
|
|
||||||
config DW_APB_TIMER_OF
|
config DW_APB_TIMER_OF
|
||||||
bool
|
bool
|
||||||
|
select DW_APB_TIMER
|
||||||
|
select CLKSRC_OF
|
||||||
|
|
||||||
config ARMADA_370_XP_TIMER
|
config ARMADA_370_XP_TIMER
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#include <asm/mach/time.h>
|
#include <asm/mach/time.h>
|
||||||
#include <asm/sched_clock.h>
|
#include <asm/sched_clock.h>
|
||||||
|
@ -27,14 +28,37 @@
|
||||||
static void timer_get_base_and_rate(struct device_node *np,
|
static void timer_get_base_and_rate(struct device_node *np,
|
||||||
void __iomem **base, u32 *rate)
|
void __iomem **base, u32 *rate)
|
||||||
{
|
{
|
||||||
|
struct clk *timer_clk;
|
||||||
|
struct clk *pclk;
|
||||||
|
|
||||||
*base = of_iomap(np, 0);
|
*base = of_iomap(np, 0);
|
||||||
|
|
||||||
if (!*base)
|
if (!*base)
|
||||||
panic("Unable to map regs for %s", np->name);
|
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) &&
|
if (of_property_read_u32(np, "clock-freq", rate) &&
|
||||||
of_property_read_u32(np, "clock-frequency", 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)
|
static void add_clockevent(struct device_node *event_timer)
|
||||||
|
@ -57,6 +81,9 @@ static void add_clockevent(struct device_node *event_timer)
|
||||||
dw_apb_clockevent_register(ced);
|
dw_apb_clockevent_register(ced);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __iomem *sched_io_base;
|
||||||
|
static u32 sched_rate;
|
||||||
|
|
||||||
static void add_clocksource(struct device_node *source_timer)
|
static void add_clocksource(struct device_node *source_timer)
|
||||||
{
|
{
|
||||||
void __iomem *iobase;
|
void __iomem *iobase;
|
||||||
|
@ -71,9 +98,15 @@ static void add_clocksource(struct device_node *source_timer)
|
||||||
|
|
||||||
dw_apb_clocksource_start(cs);
|
dw_apb_clocksource_start(cs);
|
||||||
dw_apb_clocksource_register(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)
|
static u32 read_sched_clock(void)
|
||||||
{
|
{
|
||||||
|
@ -89,39 +122,37 @@ static const struct of_device_id sptimer_ids[] __initconst = {
|
||||||
static void init_sched_clock(void)
|
static void init_sched_clock(void)
|
||||||
{
|
{
|
||||||
struct device_node *sched_timer;
|
struct device_node *sched_timer;
|
||||||
u32 rate;
|
|
||||||
|
|
||||||
sched_timer = of_find_matching_node(NULL, sptimer_ids);
|
sched_timer = of_find_matching_node(NULL, sptimer_ids);
|
||||||
if (!sched_timer)
|
if (sched_timer) {
|
||||||
panic("No RTC for sched clock to use");
|
timer_get_base_and_rate(sched_timer, &sched_io_base,
|
||||||
|
&sched_rate);
|
||||||
|
of_node_put(sched_timer);
|
||||||
|
}
|
||||||
|
|
||||||
timer_get_base_and_rate(sched_timer, &sched_io_base, &rate);
|
setup_sched_clock(read_sched_clock, 32, sched_rate);
|
||||||
of_node_put(sched_timer);
|
|
||||||
|
|
||||||
setup_sched_clock(read_sched_clock, 32, rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id osctimer_ids[] __initconst = {
|
static int num_called;
|
||||||
{ .compatible = "picochip,pc3x2-timer" },
|
static void __init dw_apb_timer_init(struct device_node *timer)
|
||||||
{ .compatible = "snps,dw-apb-timer-osc" },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
|
|
||||||
void __init dw_apb_timer_init(void)
|
|
||||||
{
|
{
|
||||||
struct device_node *event_timer, *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;
|
||||||
|
}
|
||||||
|
|
||||||
event_timer = of_find_matching_node(NULL, osctimer_ids);
|
num_called++;
|
||||||
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);
|
|
||||||
|
|
||||||
init_sched_clock();
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
|
|
@ -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);
|
cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs);
|
||||||
void dw_apb_clocksource_unregister(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__ */
|
#endif /* __DW_APB_TIMER_H__ */
|
||||||
|
|
Loading…
Add table
Reference in a new issue