Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/bcm63xx/timer.c
10817 views
1
/*
2
* This file is subject to the terms and conditions of the GNU General Public
3
* License. See the file "COPYING" in the main directory of this archive
4
* for more details.
5
*
6
* Copyright (C) 2008 Maxime Bizon <[email protected]>
7
*/
8
9
#include <linux/kernel.h>
10
#include <linux/err.h>
11
#include <linux/module.h>
12
#include <linux/spinlock.h>
13
#include <linux/interrupt.h>
14
#include <linux/clk.h>
15
#include <bcm63xx_cpu.h>
16
#include <bcm63xx_io.h>
17
#include <bcm63xx_timer.h>
18
#include <bcm63xx_regs.h>
19
20
static DEFINE_RAW_SPINLOCK(timer_reg_lock);
21
static DEFINE_RAW_SPINLOCK(timer_data_lock);
22
static struct clk *periph_clk;
23
24
static struct timer_data {
25
void (*cb)(void *);
26
void *data;
27
} timer_data[BCM63XX_TIMER_COUNT];
28
29
static irqreturn_t timer_interrupt(int irq, void *dev_id)
30
{
31
u32 stat;
32
int i;
33
34
raw_spin_lock(&timer_reg_lock);
35
stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
36
bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
37
raw_spin_unlock(&timer_reg_lock);
38
39
for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
40
if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
41
continue;
42
43
raw_spin_lock(&timer_data_lock);
44
if (!timer_data[i].cb) {
45
raw_spin_unlock(&timer_data_lock);
46
continue;
47
}
48
49
timer_data[i].cb(timer_data[i].data);
50
raw_spin_unlock(&timer_data_lock);
51
}
52
53
return IRQ_HANDLED;
54
}
55
56
int bcm63xx_timer_enable(int id)
57
{
58
u32 reg;
59
unsigned long flags;
60
61
if (id >= BCM63XX_TIMER_COUNT)
62
return -EINVAL;
63
64
raw_spin_lock_irqsave(&timer_reg_lock, flags);
65
66
reg = bcm_timer_readl(TIMER_CTLx_REG(id));
67
reg |= TIMER_CTL_ENABLE_MASK;
68
bcm_timer_writel(reg, TIMER_CTLx_REG(id));
69
70
reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
71
reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
72
bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
73
74
raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
75
return 0;
76
}
77
78
EXPORT_SYMBOL(bcm63xx_timer_enable);
79
80
int bcm63xx_timer_disable(int id)
81
{
82
u32 reg;
83
unsigned long flags;
84
85
if (id >= BCM63XX_TIMER_COUNT)
86
return -EINVAL;
87
88
raw_spin_lock_irqsave(&timer_reg_lock, flags);
89
90
reg = bcm_timer_readl(TIMER_CTLx_REG(id));
91
reg &= ~TIMER_CTL_ENABLE_MASK;
92
bcm_timer_writel(reg, TIMER_CTLx_REG(id));
93
94
reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
95
reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
96
bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
97
98
raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
99
return 0;
100
}
101
102
EXPORT_SYMBOL(bcm63xx_timer_disable);
103
104
int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
105
{
106
unsigned long flags;
107
int ret;
108
109
if (id >= BCM63XX_TIMER_COUNT || !callback)
110
return -EINVAL;
111
112
ret = 0;
113
raw_spin_lock_irqsave(&timer_data_lock, flags);
114
if (timer_data[id].cb) {
115
ret = -EBUSY;
116
goto out;
117
}
118
119
timer_data[id].cb = callback;
120
timer_data[id].data = data;
121
122
out:
123
raw_spin_unlock_irqrestore(&timer_data_lock, flags);
124
return ret;
125
}
126
127
EXPORT_SYMBOL(bcm63xx_timer_register);
128
129
void bcm63xx_timer_unregister(int id)
130
{
131
unsigned long flags;
132
133
if (id >= BCM63XX_TIMER_COUNT)
134
return;
135
136
raw_spin_lock_irqsave(&timer_data_lock, flags);
137
timer_data[id].cb = NULL;
138
raw_spin_unlock_irqrestore(&timer_data_lock, flags);
139
}
140
141
EXPORT_SYMBOL(bcm63xx_timer_unregister);
142
143
unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
144
{
145
return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
146
}
147
148
EXPORT_SYMBOL(bcm63xx_timer_countdown);
149
150
int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
151
{
152
u32 reg, countdown;
153
unsigned long flags;
154
155
if (id >= BCM63XX_TIMER_COUNT)
156
return -EINVAL;
157
158
countdown = bcm63xx_timer_countdown(countdown_us);
159
if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
160
return -EINVAL;
161
162
raw_spin_lock_irqsave(&timer_reg_lock, flags);
163
reg = bcm_timer_readl(TIMER_CTLx_REG(id));
164
165
if (monotonic)
166
reg &= ~TIMER_CTL_MONOTONIC_MASK;
167
else
168
reg |= TIMER_CTL_MONOTONIC_MASK;
169
170
reg &= ~TIMER_CTL_COUNTDOWN_MASK;
171
reg |= countdown;
172
bcm_timer_writel(reg, TIMER_CTLx_REG(id));
173
174
raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
175
return 0;
176
}
177
178
EXPORT_SYMBOL(bcm63xx_timer_set);
179
180
int bcm63xx_timer_init(void)
181
{
182
int ret, irq;
183
u32 reg;
184
185
reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
186
reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
187
reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
188
reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
189
bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
190
191
periph_clk = clk_get(NULL, "periph");
192
if (IS_ERR(periph_clk))
193
return -ENODEV;
194
195
irq = bcm63xx_get_irq_number(IRQ_TIMER);
196
ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
197
if (ret) {
198
printk(KERN_ERR "bcm63xx_timer: failed to register irq\n");
199
return ret;
200
}
201
202
return 0;
203
}
204
205
arch_initcall(bcm63xx_timer_init);
206
207