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
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
9 #include <linux/kernel.h>
10 #include <linux/err.h>
11 #include <linux/init.h>
12 #include <linux/export.h>
13 #include <linux/spinlock.h>
14 #include <linux/interrupt.h>
15 #include <linux/clk.h>
16 #include <bcm63xx_cpu.h>
17 #include <bcm63xx_io.h>
18 #include <bcm63xx_timer.h>
19 #include <bcm63xx_regs.h>
21 static DEFINE_RAW_SPINLOCK(timer_reg_lock
);
22 static DEFINE_RAW_SPINLOCK(timer_data_lock
);
23 static struct clk
*periph_clk
;
25 static struct timer_data
{
28 } timer_data
[BCM63XX_TIMER_COUNT
];
30 static irqreturn_t
timer_interrupt(int irq
, void *dev_id
)
35 raw_spin_lock(&timer_reg_lock
);
36 stat
= bcm_timer_readl(TIMER_IRQSTAT_REG
);
37 bcm_timer_writel(stat
, TIMER_IRQSTAT_REG
);
38 raw_spin_unlock(&timer_reg_lock
);
40 for (i
= 0; i
< BCM63XX_TIMER_COUNT
; i
++) {
41 if (!(stat
& TIMER_IRQSTAT_TIMER_CAUSE(i
)))
44 raw_spin_lock(&timer_data_lock
);
45 if (!timer_data
[i
].cb
) {
46 raw_spin_unlock(&timer_data_lock
);
50 timer_data
[i
].cb(timer_data
[i
].data
);
51 raw_spin_unlock(&timer_data_lock
);
57 int bcm63xx_timer_enable(int id
)
62 if (id
>= BCM63XX_TIMER_COUNT
)
65 raw_spin_lock_irqsave(&timer_reg_lock
, flags
);
67 reg
= bcm_timer_readl(TIMER_CTLx_REG(id
));
68 reg
|= TIMER_CTL_ENABLE_MASK
;
69 bcm_timer_writel(reg
, TIMER_CTLx_REG(id
));
71 reg
= bcm_timer_readl(TIMER_IRQSTAT_REG
);
72 reg
|= TIMER_IRQSTAT_TIMER_IR_EN(id
);
73 bcm_timer_writel(reg
, TIMER_IRQSTAT_REG
);
75 raw_spin_unlock_irqrestore(&timer_reg_lock
, flags
);
79 EXPORT_SYMBOL(bcm63xx_timer_enable
);
81 int bcm63xx_timer_disable(int id
)
86 if (id
>= BCM63XX_TIMER_COUNT
)
89 raw_spin_lock_irqsave(&timer_reg_lock
, flags
);
91 reg
= bcm_timer_readl(TIMER_CTLx_REG(id
));
92 reg
&= ~TIMER_CTL_ENABLE_MASK
;
93 bcm_timer_writel(reg
, TIMER_CTLx_REG(id
));
95 reg
= bcm_timer_readl(TIMER_IRQSTAT_REG
);
96 reg
&= ~TIMER_IRQSTAT_TIMER_IR_EN(id
);
97 bcm_timer_writel(reg
, TIMER_IRQSTAT_REG
);
99 raw_spin_unlock_irqrestore(&timer_reg_lock
, flags
);
103 EXPORT_SYMBOL(bcm63xx_timer_disable
);
105 int bcm63xx_timer_register(int id
, void (*callback
)(void *data
), void *data
)
110 if (id
>= BCM63XX_TIMER_COUNT
|| !callback
)
114 raw_spin_lock_irqsave(&timer_data_lock
, flags
);
115 if (timer_data
[id
].cb
) {
120 timer_data
[id
].cb
= callback
;
121 timer_data
[id
].data
= data
;
124 raw_spin_unlock_irqrestore(&timer_data_lock
, flags
);
128 EXPORT_SYMBOL(bcm63xx_timer_register
);
130 void bcm63xx_timer_unregister(int id
)
134 if (id
>= BCM63XX_TIMER_COUNT
)
137 raw_spin_lock_irqsave(&timer_data_lock
, flags
);
138 timer_data
[id
].cb
= NULL
;
139 raw_spin_unlock_irqrestore(&timer_data_lock
, flags
);
142 EXPORT_SYMBOL(bcm63xx_timer_unregister
);
144 unsigned int bcm63xx_timer_countdown(unsigned int countdown_us
)
146 return (clk_get_rate(periph_clk
) / (1000 * 1000)) * countdown_us
;
149 EXPORT_SYMBOL(bcm63xx_timer_countdown
);
151 int bcm63xx_timer_set(int id
, int monotonic
, unsigned int countdown_us
)
156 if (id
>= BCM63XX_TIMER_COUNT
)
159 countdown
= bcm63xx_timer_countdown(countdown_us
);
160 if (countdown
& ~TIMER_CTL_COUNTDOWN_MASK
)
163 raw_spin_lock_irqsave(&timer_reg_lock
, flags
);
164 reg
= bcm_timer_readl(TIMER_CTLx_REG(id
));
167 reg
&= ~TIMER_CTL_MONOTONIC_MASK
;
169 reg
|= TIMER_CTL_MONOTONIC_MASK
;
171 reg
&= ~TIMER_CTL_COUNTDOWN_MASK
;
173 bcm_timer_writel(reg
, TIMER_CTLx_REG(id
));
175 raw_spin_unlock_irqrestore(&timer_reg_lock
, flags
);
179 EXPORT_SYMBOL(bcm63xx_timer_set
);
181 static int bcm63xx_timer_init(void)
186 reg
= bcm_timer_readl(TIMER_IRQSTAT_REG
);
187 reg
&= ~TIMER_IRQSTAT_TIMER0_IR_EN
;
188 reg
&= ~TIMER_IRQSTAT_TIMER1_IR_EN
;
189 reg
&= ~TIMER_IRQSTAT_TIMER2_IR_EN
;
190 bcm_timer_writel(reg
, TIMER_IRQSTAT_REG
);
192 periph_clk
= clk_get(NULL
, "periph");
193 if (IS_ERR(periph_clk
))
196 irq
= bcm63xx_get_irq_number(IRQ_TIMER
);
197 ret
= request_irq(irq
, timer_interrupt
, 0, "bcm63xx_timer", NULL
);
199 pr_err("%s: failed to register irq\n", __func__
);
206 arch_initcall(bcm63xx_timer_init
);