nt9856x/BSP/linux-kernel/drivers/clocksource/timer-novatek.c
2023-03-28 15:07:53 +08:00

367 lines
9.2 KiB
C

/**
NVT clocksource function
NVT clocksource driver
@file timer-novatek.c
@ingroup
@note
Copyright Novatek Microelectronics Corp. 2021. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/irqreturn.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include "timer-of.h"
/* For clock event */
#define MAX_VALUE 0xFFFFFFFF
#define SHIFT 32ULL
#define MAX_HZ 3000000
#define MAX_DIV_BIT 255
#define TIMER_NUM (19)
#define TIMER_DIV_SRC 1
#if (TIMER_DIV_SRC == 0)
#define TIMER_DIV_DEF 0xFFFFFF00
#else
#define TIMER_DIV_DEF 0xFFFF00FF
#endif
#define TIMER_FREE_RUN (0x3 + (TIMER_DIV_SRC * 0x4))
#define TIMER_ONE_SHOT (0x1 + (TIMER_DIV_SRC * 0x4))
#define TIMER_DIV_BASE 0x30
#define TIMER_DIV_VAL (1 << ( 16 + TIMER_DIV_SRC) )
#define TIMER_IRQ_ENABLE 0x00
#define TIMER_IRQ_STS 0x10
#define TIMER_IRQ_BASE 0x20
#define TIMER_IRQ_VAL (1 << TIMER_NUM)
#define TIMER_CTL_BASE (0x10 * TIMER_NUM + 0x100)
#define TIMER_TVAL_BASE (TIMER_CTL_BASE + 0x04)
#define TIMER_VALUE_BASE (TIMER_CTL_BASE + 0x08)
#define TIMER_RLD_BASE (TIMER_CTL_BASE + 0x0C)
#define TIMER_ENABLE 1
#define TIMER_DISABLE 0
#define TIMER_FREQ_SHIFT (TIMER_DIV_SRC * 8)
#define TIMER_UP (0x3 + 0x4 * TIMER_DIV_SRC)
#define TIMER_DOWN (TIMER_UP - 1)
/* For NVT HW timer frame work */
#define MAX_TIMER_NUM 20
void __iomem *nvttmr_base;
spinlock_t nvttmr_lock;
u32 nvttmr_in_use;
EXPORT_SYMBOL(nvttmr_base);
EXPORT_SYMBOL(nvttmr_lock);
EXPORT_SYMBOL(nvttmr_in_use);
static struct timer_of to = {
.flags = TIMER_OF_BASE,
.clkevt = {
.name = "nvt-clkevt",
.rating = 100,
.cpumask = cpu_possible_mask,
},
.of_irq = {
.flags = IRQF_SHARED | IRQF_TIMER,
},
};
/*static u64 notrace nvt_read_sched_clock(void)
{
u32 sum1;
u64 ns;
sum1 = readl(timer_of_base(&to) + timer_value_base);
ns = (s64)sum1;
return ns;
}
*/
static void nvt_clkevt_time_stop(struct timer_of *to)
{
u32 val;
unsigned long flags;
spin_lock_irqsave(&nvttmr_lock, flags);
val = readl(timer_of_base(to) + TIMER_CTL_BASE);
val |= TIMER_DOWN;
writel( val, (timer_of_base(to) + TIMER_CTL_BASE));
spin_unlock_irqrestore(&nvttmr_lock, flags);
}
static void nvt_clkevt_time_setup(struct timer_of *to,
unsigned long delay)
{
unsigned long flags;
u32 val = (u32)(delay);
spin_lock_irqsave(&nvttmr_lock, flags);
writel(val, (timer_of_base(to) + TIMER_TVAL_BASE));
spin_unlock_irqrestore(&nvttmr_lock, flags);
}
static void nvt_clkevt_time_start(struct timer_of *to,
bool periodic)
{
u32 val;
unsigned long flags;
spin_lock_irqsave(&nvttmr_lock, flags);
val = readl(timer_of_base(to) + TIMER_CTL_BASE);
if (periodic)
val |= TIMER_FREE_RUN;
else
val |= TIMER_ONE_SHOT;
writel( val, (timer_of_base(to) + TIMER_CTL_BASE));
spin_unlock_irqrestore(&nvttmr_lock, flags);
}
static int nvt_clkevt_shutdown(struct clock_event_device *clk)
{
nvt_clkevt_time_stop(to_timer_of(clk));
return 0;
}
static int nvt_clkevt_set_periodic(struct clock_event_device *clk)
{
struct timer_of *to = to_timer_of(clk);
nvt_clkevt_time_stop(to);
nvt_clkevt_time_setup(to, to->of_clk.period);
nvt_clkevt_time_start(to, true);
return 0;
}
static int nvt_clkevt_set_oneshot(struct clock_event_device *clk)
{
struct timer_of *to = to_timer_of(clk);
nvt_clkevt_time_stop(to);
nvt_clkevt_time_setup(to, to->of_clk.period);
nvt_clkevt_time_start(to, false);
return 0;
}
static int nvt_clkevt_next_event(unsigned long event,
struct clock_event_device *clk)
{
struct timer_of *to = to_timer_of(clk);
nvt_clkevt_time_stop(to);
nvt_clkevt_time_setup(to, event);
nvt_clkevt_time_start(to, false);
return 0;
}
static irqreturn_t nvt_interrupt(int irq, void *dev_id)
{
struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
u32 val;
unsigned long flags;
int ret = IRQ_HANDLED;
spin_lock_irqsave(&nvttmr_lock, flags);
val = readl(timer_of_base(&to) + TIMER_IRQ_STS);
if (val & (1 << TIMER_NUM)) {
val = (1 << TIMER_NUM);
writel( val, (timer_of_base(&to) + TIMER_IRQ_STS));
if (clkevt->event_handler != NULL) {
clkevt->event_handler(clkevt);
}
}
else
ret = IRQ_NONE;
spin_unlock_irqrestore(&nvttmr_lock, flags);
return ret;
}
static int
__init nvt_setup(struct timer_of *to, u32 freq)
{
u32 val;
unsigned long flags;
u8 freq_bit = (MAX_HZ / freq) - 1;
if ((MAX_HZ / freq) > MAX_DIV_BIT) {
pr_err("%s: freq too slow should > %d\n", __func__, MAX_HZ / MAX_DIV_BIT);
return -EINVAL;
}
spin_lock_irqsave(&nvttmr_lock, flags);
val = MAX_VALUE;
writel( val, (timer_of_base(to) + TIMER_IRQ_STS));
val = readl(timer_of_base(to) + TIMER_IRQ_ENABLE);
val |= MAX_VALUE;
writel( val, (timer_of_base(to) + TIMER_IRQ_ENABLE));
val = readl(timer_of_base(to) + TIMER_IRQ_BASE);
val = TIMER_IRQ_VAL;
writel( val, (timer_of_base(to) + TIMER_IRQ_BASE));
val = readl(timer_of_base(to) + TIMER_DIV_BASE);
val &= TIMER_DIV_DEF;
val |= ((freq_bit << TIMER_FREQ_SHIFT) + TIMER_DIV_VAL);
writel( val, (timer_of_base(to) + TIMER_DIV_BASE));
val = readl(timer_of_base(to) + TIMER_TVAL_BASE);
val |= MAX_VALUE;
writel( val, (timer_of_base(to) + TIMER_TVAL_BASE));
val = readl(timer_of_base(to) + TIMER_RLD_BASE);
val &= ~(MAX_VALUE);
writel( val, (timer_of_base(to) + TIMER_RLD_BASE));
val = readl(timer_of_base(to) + TIMER_CTL_BASE);
val |= TIMER_UP;
writel( val, (timer_of_base(to) + TIMER_CTL_BASE));
spin_unlock_irqrestore(&nvttmr_lock, flags);
return 0;
}
static int __init nvt_common_init(struct device_node *node)
{
int ret = 0;
u32 *pfreq = 0;
u32 freq;
u32 *pclksrc, clksrc = 0;
u32 *pbits, bits = 0;
struct resource res;
if (MAX_TIMER_NUM < TIMER_NUM) {
pr_err("Timer num not support\n");
return -EINVAL;
}
if ((nvttmr_in_use & (1 << TIMER_NUM))) {
pr_err("Timer num %d already in use\n",TIMER_NUM);
return -EINVAL;
} else {
nvttmr_in_use |= (1 << TIMER_NUM);
}
to.of_clk.clk = clk_get_sys(node->full_name,"f0040000.timer");
if (IS_ERR(to.of_clk.clk)) {
pr_err("%s:%d clk_get fail\n", __func__, __LINE__);
return PTR_ERR(to.of_clk.clk);
}
if (clk_prepare_enable(to.of_clk.clk) != 0) {
pr_err("%s:%d enable clk fail\n", __func__, __LINE__);
return PTR_ERR(to.of_clk.clk);
}
to.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
to.clkevt.set_state_shutdown = nvt_clkevt_shutdown;
to.clkevt.set_state_periodic = nvt_clkevt_set_periodic;
to.clkevt.set_state_oneshot = nvt_clkevt_set_oneshot;
to.clkevt.tick_resume = nvt_clkevt_shutdown;
to.clkevt.set_next_event = nvt_clkevt_next_event;
to.of_irq.handler = nvt_interrupt;
to.clkevt.name = of_get_property(node, "compatible", NULL);
to.of_clk.name = of_get_property(node, "clock-names", NULL);
//printk("fttmr010_of_init...fttmr010_clkevt.name=%s base=%x, irq=%d, freq=%d\n", fttmr010_clkevt.name, base, irq, freq);
pfreq = (u32 *)of_get_property(node, "clock-frequency2", NULL);
freq = __be32_to_cpu(*pfreq);
pclksrc = (u32 *)of_get_property(node, "clock-source", NULL);
clksrc = __be32_to_cpu(*pclksrc);
pbits = (u32 *)of_get_property(node, "bits", NULL);
bits = __be32_to_cpu(*pbits);
to.of_clk.rate = freq;
//clocks_calc_mult_shift(&to.clkevt.mult,&to.clkevt.shift,freq,nsec_per_sec,60);
//ret = timer_of_init(node, &to);
//to.flags ^= timer_of_irq;
ret = of_address_to_resource(node, 0, &res);
if (ret) {
pr_err("%s:%d of_address_to_resource fail\n", __func__, __LINE__);
goto resource_err;
}
if (!request_mem_region(res.start, resource_size(&res),node->full_name)) {
pr_err("%s:%d request_mem_region fail\n", __func__, __LINE__);
goto req_mem_err;
}
spin_lock_init(&nvttmr_lock);
ret = timer_of_init(node, &to);
if (ret)
goto time_init_err;
ret = nvt_setup(&to, freq);
if (ret)
goto time_setup_err;
to.flags |= TIMER_OF_CLOCK;
to.of_irq.irq = irq_of_parse_and_map(node, to.of_irq.index);
if (to.of_irq.irq < 0) {
pr_err("%s:%d irq_of_parse_and_map\n", __func__, __LINE__);
goto irq_err;
}
ret = request_irq(to.of_irq.irq, to.of_irq.handler,
to.of_irq.flags ? to.of_irq.flags : IRQF_TIMER,
node->full_name, &to.clkevt);
//ret = timer_of_irq_init(np, &to.of_irq);
if (ret) {
pr_err("%s:%d request_irq fail\n", __func__, __LINE__);
goto irq_err;
}
to.flags |= TIMER_OF_IRQ;
nvttmr_base = timer_of_base(&to);
/* configure clock source */
/*clocksource_mmio_init(timer_of_base(&to) + timer_value_base,
node->name, timer_of_rate(&to), freq, bits,
clocksource_mmio_readl_up);
*/
//sched_clock_register(nvt_read_sched_clock, 32, timer_of_rate(&to));
/* configure clock event */
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
1, 0xffffffff);
return 0;
irq_err:
time_setup_err:
time_init_err:
release_mem_region(res.start, (res.end - res.start + 1));
req_mem_err:
resource_err:
clk_disable(to.of_clk.clk);
return ret;
}
static __init int nvt_timer_init(struct device_node *np)
{
return nvt_common_init(np);
}
TIMER_OF_DECLARE(nvt_timer, "nvt,nvt_clk_src", nvt_timer_init);