Skip to content

Commit 9b8bb77

Browse files
baruchsiachdlezcano
authored andcommitted
clocksource: Driver for Conexant Digicolor SoC timer
Add clocksource driver to the Conexant CX92755 SoC, part of the Digicolor SoCs series. Hardware provides 8 timers, A to H. Timer A is dedicated to a future watchdog driver so we don't use it here. Use timer B for sched_clock, and timer C for clock_event. Signed-off-by: Baruch Siach <baruch@tkos.co.il> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
1 parent 9ff99be commit 9b8bb77

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed

drivers/clocksource/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ config CLKBLD_I8253
1818
config CLKSRC_MMIO
1919
bool
2020

21+
config DIGICOLOR_TIMER
22+
bool
23+
2124
config DW_APB_TIMER
2225
bool
2326

drivers/clocksource/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
1010
obj-$(CONFIG_EM_TIMER_STI) += em_sti.o
1111
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
1212
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
13+
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
1314
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
1415
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
1516
obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o

drivers/clocksource/timer-digicolor.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Conexant Digicolor timer driver
3+
*
4+
* Author: Baruch Siach <baruch@tkos.co.il>
5+
*
6+
* Copyright (C) 2014 Paradox Innovation Ltd.
7+
*
8+
* Based on:
9+
* Allwinner SoCs hstimer driver
10+
*
11+
* Copyright (C) 2013 Maxime Ripard
12+
*
13+
* Maxime Ripard <maxime.ripard@free-electrons.com>
14+
*
15+
* This file is licensed under the terms of the GNU General Public
16+
* License version 2. This program is licensed "as is" without any
17+
* warranty of any kind, whether express or implied.
18+
*/
19+
20+
/*
21+
* Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to
22+
* "Timer H". Timer A is the only one with watchdog support, so it is dedicated
23+
* to the watchdog driver. This driver uses Timer B for sched_clock(), and
24+
* Timer C for clockevents.
25+
*/
26+
27+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28+
29+
#include <linux/clk.h>
30+
#include <linux/clockchips.h>
31+
#include <linux/interrupt.h>
32+
#include <linux/irq.h>
33+
#include <linux/irqreturn.h>
34+
#include <linux/sched_clock.h>
35+
#include <linux/of.h>
36+
#include <linux/of_address.h>
37+
#include <linux/of_irq.h>
38+
39+
enum {
40+
TIMER_A,
41+
TIMER_B,
42+
TIMER_C,
43+
TIMER_D,
44+
TIMER_E,
45+
TIMER_F,
46+
TIMER_G,
47+
TIMER_H,
48+
};
49+
50+
#define CONTROL(t) ((t)*8)
51+
#define COUNT(t) ((t)*8 + 4)
52+
53+
#define CONTROL_DISABLE 0
54+
#define CONTROL_ENABLE BIT(0)
55+
#define CONTROL_MODE(m) ((m) << 4)
56+
#define CONTROL_MODE_ONESHOT CONTROL_MODE(1)
57+
#define CONTROL_MODE_PERIODIC CONTROL_MODE(2)
58+
59+
struct digicolor_timer {
60+
struct clock_event_device ce;
61+
void __iomem *base;
62+
u32 ticks_per_jiffy;
63+
int timer_id; /* one of TIMER_* */
64+
};
65+
66+
struct digicolor_timer *dc_timer(struct clock_event_device *ce)
67+
{
68+
return container_of(ce, struct digicolor_timer, ce);
69+
}
70+
71+
static inline void dc_timer_disable(struct clock_event_device *ce)
72+
{
73+
struct digicolor_timer *dt = dc_timer(ce);
74+
writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id));
75+
}
76+
77+
static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode)
78+
{
79+
struct digicolor_timer *dt = dc_timer(ce);
80+
writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id));
81+
}
82+
83+
static inline void dc_timer_set_count(struct clock_event_device *ce,
84+
unsigned long count)
85+
{
86+
struct digicolor_timer *dt = dc_timer(ce);
87+
writel(count, dt->base + COUNT(dt->timer_id));
88+
}
89+
90+
static void digicolor_clkevt_mode(enum clock_event_mode mode,
91+
struct clock_event_device *ce)
92+
{
93+
struct digicolor_timer *dt = dc_timer(ce);
94+
95+
switch (mode) {
96+
case CLOCK_EVT_MODE_PERIODIC:
97+
dc_timer_disable(ce);
98+
dc_timer_set_count(ce, dt->ticks_per_jiffy);
99+
dc_timer_enable(ce, CONTROL_MODE_PERIODIC);
100+
break;
101+
case CLOCK_EVT_MODE_ONESHOT:
102+
dc_timer_disable(ce);
103+
dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
104+
break;
105+
case CLOCK_EVT_MODE_UNUSED:
106+
case CLOCK_EVT_MODE_SHUTDOWN:
107+
default:
108+
dc_timer_disable(ce);
109+
break;
110+
}
111+
}
112+
113+
static int digicolor_clkevt_next_event(unsigned long evt,
114+
struct clock_event_device *ce)
115+
{
116+
dc_timer_disable(ce);
117+
dc_timer_set_count(ce, evt);
118+
dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
119+
120+
return 0;
121+
}
122+
123+
static struct digicolor_timer dc_timer_dev = {
124+
.ce = {
125+
.name = "digicolor_tick",
126+
.rating = 340,
127+
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
128+
.set_mode = digicolor_clkevt_mode,
129+
.set_next_event = digicolor_clkevt_next_event,
130+
},
131+
.timer_id = TIMER_C,
132+
};
133+
134+
static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id)
135+
{
136+
struct clock_event_device *evt = dev_id;
137+
138+
evt->event_handler(evt);
139+
140+
return IRQ_HANDLED;
141+
}
142+
143+
static u64 digicolor_timer_sched_read(void)
144+
{
145+
return ~readl(dc_timer_dev.base + COUNT(TIMER_B));
146+
}
147+
148+
static void __init digicolor_timer_init(struct device_node *node)
149+
{
150+
unsigned long rate;
151+
struct clk *clk;
152+
int ret, irq;
153+
154+
/*
155+
* timer registers are shared with the watchdog timer;
156+
* don't map exclusively
157+
*/
158+
dc_timer_dev.base = of_iomap(node, 0);
159+
if (!dc_timer_dev.base) {
160+
pr_err("Can't map registers");
161+
return;
162+
}
163+
164+
irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id);
165+
if (irq <= 0) {
166+
pr_err("Can't parse IRQ");
167+
return;
168+
}
169+
170+
clk = of_clk_get(node, 0);
171+
if (IS_ERR(clk)) {
172+
pr_err("Can't get timer clock");
173+
return;
174+
}
175+
clk_prepare_enable(clk);
176+
rate = clk_get_rate(clk);
177+
dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
178+
179+
writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B));
180+
writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B));
181+
writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B));
182+
183+
sched_clock_register(digicolor_timer_sched_read, 32, rate);
184+
clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name,
185+
rate, 340, 32, clocksource_mmio_readl_down);
186+
187+
ret = request_irq(irq, digicolor_timer_interrupt,
188+
IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC",
189+
&dc_timer_dev.ce);
190+
if (ret)
191+
pr_warn("request of timer irq %d failed (%d)\n", irq, ret);
192+
193+
dc_timer_dev.ce.cpumask = cpu_possible_mask;
194+
dc_timer_dev.ce.irq = irq;
195+
196+
clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff);
197+
}
198+
CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
199+
digicolor_timer_init);

0 commit comments

Comments
 (0)