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