cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

timer.c (4524B)


      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);