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 <time.h> 14 #include <asm/io.h> 15 #include <asm/arch/imx-regs.h> 16 #include <asm/arch/sys_proto.h> 17 18 /* Maximum fixed count */ 19 #if defined(CONFIG_MX23) 20 #define TIMER_LOAD_VAL 0xffff 21 #elif defined(CONFIG_MX28) 22 #define TIMER_LOAD_VAL 0xffffffff 23 #endif 24 25 DECLARE_GLOBAL_DATA_PTR; 26 27 #define timestamp (gd->arch.tbl) 28 #define lastdec (gd->arch.lastinc) 29 30 /* 31 * This driver uses 1kHz clock source. 32 */ 33 #define MXS_INCREMENTER_HZ 1000 34 tick_to_time(unsigned long tick)35static inline unsigned long tick_to_time(unsigned long tick) 36 { 37 return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 38 } 39 time_to_tick(unsigned long time)40static inline unsigned long time_to_tick(unsigned long time) 41 { 42 return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 43 } 44 45 /* Calculate how many ticks happen in "us" microseconds */ us_to_tick(unsigned long us)46static inline unsigned long us_to_tick(unsigned long us) 47 { 48 return (us * MXS_INCREMENTER_HZ) / 1000000; 49 } 50 timer_init(void)51int timer_init(void) 52 { 53 struct mxs_timrot_regs *timrot_regs = 54 (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 55 56 /* Reset Timers and Rotary Encoder module */ 57 mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); 58 59 /* Set fixed_count to 0 */ 60 #if defined(CONFIG_MX23) 61 writel(0, &timrot_regs->hw_timrot_timcount0); 62 #elif defined(CONFIG_MX28) 63 writel(0, &timrot_regs->hw_timrot_fixed_count0); 64 #endif 65 66 /* Set UPDATE bit and 1Khz frequency */ 67 writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | 68 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, 69 &timrot_regs->hw_timrot_timctrl0); 70 71 /* Set fixed_count to maximal value */ 72 #if defined(CONFIG_MX23) 73 writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0); 74 #elif defined(CONFIG_MX28) 75 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); 76 #endif 77 78 return 0; 79 } 80 get_ticks(void)81unsigned long long get_ticks(void) 82 { 83 struct mxs_timrot_regs *timrot_regs = 84 (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 85 uint32_t now; 86 87 /* Current tick value */ 88 #if defined(CONFIG_MX23) 89 /* Upper bits are the valid ones. */ 90 now = readl(&timrot_regs->hw_timrot_timcount0) >> 91 TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET; 92 #elif defined(CONFIG_MX28) 93 now = readl(&timrot_regs->hw_timrot_running_count0); 94 #else 95 #error "Don't know how to read timrot_regs" 96 #endif 97 98 if (lastdec >= now) { 99 /* 100 * normal mode (non roll) 101 * move stamp forward with absolut diff ticks 102 */ 103 timestamp += (lastdec - now); 104 } else { 105 /* we have rollover of decrementer */ 106 timestamp += (TIMER_LOAD_VAL - now) + lastdec; 107 108 } 109 lastdec = now; 110 111 return timestamp; 112 } 113 get_timer(ulong base)114ulong get_timer(ulong base) 115 { 116 return tick_to_time(get_ticks()) - base; 117 } 118 119 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ 120 #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0 121 __udelay(unsigned long usec)122void __udelay(unsigned long usec) 123 { 124 uint32_t old, new, incr; 125 uint32_t counter = 0; 126 127 old = readl(MXS_HW_DIGCTL_MICROSECONDS); 128 129 while (counter < usec) { 130 new = readl(MXS_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 get_tbclk(void)153ulong get_tbclk(void) 154 { 155 return MXS_INCREMENTER_HZ; 156 } 157