blob: cf26ae1a07f589fa68c0aebba860af0a3632d2b0 [file] [log] [blame]
/*
* drivers/amlogic/clocksource/meson_bc_timer.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 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.
*
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/stat.h>
#include <asm/memory.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <asm/smp_plat.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/cpu.h>
#undef pr_fmt
#define pr_fmt(fmt) "meson_bc_timer: " fmt
/***********************************************************************
* System timer
**********************************************************************/
#define TIMER_E_RESOLUTION_BIT 8
#define TIMER_E_ENABLE_BIT 20
#define TIMER_E_RESOLUTION_MASK (7UL << TIMER_E_RESOLUTION_BIT)
#define TIMER_E_RESOLUTION_SYS 0
#define TIMER_E_RESOLUTION_1us 1
#define TIMER_E_RESOLUTION_10us 2
#define TIMER_E_RESOLUTION_100us 3
#define TIMER_E_RESOLUTION_1ms 4
#define TIMER_DI_RESOLUTION_BIT 6
#define TIMER_CH_RESOLUTION_BIT 4
#define TIMER_BG_RESOLUTION_BIT 2
#define TIMER_AF_RESOLUTION_BIT 0
#define TIMER_DI_ENABLE_BIT 19
#define TIMER_CH_ENABLE_BIT 18
#define TIMER_BG_ENABLE_BIT 17
#define TIMER_AF_ENABLE_BIT 16
#define TIMER_DI_MODE_BIT 15
#define TIMER_CH_MODE_BIT 14
#define TIMER_BG_MODE_BIT 13
#define TIMER_AF_MODE_BIT 12
#define TIMER_RESOLUTION_1us 0
#define TIMER_RESOLUTION_10us 1
#define TIMER_RESOLUTION_100us 2
#define TIMER_RESOLUTION_1ms 3
void __iomem *timer_ctrl_base;
static inline void aml_set_reg32_mask(void __iomem *_reg, const uint32_t _mask)
{
uint32_t _val;
_val = readl_relaxed(_reg) | _mask;
writel_relaxed(_val, _reg);
}
static inline void aml_write_reg32(void __iomem *_reg, const uint32_t _value)
{
writel_relaxed(_value, _reg);
}
static inline void aml_clr_reg32_mask(void __iomem *_reg, const uint32_t _mask)
{
writel_relaxed((readl_relaxed(_reg) & (~(_mask))), _reg);
}
static inline void
aml_set_reg32_bits(void __iomem *_reg, const uint32_t _value,
const uint32_t _start, const uint32_t _len)
{
writel_relaxed(((readl_relaxed(_reg) &
~(((1L << (_len))-1) << (_start))) |
(((_value)&((1L<<(_len))-1)) << (_start))),
_reg);
}
/********** Clock Event Device, Timer-ABCD/FGHI *********/
struct meson_clock {
struct irqaction irq;
const char *name; /*A,B,C,D,F,G,H,I*/
int bit_enable;
int bit_mode;
int bit_resolution;
void __iomem *mux_reg;
void __iomem *reg;
unsigned int init_flag;
};
static struct meson_clock bc_clock;
static irqreturn_t meson_timer_interrupt(int irq, void *dev_id);
static int meson_set_next_event(unsigned long evt,
struct clock_event_device *dev);
static DEFINE_SPINLOCK(time_lock);
static int meson_set_next_event(unsigned long evt,
struct clock_event_device *dev)
{
struct meson_clock *clk = &bc_clock;
/* use a big number to clear previous trigger cleanly */
aml_set_reg32_mask(clk->reg, evt & 0xffff);
/* then set next event */
aml_set_reg32_bits(clk->reg, evt, 0, 16);
return 0;
}
static int meson_clkevt_shutdown(struct clock_event_device *dev)
{
struct meson_clock *clk = &bc_clock;
spin_lock(&time_lock);
/* pr_info("Disable timer %p %s\n",dev,dev->name); */
aml_set_reg32_bits(clk->mux_reg, 0, clk->bit_enable, 1);
spin_unlock(&time_lock);
return 0;
}
static int meson_clkevt_set_periodic(struct clock_event_device *dev)
{
struct meson_clock *clk = &bc_clock;
spin_lock(&time_lock);
aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_mode, 1);
aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_enable, 1);
/* pr_info("Periodic timer %s!,mux_reg=%x\n",*/
/* dev->name,readl_relaxed(clk->mux_reg)); */
spin_unlock(&time_lock);
return 0;
}
static int meson_clkevt_set_oneshot(struct clock_event_device *dev)
{
struct meson_clock *clk = &bc_clock;
spin_lock(&time_lock);
aml_set_reg32_bits(clk->mux_reg, 0, clk->bit_mode, 1);
aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_enable, 1);
/* pr_info("One shot timer %s!mux_reg=%x\n",*/
/* dev->name,readl_relaxed(clk->mux_reg)); */
spin_unlock(&time_lock);
return 0;
}
static int meson_clkevt_resume(struct clock_event_device *dev)
{
struct meson_clock *clk = &bc_clock;
spin_lock(&time_lock);
/* pr_info("Resume timer%s\n", dev->name); */
aml_set_reg32_bits(clk->mux_reg, 1, clk->bit_enable, 1);
spin_unlock(&time_lock);
return 0;
}
/* Clock event timer interrupt handler */
static irqreturn_t meson_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
if (evt == NULL || evt->event_handler == NULL) {
WARN_ONCE(evt == NULL || evt->event_handler == NULL,
"%p %s %p %d",
evt, evt?evt->name:NULL,
evt?evt->event_handler:NULL, irq);
return IRQ_HANDLED;
}
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct clock_event_device meson_clockevent = {
.cpumask = cpu_all_mask,
.set_next_event = meson_set_next_event,
.set_state_shutdown = meson_clkevt_shutdown,
.set_state_periodic = meson_clkevt_set_periodic,
.set_state_oneshot = meson_clkevt_set_oneshot,
.tick_resume = meson_clkevt_resume,
};
/*
* This sets up the system timers, clock source and clock event.
*/
int clockevent_init_and_register(struct device_node *np)
{
struct device_node *timer;
struct meson_clock *mclk = &bc_clock;
timer = np;
if (!timer) {
pr_info(" * missing timer phandle\n");
return -1;
}
if (of_property_read_string(timer, "timer_name",
&meson_clockevent.name))
return -1;
if (of_property_read_u32(timer, "clockevent-rating",
&meson_clockevent.rating))
return -1;
if (of_property_read_u32(timer, "clockevent-shift",
&meson_clockevent.shift))
return -1;
if (of_property_read_u32(timer, "clockevent-features",
&meson_clockevent.features))
return -1;
if (of_property_read_u32(timer, "bit_enable", &mclk->bit_enable))
return -1;
if (of_property_read_u32(timer, "bit_mode", &mclk->bit_mode))
return -1;
if (of_property_read_u32(timer, "bit_resolution",
&mclk->bit_resolution))
return -1;
mclk->mux_reg = timer_ctrl_base;
mclk->reg = of_iomap(timer, 1);
pr_info("mclk->mux_reg =%p,mclk->reg =%p\n", mclk->mux_reg, mclk->reg);
mclk->irq.irq = irq_of_parse_and_map(timer, 0);
aml_set_reg32_mask(mclk->mux_reg,
((1 << mclk->bit_mode)
|(TIMER_RESOLUTION_1us << mclk->bit_resolution)));
meson_clockevent.mult = div_sc(1000000, NSEC_PER_SEC, 20);
meson_clockevent.max_delta_ns =
clockevent_delta2ns(0xfffe, &meson_clockevent);
meson_clockevent.min_delta_ns =
clockevent_delta2ns(1, &meson_clockevent);
mclk->irq.dev_id = &meson_clockevent;
mclk->irq.handler = meson_timer_interrupt;
mclk->irq.name = meson_clockevent.name;
mclk->irq.flags =
IRQF_TIMER | IRQF_IRQPOLL|IRQF_TRIGGER_RISING;
/* Set up the IRQ handler */
meson_clockevent.irq = mclk->irq.irq;
clockevents_register_device(&meson_clockevent);
setup_irq(mclk->irq.irq, &mclk->irq);
return 0;
}
/*meson broadcast timer*/
static int __init meson_timer_init(struct device_node *np)
{
timer_ctrl_base = of_iomap(np, 0);
if (clockevent_init_and_register(np) < 0)
pr_err("%s err\n", __func__);
return 0;
}
CLOCKSOURCE_OF_DECLARE(meson_timer, "arm, meson-bc-timer", meson_timer_init);