434 lines
12 KiB
C
Executable File
434 lines
12 KiB
C
Executable File
/*
|
|
* ms_piu_timer.c- Sigmastar
|
|
*
|
|
* Copyright (c) [2019~2020] SigmaStar Technology.
|
|
*
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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 version 2 for more details.
|
|
*
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/clocksource.h>
|
|
#include <linux/clockchips.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/sched_clock.h>
|
|
#include <linux/slab.h>
|
|
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include "ms_platform.h"
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Macros
|
|
//------------------------------------------------------------------------------
|
|
#define COPY_RTC_CNT 0
|
|
#define USE_ARM_GT 0 //# for better schedule resolution
|
|
|
|
#if COPY_RTC_CNT
|
|
|
|
#define TIMER_RTC_BASE GET_REG_ADDR(MS_BASE_REG_RIU_PA, 0x000900)
|
|
#define CHIPTOP_BASE GET_REG_ADDR(MS_BASE_REG_RIU_PA, 0x080F00)
|
|
#define CPY_RTC_TIMER_INTERVAL 1000 / HZ
|
|
|
|
//volatile PREG_TIMER_st g_pTimer2Regs = NULL;
|
|
volatile PREG_CHIPTOP_st g_pCHIPTOPReg = NULL;
|
|
volatile PREG_RTC_st g_pRTCReg = NULL;
|
|
|
|
struct timer_list cpy_rtc_timer;
|
|
|
|
extern DEVINFO_RTK_FLAG ms_devinfo_rtk_flag(void);
|
|
#endif
|
|
|
|
|
|
extern unsigned long XTAL_CLK;
|
|
|
|
#ifdef __arm__
|
|
void cap_delay_t(u32 tick)
|
|
{
|
|
__asm__ __volatile__(
|
|
"1: nop\n"
|
|
" subs %0, %0, #1\n"
|
|
" bne 1b"
|
|
:
|
|
: "r" (tick));
|
|
}
|
|
#endif
|
|
|
|
#define TIMER_ENABLE (0x1)
|
|
#define TIMER_TRIG (0x2)
|
|
#define TIMER_INTERRUPT (0x100)
|
|
#define TIMER_CLEAR (0x4)
|
|
#define TIMER_CAPTURE (0x8)
|
|
#define ADDR_TIMER_MAX_LOW (0x2<<2)
|
|
#define ADDR_TIMER_MAX_HIGH (0x3<<2)
|
|
|
|
|
|
struct ms_piu_timer {
|
|
void __iomem *base;
|
|
unsigned long freq;
|
|
int irq;
|
|
};
|
|
|
|
struct ms_piu_timer_clockevent {
|
|
struct clock_event_device event;
|
|
struct ms_piu_timer timer;
|
|
struct irqaction irqaction;
|
|
};
|
|
|
|
struct ms_piu_timer_clocksource {
|
|
struct ms_piu_timer timer;
|
|
struct clocksource source;
|
|
};
|
|
|
|
|
|
static int timer_set_next_event(unsigned long next, struct clock_event_device *evt);
|
|
static int timer_set_state_periodic(struct clock_event_device *evt);
|
|
static int timer_set_state_oneshot(struct clock_event_device *evt);
|
|
static int timer_set_oneshot_stopped(struct clock_event_device *evt);
|
|
static int timer_set_state_shutdown(struct clock_event_device *evt);
|
|
|
|
static inline struct ms_piu_timer_clockevent *to_ms_clockevent(struct clock_event_device *evt)
|
|
{
|
|
return container_of(evt, struct ms_piu_timer_clockevent, event);
|
|
}
|
|
|
|
static inline struct ms_piu_timer_clocksource *to_ms_clocksource(struct clocksource *cs)
|
|
{
|
|
return container_of(cs, struct ms_piu_timer_clocksource, source);
|
|
}
|
|
|
|
|
|
static cycle_t timer_read(struct clocksource *cs)
|
|
{
|
|
|
|
struct ms_piu_timer_clocksource *ms_cs=to_ms_clocksource(cs);
|
|
#if 0
|
|
unsigned int high=INREG16(ms_cs->timer.base+(0x5<<2));
|
|
unsigned int low0=INREG16(ms_cs->timer.base+(0x4<<2));
|
|
unsigned int low1=INREG16(ms_cs->timer.base+(0x4<<2));
|
|
if(low0>low1)
|
|
{
|
|
high=INREG16(ms_cs->timer.base+(0x5<<2));
|
|
}
|
|
#else
|
|
//hardware capture
|
|
unsigned int low0=INREG16(ms_cs->timer.base+(0x4<<2));
|
|
unsigned int high=INREG16(ms_cs->timer.base+(0x5<<2));
|
|
#endif
|
|
return ((high&0xffff)<<16) + (low0&0xffff) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ms_piu_timer_clocksource* sched_clocksource=NULL;
|
|
static u64 notrace timer_read_sched_clock(void)
|
|
{
|
|
if(sched_clocksource)
|
|
{
|
|
return timer_read(&sched_clocksource->source);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int __init ms_piu_timer_clocksource_of_init(struct device_node *np)
|
|
{
|
|
|
|
struct clk *clk;
|
|
struct resource res;
|
|
|
|
struct ms_piu_timer_clocksource *ms_cs = kzalloc(sizeof(*ms_cs), GFP_KERNEL);
|
|
BUG_ON(ms_cs==NULL);
|
|
BUG_ON(of_address_to_resource(np, 0, &res));
|
|
|
|
clk = of_clk_get(np, 0);
|
|
BUG_ON(IS_ERR(clk));
|
|
|
|
|
|
ms_cs->timer.base=(void *)res.start;
|
|
|
|
ms_cs->timer.freq=clk_get_rate(clk);
|
|
|
|
clk_put(clk);
|
|
|
|
ms_cs->source.name = "timer_clksrc";
|
|
ms_cs->source.rating = 200;
|
|
ms_cs->source.read = timer_read;
|
|
ms_cs->source.mask = CLOCKSOURCE_MASK(32);
|
|
ms_cs->source.flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
|
|
|
|
|
/* setup timer 2 as free-running clocksource */
|
|
//make sure timer 2 is disable
|
|
CLRREG16(ms_cs->timer.base, TIMER_ENABLE);
|
|
|
|
//set max period
|
|
OUTREG16(ms_cs->timer.base+(0x2<<2),0xffff);
|
|
OUTREG16(ms_cs->timer.base+(0x3<<2),0xffff);
|
|
|
|
//enable timer 2
|
|
SETREG16(ms_cs->timer.base, TIMER_ENABLE);
|
|
|
|
|
|
clocksource_register_hz(&ms_cs->source, ms_cs->timer.freq);
|
|
|
|
sched_clocksource=ms_cs;
|
|
sched_clock_register(timer_read_sched_clock, 32, ms_cs->timer.freq);
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
static int timer_set_state_periodic(struct clock_event_device *evt)
|
|
{
|
|
unsigned short ctl=TIMER_INTERRUPT;
|
|
unsigned long interval;
|
|
struct ms_piu_timer_clockevent *ms_ce=to_ms_clockevent(evt);
|
|
|
|
interval = (ms_ce->timer.freq / HZ) ;
|
|
OUTREG16(ms_ce->timer.base + ADDR_TIMER_MAX_LOW, (interval &0xffff));
|
|
OUTREG16(ms_ce->timer.base + ADDR_TIMER_MAX_HIGH, (interval >>16));
|
|
ctl|=TIMER_ENABLE;
|
|
SETREG16(ms_ce->timer.base, ctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int timer_set_state_oneshot(struct clock_event_device *evt)
|
|
{
|
|
unsigned short ctl=TIMER_INTERRUPT;
|
|
struct ms_piu_timer_clockevent *ms_ce=to_ms_clockevent(evt);
|
|
|
|
/* period set, and timer enabled in 'next_event' hook */
|
|
ctl|=TIMER_TRIG;
|
|
SETREG16(ms_ce->timer.base, ctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int timer_set_oneshot_stopped(struct clock_event_device *evt)
|
|
{
|
|
unsigned short ctl=TIMER_INTERRUPT;
|
|
struct ms_piu_timer_clockevent *ms_ce=to_ms_clockevent(evt);
|
|
|
|
/* period set, and timer enabled in 'next_event' hook */
|
|
ctl&=~TIMER_TRIG;
|
|
SETREG16(ms_ce->timer.base, ctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int timer_set_state_shutdown(struct clock_event_device *evt)
|
|
{
|
|
unsigned short ctl=TIMER_INTERRUPT;
|
|
struct ms_piu_timer_clockevent *ms_ce=to_ms_clockevent(evt);
|
|
|
|
ctl|=TIMER_ENABLE;
|
|
CLRREG16(ms_ce->timer.base, ctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int timer_set_next_event(unsigned long next, struct clock_event_device *evt)
|
|
{
|
|
|
|
struct ms_piu_timer_clockevent *ms_ce=to_ms_clockevent(evt);
|
|
//stop timer
|
|
//OUTREG16(clkevt_base, 0x0);
|
|
|
|
//set period
|
|
OUTREG16(ms_ce->timer.base + ADDR_TIMER_MAX_LOW, (next &0xffff));
|
|
OUTREG16(ms_ce->timer.base + ADDR_TIMER_MAX_HIGH, (next >>16));
|
|
|
|
//enable timer
|
|
SETREG16(ms_ce->timer.base, TIMER_TRIG|TIMER_INTERRUPT);//default
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//static DEFINE_SPINLOCK(hw_sys_timer_lock);
|
|
#if COPY_RTC_CNT
|
|
|
|
#define RTC_FREQ 32768
|
|
static void ms_copy_rtc_cnt(unsigned long dummy)
|
|
{
|
|
u32 rtc0, rtc1, secl, sech;
|
|
u32 u32RTCCounts;
|
|
static u32 dtmr = 0, drtc = 0;
|
|
|
|
//rtc freq 32k
|
|
rtc0 = INREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SECOND_CNT_H) << 16;
|
|
rtc0 += INREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SECOND_CNT_L);
|
|
rtc0 = rtc0 * RTC_FREQ + INREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SUB_CNT);
|
|
|
|
secl = INREG16(&g_pRTCReg->RTC_RAW_SEC_CNT_L);
|
|
sech = INREG16(&g_pRTCReg->RTC_RAW_SEC_CNT_H);
|
|
u32RTCCounts = INREG16(&g_pRTCReg->RTC_RAW_SUB_CNT);
|
|
|
|
// Lock mutex
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SUB_CNT, 0x8000);
|
|
|
|
// Update RTC sec in CHIPTOP dummy register for GPS
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SECOND_CNT_L, secl);
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SECOND_CNT_H, sech);
|
|
|
|
// Reset Timer2
|
|
//OUTREG16(&g_pTimer2Regs->TIMER_MAX_L, 0x0);
|
|
|
|
// Update RTC sub cnt in CHIPTOP dummy register for GPS
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SUB_CNT, u32RTCCounts);
|
|
|
|
// Calculate tmr to rtc ratio
|
|
rtc1 = (sech << 16) + secl;
|
|
rtc1 = rtc1 * RTC_FREQ + u32RTCCounts;
|
|
drtc += (rtc1 - rtc0);
|
|
dtmr += timer_read(&clocksource_timer) - dtmr;
|
|
|
|
if (drtc > RTC_FREQ) // accumulate for 1 sec
|
|
{
|
|
OUTREG16(&g_pCHIPTOPReg->REG_CHIPTOP_1F, (dtmr / drtc));
|
|
dtmr = drtc = 0;
|
|
}
|
|
|
|
cpy_rtc_timer.expires = jiffies + msecs_to_jiffies(CPY_RTC_TIMER_INTERVAL);
|
|
add_timer(&cpy_rtc_timer);
|
|
}
|
|
|
|
static void ms_copy_rtc_cnt_init(void)
|
|
{
|
|
g_pRTCReg = (PREG_RTC_st)(TIMER_RTC_BASE);
|
|
//g_pTimer2Regs = (PREG_TIMER_st)(TIMER_CLKSRC_BASE);
|
|
g_pCHIPTOPReg = (PREG_CHIPTOP_st)(CHIPTOP_BASE);
|
|
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SECOND_CNT_L, INREG16(&g_pRTCReg->RTC_SECOND_CNT_L));
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SECOND_CNT_H, INREG16(&g_pRTCReg->RTC_SECOND_CNT_H));
|
|
OUTREG16(&g_pCHIPTOPReg->u16DUMMY_RTC_SUB_CNT, INREG16(&g_pRTCReg->RTC_SUB_CNT));
|
|
|
|
init_timer(&cpy_rtc_timer);
|
|
cpy_rtc_timer.expires = jiffies + msecs_to_jiffies(CPY_RTC_TIMER_INTERVAL);
|
|
cpy_rtc_timer.data = 0;
|
|
cpy_rtc_timer.function = ms_copy_rtc_cnt;
|
|
add_timer(&cpy_rtc_timer);
|
|
|
|
//call right now here
|
|
ms_copy_rtc_cnt(0);
|
|
}
|
|
#endif
|
|
|
|
static irqreturn_t ms_timer_interrupt(int irq, void *dev_id)
|
|
{
|
|
|
|
struct clock_event_device *evt = dev_id;
|
|
#if defined(CONFIG_MSTAR_CEDRIC) && defined(CONFIG_PROFILING) && defined(CONFIG_OPROFILE)
|
|
int cpu = smp_processor_id();
|
|
extern u32 armpmu_enable_flag[];
|
|
#endif
|
|
|
|
evt->event_handler(evt);
|
|
|
|
/* Mantis 212633 oprofile can't work */
|
|
#if defined(CONFIG_MSTAR_CEDRIC) && defined(CONFIG_PROFILING) && defined(CONFIG_OPROFILE)
|
|
/* local_timer call pmu_handle_irq instead of PMU interrupt can't raise by itself. */
|
|
if (armpmu_enable_flag[cpu]) {
|
|
extern irqreturn_t armpmu_handle_irq(int irq_num, void *dev);
|
|
armpmu_handle_irq(cpu, NULL);
|
|
}
|
|
#endif
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init ms_piu_timer_clockevent_of_init(struct device_node *np)
|
|
{
|
|
struct ms_piu_timer_clockevent *ms_ce = kzalloc(sizeof(*ms_ce), GFP_KERNEL);
|
|
|
|
struct clk *clk;
|
|
struct resource res;
|
|
|
|
BUG_ON(ms_ce==NULL);
|
|
BUG_ON(of_address_to_resource(np, 0, &res));
|
|
|
|
clk = of_clk_get(np, 0);
|
|
BUG_ON(IS_ERR(clk));
|
|
|
|
ms_ce->timer.base=(void *)res.start;
|
|
|
|
ms_ce->timer.freq=clk_get_rate(clk);
|
|
|
|
clk_put(clk);
|
|
|
|
ms_ce->timer.irq =irq_of_parse_and_map(np, 0);
|
|
printk("%s: irq %d", __func__, ms_ce->timer.irq);
|
|
if (ms_ce->timer.irq == 0)
|
|
{
|
|
panic("No IRQ for clock event timer");
|
|
}
|
|
|
|
ms_ce->event.name = "timer_clkevt";
|
|
ms_ce->event.shift = 32;
|
|
ms_ce->event.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
|
|
#if 1
|
|
ms_ce->event.set_state_periodic = timer_set_state_periodic;
|
|
ms_ce->event.set_state_oneshot = timer_set_state_oneshot;
|
|
ms_ce->event.set_state_oneshot_stopped = timer_set_oneshot_stopped;
|
|
ms_ce->event.set_state_shutdown = timer_set_state_shutdown;
|
|
#else
|
|
ms_ce->event.set_mode = timer_set_mode;
|
|
#endif
|
|
ms_ce->event.set_next_event = timer_set_next_event;
|
|
ms_ce->event.rating = 200;
|
|
ms_ce->event.cpumask = cpu_all_mask;
|
|
ms_ce->event.irq=ms_ce->timer.irq;
|
|
|
|
ms_ce->irqaction.name = "MS Timer Tick";
|
|
ms_ce->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL;
|
|
ms_ce->irqaction.handler = ms_timer_interrupt;
|
|
ms_ce->irqaction.dev_id = &ms_ce->event;
|
|
|
|
|
|
|
|
|
|
clockevents_calc_mult_shift(&ms_ce->event,ms_ce->timer.freq,5);
|
|
ms_ce->event.max_delta_ns = clockevent_delta2ns(0xffffffff, &ms_ce->event);
|
|
ms_ce->event.min_delta_ns = clockevent_delta2ns(0xf, &ms_ce->event);
|
|
BUG_ON( setup_irq(ms_ce->event.irq, &ms_ce->irqaction) );
|
|
|
|
clockevents_register_device( &ms_ce->event);
|
|
|
|
|
|
#if COPY_RTC_CNT
|
|
//put it last, because it will use the clock event device
|
|
if (DEVINFO_RTK_FLAG_1 == ms_devinfo_rtk_flag())
|
|
ms_copy_rtc_cnt_init();
|
|
#endif
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
CLOCKSOURCE_OF_DECLARE(ms_piu_timer_cs, "sstar,piu-clocksource", ms_piu_timer_clocksource_of_init);
|
|
CLOCKSOURCE_OF_DECLARE(ms_piu_timer_ce, "sstar,piu-clockevent", ms_piu_timer_clockevent_of_init);
|