aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files-5.10/arch/mips/kernel/cevt-rtl9300.c
blob: cf3a4fe437d81f55ea9ca25ffbb0f4d8ec59ee11 (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/clockchips.h>
#include <linux/init.h>
#include <asm/time.h>
#include <asm/idle.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>

#include <mach-rtl83xx.h>

/* 
 * Timer registers
 * the RTL9300/9310 SoCs have 6 timers, each register block 0x10 apart
 */
#define RTL9300_TC_DATA		0x0
#define RTL9300_TC_CNT		0x4
#define RTL9300_TC_CTRL		0x8
#define RTL9300_TC_CTRL_MODE	BIT(24)
#define RTL9300_TC_CTRL_EN	BIT(28)
#define RTL9300_TC_INT		0xc
#define RTL9300_TC_INT_IP	BIT(16)
#define RTL9300_TC_INT_IE	BIT(20)

// Clocksource is using timer 0, clock event uses timer 1
#define TIMER_CLK_SRC		0
#define TIMER_CLK_EVT		0
#define TIMER_BLK_EVT		(TIMER_CLK_EVT << 4)

// Timer modes
#define TIMER_MODE_REPEAT	1
#define TIMER_MODE_ONCE		0

// Minimum divider is 2
#define DIVISOR_RTL9300		2

#define N_BITS			28

#define RTL9300_TC1_IRQ		8
#define RTL9300_CLOCK_RATE	87500000
#define RTL9300_TC0_BASE	(void *)0xb8003200

int irq_tc0 = 7;

static void __iomem *rtl9300_tc_base(struct clock_event_device *clk)
{
	struct irq_desc *desc = irq_to_desc(clk->irq);
	int tc = desc->irq_data.hwirq - irq_tc0;

	return RTL9300_TC0_BASE + (tc << 4);
}

static irqreturn_t rtl9300_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *clk = dev_id;
//	int cpu = smp_processor_id();
	struct irq_desc *desc = irq_to_desc(irq);
	int tc = desc->irq_data.hwirq - irq_tc0;
	void __iomem *base = RTL9300_TC0_BASE + (tc << 4);
	static atomic_t count = ATOMIC_INIT(0);
	unsigned int c;
	u32 v = readl(base + RTL9300_TC_INT);

	c = (unsigned int)atomic_inc_return(&count);

	// Acknowledge the IRQ
	v |= RTL9300_TC_INT_IP;
	writel(v, base + RTL9300_TC_INT);
	if (readl(base + RTL9300_TC_INT) & RTL9300_TC_INT_IP)
		dump_stack();

	clk->event_handler(clk);
	return IRQ_HANDLED;
}

static void rtl9300_clock_stop(void __iomem *base)
{
	u32 v;

	writel(0, base + RTL9300_TC_CTRL);

	// Acknowledge possibly pending IRQ
	v = readl(base + RTL9300_TC_INT);
//	if (v & RTL9300_TC_INT_IP)
		writel(v | RTL9300_TC_INT_IP, base + RTL9300_TC_INT);
	if (readl(base + RTL9300_TC_INT) & RTL9300_TC_INT_IP)
		dump_stack();
}

static void rtl9300_timer_start(void __iomem *base, bool periodic)
{
	u32 v = (periodic ? RTL9300_TC_CTRL_MODE : 0) | RTL9300_TC_CTRL_EN | DIVISOR_RTL9300;

	writel(0, base + RTL9300_TC_CNT);
	pr_debug("------------- starting timer base %08x\n", (u32)base);
	writel(v, base + RTL9300_TC_CTRL);
}

static int rtl9300_next_event(unsigned long delta, struct clock_event_device *clk)
{
	void __iomem *base = rtl9300_tc_base(clk);

	rtl9300_clock_stop(base);
	writel(delta, base + RTL9300_TC_DATA);
	rtl9300_timer_start(base, TIMER_MODE_ONCE);

	return 0;
}

static int rtl9300_state_periodic(struct clock_event_device *clk)
{
	void __iomem *base = rtl9300_tc_base(clk);

	pr_debug("------------- rtl9300_state_periodic %08x\n", (u32)base);
	rtl9300_clock_stop(base);
	writel(RTL9300_CLOCK_RATE / HZ, base + RTL9300_TC_DATA);
	rtl9300_timer_start(base, TIMER_MODE_REPEAT);
	return 0;
}

static int rtl9300_state_oneshot(struct clock_event_device *clk)
{
	void __iomem *base = rtl9300_tc_base(clk);

	pr_debug("------------- rtl9300_state_oneshot %08x\n", (u32)base);
	rtl9300_clock_stop(base);
	writel(RTL9300_CLOCK_RATE / HZ, base + RTL9300_TC_DATA);
	rtl9300_timer_start(base, TIMER_MODE_ONCE);
	return 0;
}

static int rtl9300_shutdown(struct clock_event_device *clk)
{
	void __iomem *base = rtl9300_tc_base(clk);

	pr_debug("------------- rtl9300_shutdown %08x\n", (u32)base);
	rtl9300_clock_stop(base);
	return 0;
}

static void rtl9300_clock_setup(void __iomem *base)
{
	u32 v;

	// Disable timer
	writel(0, base + RTL9300_TC_CTRL);

	// Acknowledge possibly pending IRQ
	v = readl(base + RTL9300_TC_INT);
//	if (v & RTL9300_TC_INT_IP)
		writel(v | RTL9300_TC_INT_IP, base + RTL9300_TC_INT);
	if (readl(base + RTL9300_TC_INT) & RTL9300_TC_INT_IP)
		dump_stack();

	// Setup maximum period (for use as clock-source)
	writel(0x0fffffff, base + RTL9300_TC_DATA);
}

static DEFINE_PER_CPU(struct clock_event_device, rtl9300_clockevent);
static DEFINE_PER_CPU(char [18], rtl9300_clock_name);

void rtl9300_clockevent_init(void)
{
	int cpu = smp_processor_id();
	int irq;
	struct clock_event_device *cd = &per_cpu(rtl9300_clockevent, cpu);
	unsigned char *name = per_cpu(rtl9300_clock_name, cpu);
	unsigned long flags =  IRQF_PERCPU | IRQF_TIMER;
	struct device_node *node;

	pr_info("%s called for cpu%d\n", __func__, cpu);
	BUG_ON(cpu > 3);	/* Only have 4 general purpose timers */

	node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300clock");
	if (!node) {
		pr_err("No DT entry found for realtek,rtl9300clock\n");
		return;
	}

	irq = irq_of_parse_and_map(node, cpu);
	pr_info("%s using IRQ %d\n", __func__, irq);

	rtl9300_clock_setup(RTL9300_TC0_BASE + TIMER_BLK_EVT + (cpu << 4));

	sprintf(name, "rtl9300-counter-%d", cpu);
	cd->name		= name;
	cd->features		= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;

	clockevent_set_clock(cd, RTL9300_CLOCK_RATE);

	cd->max_delta_ns	= clockevent_delta2ns(0x0fffffff, cd);
	cd->max_delta_ticks	= 0x0fffffff;
	cd->min_delta_ns	= clockevent_delta2ns(0x20, cd);
	cd->min_delta_ticks	= 0x20;
	cd->rating		= 300;
	cd->irq			= irq;
	cd->cpumask		= cpumask_of(cpu);
	cd->set_next_event	= rtl9300_next_event;
	cd->set_state_shutdown	= rtl9300_shutdown;
	cd->set_state_periodic	= rtl9300_state_periodic;
	cd->set_state_oneshot	= rtl9300_state_oneshot;
	clockevents_register_device(cd);

	irq_set_affinity(irq, cd->cpumask);

	if (request_irq(irq, rtl9300_timer_interrupt, flags, name, cd))
		pr_err("Failed to request irq %d (%s)\n", irq, name);

	writel(RTL9300_TC_INT_IE, RTL9300_TC0_BASE + TIMER_BLK_EVT + (cpu << 4) + RTL9300_TC_INT);
}