1*4882a593Smuzhiyun /* 2*4882a593Smuzhiyun * Freescale i.MX28 timer driver 3*4882a593Smuzhiyun * 4*4882a593Smuzhiyun * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> 5*4882a593Smuzhiyun * on behalf of DENX Software Engineering GmbH 6*4882a593Smuzhiyun * 7*4882a593Smuzhiyun * Based on code from LTIB: 8*4882a593Smuzhiyun * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. 9*4882a593Smuzhiyun * 10*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+ 11*4882a593Smuzhiyun */ 12*4882a593Smuzhiyun 13*4882a593Smuzhiyun #include <common.h> 14*4882a593Smuzhiyun #include <asm/io.h> 15*4882a593Smuzhiyun #include <asm/arch/imx-regs.h> 16*4882a593Smuzhiyun #include <asm/arch/sys_proto.h> 17*4882a593Smuzhiyun 18*4882a593Smuzhiyun /* Maximum fixed count */ 19*4882a593Smuzhiyun #if defined(CONFIG_MX23) 20*4882a593Smuzhiyun #define TIMER_LOAD_VAL 0xffff 21*4882a593Smuzhiyun #elif defined(CONFIG_MX28) 22*4882a593Smuzhiyun #define TIMER_LOAD_VAL 0xffffffff 23*4882a593Smuzhiyun #endif 24*4882a593Smuzhiyun 25*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR; 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun #define timestamp (gd->arch.tbl) 28*4882a593Smuzhiyun #define lastdec (gd->arch.lastinc) 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun /* 31*4882a593Smuzhiyun * This driver uses 1kHz clock source. 32*4882a593Smuzhiyun */ 33*4882a593Smuzhiyun #define MXS_INCREMENTER_HZ 1000 34*4882a593Smuzhiyun tick_to_time(unsigned long tick)35*4882a593Smuzhiyunstatic inline unsigned long tick_to_time(unsigned long tick) 36*4882a593Smuzhiyun { 37*4882a593Smuzhiyun return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 38*4882a593Smuzhiyun } 39*4882a593Smuzhiyun time_to_tick(unsigned long time)40*4882a593Smuzhiyunstatic inline unsigned long time_to_tick(unsigned long time) 41*4882a593Smuzhiyun { 42*4882a593Smuzhiyun return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 43*4882a593Smuzhiyun } 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun /* Calculate how many ticks happen in "us" microseconds */ us_to_tick(unsigned long us)46*4882a593Smuzhiyunstatic inline unsigned long us_to_tick(unsigned long us) 47*4882a593Smuzhiyun { 48*4882a593Smuzhiyun return (us * MXS_INCREMENTER_HZ) / 1000000; 49*4882a593Smuzhiyun } 50*4882a593Smuzhiyun timer_init(void)51*4882a593Smuzhiyunint timer_init(void) 52*4882a593Smuzhiyun { 53*4882a593Smuzhiyun struct mxs_timrot_regs *timrot_regs = 54*4882a593Smuzhiyun (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun /* Reset Timers and Rotary Encoder module */ 57*4882a593Smuzhiyun mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun /* Set fixed_count to 0 */ 60*4882a593Smuzhiyun #if defined(CONFIG_MX23) 61*4882a593Smuzhiyun writel(0, &timrot_regs->hw_timrot_timcount0); 62*4882a593Smuzhiyun #elif defined(CONFIG_MX28) 63*4882a593Smuzhiyun writel(0, &timrot_regs->hw_timrot_fixed_count0); 64*4882a593Smuzhiyun #endif 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun /* Set UPDATE bit and 1Khz frequency */ 67*4882a593Smuzhiyun writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | 68*4882a593Smuzhiyun TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, 69*4882a593Smuzhiyun &timrot_regs->hw_timrot_timctrl0); 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun /* Set fixed_count to maximal value */ 72*4882a593Smuzhiyun #if defined(CONFIG_MX23) 73*4882a593Smuzhiyun writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0); 74*4882a593Smuzhiyun #elif defined(CONFIG_MX28) 75*4882a593Smuzhiyun writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); 76*4882a593Smuzhiyun #endif 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun return 0; 79*4882a593Smuzhiyun } 80*4882a593Smuzhiyun get_ticks(void)81*4882a593Smuzhiyununsigned long long get_ticks(void) 82*4882a593Smuzhiyun { 83*4882a593Smuzhiyun struct mxs_timrot_regs *timrot_regs = 84*4882a593Smuzhiyun (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 85*4882a593Smuzhiyun uint32_t now; 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun /* Current tick value */ 88*4882a593Smuzhiyun #if defined(CONFIG_MX23) 89*4882a593Smuzhiyun /* Upper bits are the valid ones. */ 90*4882a593Smuzhiyun now = readl(&timrot_regs->hw_timrot_timcount0) >> 91*4882a593Smuzhiyun TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET; 92*4882a593Smuzhiyun #elif defined(CONFIG_MX28) 93*4882a593Smuzhiyun now = readl(&timrot_regs->hw_timrot_running_count0); 94*4882a593Smuzhiyun #else 95*4882a593Smuzhiyun #error "Don't know how to read timrot_regs" 96*4882a593Smuzhiyun #endif 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun if (lastdec >= now) { 99*4882a593Smuzhiyun /* 100*4882a593Smuzhiyun * normal mode (non roll) 101*4882a593Smuzhiyun * move stamp forward with absolut diff ticks 102*4882a593Smuzhiyun */ 103*4882a593Smuzhiyun timestamp += (lastdec - now); 104*4882a593Smuzhiyun } else { 105*4882a593Smuzhiyun /* we have rollover of decrementer */ 106*4882a593Smuzhiyun timestamp += (TIMER_LOAD_VAL - now) + lastdec; 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun } 109*4882a593Smuzhiyun lastdec = now; 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun return timestamp; 112*4882a593Smuzhiyun } 113*4882a593Smuzhiyun get_timer_masked(void)114*4882a593Smuzhiyunulong get_timer_masked(void) 115*4882a593Smuzhiyun { 116*4882a593Smuzhiyun return tick_to_time(get_ticks()); 117*4882a593Smuzhiyun } 118*4882a593Smuzhiyun get_timer(ulong base)119*4882a593Smuzhiyunulong get_timer(ulong base) 120*4882a593Smuzhiyun { 121*4882a593Smuzhiyun return get_timer_masked() - base; 122*4882a593Smuzhiyun } 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ 125*4882a593Smuzhiyun #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0 126*4882a593Smuzhiyun __udelay(unsigned long usec)127*4882a593Smuzhiyunvoid __udelay(unsigned long usec) 128*4882a593Smuzhiyun { 129*4882a593Smuzhiyun uint32_t old, new, incr; 130*4882a593Smuzhiyun uint32_t counter = 0; 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun old = readl(MXS_HW_DIGCTL_MICROSECONDS); 133*4882a593Smuzhiyun 134*4882a593Smuzhiyun while (counter < usec) { 135*4882a593Smuzhiyun new = readl(MXS_HW_DIGCTL_MICROSECONDS); 136*4882a593Smuzhiyun 137*4882a593Smuzhiyun /* Check if the timer wrapped. */ 138*4882a593Smuzhiyun if (new < old) { 139*4882a593Smuzhiyun incr = 0xffffffff - old; 140*4882a593Smuzhiyun incr += new; 141*4882a593Smuzhiyun } else { 142*4882a593Smuzhiyun incr = new - old; 143*4882a593Smuzhiyun } 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun /* 146*4882a593Smuzhiyun * Check if we are close to the maximum time and the counter 147*4882a593Smuzhiyun * would wrap if incremented. If that's the case, break out 148*4882a593Smuzhiyun * from the loop as the requested delay time passed. 149*4882a593Smuzhiyun */ 150*4882a593Smuzhiyun if (counter + incr < counter) 151*4882a593Smuzhiyun break; 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun counter += incr; 154*4882a593Smuzhiyun old = new; 155*4882a593Smuzhiyun } 156*4882a593Smuzhiyun } 157*4882a593Smuzhiyun get_tbclk(void)158*4882a593Smuzhiyunulong get_tbclk(void) 159*4882a593Smuzhiyun { 160*4882a593Smuzhiyun return MXS_INCREMENTER_HZ; 161*4882a593Smuzhiyun } 162