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 * 103a0398d7SOtavio Salvador * See file CREDITS for list of people who contributed to this 113a0398d7SOtavio Salvador * project. 123a0398d7SOtavio Salvador * 133a0398d7SOtavio Salvador * This program is free software; you can redistribute it and/or 143a0398d7SOtavio Salvador * modify it under the terms of the GNU General Public License as 153a0398d7SOtavio Salvador * published by the Free Software Foundation; either version 2 of 163a0398d7SOtavio Salvador * the License, or (at your option) any later version. 173a0398d7SOtavio Salvador * 183a0398d7SOtavio Salvador * This program is distributed in the hope that it will be useful, 193a0398d7SOtavio Salvador * but WITHOUT ANY WARRANTY; without even the implied warranty of 203a0398d7SOtavio Salvador * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 213a0398d7SOtavio Salvador * GNU General Public License for more details. 223a0398d7SOtavio Salvador * 233a0398d7SOtavio Salvador * You should have received a copy of the GNU General Public License 243a0398d7SOtavio Salvador * along with this program; if not, write to the Free Software 253a0398d7SOtavio Salvador * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 263a0398d7SOtavio Salvador * MA 02111-1307 USA 273a0398d7SOtavio Salvador */ 283a0398d7SOtavio Salvador 293a0398d7SOtavio Salvador #include <common.h> 303a0398d7SOtavio Salvador #include <asm/io.h> 313a0398d7SOtavio Salvador #include <asm/arch/imx-regs.h> 323a0398d7SOtavio Salvador #include <asm/arch/sys_proto.h> 333a0398d7SOtavio Salvador 343a0398d7SOtavio Salvador /* Maximum fixed count */ 353a0398d7SOtavio Salvador #define TIMER_LOAD_VAL 0xffffffff 363a0398d7SOtavio Salvador 373a0398d7SOtavio Salvador DECLARE_GLOBAL_DATA_PTR; 383a0398d7SOtavio Salvador 393a0398d7SOtavio Salvador #define timestamp (gd->tbl) 403a0398d7SOtavio Salvador #define lastdec (gd->lastinc) 413a0398d7SOtavio Salvador 423a0398d7SOtavio Salvador /* 433a0398d7SOtavio Salvador * This driver uses 1kHz clock source. 443a0398d7SOtavio Salvador */ 453a0398d7SOtavio Salvador #define MX28_INCREMENTER_HZ 1000 463a0398d7SOtavio Salvador 473a0398d7SOtavio Salvador static inline unsigned long tick_to_time(unsigned long tick) 483a0398d7SOtavio Salvador { 493a0398d7SOtavio Salvador return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ); 503a0398d7SOtavio Salvador } 513a0398d7SOtavio Salvador 523a0398d7SOtavio Salvador static inline unsigned long time_to_tick(unsigned long time) 533a0398d7SOtavio Salvador { 543a0398d7SOtavio Salvador return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ); 553a0398d7SOtavio Salvador } 563a0398d7SOtavio Salvador 573a0398d7SOtavio Salvador /* Calculate how many ticks happen in "us" microseconds */ 583a0398d7SOtavio Salvador static inline unsigned long us_to_tick(unsigned long us) 593a0398d7SOtavio Salvador { 603a0398d7SOtavio Salvador return (us * MX28_INCREMENTER_HZ) / 1000000; 613a0398d7SOtavio Salvador } 623a0398d7SOtavio Salvador 633a0398d7SOtavio Salvador int timer_init(void) 643a0398d7SOtavio Salvador { 659c471142SOtavio Salvador struct mxs_timrot_regs *timrot_regs = 669c471142SOtavio Salvador (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 673a0398d7SOtavio Salvador 683a0398d7SOtavio Salvador /* Reset Timers and Rotary Encoder module */ 69*fa7a51cbSOtavio Salvador mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); 703a0398d7SOtavio Salvador 713a0398d7SOtavio Salvador /* Set fixed_count to 0 */ 723a0398d7SOtavio Salvador writel(0, &timrot_regs->hw_timrot_fixed_count0); 733a0398d7SOtavio Salvador 743a0398d7SOtavio Salvador /* Set UPDATE bit and 1Khz frequency */ 753a0398d7SOtavio Salvador writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | 763a0398d7SOtavio Salvador TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, 773a0398d7SOtavio Salvador &timrot_regs->hw_timrot_timctrl0); 783a0398d7SOtavio Salvador 793a0398d7SOtavio Salvador /* Set fixed_count to maximal value */ 803a0398d7SOtavio Salvador writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); 813a0398d7SOtavio Salvador 823a0398d7SOtavio Salvador return 0; 833a0398d7SOtavio Salvador } 843a0398d7SOtavio Salvador 853a0398d7SOtavio Salvador unsigned long long get_ticks(void) 863a0398d7SOtavio Salvador { 879c471142SOtavio Salvador struct mxs_timrot_regs *timrot_regs = 889c471142SOtavio Salvador (struct mxs_timrot_regs *)MXS_TIMROT_BASE; 893a0398d7SOtavio Salvador 903a0398d7SOtavio Salvador /* Current tick value */ 913a0398d7SOtavio Salvador uint32_t now = readl(&timrot_regs->hw_timrot_running_count0); 923a0398d7SOtavio Salvador 933a0398d7SOtavio Salvador if (lastdec >= now) { 943a0398d7SOtavio Salvador /* 953a0398d7SOtavio Salvador * normal mode (non roll) 963a0398d7SOtavio Salvador * move stamp forward with absolut diff ticks 973a0398d7SOtavio Salvador */ 983a0398d7SOtavio Salvador timestamp += (lastdec - now); 993a0398d7SOtavio Salvador } else { 1003a0398d7SOtavio Salvador /* we have rollover of decrementer */ 1013a0398d7SOtavio Salvador timestamp += (TIMER_LOAD_VAL - now) + lastdec; 1023a0398d7SOtavio Salvador 1033a0398d7SOtavio Salvador } 1043a0398d7SOtavio Salvador lastdec = now; 1053a0398d7SOtavio Salvador 1063a0398d7SOtavio Salvador return timestamp; 1073a0398d7SOtavio Salvador } 1083a0398d7SOtavio Salvador 1093a0398d7SOtavio Salvador ulong get_timer_masked(void) 1103a0398d7SOtavio Salvador { 1113a0398d7SOtavio Salvador return tick_to_time(get_ticks()); 1123a0398d7SOtavio Salvador } 1133a0398d7SOtavio Salvador 1143a0398d7SOtavio Salvador ulong get_timer(ulong base) 1153a0398d7SOtavio Salvador { 1163a0398d7SOtavio Salvador return get_timer_masked() - base; 1173a0398d7SOtavio Salvador } 1183a0398d7SOtavio Salvador 1193a0398d7SOtavio Salvador /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ 1203a0398d7SOtavio Salvador #define MX28_HW_DIGCTL_MICROSECONDS 0x8001c0c0 1213a0398d7SOtavio Salvador 1223a0398d7SOtavio Salvador void __udelay(unsigned long usec) 1233a0398d7SOtavio Salvador { 1243a0398d7SOtavio Salvador uint32_t old, new, incr; 1253a0398d7SOtavio Salvador uint32_t counter = 0; 1263a0398d7SOtavio Salvador 1273a0398d7SOtavio Salvador old = readl(MX28_HW_DIGCTL_MICROSECONDS); 1283a0398d7SOtavio Salvador 1293a0398d7SOtavio Salvador while (counter < usec) { 1303a0398d7SOtavio Salvador new = readl(MX28_HW_DIGCTL_MICROSECONDS); 1313a0398d7SOtavio Salvador 1323a0398d7SOtavio Salvador /* Check if the timer wrapped. */ 1333a0398d7SOtavio Salvador if (new < old) { 1343a0398d7SOtavio Salvador incr = 0xffffffff - old; 1353a0398d7SOtavio Salvador incr += new; 1363a0398d7SOtavio Salvador } else { 1373a0398d7SOtavio Salvador incr = new - old; 1383a0398d7SOtavio Salvador } 1393a0398d7SOtavio Salvador 1403a0398d7SOtavio Salvador /* 1413a0398d7SOtavio Salvador * Check if we are close to the maximum time and the counter 1423a0398d7SOtavio Salvador * would wrap if incremented. If that's the case, break out 1433a0398d7SOtavio Salvador * from the loop as the requested delay time passed. 1443a0398d7SOtavio Salvador */ 1453a0398d7SOtavio Salvador if (counter + incr < counter) 1463a0398d7SOtavio Salvador break; 1473a0398d7SOtavio Salvador 1483a0398d7SOtavio Salvador counter += incr; 1493a0398d7SOtavio Salvador old = new; 1503a0398d7SOtavio Salvador } 1513a0398d7SOtavio Salvador } 1523a0398d7SOtavio Salvador 1533a0398d7SOtavio Salvador ulong get_tbclk(void) 1543a0398d7SOtavio Salvador { 1553a0398d7SOtavio Salvador return MX28_INCREMENTER_HZ; 1563a0398d7SOtavio Salvador } 157