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

495 lines
14 KiB
C
Executable File

/*
* Novatek tmr010 driver.
*
* Copyright (C) 2019 Novatek MicroElectronics Corp.
*
* Updated by JJ Chen Jan 2020
*
* ----------------------------------------------------------------------------
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
*
*/
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#include <asm/io.h>
#include <asm/mach/irq.h>
#include <plat/nvttmr010.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/platform_device.h>
//For reset and clk-gating
#include <plat/hardware.h>
#include <plat/cg-reg.h>
#define DRV_VERSION "1.0.0" // major change, interface update, bug fix
#define NVTTMR010_COUNTER 0x00
#define NVTTMR010_LOAD 0x04
#define NVTTMR010_MATCH1 0x08
#define NVTTMR010_MATCH2 0x0c
#define NVTTMR010_TIMER(x) ((x) * 0x10)
#define NVTTMR010_CR 0x30
#define NVTTMR010_INTR_STATE 0x34
#define NVTTMR010_INTR_MASK 0x38
/*
* Timer Control Register
*/
#define NVTTMR010_TM3_UPDOWN (1 << 11)
#define NVTTMR010_TM2_UPDOWN (1 << 10)
#define NVTTMR010_TM1_UPDOWN (1 << 9)
#define NVTTMR010_TM3_OFENABLE (1 << 8)
#define NVTTMR010_TM3_CLOCK (1 << 7)
#define NVTTMR010_TM3_ENABLE (1 << 6)
#define NVTTMR010_TM2_OFENABLE (1 << 5)
#define NVTTMR010_TM2_CLOCK (1 << 4)
#define NVTTMR010_TM2_ENABLE (1 << 3)
#define NVTTMR010_TM1_OFENABLE (1 << 2)
#define NVTTMR010_TM1_CLOCK (1 << 1)
#define NVTTMR010_TM1_ENABLE (1 << 0)
/*
* Timer Interrupt State & Mask Registers
*/
#define NVTTMR010_TM3_OVERFLOW (1 << 8)
#define NVTTMR010_TM3_MATCH2 (1 << 7)
#define NVTTMR010_TM3_MATCH1 (1 << 6)
#define NVTTMR010_TM2_OVERFLOW (1 << 5)
#define NVTTMR010_TM2_MATCH2 (1 << 4)
#define NVTTMR010_TM2_MATCH1 (1 << 3)
#define NVTTMR010_TM1_OVERFLOW (1 << 2)
#define NVTTMR010_TM1_MATCH2 (1 << 1)
#define NVTTMR010_TM1_MATCH1 (1 << 0)
#define EVTTMR 0
#define SRCTMR 1
static void __iomem *nvttmr010_base;
static unsigned int clk_reload;
static unsigned int ext_clk;
/******************************************************************************
* internal functions
*****************************************************************************/
static const unsigned int nvttmr010_cr_mask[3] = {
NVTTMR010_TM1_ENABLE | NVTTMR010_TM1_CLOCK |
NVTTMR010_TM1_OFENABLE | NVTTMR010_TM1_UPDOWN,
NVTTMR010_TM2_ENABLE | NVTTMR010_TM2_CLOCK |
NVTTMR010_TM2_OFENABLE | NVTTMR010_TM2_UPDOWN,
NVTTMR010_TM3_ENABLE | NVTTMR010_TM3_CLOCK |
NVTTMR010_TM3_OFENABLE | NVTTMR010_TM3_UPDOWN,
};
/* we always use down counter */
static const unsigned int nvttmr010_cr_enable_flag[3] = {
NVTTMR010_TM1_ENABLE | NVTTMR010_TM1_OFENABLE,
NVTTMR010_TM2_ENABLE | NVTTMR010_TM2_OFENABLE,
NVTTMR010_TM3_ENABLE | NVTTMR010_TM3_OFENABLE,
};
static const unsigned int nvttmr010_cr_enable_noirq_flag[3] = {
NVTTMR010_TM1_ENABLE,
NVTTMR010_TM2_ENABLE,
NVTTMR010_TM3_ENABLE,
};
static const unsigned int nvttmr010_ext_clk[3] = {
NVTTMR010_TM1_CLOCK,
NVTTMR010_TM2_CLOCK,
NVTTMR010_TM3_CLOCK,
};
static void nvttmr010_enable(unsigned int id)
{
unsigned int cr = readl(nvttmr010_base + NVTTMR010_CR);
cr &= ~nvttmr010_cr_mask[id];
cr |= nvttmr010_cr_enable_flag[id];
if (ext_clk==1) {
cr |= nvttmr010_ext_clk[id];
}
writel(cr, nvttmr010_base + NVTTMR010_CR);
panic("333");
}
static void nvttmr010_enable_noirq(unsigned int id)
{
unsigned int cr = readl(nvttmr010_base + NVTTMR010_CR);
cr &= ~nvttmr010_cr_mask[id];
cr |= nvttmr010_cr_enable_noirq_flag[id];
if (ext_clk==1) {
cr |= nvttmr010_ext_clk[id];
}
writel(cr, nvttmr010_base + NVTTMR010_CR);
}
static void nvttmr010_disable(unsigned int id)
{
unsigned int cr = readl(nvttmr010_base + NVTTMR010_CR);
cr &= ~nvttmr010_cr_mask[id];
writel(cr, nvttmr010_base + NVTTMR010_CR);
}
static void nvttmr010_disable_all(void)
{
int id;
unsigned int cr = readl(nvttmr010_base + NVTTMR010_CR);
for (id = 0; id < ARRAY_SIZE(nvttmr010_cr_mask); id ++)
cr &= ~nvttmr010_cr_mask[id];
writel(cr, nvttmr010_base + NVTTMR010_CR);
}
static const unsigned int nvttmr010_irq_mask[3] = {
NVTTMR010_TM1_MATCH1 | NVTTMR010_TM1_MATCH2 | NVTTMR010_TM1_OVERFLOW,
NVTTMR010_TM2_MATCH1 | NVTTMR010_TM2_MATCH2 | NVTTMR010_TM2_OVERFLOW,
NVTTMR010_TM3_MATCH1 | NVTTMR010_TM3_MATCH2 | NVTTMR010_TM3_OVERFLOW,
};
static void nvttmr010_mask_allirq(void)
{
unsigned int id, mask = 0;
for (id = 0; id < ARRAY_SIZE(nvttmr010_irq_mask); id ++)
mask |= nvttmr010_irq_mask[id];
writel(mask, nvttmr010_base + NVTTMR010_INTR_MASK);
}
static void nvttmr010_unmask_irq(unsigned int id)
{
unsigned int mask;
mask = readl(nvttmr010_base + NVTTMR010_INTR_MASK);
mask &= ~nvttmr010_irq_mask[id];
writel(mask, nvttmr010_base + NVTTMR010_INTR_MASK);
}
static inline void nvttmr010_write_timer(unsigned int id, unsigned int reg,
unsigned int value)
{
void __iomem *addr = nvttmr010_base + NVTTMR010_TIMER(id) + reg;
writel (value, addr);
}
static inline unsigned int nvttmr010_read_timer(unsigned int id,
unsigned int reg)
{
void __iomem *addr = nvttmr010_base + NVTTMR010_TIMER(id) + reg;
return readl(addr);
}
static void nvttmr010_set_counter(unsigned int id, unsigned int value)
{
nvttmr010_write_timer(id, NVTTMR010_COUNTER, value);
}
static void nvttmr010_set_reload(unsigned int id, unsigned int value)
{
nvttmr010_write_timer(id, NVTTMR010_LOAD, value);
}
static void nvttmr010_set_match1(unsigned int id, unsigned int value)
{
nvttmr010_write_timer(id, NVTTMR010_MATCH1, value);
}
static void nvttmr010_set_match2(unsigned int id, unsigned int value)
{
nvttmr010_write_timer(id, NVTTMR010_MATCH2, value);
}
/******************************************************************************
* clockevent functions
*****************************************************************************/
static int nvttmr010_set_next_event(unsigned long clc,
struct clock_event_device *ce)
{
nvttmr010_set_counter(EVTTMR, clc);
return 0;
}
/*
static void nvttmr010_set_mode(enum clock_event_state mode,
struct clock_event_device *ce)
{
switch (mode) {
case CLOCK_EVT_STATE_PERIODIC:
nvttmr010_set_reload(EVTTMR, clk_reload);
nvttmr010_set_counter(EVTTMR, clk_reload);
nvttmr010_enable(EVTTMR);
break;
case CLOCK_EVT_STATE_ONESHOT_STOPPED:
nvttmr010_enable(EVTTMR);
break;
case CLOCK_EVT_STATE_ONESHOT:
nvttmr010_set_reload(EVTTMR, 0);
nvttmr010_enable(EVTTMR);
break;
case CLOCK_EVT_STATE_DETACHED:
case CLOCK_EVT_STATE_SHUTDOWN:
default:
nvttmr010_disable(EVTTMR);
printk("%s, shutdown tmr%d \n", __func__, EVTTMR);
break;
}
}
*/
static int nvttmr010_clock_event_shutdown(struct clock_event_device *ce)
{
nvttmr010_disable(EVTTMR);
printk("%s, shutdown tmr%d \n", __func__, EVTTMR);
return 0;
}
static int nvttmr010_clock_event_periodic(struct clock_event_device *ce)
{
nvttmr010_set_reload(EVTTMR, clk_reload);
nvttmr010_set_counter(EVTTMR, clk_reload);
nvttmr010_enable(EVTTMR);
return 0;
}
static int nvttmr010_clock_event_oneshot(struct clock_event_device *ce)
{
nvttmr010_set_reload(EVTTMR, 0);
nvttmr010_enable(EVTTMR);
return 0;
}
static irqreturn_t nvttmr010_clockevent_interrupt(int irq, void *dev_id)
{
unsigned int state;
struct clock_event_device *ce = (struct clock_event_device *)dev_id;
state = readl_relaxed(nvttmr010_base + NVTTMR010_INTR_STATE);
if (!(state & nvttmr010_irq_mask[EVTTMR])) {
printk("%s, error, tmrid=%d, state:0x%x, mask:0x%x! \n", __func__, EVTTMR, state, nvttmr010_irq_mask[EVTTMR]);
return IRQ_NONE;
}
//state &= ~nvttmr010_irq_mask[EVTTMR];
writel_relaxed(state, nvttmr010_base + NVTTMR010_INTR_STATE);
if (ce == NULL || ce->event_handler == NULL) {
pr_warn("nvttmr010: event_handler is not found!\n");
return IRQ_NONE;
}
ce->event_handler(ce);
return IRQ_HANDLED;
}
static struct clock_event_device nvttmr010_clkevt = {
.name = "nvttmr010",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = nvttmr010_set_next_event,
//.set_mode = nvttmr010_set_mode,
.set_state_shutdown = nvttmr010_clock_event_shutdown,
.set_state_periodic = nvttmr010_clock_event_periodic,
.set_state_oneshot = nvttmr010_clock_event_oneshot,
};
static struct irqaction nvttmr010_irq = {
.name = "nvttmr010",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = nvttmr010_clockevent_interrupt,
.dev_id = &nvttmr010_clkevt,
};
#if 0 // register nvttmr010 timer as the sched_clock is not necessarily required
static u64 notrace nvttmr010_sched_clock_read(void)
{
return ~readl_relaxed(nvttmr010_base + NVTTMR010_TIMER(SRCTMR) +
NVTTMR010_COUNTER);
}
#endif
struct clocksource_mmio {
void __iomem *reg;
struct clocksource clksrc;
};
struct nvttmr010_clocksource nvttmr010;
bool nvttmr010_clksrc_is_init = false;
struct clocksource_mmio *cs = NULL;
int __init nvttmr010_clocksource_init(void __iomem *base, const char *name,
unsigned long hz, int rating, unsigned bits,
u64 (*read)(struct clocksource *))
{
if (bits > 32 || bits < 16)
return -EINVAL;
cs = kzalloc(sizeof(struct clocksource_mmio), GFP_KERNEL);
if (!cs)
return -ENOMEM;
cs->reg = base;
cs->clksrc.name = name;
cs->clksrc.rating = rating;
cs->clksrc.read = read;
cs->clksrc.mask = CLOCKSOURCE_MASK(bits);
cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
return clocksource_register_hz(&cs->clksrc, hz);
}
u64 clocksource_mmio_readl_downA(struct clocksource *c)
{
//return ~(u64)readl_relaxed(to_mmio_clksrc(c)->reg) & c->mask;
return ~(u64)readl_relaxed(nvttmr010_base + NVTTMR010_TIMER(SRCTMR) +
NVTTMR010_COUNTER);
}
nvttmr010_clocksource nvttmr010_get_clocksource(void)
{
if (!nvttmr010_clksrc_is_init) {
return NULL;
} else {
return &nvttmr010;
}
}
void __init nvttmr010_init(void __iomem *base, int irq, unsigned long clk_freq)
{
nvttmr010_base = base;
nvttmr010_irq.irq = irq;
/* disable all timers first because they may be used in uboot or romcode */
nvttmr010_disable_all();
nvttmr010_mask_allirq();
/* setup as free-running clocksource */
nvttmr010_disable(SRCTMR);
nvttmr010_set_match1(SRCTMR, 0);
nvttmr010_set_match2(SRCTMR, 0);
nvttmr010_set_reload(SRCTMR, 0xffffffff);
nvttmr010_set_counter(SRCTMR, 0xffffffff);
nvttmr010_enable_noirq(SRCTMR);
#if 0 // register nvttmr010 timer as the sched_clock is not necessarily required
sched_clock_register(nvttmr010_sched_clock_read, BITS_PER_LONG,
clk_freq);
#endif
//if (clocksource_mmio_init(nvttmr010_base + NVTTMR010_TIMER(SRCTMR) +
if (nvttmr010_clocksource_init(nvttmr010_base + NVTTMR010_TIMER(SRCTMR) +
NVTTMR010_COUNTER, "nvttmr010_clksrc", clk_freq,
200/*300*/, 32, clocksource_mmio_readl_downA)) {
pr_err("Failed to register clocksource\n");
BUG();
}
/* initialize to a known state */
nvttmr010_disable(EVTTMR);
nvttmr010_set_match1(EVTTMR, 0);
nvttmr010_set_match2(EVTTMR, 0);
nvttmr010_unmask_irq(EVTTMR);
/* setup reload value for periodic clockevents */
clk_reload = clk_freq / HZ;
/* Make irqs happen for the system timer */
if (setup_irq(nvttmr010_irq.irq, &nvttmr010_irq)) {
pr_err("Failed to register timer IRQ\n");
BUG();
}
/* setup struct clock_event_device */
#if defined(CONFIG_CPU_CA7) && defined(CONFIG_SMP)
nvttmr010_clkevt.cpumask = cpu_all_mask;
#else
nvttmr010_clkevt.cpumask = cpumask_of(0);
#endif
nvttmr010_clkevt.irq = nvttmr010_irq.irq;
clockevents_config_and_register(&nvttmr010_clkevt, clk_freq, 0xf,
0xffffffff);
/* Set clocksource */
nvttmr010.clocksource = cs->clksrc;
nvttmr010.base = base;
nvttmr010.id = SRCTMR;
nvttmr010.freq = clk_freq;
nvttmr010_clksrc_is_init = true;
}
int __init nvttmr010_of_init(struct device_node *np)
{
//struct clk *clk;
u32 *pfreq, freq=0;
u32 *pext_clk;
void __iomem *base;
int irq;
base = of_iomap(np, 0);
if (WARN_ON(!base))
return -1;
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
goto err;
//clk = of_clk_get(np, 0);
//clk_prepare_enable(clk);
//freq = clk_get_rate(clk);
//printk("nvttmr010_of_init...base=%x, irq=%d, clk=%x, freq=%d\n", base, irq, clk, freq);
printk("NVTTMR010 Driver Version: %s\n", DRV_VERSION);
nvttmr010_clkevt.name = of_get_property(np, "compatible", NULL);
//printk("nvttmr010_of_init...nvttmr010_clkevt.name=%s base=%x, irq=%d, freq=%d\n", nvttmr010_clkevt.name, base, irq, freq);
pfreq = (u32 *)of_get_property(np, "clocks", NULL);
freq = __be32_to_cpu(*pfreq);
pext_clk = (u32 *)of_get_property(np, "ext_clk", NULL);
ext_clk = __be32_to_cpu(*pext_clk);
nvttmr010_init(base, irq, freq);
return 0;
err:
iounmap(base);
return -1;
}
TIMER_OF_DECLARE(nvttmr010, "faraday,fttmr010", nvttmr010_of_init);