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