summaryrefslogtreecommitdiff
path: root/arch/mips/ralink/cevt-rt3352.c
blob: 269d4877d120e8e9249a75fd0a5c3e94858acfe7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2013 by John Crispin <john@phrozen.org>
 */

#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>

#include <asm/mach-ralink/ralink_regs.h>

#define SYSTICK_FREQ		(50 * 1000)

#define SYSTICK_CONFIG		0x00
#define SYSTICK_COMPARE		0x04
#define SYSTICK_COUNT		0x08

/* route systick irq to mips irq 7 instead of the r4k-timer */
#define CFG_EXT_STK_EN		0x2
/* enable the counter */
#define CFG_CNT_EN		0x1

struct systick_device {
	void __iomem *membase;
	struct clock_event_device dev;
	int irq_requested;
	int freq_scale;
};

static int systick_set_oneshot(struct clock_event_device *evt);
static int systick_shutdown(struct clock_event_device *evt);

static int systick_next_event(unsigned long delta,
				struct clock_event_device *evt)
{
	struct systick_device *sdev;
	u32 count;

	sdev = container_of(evt, struct systick_device, dev);
	count = ioread32(sdev->membase + SYSTICK_COUNT);
	count = (count + delta) % SYSTICK_FREQ;
	iowrite32(count, sdev->membase + SYSTICK_COMPARE);

	return 0;
}

static void systick_event_handler(struct clock_event_device *dev)
{
	/* noting to do here */
}

static irqreturn_t systick_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *dev = (struct clock_event_device *) dev_id;

	dev->event_handler(dev);

	return IRQ_HANDLED;
}

static struct systick_device systick = {
	.dev = {
		/*
		 * cevt-r4k uses 300, make sure systick
		 * gets used if available
		 */
		.rating			= 310,
		.features		= CLOCK_EVT_FEAT_ONESHOT,
		.set_next_event		= systick_next_event,
		.set_state_shutdown	= systick_shutdown,
		.set_state_oneshot	= systick_set_oneshot,
		.event_handler		= systick_event_handler,
	},
};

static int systick_shutdown(struct clock_event_device *evt)
{
	struct systick_device *sdev;

	sdev = container_of(evt, struct systick_device, dev);

	if (sdev->irq_requested)
		free_irq(systick.dev.irq, &systick.dev);
	sdev->irq_requested = 0;
	iowrite32(0, systick.membase + SYSTICK_CONFIG);

	return 0;
}

static int systick_set_oneshot(struct clock_event_device *evt)
{
	const char *name = systick.dev.name;
	struct systick_device *sdev;
	int irq = systick.dev.irq;

	sdev = container_of(evt, struct systick_device, dev);

	if (!sdev->irq_requested) {
		if (request_irq(irq, systick_interrupt,
				IRQF_PERCPU | IRQF_TIMER, name, &systick.dev))
			pr_err("Failed to request irq %d (%s)\n", irq, name);
	}
	sdev->irq_requested = 1;
	iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN,
		  systick.membase + SYSTICK_CONFIG);

	return 0;
}

static int __init ralink_systick_init(struct device_node *np)
{
	int ret;

	systick.membase = of_iomap(np, 0);
	if (!systick.membase)
		return -ENXIO;

	systick.dev.name = np->name;
	clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
	systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
	systick.dev.max_delta_ticks = 0x7fff;
	systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
	systick.dev.min_delta_ticks = 0x3;
	systick.dev.irq = irq_of_parse_and_map(np, 0);
	if (!systick.dev.irq) {
		pr_err("%pOFn: request_irq failed", np);
		return -EINVAL;
	}

	ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
				    SYSTICK_FREQ, 301, 16,
				    clocksource_mmio_readl_up);
	if (ret)
		return ret;

	clockevents_register_device(&systick.dev);

	pr_info("%pOFn: running - mult: %d, shift: %d\n",
			np, systick.dev.mult, systick.dev.shift);

	return 0;
}

TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);