blob: b98db0d0975516fc9fd7604c5e624db764cc11ba [file] [log] [blame]
/*
* drivers/clocksource/zx29_timer.c
*
* Copyright (C) 2015 ZTE-TSP
*
* 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/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/syscore_ops.h>
#include <asm/mach/time.h>
#include <mach/board.h>
#include <mach/iomap.h>
#include <mach/debug.h>
#include <mach/irqs.h>
#include <mach/timex.h>
#include <mach/pcu.h>
#if NEW_LINUX_FRAME
#include <linux/sched_clock.h>
#else
#include <asm/sched_clock.h>
#endif
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#define DEBUG_SYS_TIMER 0
#if DEBUG_SYS_TIMER
#pragma GCC optimize("O0")
typedef struct
{
unsigned int trace_t;
unsigned int count_reg;
unsigned int load_reg;
unsigned int refresh_reg;
unsigned int time_t;
}test_trace;
volatile test_trace oneshot_count_view[1000];
volatile unsigned int oneshot_count_index=0;
volatile unsigned int timer_int_stamp[1000];
volatile unsigned int timer_int_index=0;
#endif
const char *__clk_get_name(struct clk *clk);
static struct clock_event_device timer_clkevt;
volatile static unsigned int sys_time_cnt=0;
volatile static unsigned int periodic_count_value=0;
volatile static unsigned int sys_time_count_value=0;
volatile static unsigned int event_cycle=0; /*it is for translating presistent time*/
static struct clk *cs_wclk = NULL;
static struct clk *cs_pclk = NULL;
static struct clk *ce_wclk = NULL;
static struct clk *ce_pclk = NULL;
static struct clk *cd_wclk = NULL;
static struct clk *cd_pclk = NULL;
/*
* this function can only just be used for debug
*/
unsigned int test_timer_read(void)
{
sys_time_count_value=0x7fffffff-read_timer_clk(CLOCKSOURCE_BASE);
return sys_time_count_value;
}
EXPORT_SYMBOL(test_timer_read);
static unsigned long zx29_read_current_timer(void)
{
u32 elapsed=0;
u32 t=0;
t = ioread32(CLOCKDELAY_BASE+CUR_VALUE);
elapsed=0x7fffffff-t;
return elapsed;
}
/*
* udelay.
*/
void udelay(u32 delay_us)
{
u64 start_us = zx29_read_current_timer();
s64 remain_us = 0;
while(1)
{
remain_us = zx29_read_current_timer() - start_us;
if(remain_us < 0)
{
remain_us += 0x7fffffff;
}
remain_us = div64_s64(remain_us*USEC_PER_SEC, (s64)DELAY_CLOCK_RATE);
if(remain_us >= (s64)delay_us)
return;
}
}
EXPORT_SYMBOL(udelay);
/*
* IRQ handler for the timer.
*/
static irqreturn_t zx29_clock_event_isr(int irq, void *dev_id)
{
WARN_ON_ONCE(!irqs_disabled());
// pcu_int_clear(PCU_AP_TIMER0_INT);
#if DEBUG_SYS_TIMER
//zx29_gpio1v8_output_data(16,(sys_time_cnt & 0x01)); /*light a led to test systimer*/
timer_int_stamp[timer_int_index]= test_timer_read();
timer_int_index++;
if(timer_int_index==1000)
timer_int_index=0;
#endif
sys_time_cnt++;
timer_clkevt.event_handler(&timer_clkevt);
return IRQ_HANDLED;
}
void clock_event_handler(void)
{
timer_clkevt.event_handler(&timer_clkevt);
}
/*
* Clockevent device: interrupts every 1/HZ
*/
static void zx29_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
{
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
timer_set_load(CLOCKEVENT_BASE, periodic_count_value-2);
timer_set_mode(CLOCKEVENT_BASE, true);
break;
case CLOCK_EVT_MODE_ONESHOT:
timer_set_mode(CLOCKEVENT_BASE, false);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_RESUME:
break;
}
}
/*
* only for oneshot mode
*/
static int zx29_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
timer_set_load(CLOCKEVENT_BASE, cycles);
#if DEBUG_SYS_TIMER
oneshot_count_view[oneshot_count_index].trace_t = cycles;
oneshot_count_view[oneshot_count_index].count_reg = ioread32(CLOCKEVENT_BASE+CUR_VALUE);
oneshot_count_view[oneshot_count_index].load_reg = ioread32(CLOCKEVENT_BASE+LOAD_REG);
oneshot_count_view[oneshot_count_index].refresh_reg = ioread32(CLOCKEVENT_BASE+REFRESH_REG);
oneshot_count_view[oneshot_count_index].time_t = test_timer_read();
oneshot_count_index++;
if(oneshot_count_index==1000)
oneshot_count_index=0;
#endif
return 0;
}
static cycle_t zx29_read_timer_clk(struct clocksource *cs)
{
#ifdef CONFIG_ARCH_ZX297520V2FPGA
u32 t1=0,t2=0;
t1 = read_timer_clk(CLOCKSOURCE_BASE);
do
{
t2 = read_timer_clk(CLOCKSOURCE_BASE);
if(t1==t2) break;
t1=t2;
}while(1);
return 0x7fffffff-t1;
#else
return (0x7fffffff-read_timer_clk(CLOCKSOURCE_BASE));
#endif
}
static struct clk *pt_wclk = NULL;
static struct clk *pt_pclk = NULL;
#ifdef CONFIG_PM
int zx29_clocksource_suspend(void)
{
/*
*nothing should be done here.
*maybe clocksource should be stoped, but I don't do this
*there is no effect on system running, even if clocksource is not suspended actually.
*I can use clocksource in power off processing for debug.
*/
return 0;
}
void zx29_clocksource_resume(void)
{
#if 0
/*re-initiate the clocksource*/
iowrite32(0x7fffffff,SOURCE_BASE_VA+LOAD_REG); // 2^31-1
#ifdef CONFIG_ARCH_ZX297510FPGA
iowrite32(0x02,SOURCE_BASE_VA+CONFIG_REG); // auto load
#else
iowrite32(0x22,SOURCE_BASE_VA+CONFIG_REG); // main clock/13/2 , auto load
refresh_config_load_reg(SOURCE_BASE_VA); // refresh load reg and config reg
iowrite32(0x1,SOURCE_BASE_VA+START_REG); // start timer
#endif
#endif
}
static struct syscore_ops zx29_clocksource_syscore_ops = {
.suspend = zx29_clocksource_suspend,
.resume = zx29_clocksource_resume,
};
/****************************************************************
*** wake timer & persistent timer **************************
****************************************************************
*/
static struct clk *wt_wclk = NULL;
static struct clk *wt_pclk = NULL;
static irqreturn_t wake_timer_isr(int irq, void *dev_id)
{
pcu_int_clear(PCU_WAKE_TIMER_INT);
return IRQ_HANDLED;
}
static struct irqaction wake_timer_irq = {
.name = "wake_timer",
.flags = IRQF_DISABLED|IRQF_TRIGGER_RISING|IRQF_NO_THREAD,
.handler = wake_timer_isr,
};
void zx29_set_wake_timer(unsigned long cycles)
{
unsigned int t0;
timer_set_load(WAKE_TIMER_BASE, cycles);
/* wait data refresh finished. */
t0=test_timer_read();
while( (test_timer_read()-t0) < 4);
timer_start(WAKE_TIMER_BASE);
}
void zx29_stop_wake_timer(void)
{
timer_stop(WAKE_TIMER_BASE);
}
#endif /*CONFIG_PM*/
u64 read_persistent_us(void)
{
unsigned int current_cycle1 = 0;
unsigned int current_cycle2 = 0;
current_cycle1 = read_timer_clk(PERSISTENT_TIMER_BASE);
do{
current_cycle2 = read_timer_clk(PERSISTENT_TIMER_BASE);
if (current_cycle1 == current_cycle2)
break;
current_cycle1 = current_cycle2;
}while(1);
return div64_u64((u64)((u64)0x7fffffff-current_cycle1)*USEC_PER_SEC, (u64)PERSISTENT_TIMER_CLOCK_RATE);
}
EXPORT_SYMBOL(read_persistent_us);
static void __init zx29_pm_timer_resources_init(void)
{
#if SOURCE_CLK_PERSISTENT
#else
pt_wclk = timer_get_clk(PERSISTENT_TIMER_NAME, "work_clk");
if (IS_ERR(pt_wclk))
panic("failed to get persistent wclk.");
pt_pclk = timer_get_clk(PERSISTENT_TIMER_NAME, "apb_clk");
if (IS_ERR(pt_pclk))
panic("failed to get persistent pclk.");
clk_prepare_enable(pt_pclk);
#endif
#ifdef CONFIG_PM
wt_wclk = timer_get_clk(WAKE_TIMER_NAME, "work_clk");
if (IS_ERR(wt_wclk))
panic("failed to get wake timer wclk.");
wt_pclk = timer_get_clk(WAKE_TIMER_NAME, "apb_clk");
if (IS_ERR(wt_pclk))
panic("failed to get wake timer pclk.");
clk_prepare_enable(wt_pclk);
#endif
}
static void __init zx29_persistent_timer_init(void)
{
clk_set_rate(pt_wclk, PERSISTENT_TIMER_CLOCK_RATE);
clk_prepare_enable(pt_wclk);
pr_info("pt_wclk=%lu, parent=%s \n", clk_get_rate(pt_wclk), __clk_get_name(clk_get_parent(pt_wclk)));
timer_set_load(PERSISTENT_TIMER_BASE, 0x7fffffff);
timer_set_mode(PERSISTENT_TIMER_BASE, true);
timer_start(PERSISTENT_TIMER_BASE);
}
static void __init zx29_wake_timer_init(void)
{
#ifdef CONFIG_PM
int ret = 0;
clk_set_rate(wt_wclk, WAKE_TIMER_CLOCK_RATE);
clk_prepare_enable(wt_wclk);
pr_info("wt_wclk=%lu, parent=%s \n", clk_get_rate(wt_wclk), __clk_get_name(clk_get_parent(wt_wclk)));
pcu_int_clear(PCU_WAKE_TIMER_INT);
ret=setup_irq(WAKE_TIMER_INT, &wake_timer_irq);
if(ret<0)
ZDRV_ASSERT(0);
timer_set_mode(WAKE_TIMER_BASE, false);
timer_stop(WAKE_TIMER_BASE);
#endif
}
static void __init zx29_pm_timer_init(void)
{
zx29_pm_timer_resources_init();
#if SOURCE_CLK_PERSISTENT
#else
zx29_persistent_timer_init();
#endif
#ifdef CONFIG_PM
zx29_wake_timer_init();
#endif
}
/*
* read_persistent_clock - Return time from a persistent clock.
*
* Reads the time from a source which isn't disabled during PM, the
* 32k sync timer. Convert the cycles elapsed since last read into
* nsecs and adds to a monotonically increasing timespec.
*
* Generally, RTC is used to get this time,but in zx29 platform,
* precision of RTC is not enough, so I use a fix timer as persistent clock.
* Although the timer can not represent the natural time,
* but it doesn't matter, linux just needs a relative time.
*/
#if SOURCE_CLK_PERSISTENT
static struct timespec persistent_ts;
void read_persistent_clock(struct timespec *ts)
{
u64 delta, persistent_us;
struct timespec *tsp = &persistent_ts;
persistent_us = read_persistent_us();
tsp->tv_sec = (long)div64_u64(persistent_us, USEC_PER_SEC);
delta = persistent_us- (u64)tsp->tv_sec*USEC_PER_SEC;
tsp->tv_nsec = (long)delta*1000L;
*ts = *tsp;
}
/*
static struct timespec persistent_ts;
static u64 persistent_us, last_persistent_us;
void read_persistent_clock(struct timespec *ts)
{
u64 delta;
struct timespec *tsp = &persistent_ts;
last_persistent_us = persistent_us;
persistent_us = read_persistent_us();
delta = persistent_us - last_persistent_us;
timespec_add_ns(tsp, delta * NSEC_PER_USEC);
*ts = *tsp;
}
*/
#endif
static struct clocksource timer_clksrc = {
.name = CLOCKSOURCE_NAME,
.rating = 300,
.read = zx29_read_timer_clk,
.mask = CLOCKSOURCE_MASK(31),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct clock_event_device timer_clkevt = {
.name = CLOCKEVENT_NAME,
#ifdef CONFIG_ARCH_ZX297520V2FPGA
.features = CLOCK_EVT_FEAT_PERIODIC,
#else
.features = CLOCK_EVT_FEAT_PERIODIC|CLOCK_EVT_FEAT_ONESHOT,
#endif
.shift = 32,
.rating = 300,
.set_mode = zx29_timer_set_mode,
.set_next_event = zx29_timer_set_next_event,
};
#if NEW_LINUX_FRAME
static notrace u64 zx29_sched_clock_read(void)
{
return (u64)timer_clksrc.read(&timer_clksrc);
}
#else
static notrace u32 zx29_sched_clock_read(void)
{
return timer_clksrc.read(&timer_clksrc);
}
#endif
static struct irqaction zx29_clock_event_irq =
{
.name = "zx29_tick_irq",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = zx29_clock_event_isr,
.dev_id = &timer_clkevt,
};
static void __init zx29_clockevent_init(void)
{
int ret = 0;
clk_set_rate(ce_wclk, EVENT_CLOCK_RATE);
clk_prepare_enable(ce_wclk);
pr_info("ce_wclk=%lu, parent=%s \n", clk_get_rate(ce_wclk), __clk_get_name(clk_get_parent(ce_wclk)));
/* set count value */
periodic_count_value=(EVENT_CLOCK_RATE+HZ/2)/HZ;
/* Set up irq handler */
ret=setup_irq(CLOCKEVENT_INT, &zx29_clock_event_irq);
if(ret<0)
ZDRV_ASSERT(0);
/* Set up and register clockevents */
timer_clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&timer_clkevt, EVENT_CLOCK_RATE, 50, 0x7fffffff);
timer_start(CLOCKEVENT_BASE);
}
#ifdef CONFIG_PROC_FS
static int persistent_time_show(struct seq_file * m, void * v)
{
u64 persistent_time;
persistent_time = read_persistent_us();
seq_printf(m, "%llu\n", persistent_time);
return 0;
}
static int persistent_time_open(struct inode *inode, struct file *file)
{
return single_open(file, persistent_time_show, NULL);
}
static const struct file_operations persistent_clock_fops = {
.open = persistent_time_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void persistent_clock_procfs(void)
{
struct proc_dir_entry *pde;
pde = proc_create("persistent_time", 0444, NULL, &persistent_clock_fops);
}
#endif
static void __init zx29_clocksource_init(void)
{
int ret = 0;
#if defined(_USE_CAP_SYS) && !defined(CONFIG_ARCH_ZX297520V3_CAP) && !defined(CONFIG_SYSTEM_RECOVERY)
#else
clk_set_rate(cs_wclk, SOURCE_CLOCK_RATE);
clk_prepare_enable(cs_wclk);
pr_info("cs_wclk=%lu, parent=%s \n", clk_get_rate(cs_wclk), __clk_get_name(clk_get_parent(cs_wclk)));
timer_set_load(CLOCKSOURCE_BASE, 0x7fffffff);
timer_set_mode(CLOCKSOURCE_BASE, true);
timer_start(CLOCKSOURCE_BASE);
#endif
/* Register clocksource. */
ret = clocksource_register_hz(&timer_clksrc, SOURCE_CLOCK_RATE);
/* if (ret){
printk(KERN_INFO"clocksource_register failed\n");
ZDRV_ASSERT(0);
}*/
/*register hardware time stamp*/
#if NEW_LINUX_FRAME
sched_clock_register(zx29_sched_clock_read, 31, SOURCE_CLOCK_RATE);
#else
setup_sched_clock(zx29_sched_clock_read, 31, SOURCE_CLOCK_RATE);
#endif
#ifdef CONFIG_PM
register_syscore_ops(&zx29_clocksource_syscore_ops);
#endif
#ifdef CONFIG_PROC_FS
persistent_clock_procfs();
#endif
}
static void __init zx29_clockdelay_init(void)
{
clk_set_rate(cd_wclk, DELAY_CLOCK_RATE);
clk_prepare_enable(cd_wclk);
pr_info("cd_wclk=%lu, parent=%s \n", clk_get_rate(cd_wclk), __clk_get_name(clk_get_parent(cd_wclk)));
timer_set_load(CLOCKDELAY_BASE, 0x7fffffff);
timer_set_mode(CLOCKDELAY_BASE, true);
timer_start(CLOCKDELAY_BASE);
}
static void __init zx29_timer_resources_init(void)
{
cs_wclk = timer_get_clk(CLOCKSOURCE_NAME, "work_clk");
if (IS_ERR(cs_wclk))
panic("failed to get clocksource wclk.");
cs_pclk = timer_get_clk(CLOCKSOURCE_NAME, "apb_clk");
if (IS_ERR(cs_pclk))
panic("failed to get clocksource pclk.");
clk_prepare_enable(cs_pclk);
ce_wclk = timer_get_clk(CLOCKEVENT_NAME, "work_clk");
if (IS_ERR(ce_wclk))
panic("failed to get clockevent wclk.");
ce_pclk = timer_get_clk(CLOCKEVENT_NAME, "apb_clk");
if (IS_ERR(ce_pclk))
panic("failed to get clockevent pclk.");
clk_prepare_enable(ce_pclk);
cd_wclk = timer_get_clk(DELAY_TIMER_NAME, "work_clk");
if (IS_ERR(cd_wclk))
panic("failed to get clockdelay wclk.");
cd_pclk = timer_get_clk(DELAY_TIMER_NAME, "apb_clk");
if (IS_ERR(cd_pclk))
panic("failed to get clockdelay pclk.");
clk_prepare_enable(cd_pclk);
}
/*
* Set up both clocksource and clockevent support.
*/
void __init zx29_timer_init(void)
{
#if DEBUG_SYS_TIMER
//zx29_gpio1v8_function_sel(16,0); /*GPIO*/
//zx29_gpio1v8_set_direction(16,0); /*output*/
#endif
zx29_timer_resources_init();
zx29_clockevent_init();
zx29_clocksource_init();
zx29_clockdelay_init();
//#ifdef CONFIG_PM
zx29_pm_timer_init();
//#endif
pr_info("zx297520v system timer initialized\n");
}
#if NEW_LINUX_FRAME
#else
struct sys_timer zx29_timer = {
.init = zx29_timer_init,
};
#endif
#if 0
extern void cpufreq_test(unsigned int old_index, unsigned int new_index);
void test(void)
{
unsigned int t1,t2,delay;
//set cpu rate(156/624)
cpufreq_test(0,2);//208
t1=zx29_read_current_timer();
udelay(800);
t2=zx29_read_current_timer();
delay = (t2-t1)/26;
printk("xxx 208M delay:%d us t1 = %d t2 = %d \n",delay,t1,t2);
cpufreq_test(2,1);//312
t1=zx29_read_current_timer();
udelay(800);
t2=zx29_read_current_timer();
delay = (t2-t1)/26;
printk("xxx 312M delay:%d us t1 = %d t2 = %d \n",delay,t1,t2);
cpufreq_test(1,0);//624
t1=zx29_read_current_timer();
udelay(800);
t2=zx29_read_current_timer();
delay = (t2-t1)/26;
printk("xxx 624M delay:%d us t1 = %d t2 = %d \n",delay,t1,t2);
}
#endif