blob: 4ed75e604ca8b589ffb0592cff24c0b3a0d866c9 [file] [log] [blame]
/*
* Freescale i.MX28 timer driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
/* Maximum fixed count */
#define TIMER_LOAD_VAL 0xffffffff
DECLARE_GLOBAL_DATA_PTR;
#define timestamp (gd->tbl)
#define lastdec (gd->lastinc)
/*
* This driver uses 1kHz clock source.
*/
#define MX28_INCREMENTER_HZ 1000
static inline unsigned long tick_to_time(unsigned long tick)
{
return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
}
static inline unsigned long time_to_tick(unsigned long time)
{
return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
}
/* Calculate how many ticks happen in "us" microseconds */
static inline unsigned long us_to_tick(unsigned long us)
{
return (us * MX28_INCREMENTER_HZ) / 1000000;
}
int timer_init(void)
{
struct mxs_timrot_regs *timrot_regs =
(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
/* Reset Timers and Rotary Encoder module */
mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
/* Set fixed_count to 0 */
writel(0, &timrot_regs->hw_timrot_fixed_count0);
/* Set UPDATE bit and 1Khz frequency */
writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
&timrot_regs->hw_timrot_timctrl0);
/* Set fixed_count to maximal value */
writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
return 0;
}
unsigned long long get_ticks(void)
{
struct mxs_timrot_regs *timrot_regs =
(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
/* Current tick value */
uint32_t now = readl(&timrot_regs->hw_timrot_running_count0);
if (lastdec >= now) {
/*
* normal mode (non roll)
* move stamp forward with absolut diff ticks
*/
timestamp += (lastdec - now);
} else {
/* we have rollover of decrementer */
timestamp += (TIMER_LOAD_VAL - now) + lastdec;
}
lastdec = now;
return timestamp;
}
ulong get_timer_masked(void)
{
return tick_to_time(get_ticks());
}
ulong get_timer(ulong base)
{
return get_timer_masked() - base;
}
/* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
#define MX28_HW_DIGCTL_MICROSECONDS 0x8001c0c0
void __udelay(unsigned long usec)
{
uint32_t old, new, incr;
uint32_t counter = 0;
old = readl(MX28_HW_DIGCTL_MICROSECONDS);
while (counter < usec) {
new = readl(MX28_HW_DIGCTL_MICROSECONDS);
/* Check if the timer wrapped. */
if (new < old) {
incr = 0xffffffff - old;
incr += new;
} else {
incr = new - old;
}
/*
* Check if we are close to the maximum time and the counter
* would wrap if incremented. If that's the case, break out
* from the loop as the requested delay time passed.
*/
if (counter + incr < counter)
break;
counter += incr;
old = new;
}
}
ulong get_tbclk(void)
{
return MX28_INCREMENTER_HZ;
}