/* * (C) Copyright 2007-2013 * Allwinner Technology Co., Ltd. * Jerry Wang * * 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 #include #include #include #include #include #include #include #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<tirqen &= ~(1<