uboot/arch/arm/cpu/arm926ejs/mxs/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 * SPDX-License-Identifier:     GPL-2.0+
  11 */
  12
  13#include <common.h>
  14#include <asm/io.h>
  15#include <asm/arch/imx-regs.h>
  16#include <asm/arch/sys_proto.h>
  17
  18/* Maximum fixed count */
  19#if defined(CONFIG_MX23)
  20#define TIMER_LOAD_VAL 0xffff
  21#elif defined(CONFIG_MX28)
  22#define TIMER_LOAD_VAL 0xffffffff
  23#endif
  24
  25DECLARE_GLOBAL_DATA_PTR;
  26
  27#define timestamp (gd->arch.tbl)
  28#define lastdec (gd->arch.lastinc)
  29
  30/*
  31 * This driver uses 1kHz clock source.
  32 */
  33#define MXS_INCREMENTER_HZ              1000
  34
  35static inline unsigned long tick_to_time(unsigned long tick)
  36{
  37        return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
  38}
  39
  40static inline unsigned long time_to_tick(unsigned long time)
  41{
  42        return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
  43}
  44
  45/* Calculate how many ticks happen in "us" microseconds */
  46static inline unsigned long us_to_tick(unsigned long us)
  47{
  48        return (us * MXS_INCREMENTER_HZ) / 1000000;
  49}
  50
  51int timer_init(void)
  52{
  53        struct mxs_timrot_regs *timrot_regs =
  54                (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
  55
  56        /* Reset Timers and Rotary Encoder module */
  57        mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
  58
  59        /* Set fixed_count to 0 */
  60#if defined(CONFIG_MX23)
  61        writel(0, &timrot_regs->hw_timrot_timcount0);
  62#elif defined(CONFIG_MX28)
  63        writel(0, &timrot_regs->hw_timrot_fixed_count0);
  64#endif
  65
  66        /* Set UPDATE bit and 1Khz frequency */
  67        writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
  68                TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
  69                &timrot_regs->hw_timrot_timctrl0);
  70
  71        /* Set fixed_count to maximal value */
  72#if defined(CONFIG_MX23)
  73        writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
  74#elif defined(CONFIG_MX28)
  75        writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
  76#endif
  77
  78        return 0;
  79}
  80
  81unsigned long long get_ticks(void)
  82{
  83        struct mxs_timrot_regs *timrot_regs =
  84                (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
  85        uint32_t now;
  86
  87        /* Current tick value */
  88#if defined(CONFIG_MX23)
  89        /* Upper bits are the valid ones. */
  90        now = readl(&timrot_regs->hw_timrot_timcount0) >>
  91                TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
  92#elif defined(CONFIG_MX28)
  93        now = readl(&timrot_regs->hw_timrot_running_count0);
  94#else
  95#error "Don't know how to read timrot_regs"
  96#endif
  97
  98        if (lastdec >= now) {
  99                /*
 100                 * normal mode (non roll)
 101                 * move stamp forward with absolut diff ticks
 102                 */
 103                timestamp += (lastdec - now);
 104        } else {
 105                /* we have rollover of decrementer */
 106                timestamp += (TIMER_LOAD_VAL - now) + lastdec;
 107
 108        }
 109        lastdec = now;
 110
 111        return timestamp;
 112}
 113
 114ulong get_timer_masked(void)
 115{
 116        return tick_to_time(get_ticks());
 117}
 118
 119ulong get_timer(ulong base)
 120{
 121        return get_timer_masked() - base;
 122}
 123
 124/* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
 125#define MXS_HW_DIGCTL_MICROSECONDS      0x8001c0c0
 126
 127void __udelay(unsigned long usec)
 128{
 129        uint32_t old, new, incr;
 130        uint32_t counter = 0;
 131
 132        old = readl(MXS_HW_DIGCTL_MICROSECONDS);
 133
 134        while (counter < usec) {
 135                new = readl(MXS_HW_DIGCTL_MICROSECONDS);
 136
 137                /* Check if the timer wrapped. */
 138                if (new < old) {
 139                        incr = 0xffffffff - old;
 140                        incr += new;
 141                } else {
 142                        incr = new - old;
 143                }
 144
 145                /*
 146                 * Check if we are close to the maximum time and the counter
 147                 * would wrap if incremented. If that's the case, break out
 148                 * from the loop as the requested delay time passed.
 149                 */
 150                if (counter + incr < counter)
 151                        break;
 152
 153                counter += incr;
 154                old = new;
 155        }
 156}
 157
 158ulong get_tbclk(void)
 159{
 160        return MXS_INCREMENTER_HZ;
 161}
 162