353 lines
8.1 KiB
C
Executable File
353 lines
8.1 KiB
C
Executable File
/*
|
||
* (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 ;
|
||
}
|
||
|
||
|