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