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