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