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