linux-dsm232/drivers/clocksource/cnc1800l_timer.c
2022-06-30 19:18:23 +09:00

179 lines
4.5 KiB
C

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/bitops.h>
/*
* arch/arm/mach-celestial/include/mach/time.h
*
* This file contains the hardware definitions of the Celestial Platform.
*
* Copyright (C) 2010 Celestial Semiconductor
* Copyright (C) 2011 Cavium
* Author: Xaodong Fan <xiaodong.fan@caviumneworks.com>
*/
/*
* Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
*/
#define TIMER_PRE_LOAD_0 0x0
#define TIMER_PRE_LOAD_1 0x4
#define TIMER_THRESHOLD_0 0x8
#define TIMER_THRESHOLD_1 0xc
#define TIMER_MODE 0x10
#define TIMER_INTR_EN 0x14
#define TIMER_INTR_CLR 0x18
#define TIMER_ENABLE 0x1c
#define TIMER_CURRENT_VAL_0 0x20
#define TIMER_CURRENT_VAL_1 0x24
#define TIMER_INTR_STATUS 0x28
#define TIMER_INTR_RAW_STATUS 0x2c
static void __iomem *cnct_base;
static unsigned long cnct_reload;
static int cnc1800l_timer_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned long flags;
if(delta == 0){
return -ETIME;
}
raw_local_irq_save(flags);
writel_relaxed(0,cnct_base + TIMER_ENABLE);
writel_relaxed(0x7,cnct_base +TIMER_INTR_CLR);
writel_relaxed(0x0,cnct_base +TIMER_INTR_CLR);
writel_relaxed(delta,cnct_base + TIMER_THRESHOLD_0);
writel_relaxed(0,cnct_base + TIMER_PRE_LOAD_0);
writel_relaxed(0x1,cnct_base + TIMER_MODE);
writel_relaxed(0x1,cnct_base + TIMER_INTR_EN);
writel_relaxed(0x1,cnct_base + TIMER_ENABLE);
raw_local_irq_restore(flags);
return 0;
}
static int cnc1800l_timer_shutdown(struct clock_event_device *evt)
{
writel_relaxed(0x0, cnct_base + TIMER_ENABLE);
return 0;
}
static int cnc1800l_timer_set_periodic(struct clock_event_device *evt)
{
unsigned long flags;
raw_local_irq_save(flags);
writel_relaxed(0,cnct_base + TIMER_ENABLE);
writel_relaxed(cnct_reload,cnct_base + TIMER_THRESHOLD_0);
writel_relaxed(0,cnct_base + TIMER_PRE_LOAD_0);
writel_relaxed(0,cnct_base + TIMER_MODE);
writel_relaxed(0x1,cnct_base + TIMER_INTR_EN);
writel_relaxed(0x1,cnct_base + TIMER_ENABLE);
raw_local_irq_restore(flags);
return 0;
}
static int cnc1800l_timer_set_oneshot(struct clock_event_device *evt)
{
unsigned long flags;
raw_local_irq_save(flags);
writel_relaxed(0x7, cnct_base + TIMER_INTR_CLR);
writel_relaxed(0x0, cnct_base + TIMER_INTR_CLR);
writel_relaxed(0x0,cnct_base + TIMER_ENABLE);
raw_local_irq_restore(flags);
return 0;
}
static struct clock_event_device event_dev = {
.name = "cnc1800l-timer",
.rating = 400,
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.shift = 32,
.set_next_event = cnc1800l_timer_set_next_event,
.set_state_shutdown = cnc1800l_timer_shutdown,
.set_state_periodic = cnc1800l_timer_set_periodic,
.set_state_oneshot = cnc1800l_timer_set_oneshot,
.tick_resume = cnc1800l_timer_shutdown,
};
static irqreturn_t cnc1800l_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
writel_relaxed(0x1,cnct_base + TIMER_INTR_CLR);
writel_relaxed(0x0,cnct_base + TIMER_INTR_CLR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int __init cnc1800l_timer_init(struct device_node *np)
{
int irq;
struct clk *clk;
int ret;
unsigned long rate;
cnct_base = of_io_request_and_map(np, 0, np->name);
if (IS_ERR(cnct_base)) {
pr_err("%pOFn: unable to map resource\n", np);
return PTR_ERR(cnct_base);
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get clk!\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Failed to enable clk!\n");
return ret;
}
irq = irq_of_parse_and_map(np, 0);
ret = request_irq(irq, cnc1800l_timer_interrupt, IRQF_TIMER,
"cnc1800l-timer", &event_dev);
if (ret) {
pr_err("Failed to setup irq!\n");
return ret;
}
writel_relaxed(0x0,cnct_base + TIMER_INTR_EN);
writel_relaxed(0x0,cnct_base + TIMER_ENABLE);
writel_relaxed(0x1,cnct_base + TIMER_INTR_CLR);
writel_relaxed(0x0,cnct_base + TIMER_INTR_CLR);
rate = clk_get_rate(clk);
cnct_reload = DIV_ROUND_CLOSEST(rate,HZ);
event_dev.cpumask = cpumask_of(0);
clockevents_config_and_register(&event_dev, rate, 0xf, 0xfffffffc);
return 0;
}
TIMER_OF_DECLARE(cnc1800l_timer, "cavium,cnc1800l-timer",
cnc1800l_timer_init);