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