1 /*
2  * Copyright (c) 2011 Picochip Ltd., Jamie Iles
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * All enquiries to support@picochip.com
9  */
10 #include <linux/dw_apb_timer.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/of_irq.h>
14 
15 #include <asm/mach/time.h>
16 #include <asm/sched_clock.h>
17 
18 #include "common.h"
19 
timer_get_base_and_rate(struct device_node * np,void __iomem ** base,u32 * rate)20 static void timer_get_base_and_rate(struct device_node *np,
21 				    void __iomem **base, u32 *rate)
22 {
23 	*base = of_iomap(np, 0);
24 
25 	if (!*base)
26 		panic("Unable to map regs for %s", np->name);
27 
28 	if (of_property_read_u32(np, "clock-freq", rate))
29 		panic("No clock-freq property for %s", np->name);
30 }
31 
picoxcell_add_clockevent(struct device_node * event_timer)32 static void picoxcell_add_clockevent(struct device_node *event_timer)
33 {
34 	void __iomem *iobase;
35 	struct dw_apb_clock_event_device *ced;
36 	u32 irq, rate;
37 
38 	irq = irq_of_parse_and_map(event_timer, 0);
39 	if (irq == NO_IRQ)
40 		panic("No IRQ for clock event timer");
41 
42 	timer_get_base_and_rate(event_timer, &iobase, &rate);
43 
44 	ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq,
45 				     rate);
46 	if (!ced)
47 		panic("Unable to initialise clockevent device");
48 
49 	dw_apb_clockevent_register(ced);
50 }
51 
picoxcell_add_clocksource(struct device_node * source_timer)52 static void picoxcell_add_clocksource(struct device_node *source_timer)
53 {
54 	void __iomem *iobase;
55 	struct dw_apb_clocksource *cs;
56 	u32 rate;
57 
58 	timer_get_base_and_rate(source_timer, &iobase, &rate);
59 
60 	cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
61 	if (!cs)
62 		panic("Unable to initialise clocksource device");
63 
64 	dw_apb_clocksource_start(cs);
65 	dw_apb_clocksource_register(cs);
66 }
67 
68 static void __iomem *sched_io_base;
69 
picoxcell_read_sched_clock(void)70 static u32 picoxcell_read_sched_clock(void)
71 {
72 	return __raw_readl(sched_io_base);
73 }
74 
75 static const struct of_device_id picoxcell_rtc_ids[] __initconst = {
76 	{ .compatible = "picochip,pc3x2-rtc" },
77 	{ /* Sentinel */ },
78 };
79 
picoxcell_init_sched_clock(void)80 static void picoxcell_init_sched_clock(void)
81 {
82 	struct device_node *sched_timer;
83 	u32 rate;
84 
85 	sched_timer = of_find_matching_node(NULL, picoxcell_rtc_ids);
86 	if (!sched_timer)
87 		panic("No RTC for sched clock to use");
88 
89 	timer_get_base_and_rate(sched_timer, &sched_io_base, &rate);
90 	of_node_put(sched_timer);
91 
92 	setup_sched_clock(picoxcell_read_sched_clock, 32, rate);
93 }
94 
95 static const struct of_device_id picoxcell_timer_ids[] __initconst = {
96 	{ .compatible = "picochip,pc3x2-timer" },
97 	{},
98 };
99 
picoxcell_timer_init(void)100 static void __init picoxcell_timer_init(void)
101 {
102 	struct device_node *event_timer, *source_timer;
103 
104 	event_timer = of_find_matching_node(NULL, picoxcell_timer_ids);
105 	if (!event_timer)
106 		panic("No timer for clockevent");
107 	picoxcell_add_clockevent(event_timer);
108 
109 	source_timer = of_find_matching_node(event_timer, picoxcell_timer_ids);
110 	if (!source_timer)
111 		panic("No timer for clocksource");
112 	picoxcell_add_clocksource(source_timer);
113 
114 	of_node_put(source_timer);
115 
116 	picoxcell_init_sched_clock();
117 }
118 
119 struct sys_timer picoxcell_timer = {
120 	.init = picoxcell_timer_init,
121 };
122