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 #define TIMER_LOAD_VAL 0xffffffff 36 37 DECLARE_GLOBAL_DATA_PTR; 38 39 #define timestamp (gd->tbl) 40 #define lastdec (gd->lastinc) 41 42 /* 43 * This driver uses 1kHz clock source. 44 */ 45 #define MX28_INCREMENTER_HZ 1000 46 47 static inline unsigned long tick_to_time(unsigned long tick) 48 { 49 return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ); 50 } 51 52 static inline unsigned long time_to_tick(unsigned long time) 53 { 54 return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ); 55 } 56 57 /* Calculate how many ticks happen in "us" microseconds */ 58 static inline unsigned long us_to_tick(unsigned long us) 59 { 60 return (us * MX28_INCREMENTER_HZ) / 1000000; 61 } 62 63 int timer_init(void) 64 { 65 struct mxs_timrot_regs *timrot_regs = 66 (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 67 68 /* Reset Timers and Rotary Encoder module */ 69 mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); 70 71 /* Set fixed_count to 0 */ 72 writel(0, &timrot_regs->hw_timrot_fixed_count0); 73 74 /* Set UPDATE bit and 1Khz frequency */ 75 writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | 76 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, 77 &timrot_regs->hw_timrot_timctrl0); 78 79 /* Set fixed_count to maximal value */ 80 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); 81 82 return 0; 83 } 84 85 unsigned long long get_ticks(void) 86 { 87 struct mxs_timrot_regs *timrot_regs = 88 (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 89 90 /* Current tick value */ 91 uint32_t now = readl(&timrot_regs->hw_timrot_running_count0); 92 93 if (lastdec >= now) { 94 /* 95 * normal mode (non roll) 96 * move stamp forward with absolut diff ticks 97 */ 98 timestamp += (lastdec - now); 99 } else { 100 /* we have rollover of decrementer */ 101 timestamp += (TIMER_LOAD_VAL - now) + lastdec; 102 103 } 104 lastdec = now; 105 106 return timestamp; 107 } 108 109 ulong get_timer_masked(void) 110 { 111 return tick_to_time(get_ticks()); 112 } 113 114 ulong get_timer(ulong base) 115 { 116 return get_timer_masked() - base; 117 } 118 119 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ 120 #define MX28_HW_DIGCTL_MICROSECONDS 0x8001c0c0 121 122 void __udelay(unsigned long usec) 123 { 124 uint32_t old, new, incr; 125 uint32_t counter = 0; 126 127 old = readl(MX28_HW_DIGCTL_MICROSECONDS); 128 129 while (counter < usec) { 130 new = readl(MX28_HW_DIGCTL_MICROSECONDS); 131 132 /* Check if the timer wrapped. */ 133 if (new < old) { 134 incr = 0xffffffff - old; 135 incr += new; 136 } else { 137 incr = new - old; 138 } 139 140 /* 141 * Check if we are close to the maximum time and the counter 142 * would wrap if incremented. If that's the case, break out 143 * from the loop as the requested delay time passed. 144 */ 145 if (counter + incr < counter) 146 break; 147 148 counter += incr; 149 old = new; 150 } 151 } 152 153 ulong get_tbclk(void) 154 { 155 return MX28_INCREMENTER_HZ; 156 } 157