13a0398d7SOtavio Salvador /* 23a0398d7SOtavio Salvador * Freescale i.MX28 timer driver 33a0398d7SOtavio Salvador * 43a0398d7SOtavio Salvador * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> 53a0398d7SOtavio Salvador * on behalf of DENX Software Engineering GmbH 63a0398d7SOtavio Salvador * 73a0398d7SOtavio Salvador * Based on code from LTIB: 83a0398d7SOtavio Salvador * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. 93a0398d7SOtavio Salvador * 101a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 113a0398d7SOtavio Salvador */ 123a0398d7SOtavio Salvador 133a0398d7SOtavio Salvador #include <common.h> 143a0398d7SOtavio Salvador #include <asm/io.h> 153a0398d7SOtavio Salvador #include <asm/arch/imx-regs.h> 163a0398d7SOtavio Salvador #include <asm/arch/sys_proto.h> 173a0398d7SOtavio Salvador 183a0398d7SOtavio Salvador /* Maximum fixed count */ 196ecd05d2SFadil Berisha #if defined(CONFIG_MX23) 206ecd05d2SFadil Berisha #define TIMER_LOAD_VAL 0xffff 216ecd05d2SFadil Berisha #elif defined(CONFIG_MX28) 223a0398d7SOtavio Salvador #define TIMER_LOAD_VAL 0xffffffff 236ecd05d2SFadil Berisha #endif 243a0398d7SOtavio Salvador 253a0398d7SOtavio Salvador DECLARE_GLOBAL_DATA_PTR; 263a0398d7SOtavio Salvador 2766ee6923SSimon Glass #define timestamp (gd->arch.tbl) 28582601daSSimon Glass #define lastdec (gd->arch.lastinc) 293a0398d7SOtavio Salvador 303a0398d7SOtavio Salvador /* 313a0398d7SOtavio Salvador * This driver uses 1kHz clock source. 323a0398d7SOtavio Salvador */ 333e9dc930SFadil Berisha #define MXS_INCREMENTER_HZ 1000 343a0398d7SOtavio Salvador 353a0398d7SOtavio Salvador static inline unsigned long tick_to_time(unsigned long tick) 363a0398d7SOtavio Salvador { 373e9dc930SFadil Berisha return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 383a0398d7SOtavio Salvador } 393a0398d7SOtavio Salvador 403a0398d7SOtavio Salvador static inline unsigned long time_to_tick(unsigned long time) 413a0398d7SOtavio Salvador { 423e9dc930SFadil Berisha return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); 433a0398d7SOtavio Salvador } 443a0398d7SOtavio Salvador 453a0398d7SOtavio Salvador /* Calculate how many ticks happen in "us" microseconds */ 463a0398d7SOtavio Salvador static inline unsigned long us_to_tick(unsigned long us) 473a0398d7SOtavio Salvador { 483e9dc930SFadil Berisha return (us * MXS_INCREMENTER_HZ) / 1000000; 493a0398d7SOtavio Salvador } 503a0398d7SOtavio Salvador 513a0398d7SOtavio Salvador int timer_init(void) 523a0398d7SOtavio Salvador { 539c471142SOtavio Salvador struct mxs_timrot_regs *timrot_regs = 549c471142SOtavio Salvador (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 553a0398d7SOtavio Salvador 563a0398d7SOtavio Salvador /* Reset Timers and Rotary Encoder module */ 57fa7a51cbSOtavio Salvador mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); 583a0398d7SOtavio Salvador 593a0398d7SOtavio Salvador /* Set fixed_count to 0 */ 606ecd05d2SFadil Berisha #if defined(CONFIG_MX23) 616ecd05d2SFadil Berisha writel(0, &timrot_regs->hw_timrot_timcount0); 626ecd05d2SFadil Berisha #elif defined(CONFIG_MX28) 633a0398d7SOtavio Salvador writel(0, &timrot_regs->hw_timrot_fixed_count0); 646ecd05d2SFadil Berisha #endif 653a0398d7SOtavio Salvador 663a0398d7SOtavio Salvador /* Set UPDATE bit and 1Khz frequency */ 673a0398d7SOtavio Salvador writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | 683a0398d7SOtavio Salvador TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, 693a0398d7SOtavio Salvador &timrot_regs->hw_timrot_timctrl0); 703a0398d7SOtavio Salvador 713a0398d7SOtavio Salvador /* Set fixed_count to maximal value */ 726ecd05d2SFadil Berisha #if defined(CONFIG_MX23) 736ecd05d2SFadil Berisha writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0); 746ecd05d2SFadil Berisha #elif defined(CONFIG_MX28) 753a0398d7SOtavio Salvador writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); 766ecd05d2SFadil Berisha #endif 773a0398d7SOtavio Salvador 783a0398d7SOtavio Salvador return 0; 793a0398d7SOtavio Salvador } 803a0398d7SOtavio Salvador 813a0398d7SOtavio Salvador unsigned long long get_ticks(void) 823a0398d7SOtavio Salvador { 839c471142SOtavio Salvador struct mxs_timrot_regs *timrot_regs = 849c471142SOtavio Salvador (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 856ecd05d2SFadil Berisha uint32_t now; 863a0398d7SOtavio Salvador 873a0398d7SOtavio Salvador /* Current tick value */ 886ecd05d2SFadil Berisha #if defined(CONFIG_MX23) 896ecd05d2SFadil Berisha /* Upper bits are the valid ones. */ 906ecd05d2SFadil Berisha now = readl(&timrot_regs->hw_timrot_timcount0) >> 916ecd05d2SFadil Berisha TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET; 926ecd05d2SFadil Berisha #elif defined(CONFIG_MX28) 936ecd05d2SFadil Berisha now = readl(&timrot_regs->hw_timrot_running_count0); 94*feb8cf4aSWolfgang Denk #else 95*feb8cf4aSWolfgang Denk #error "Don't know how to read timrot_regs" 966ecd05d2SFadil Berisha #endif 973a0398d7SOtavio Salvador 983a0398d7SOtavio Salvador if (lastdec >= now) { 993a0398d7SOtavio Salvador /* 1003a0398d7SOtavio Salvador * normal mode (non roll) 1013a0398d7SOtavio Salvador * move stamp forward with absolut diff ticks 1023a0398d7SOtavio Salvador */ 1033a0398d7SOtavio Salvador timestamp += (lastdec - now); 1043a0398d7SOtavio Salvador } else { 1053a0398d7SOtavio Salvador /* we have rollover of decrementer */ 1063a0398d7SOtavio Salvador timestamp += (TIMER_LOAD_VAL - now) + lastdec; 1073a0398d7SOtavio Salvador 1083a0398d7SOtavio Salvador } 1093a0398d7SOtavio Salvador lastdec = now; 1103a0398d7SOtavio Salvador 1113a0398d7SOtavio Salvador return timestamp; 1123a0398d7SOtavio Salvador } 1133a0398d7SOtavio Salvador 1143a0398d7SOtavio Salvador ulong get_timer_masked(void) 1153a0398d7SOtavio Salvador { 1163a0398d7SOtavio Salvador return tick_to_time(get_ticks()); 1173a0398d7SOtavio Salvador } 1183a0398d7SOtavio Salvador 1193a0398d7SOtavio Salvador ulong get_timer(ulong base) 1203a0398d7SOtavio Salvador { 1213a0398d7SOtavio Salvador return get_timer_masked() - base; 1223a0398d7SOtavio Salvador } 1233a0398d7SOtavio Salvador 1243a0398d7SOtavio Salvador /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ 1253e9dc930SFadil Berisha #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0 1263a0398d7SOtavio Salvador 1273a0398d7SOtavio Salvador void __udelay(unsigned long usec) 1283a0398d7SOtavio Salvador { 1293a0398d7SOtavio Salvador uint32_t old, new, incr; 1303a0398d7SOtavio Salvador uint32_t counter = 0; 1313a0398d7SOtavio Salvador 1323e9dc930SFadil Berisha old = readl(MXS_HW_DIGCTL_MICROSECONDS); 1333a0398d7SOtavio Salvador 1343a0398d7SOtavio Salvador while (counter < usec) { 1353e9dc930SFadil Berisha new = readl(MXS_HW_DIGCTL_MICROSECONDS); 1363a0398d7SOtavio Salvador 1373a0398d7SOtavio Salvador /* Check if the timer wrapped. */ 1383a0398d7SOtavio Salvador if (new < old) { 1393a0398d7SOtavio Salvador incr = 0xffffffff - old; 1403a0398d7SOtavio Salvador incr += new; 1413a0398d7SOtavio Salvador } else { 1423a0398d7SOtavio Salvador incr = new - old; 1433a0398d7SOtavio Salvador } 1443a0398d7SOtavio Salvador 1453a0398d7SOtavio Salvador /* 1463a0398d7SOtavio Salvador * Check if we are close to the maximum time and the counter 1473a0398d7SOtavio Salvador * would wrap if incremented. If that's the case, break out 1483a0398d7SOtavio Salvador * from the loop as the requested delay time passed. 1493a0398d7SOtavio Salvador */ 1503a0398d7SOtavio Salvador if (counter + incr < counter) 1513a0398d7SOtavio Salvador break; 1523a0398d7SOtavio Salvador 1533a0398d7SOtavio Salvador counter += incr; 1543a0398d7SOtavio Salvador old = new; 1553a0398d7SOtavio Salvador } 1563a0398d7SOtavio Salvador } 1573a0398d7SOtavio Salvador 1583a0398d7SOtavio Salvador ulong get_tbclk(void) 1593a0398d7SOtavio Salvador { 1603e9dc930SFadil Berisha return MXS_INCREMENTER_HZ; 1613a0398d7SOtavio Salvador } 162