timer-imx-tpm.c
4.29 KB
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
/*
* Copyright 2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#define TPM_GLOBAL 0x8
#define TPM_SC 0x10
#define TPM_CNT 0x14
#define TPM_MOD 0x18
#define TPM_STATUS 0x1c
#define TPM_C0SC 0x20
#define TPM_C0V 0x24
#define TPM_STATUS_CH0F 0x1
#define TPM_C0SC_MSA 0x4
static void __iomem *timer_base;
static struct clock_event_device clockevent_tpm;
static inline void tpm_timer_disable(void)
{
unsigned int val;
val = __raw_readl(timer_base + TPM_C0SC);
val &= ~(0x5 << TPM_C0SC_MSA);
__raw_writel(val, timer_base + TPM_C0SC);
}
static inline void tpm_timer_enable(void)
{
unsigned int val;
val = __raw_readl(timer_base + TPM_C0SC);
val |= (0x5 << TPM_C0SC_MSA);
__raw_writel(val, timer_base + TPM_C0SC);
}
static inline void tpm_irq_acknowledge(void)
{
__raw_writel(1, timer_base + TPM_STATUS);
}
static struct delay_timer tpm_delay_timer;
static unsigned long tpm_read_current_timer(void)
{
return __raw_readl(timer_base + TPM_CNT);
}
static u64 notrace tpm_read_sched_clock(void)
{
return __raw_readl(timer_base + TPM_CNT);
}
static int __init tpm_clocksource_init(unsigned long rate)
{
tpm_delay_timer.read_current_timer = &tpm_read_current_timer;
tpm_delay_timer.freq = rate;
register_current_timer_delay(&tpm_delay_timer);
sched_clock_register(tpm_read_sched_clock, 32, rate);
return clocksource_mmio_init(timer_base + TPM_CNT, "imx-tpm",
rate, 200, 32, clocksource_mmio_readl_up);
}
static int tpm_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned long next, now, ret;
now = __raw_readl(timer_base + TPM_CNT);
next = now + delta;
__raw_writel(next, timer_base + TPM_C0V);
now = __raw_readl(timer_base + TPM_CNT);
ret = next - now;
return (ret > delta) ? -ETIME : 0;
}
static int tpm_set_state_oneshot(struct clock_event_device *evt)
{
/* enable timer */
tpm_timer_enable();
return 0;
}
static int tpm_set_state_shutdown(struct clock_event_device *evt)
{
/* disable timer */
tpm_timer_disable();
return 0;
}
static irqreturn_t tpm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &clockevent_tpm;
tpm_irq_acknowledge();
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct clock_event_device clockevent_tpm = {
.name = "i.MX7ULP tpm timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_state_oneshot = tpm_set_state_oneshot,
.set_next_event = tpm_set_next_event,
.set_state_shutdown = tpm_set_state_shutdown,
.rating = 200,
};
static struct irqaction tpm_timer_irq = {
.name = "i.MX7ULP tpm timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = tpm_timer_interrupt,
.dev_id = &clockevent_tpm,
};
static int __init tpm_clockevent_init(unsigned long rate, int irq)
{
/* init the channel */
setup_irq(irq, &tpm_timer_irq);
clockevent_tpm.cpumask = cpumask_of(0);
clockevent_tpm.irq = irq;
clockevents_config_and_register(&clockevent_tpm,
rate, 300, 0xfffffffe);
return 0;
}
static int __init tpm_timer_init(struct device_node *np)
{
struct clk *clk;
uint32_t val;
int irq;
timer_base = of_iomap(np, 0);
BUG_ON(!timer_base);
irq = irq_of_parse_and_map(np, 0);
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
return PTR_ERR(clk);
/* clock shoube be enabled before access to the timer registers */
clk_prepare_enable(clk);
/* Initialize tpm module to a known state(counter disabled). */
__raw_writel(0, timer_base + TPM_SC);
__raw_writel(0, timer_base + TPM_CNT);
__raw_writel(0, timer_base + TPM_C0SC);
/* set the prescale div, div by 8 = 3MHz */
__raw_writel(0xb, timer_base + TPM_SC);
/* set the MOD register to 0xffffffff for free running counter */
__raw_writel(0xffffffff, timer_base + TPM_MOD);
tpm_clocksource_init(clk_get_rate(clk) / 8);
tpm_clockevent_init(clk_get_rate(clk) / 8, irq);
val = __raw_readl(timer_base);
return 0;
}
CLOCKSOURCE_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init);