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