1 /* 2 * Freescale i.MX28 timer driver 3 * 4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> 5 * on behalf of DENX Software Engineering GmbH 6 * 7 * Based on code from LTIB: 8 * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. 9 * 10 * See file CREDITS for list of people who contributed to this 11 * project. 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License as 15 * published by the Free Software Foundation; either version 2 of 16 * the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 26 * MA 02111-1307 USA 27 */ 28 29 #include <common.h> 30 #include <asm/io.h> 31 #include <asm/arch/imx-regs.h> 32 #include <asm/arch/sys_proto.h> 33 34 /* Maximum fixed count */ 35 #if defined(CONFIG_MX23) 36 #define TIMER_LOAD_VAL 0xffff 37 #elif defined(CONFIG_MX28) 38 #define TIMER_LOAD_VAL 0xffffffff 39 #endif 40 41 DECLARE_GLOBAL_DATA_PTR; 42 43 #define timestamp (gd->arch.tbl) 44 #define lastdec (gd->arch.lastinc) 45 46 /* 47 * This driver uses 1kHz clock source. 48 */ 49 #define MXS_INCREMENTER_HZ 1000 50 51 static inline unsigned long tick_to_time(unsigned long tick) 52 { 53 return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 54 } 55 56 static inline unsigned long time_to_tick(unsigned long time) 57 { 58 return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 59 } 60 61 /* Calculate how many ticks happen in "us" microseconds */ 62 static inline unsigned long us_to_tick(unsigned long us) 63 { 64 return (us * MXS_INCREMENTER_HZ) / 1000000; 65 } 66 67 int timer_init(void) 68 { 69 struct mxs_timrot_regs *timrot_regs = 70 (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 71 72 /* Reset Timers and Rotary Encoder module */ 73 mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); 74 75 /* Set fixed_count to 0 */ 76 #if defined(CONFIG_MX23) 77 writel(0, &timrot_regs->hw_timrot_timcount0); 78 #elif defined(CONFIG_MX28) 79 writel(0, &timrot_regs->hw_timrot_fixed_count0); 80 #endif 81 82 /* Set UPDATE bit and 1Khz frequency */ 83 writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | 84 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, 85 &timrot_regs->hw_timrot_timctrl0); 86 87 /* Set fixed_count to maximal value */ 88 #if defined(CONFIG_MX23) 89 writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0); 90 #elif defined(CONFIG_MX28) 91 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); 92 #endif 93 94 return 0; 95 } 96 97 unsigned long long get_ticks(void) 98 { 99 struct mxs_timrot_regs *timrot_regs = 100 (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 101 uint32_t now; 102 103 /* Current tick value */ 104 #if defined(CONFIG_MX23) 105 /* Upper bits are the valid ones. */ 106 now = readl(&timrot_regs->hw_timrot_timcount0) >> 107 TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET; 108 #elif defined(CONFIG_MX28) 109 now = readl(&timrot_regs->hw_timrot_running_count0); 110 #endif 111 112 if (lastdec >= now) { 113 /* 114 * normal mode (non roll) 115 * move stamp forward with absolut diff ticks 116 */ 117 timestamp += (lastdec - now); 118 } else { 119 /* we have rollover of decrementer */ 120 timestamp += (TIMER_LOAD_VAL - now) + lastdec; 121 122 } 123 lastdec = now; 124 125 return timestamp; 126 } 127 128 ulong get_timer_masked(void) 129 { 130 return tick_to_time(get_ticks()); 131 } 132 133 ulong get_timer(ulong base) 134 { 135 return get_timer_masked() - base; 136 } 137 138 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ 139 #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0 140 141 void __udelay(unsigned long usec) 142 { 143 uint32_t old, new, incr; 144 uint32_t counter = 0; 145 146 old = readl(MXS_HW_DIGCTL_MICROSECONDS); 147 148 while (counter < usec) { 149 new = readl(MXS_HW_DIGCTL_MICROSECONDS); 150 151 /* Check if the timer wrapped. */ 152 if (new < old) { 153 incr = 0xffffffff - old; 154 incr += new; 155 } else { 156 incr = new - old; 157 } 158 159 /* 160 * Check if we are close to the maximum time and the counter 161 * would wrap if incremented. If that's the case, break out 162 * from the loop as the requested delay time passed. 163 */ 164 if (counter + incr < counter) 165 break; 166 167 counter += incr; 168 old = new; 169 } 170 } 171 172 ulong get_tbclk(void) 173 { 174 return MXS_INCREMENTER_HZ; 175 } 176