0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/kernel.h>
0010 #include <linux/err.h>
0011 #include <linux/init.h>
0012 #include <linux/export.h>
0013 #include <linux/spinlock.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/clk.h>
0016 #include <bcm63xx_cpu.h>
0017 #include <bcm63xx_io.h>
0018 #include <bcm63xx_timer.h>
0019 #include <bcm63xx_regs.h>
0020
0021 static DEFINE_RAW_SPINLOCK(timer_reg_lock);
0022 static DEFINE_RAW_SPINLOCK(timer_data_lock);
0023 static struct clk *periph_clk;
0024
0025 static struct timer_data {
0026 void (*cb)(void *);
0027 void *data;
0028 } timer_data[BCM63XX_TIMER_COUNT];
0029
0030 static irqreturn_t timer_interrupt(int irq, void *dev_id)
0031 {
0032 u32 stat;
0033 int i;
0034
0035 raw_spin_lock(&timer_reg_lock);
0036 stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
0037 bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
0038 raw_spin_unlock(&timer_reg_lock);
0039
0040 for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
0041 if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
0042 continue;
0043
0044 raw_spin_lock(&timer_data_lock);
0045 if (!timer_data[i].cb) {
0046 raw_spin_unlock(&timer_data_lock);
0047 continue;
0048 }
0049
0050 timer_data[i].cb(timer_data[i].data);
0051 raw_spin_unlock(&timer_data_lock);
0052 }
0053
0054 return IRQ_HANDLED;
0055 }
0056
0057 int bcm63xx_timer_enable(int id)
0058 {
0059 u32 reg;
0060 unsigned long flags;
0061
0062 if (id >= BCM63XX_TIMER_COUNT)
0063 return -EINVAL;
0064
0065 raw_spin_lock_irqsave(&timer_reg_lock, flags);
0066
0067 reg = bcm_timer_readl(TIMER_CTLx_REG(id));
0068 reg |= TIMER_CTL_ENABLE_MASK;
0069 bcm_timer_writel(reg, TIMER_CTLx_REG(id));
0070
0071 reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
0072 reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
0073 bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
0074
0075 raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
0076 return 0;
0077 }
0078
0079 EXPORT_SYMBOL(bcm63xx_timer_enable);
0080
0081 int bcm63xx_timer_disable(int id)
0082 {
0083 u32 reg;
0084 unsigned long flags;
0085
0086 if (id >= BCM63XX_TIMER_COUNT)
0087 return -EINVAL;
0088
0089 raw_spin_lock_irqsave(&timer_reg_lock, flags);
0090
0091 reg = bcm_timer_readl(TIMER_CTLx_REG(id));
0092 reg &= ~TIMER_CTL_ENABLE_MASK;
0093 bcm_timer_writel(reg, TIMER_CTLx_REG(id));
0094
0095 reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
0096 reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
0097 bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
0098
0099 raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
0100 return 0;
0101 }
0102
0103 EXPORT_SYMBOL(bcm63xx_timer_disable);
0104
0105 int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
0106 {
0107 unsigned long flags;
0108 int ret;
0109
0110 if (id >= BCM63XX_TIMER_COUNT || !callback)
0111 return -EINVAL;
0112
0113 ret = 0;
0114 raw_spin_lock_irqsave(&timer_data_lock, flags);
0115 if (timer_data[id].cb) {
0116 ret = -EBUSY;
0117 goto out;
0118 }
0119
0120 timer_data[id].cb = callback;
0121 timer_data[id].data = data;
0122
0123 out:
0124 raw_spin_unlock_irqrestore(&timer_data_lock, flags);
0125 return ret;
0126 }
0127
0128 EXPORT_SYMBOL(bcm63xx_timer_register);
0129
0130 void bcm63xx_timer_unregister(int id)
0131 {
0132 unsigned long flags;
0133
0134 if (id >= BCM63XX_TIMER_COUNT)
0135 return;
0136
0137 raw_spin_lock_irqsave(&timer_data_lock, flags);
0138 timer_data[id].cb = NULL;
0139 raw_spin_unlock_irqrestore(&timer_data_lock, flags);
0140 }
0141
0142 EXPORT_SYMBOL(bcm63xx_timer_unregister);
0143
0144 unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
0145 {
0146 return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
0147 }
0148
0149 EXPORT_SYMBOL(bcm63xx_timer_countdown);
0150
0151 int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
0152 {
0153 u32 reg, countdown;
0154 unsigned long flags;
0155
0156 if (id >= BCM63XX_TIMER_COUNT)
0157 return -EINVAL;
0158
0159 countdown = bcm63xx_timer_countdown(countdown_us);
0160 if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
0161 return -EINVAL;
0162
0163 raw_spin_lock_irqsave(&timer_reg_lock, flags);
0164 reg = bcm_timer_readl(TIMER_CTLx_REG(id));
0165
0166 if (monotonic)
0167 reg &= ~TIMER_CTL_MONOTONIC_MASK;
0168 else
0169 reg |= TIMER_CTL_MONOTONIC_MASK;
0170
0171 reg &= ~TIMER_CTL_COUNTDOWN_MASK;
0172 reg |= countdown;
0173 bcm_timer_writel(reg, TIMER_CTLx_REG(id));
0174
0175 raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
0176 return 0;
0177 }
0178
0179 EXPORT_SYMBOL(bcm63xx_timer_set);
0180
0181 int bcm63xx_timer_init(void)
0182 {
0183 int ret, irq;
0184 u32 reg;
0185
0186 reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
0187 reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
0188 reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
0189 reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
0190 bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
0191
0192 periph_clk = clk_get(NULL, "periph");
0193 if (IS_ERR(periph_clk))
0194 return -ENODEV;
0195
0196 irq = bcm63xx_get_irq_number(IRQ_TIMER);
0197 ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
0198 if (ret) {
0199 pr_err("%s: failed to register irq\n", __func__);
0200 return ret;
0201 }
0202
0203 return 0;
0204 }
0205
0206 arch_initcall(bcm63xx_timer_init);