uboot/arch/arm/cpu/arm926ejs/mx28/timer.c
<<
>>
Prefs
   1/*
   2 * Freescale i.MX28 timer driver
   3 *
   4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
   5 * on behalf of DENX Software Engineering GmbH
   6 *
   7 * Based on code from LTIB:
   8 * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
   9 *
  10 * See file CREDITS for list of people who contributed to this
  11 * project.
  12 *
  13 * This program is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU General Public License as
  15 * published by the Free Software Foundation; either version 2 of
  16 * the License, or (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, write to the Free Software
  25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  26 * MA 02111-1307 USA
  27 */
  28
  29#include <common.h>
  30#include <asm/io.h>
  31#include <asm/arch/imx-regs.h>
  32#include <asm/arch/sys_proto.h>
  33
  34/* Maximum fixed count */
  35#define TIMER_LOAD_VAL  0xffffffff
  36
  37DECLARE_GLOBAL_DATA_PTR;
  38
  39#define timestamp (gd->tbl)
  40#define lastdec (gd->lastinc)
  41
  42/*
  43 * This driver uses 1kHz clock source.
  44 */
  45#define MX28_INCREMENTER_HZ             1000
  46
  47static inline unsigned long tick_to_time(unsigned long tick)
  48{
  49        return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
  50}
  51
  52static inline unsigned long time_to_tick(unsigned long time)
  53{
  54        return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
  55}
  56
  57/* Calculate how many ticks happen in "us" microseconds */
  58static inline unsigned long us_to_tick(unsigned long us)
  59{
  60        return (us * MX28_INCREMENTER_HZ) / 1000000;
  61}
  62
  63int timer_init(void)
  64{
  65        struct mx28_timrot_regs *timrot_regs =
  66                (struct mx28_timrot_regs *)MXS_TIMROT_BASE;
  67
  68        /* Reset Timers and Rotary Encoder module */
  69        mx28_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
  70
  71        /* Set fixed_count to 0 */
  72        writel(0, &timrot_regs->hw_timrot_fixed_count0);
  73
  74        /* Set UPDATE bit and 1Khz frequency */
  75        writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
  76                TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
  77                &timrot_regs->hw_timrot_timctrl0);
  78
  79        /* Set fixed_count to maximal value */
  80        writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
  81
  82        return 0;
  83}
  84
  85unsigned long long get_ticks(void)
  86{
  87        struct mx28_timrot_regs *timrot_regs =
  88                (struct mx28_timrot_regs *)MXS_TIMROT_BASE;
  89
  90        /* Current tick value */
  91        uint32_t now = readl(&timrot_regs->hw_timrot_running_count0);
  92
  93        if (lastdec >= now) {
  94                /*
  95                 * normal mode (non roll)
  96                 * move stamp forward with absolut diff ticks
  97                 */
  98                timestamp += (lastdec - now);
  99        } else {
 100                /* we have rollover of decrementer */
 101                timestamp += (TIMER_LOAD_VAL - now) + lastdec;
 102
 103        }
 104        lastdec = now;
 105
 106        return timestamp;
 107}
 108
 109ulong get_timer_masked(void)
 110{
 111        return tick_to_time(get_ticks());
 112}
 113
 114ulong get_timer(ulong base)
 115{
 116        return get_timer_masked() - base;
 117}
 118
 119/* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
 120#define MX28_HW_DIGCTL_MICROSECONDS     0x8001c0c0
 121
 122void __udelay(unsigned long usec)
 123{
 124        uint32_t old, new, incr;
 125        uint32_t counter = 0;
 126
 127        old = readl(MX28_HW_DIGCTL_MICROSECONDS);
 128
 129        while (counter < usec) {
 130                new = readl(MX28_HW_DIGCTL_MICROSECONDS);
 131
 132                /* Check if the timer wrapped. */
 133                if (new < old) {
 134                        incr = 0xffffffff - old;
 135                        incr += new;
 136                } else {
 137                        incr = new - old;
 138                }
 139
 140                /*
 141                 * Check if we are close to the maximum time and the counter
 142                 * would wrap if incremented. If that's the case, break out
 143                 * from the loop as the requested delay time passed.
 144                 */
 145                if (counter + incr < counter)
 146                        break;
 147
 148                counter += incr;
 149                old = new;
 150        }
 151}
 152
 153ulong get_tbclk(void)
 154{
 155        return MX28_INCREMENTER_HZ;
 156}
 157