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