rockchip_timer.c 6.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * Rockchip timer support
 *
 * Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/interrupt.h>
14 15
#include <linux/sched_clock.h>
#include <linux/slab.h>
16 17 18 19 20 21
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#define TIMER_NAME "rk_timer"

22 23
#define TIMER_LOAD_COUNT0	0x00
#define TIMER_LOAD_COUNT1	0x04
24 25
#define TIMER_CURRENT_VALUE0	0x08
#define TIMER_CURRENT_VALUE1	0x0C
26 27
#define TIMER_CONTROL_REG3288	0x10
#define TIMER_CONTROL_REG3399	0x1c
28
#define TIMER_INT_STATUS	0x18
29

30 31 32 33 34
#define TIMER_DISABLE		0x0
#define TIMER_ENABLE		0x1
#define TIMER_MODE_FREE_RUNNING			(0 << 1)
#define TIMER_MODE_USER_DEFINED_COUNT		(1 << 1)
#define TIMER_INT_UNMASK			(1 << 2)
35

36
struct rk_timer {
37
	void __iomem *base;
38
	void __iomem *ctrl;
39 40
	struct clk *clk;
	struct clk *pclk;
41
	u32 freq;
42
	int irq;
43 44
};

45 46 47 48
struct rk_clkevt {
	struct clock_event_device ce;
	struct rk_timer timer;
};
49

50 51
static struct rk_clkevt *rk_clkevt;
static struct rk_timer *rk_clksrc;
52

53
static inline struct rk_timer *rk_timer(struct clock_event_device *ce)
54
{
55
	return &container_of(ce, struct rk_clkevt, ce)->timer;
56 57
}

58
static inline void rk_timer_disable(struct rk_timer *timer)
59
{
60
	writel_relaxed(TIMER_DISABLE, timer->ctrl);
61 62
}

63
static inline void rk_timer_enable(struct rk_timer *timer, u32 flags)
64
{
65
	writel_relaxed(TIMER_ENABLE | flags, timer->ctrl);
66 67 68
}

static void rk_timer_update_counter(unsigned long cycles,
69
				    struct rk_timer *timer)
70
{
71 72
	writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0);
	writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1);
73 74
}

75
static void rk_timer_interrupt_clear(struct rk_timer *timer)
76
{
77
	writel_relaxed(1, timer->base + TIMER_INT_STATUS);
78 79 80 81 82
}

static inline int rk_timer_set_next_event(unsigned long cycles,
					  struct clock_event_device *ce)
{
83 84 85 86 87 88
	struct rk_timer *timer = rk_timer(ce);

	rk_timer_disable(timer);
	rk_timer_update_counter(cycles, timer);
	rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT |
			       TIMER_INT_UNMASK);
89 90 91
	return 0;
}

92
static int rk_timer_shutdown(struct clock_event_device *ce)
93
{
94 95 96
	struct rk_timer *timer = rk_timer(ce);

	rk_timer_disable(timer);
97 98 99 100 101
	return 0;
}

static int rk_timer_set_periodic(struct clock_event_device *ce)
{
102 103 104 105 106
	struct rk_timer *timer = rk_timer(ce);

	rk_timer_disable(timer);
	rk_timer_update_counter(timer->freq / HZ - 1, timer);
	rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK);
107
	return 0;
108 109 110 111 112
}

static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *ce = dev_id;
113
	struct rk_timer *timer = rk_timer(ce);
114

115
	rk_timer_interrupt_clear(timer);
116

117
	if (clockevent_state_oneshot(ce))
118
		rk_timer_disable(timer);
119 120 121 122 123 124

	ce->event_handler(ce);

	return IRQ_HANDLED;
}

125 126 127 128 129 130 131
static u64 notrace rk_timer_sched_read(void)
{
	return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0);
}

static int __init
rk_timer_probe(struct rk_timer *timer, struct device_node *np)
132 133 134
{
	struct clk *timer_clk;
	struct clk *pclk;
135
	int ret = -EINVAL, irq;
136
	u32 ctrl_reg = TIMER_CONTROL_REG3288;
137

138 139
	timer->base = of_iomap(np, 0);
	if (!timer->base) {
140
		pr_err("Failed to get base address for '%s'\n", TIMER_NAME);
141
		return -ENXIO;
142
	}
143 144 145 146 147

	if (of_device_is_compatible(np, "rockchip,rk3399-timer"))
		ctrl_reg = TIMER_CONTROL_REG3399;

	timer->ctrl = timer->base + ctrl_reg;
148 149 150

	pclk = of_clk_get_by_name(np, "pclk");
	if (IS_ERR(pclk)) {
151
		ret = PTR_ERR(pclk);
152
		pr_err("Failed to get pclk for '%s'\n", TIMER_NAME);
153
		goto out_unmap;
154 155
	}

156 157
	ret = clk_prepare_enable(pclk);
	if (ret) {
158
		pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME);
159
		goto out_unmap;
160
	}
161
	timer->pclk = pclk;
162 163 164

	timer_clk = of_clk_get_by_name(np, "timer");
	if (IS_ERR(timer_clk)) {
165
		ret = PTR_ERR(timer_clk);
166
		pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME);
167
		goto out_timer_clk;
168 169
	}

170 171
	ret = clk_prepare_enable(timer_clk);
	if (ret) {
172
		pr_err("Failed to enable timer clock\n");
173
		goto out_timer_clk;
174
	}
175
	timer->clk = timer_clk;
176

177
	timer->freq = clk_get_rate(timer_clk);
178 179

	irq = irq_of_parse_and_map(np, 0);
180
	if (!irq) {
181
		ret = -EINVAL;
182
		pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME);
183
		goto out_irq;
184
	}
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	timer->irq = irq;

	rk_timer_interrupt_clear(timer);
	rk_timer_disable(timer);
	return 0;

out_irq:
	clk_disable_unprepare(timer_clk);
out_timer_clk:
	clk_disable_unprepare(pclk);
out_unmap:
	iounmap(timer->base);

	return ret;
}

static void __init rk_timer_cleanup(struct rk_timer *timer)
{
	clk_disable_unprepare(timer->clk);
	clk_disable_unprepare(timer->pclk);
	iounmap(timer->base);
}

static int __init rk_clkevt_init(struct device_node *np)
{
	struct clock_event_device *ce;
	int ret = -EINVAL;

	rk_clkevt = kzalloc(sizeof(struct rk_clkevt), GFP_KERNEL);
	if (!rk_clkevt) {
		ret = -ENOMEM;
		goto out;
	}
218

219 220 221 222 223
	ret = rk_timer_probe(&rk_clkevt->timer, np);
	if (ret)
		goto out_probe;

	ce = &rk_clkevt->ce;
224
	ce->name = TIMER_NAME;
225 226
	ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
		       CLOCK_EVT_FEAT_DYNIRQ;
227
	ce->set_next_event = rk_timer_set_next_event;
228 229
	ce->set_state_shutdown = rk_timer_shutdown;
	ce->set_state_periodic = rk_timer_set_periodic;
230
	ce->irq = rk_clkevt->timer.irq;
231
	ce->cpumask = cpu_possible_mask;
232 233
	ce->rating = 250;

234 235
	ret = request_irq(rk_clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER,
			  TIMER_NAME, ce);
236
	if (ret) {
237 238
		pr_err("Failed to initialize '%s': %d\n",
			TIMER_NAME, ret);
239
		goto out_irq;
240 241
	}

242 243
	clockevents_config_and_register(&rk_clkevt->ce,
					rk_clkevt->timer.freq, 1, UINT_MAX);
244
	return 0;
245 246

out_irq:
247 248 249 250 251 252
	rk_timer_cleanup(&rk_clkevt->timer);
out_probe:
	kfree(rk_clkevt);
out:
	/* Leave rk_clkevt not NULL to prevent future init */
	rk_clkevt = ERR_PTR(ret);
253
	return ret;
254
}
255

256
static int __init rk_clksrc_init(struct device_node *np)
257
{
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	int ret = -EINVAL;

	rk_clksrc = kzalloc(sizeof(struct rk_timer), GFP_KERNEL);
	if (!rk_clksrc) {
		ret = -ENOMEM;
		goto out;
	}

	ret = rk_timer_probe(rk_clksrc, np);
	if (ret)
		goto out_probe;

	rk_timer_update_counter(UINT_MAX, rk_clksrc);
	rk_timer_enable(rk_clksrc, 0);

	ret = clocksource_mmio_init(rk_clksrc->base + TIMER_CURRENT_VALUE0,
		TIMER_NAME, rk_clksrc->freq, 250, 32,
		clocksource_mmio_readl_down);
	if (ret) {
		pr_err("Failed to register clocksource");
		goto out_clocksource;
	}

	sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq);
	return 0;

out_clocksource:
	rk_timer_cleanup(rk_clksrc);
out_probe:
	kfree(rk_clksrc);
out:
	/* Leave rk_clksrc not NULL to prevent future init */
	rk_clksrc = ERR_PTR(ret);
	return ret;
292 293
}

294
static int __init rk_timer_init(struct device_node *np)
295
{
296 297 298 299 300 301 302 303
	if (!rk_clkevt)
		return rk_clkevt_init(np);

	if (!rk_clksrc)
		return rk_clksrc_init(np);

	pr_err("Too many timer definitions for '%s'\n", TIMER_NAME);
	return -EINVAL;
304 305
}

306 307
TIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init);
TIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init);