SmartAudio/lichee/brandy/u-boot-2014.07/drivers/timer/sunxi_timer.c

353 lines
8.1 KiB
C
Raw Normal View History

2018-07-13 01:31:50 +00:00
/*
* (C) Copyright 2007-2013
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Jerry Wang <wangflord@allwinnertech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <asm/arch/ccmu.h>
#include <asm/arch/clock.h>
#include <asm/arch/timer.h>
#include <asm/arch/intc.h>
#include <div64.h>
#define TIMER_MODE (0 << 7) /* continuous mode */
#define TIMER_DIV (0 << 4) /* pre scale 1 */
#define TIMER_SRC (1 << 2) /* osc24m */
#define TIMER_RELOAD (1 << 1) /* reload internal value */
#define TIMER_EN (1 << 0) /* enable timer */
static int timer_used_status;
/* init timer register */
int timer_init(void)
{
struct sunxi_timer_reg *timer_reg = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
#ifdef CONFIG_ARCH_SUN3IW1P1
writel(readl(CCMU_AVS_CLK_REG)|(1U << 31),CCMU_AVS_CLK_REG);
timer_reg->avs.ctl = 3;
timer_reg->avs.div |= 0xb04af;
#endif
timer_reg->tirqen = 0;
timer_reg->tirqsta |= 0x03;
return 0;
}
void timer_exit(void)
{
struct sunxi_timer_reg *timer_reg = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
timer_reg->tirqen = 0;
timer_reg->tirqsta |= 0x043f;
timer_reg->timer[0].ctl = 0;
timer_reg->timer[1].ctl = 0;
#ifdef CONFIG_ARCH_SUN3IW1P1
timer_reg->avs.ctl = 0;
timer_reg->avs.div = 0x05DB05DB;
writel(readl(CCMU_AVS_CLK_REG) & 0x0fffffff, CCMU_AVS_CLK_REG);
#endif
}
/*
* This function is derived from PowerPC code (timebase clock frequency).
* On ARM it returns the number of timer ticks per second.
*/
ulong get_tbclk(void)
{
return 24000000;
}
#ifdef CONFIG_ARCH_SUN3IW1P1
static inline u64 get_arch_counter(void)
{
u32 t_us;
struct sunxi_timer_reg *timer_reg = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
t_us = timer_reg->avs.cnt1;
return (u64)(t_us*24);
}
#else
/*
* 64bit arch timer.CNTPCT
* Freq = 24000000Hz
*/
static inline u64 get_arch_counter(void)
{
u32 low=0, high = 0;
asm volatile("mrrc p15, 0, %0, %1, c14"
: "=r" (low), "=r" (high)
:
: "memory");
return (((u64)high)<<32 | low);
}
#endif
void __usdelay(unsigned long us)
{
u64 t1, t2;
t1 = get_arch_counter();
t2 = t1 + us*24;
do
{
t1 = get_arch_counter();
}
while(t2 >= t1);
}
void __msdelay(unsigned long ms)
{
__usdelay(ms*1000);
return ;
}
/* get the current time(ms), freq = 24000000Hz*/
int runtime_tick(void)
{
u64 cnt= 0;
cnt = get_arch_counter();
return lldiv(cnt, 24000);
}
ulong get_timer_masked(void)
{
/* current tick value */
ulong now = runtime_tick();
return now;
}
/* timer without interrupts */
/* count the delay by seconds */
ulong get_timer(ulong base)
{
return get_timer_masked() - base;
}
/*
* This function is derived from PowerPC code (read timebase as long long).
* On ARM it just returns the timer value.
*/
unsigned long long get_ticks(void)
{
return get_timer(0);
}
/*
* This function is derived from PowerPC code (timebase clock frequency).
* On ARM it returns the number of timer ticks per second.
*/
static void timerX_callback_default(void *data);
struct __timer_callback
{
void (*func_back)( void *data);
unsigned long data;
};
struct __timer_callback timer_callback[2] =
{
{timerX_callback_default, 0},
{timerX_callback_default, 1}
};
static void timerX_callback_default(void *data)
{
printf("this is only for test, timer number=%d\n", (uint)(ulong)data);
}
void timer0_func(void *data)
{
struct sunxi_timer_reg *timer_control = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
if(!(timer_control->tirqsta & 0x01))
{
return ;
}
timer_control->tirqen &= ~0x01;
timer_control->tirqsta = 0x01;
irq_disable(AW_IRQ_TIMER0);
timer_used_status &= ~1;
debug("timer 0 occur\n");
timer_callback[0].func_back((void *)timer_callback[0].data);
}
void timer1_func(void *data)
{
struct sunxi_timer_reg *timer_control = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
if(!(timer_control->tirqsta & 0x02))
{
return ;
}
timer_control->tirqen &= ~0x02;
timer_control->tirqsta = 0x02;
irq_disable(AW_IRQ_TIMER1);
timer_used_status &= ~(1<<1);
debug("timer 1 occur\n");
timer_callback[1].func_back((void *)timer_callback[1].data);
}
void init_timer(struct timer_list *timer)
{
return ;
}
void add_timer(struct timer_list *timer)
{
u32 reg_val;
int timer_num;
struct sunxi_timer *timer_tcontrol;
struct sunxi_timer_reg *timer_reg;
if(timer->expires <= 0)
{
timer->expires = 1000;
}
if(!timer->expires)
{
return ;
}
debug("timer delay time %d\n", timer->expires);
if(!(timer_used_status & 0x01))
{
timer_used_status |= 0x01;
timer_num = 0;
}
else if(!(timer_used_status & 0x02))
{
timer_used_status |= 0x02;
timer_num = 1;
}
else
{
printf("timer err: there is no timer cound be used\n");
return ;
}
//debug("timer status = %x, number = %d\n", timer_used_status, timer_num);
timer->timer_num = timer_num;
timer_reg = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
timer_tcontrol = &((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->timer[timer_num];
#ifndef FPGA_PLATFORM
reg_val = (0 << 0) | // 不启动TIMER
(1 << 1) | // 使用单次模式
(1 << 2) | // 使用高频晶振24M
(5 << 4) | // 除频系统32保证当设置时间是1的时候触发延时1ms
(1 << 7);
#else
reg_val = (0 << 0) | // 不启动TIMER
(1 << 1) | // 使用单次模式
(0 << 2) | // 使用高频晶振24M
(0 << 4) | //
(1 << 7);
#endif
timer_tcontrol->ctl = reg_val;
#ifndef FPGA_PLATFORM
timer_tcontrol->inter = timer->expires * (24000 / 32);
#else
timer_tcontrol->inter = timer->expires * 1000/32;
#endif
timer_callback[timer_num].func_back = timer->function;
timer_callback[timer_num].data = timer->data;
if(!timer_num)
{
irq_install_handler(AW_IRQ_TIMER0 + timer_num, timer0_func, (void *)&timer_callback[timer_num].data);
}
else
{
irq_install_handler(AW_IRQ_TIMER0 + timer_num, timer1_func, (void *)&timer_callback[timer_num].data);
}
timer_tcontrol->ctl |= (1 << 1);
while(timer_tcontrol->ctl & 0x02);
//debug("timer number = %d\n", timer_num);
irq_enable(AW_IRQ_TIMER0 + timer_num);
timer_tcontrol->ctl |= 1;
timer_reg->tirqsta = (1 << timer_num);
timer_reg->tirqen |= (1 << timer_num);
//debug("timer number = %d\n", timer_num);
return ;
}
void del_timer(struct timer_list *timer)
{
struct sunxi_timer *timer_tcontrol;
struct sunxi_timer_reg *timer_reg;
int num = timer->timer_num;
//debug("timer status at delling begin = %x, number = %d\n", timer_used_status, num);
timer_reg = (struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
timer_tcontrol = &((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->timer[num];
irq_disable(AW_IRQ_TIMER0 + num);
timer_tcontrol->ctl &= ~1;
timer_reg->tirqsta = (1<<num);
timer_reg->tirqen &= ~(1<<num);
timer_callback[num].data = num;
timer_callback[num].func_back = timerX_callback_default;
timer_used_status &= ~(1 << num);
//debug("timer status at delling end = %x\n", timer_used_status);
return ;
}
void stick_printf(void)
{
uint time, time_sec, time_rest;
time = get_ticks();
time_sec = time/1000;
time_rest = time%1000;
printf("[%8d.%3d]\n",time_sec, time_rest);
return ;
}
void tick0_printf(char *s, int line)
{
uint time, time_sec, time_rest;
time = get_ticks();
time_sec = time/1000; time_rest = time%1000;
if(s == NULL)
{
printf("[%8d.%3d]\n",time_sec, time_rest);
}
else
{
printf("[%8d.%3d] %s %d\n",time_sec, time_rest, s, line);
}
return ;
}