uboot/arch/arm/mach-nexell/timer.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2016 Nexell
   4 * Hyunseok, Jung <hsjung@nexell.co.kr>
   5 */
   6
   7#include <common.h>
   8#include <log.h>
   9
  10#include <asm/io.h>
  11#include <asm/arch/nexell.h>
  12#include <asm/arch/clk.h>
  13#if defined(CONFIG_ARCH_S5P4418)
  14#include <asm/arch/reset.h>
  15#endif
  16
  17#if (CONFIG_TIMER_SYS_TICK_CH > 3)
  18#error Not support timer channel. Please use "0~3" channels.
  19#endif
  20
  21/* global variables to save timer count
  22 *
  23 * Section ".data" must be used because BSS is not available before relocation,
  24 * in board_init_f(), respectively! I.e. global variables can not be used!
  25 */
  26static unsigned long timestamp __section(".data");
  27static unsigned long lastdec __section(".data");
  28static int      timerinit __section(".data");
  29
  30/* macro to hw timer tick config */
  31static long     TIMER_FREQ  = 1000000;
  32static long     TIMER_HZ    = 1000000 / CONFIG_SYS_HZ;
  33static long     TIMER_COUNT = 0xFFFFFFFF;
  34
  35#define REG_TCFG0                       (0x00)
  36#define REG_TCFG1                       (0x04)
  37#define REG_TCON                        (0x08)
  38#define REG_TCNTB0                      (0x0C)
  39#define REG_TCMPB0                      (0x10)
  40#define REG_TCNT0                       (0x14)
  41#define REG_CSTAT                       (0x44)
  42
  43#define TCON_BIT_AUTO                   (1 << 3)
  44#define TCON_BIT_INVT                   (1 << 2)
  45#define TCON_BIT_UP                     (1 << 1)
  46#define TCON_BIT_RUN                    (1 << 0)
  47#define TCFG0_BIT_CH(ch)                ((ch) == 0 || (ch) == 1 ? 0 : 8)
  48#define TCFG1_BIT_CH(ch)                ((ch) * 4)
  49#define TCON_BIT_CH(ch)                 ((ch) ? (ch) * 4  + 4 : 0)
  50#define TINT_CH(ch)                     (ch)
  51#define TINT_CSTAT_BIT_CH(ch)           ((ch) + 5)
  52#define TINT_CSTAT_MASK                 (0x1F)
  53#define TIMER_TCNT_OFFS                 (0xC)
  54
  55void reset_timer_masked(void);
  56unsigned long get_timer_masked(void);
  57
  58/*
  59 * Timer HW
  60 */
  61static inline void timer_clock(void __iomem *base, int ch, int mux, int scl)
  62{
  63        u32 val = readl(base + REG_TCFG0) & ~(0xFF << TCFG0_BIT_CH(ch));
  64
  65        writel(val | ((scl - 1) << TCFG0_BIT_CH(ch)), base + REG_TCFG0);
  66        val = readl(base + REG_TCFG1) & ~(0xF << TCFG1_BIT_CH(ch));
  67        writel(val | (mux << TCFG1_BIT_CH(ch)), base + REG_TCFG1);
  68}
  69
  70static inline void timer_count(void __iomem *base, int ch, unsigned int cnt)
  71{
  72        writel((cnt - 1), base + REG_TCNTB0 + (TIMER_TCNT_OFFS * ch));
  73        writel((cnt - 1), base + REG_TCMPB0 + (TIMER_TCNT_OFFS * ch));
  74}
  75
  76static inline void timer_start(void __iomem *base, int ch)
  77{
  78        int on = 0;
  79        u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch);
  80
  81        writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch),
  82               base + REG_CSTAT);
  83        val = readl(base + REG_TCON) & ~(0xE << TCON_BIT_CH(ch));
  84        writel(val | (TCON_BIT_UP << TCON_BIT_CH(ch)), base + REG_TCON);
  85
  86        val &= ~(TCON_BIT_UP << TCON_BIT_CH(ch));
  87        val |= ((TCON_BIT_AUTO | TCON_BIT_RUN) << TCON_BIT_CH(ch));
  88        writel(val, base + REG_TCON);
  89        dmb();
  90}
  91
  92static inline void timer_stop(void __iomem *base, int ch)
  93{
  94        int on = 0;
  95        u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch);
  96
  97        writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch),
  98               base + REG_CSTAT);
  99        val = readl(base + REG_TCON) & ~(TCON_BIT_RUN << TCON_BIT_CH(ch));
 100        writel(val, base + REG_TCON);
 101}
 102
 103static inline unsigned long timer_read(void __iomem *base, int ch)
 104{
 105        unsigned long ret;
 106
 107        ret = TIMER_COUNT - readl(base + REG_TCNT0 + (TIMER_TCNT_OFFS * ch));
 108        return ret;
 109}
 110
 111int timer_init(void)
 112{
 113        struct clk *clk = NULL;
 114        char name[16] = "pclk";
 115        int ch = CONFIG_TIMER_SYS_TICK_CH;
 116        unsigned long rate, tclk = 0;
 117        unsigned long mout, thz, cmp = -1UL;
 118        int tcnt, tscl = 0, tmux = 0;
 119        int mux = 0, scl = 0;
 120        void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
 121
 122        if (timerinit)
 123                return 0;
 124
 125        /* get with PCLK */
 126        clk  = clk_get(name);
 127        rate = clk_get_rate(clk);
 128        for (mux = 0; mux < 5; mux++) {
 129                mout = rate / (1 << mux), scl = mout / TIMER_FREQ,
 130                thz = mout / scl;
 131                if (!(mout % TIMER_FREQ) && 256 > scl) {
 132                        tclk = thz, tmux = mux, tscl = scl;
 133                        break;
 134                }
 135                if (scl > 256)
 136                        continue;
 137                if (abs(thz - TIMER_FREQ) >= cmp)
 138                        continue;
 139                tclk = thz, tmux = mux, tscl = scl;
 140                cmp = abs(thz - TIMER_FREQ);
 141        }
 142        tcnt = tclk;    /* Timer Count := 1 Mhz counting */
 143
 144        TIMER_FREQ = tcnt;      /* Timer Count := 1 Mhz counting */
 145        TIMER_HZ = TIMER_FREQ / CONFIG_SYS_HZ;
 146        tcnt = TIMER_COUNT == 0xFFFFFFFF ? TIMER_COUNT + 1 : tcnt;
 147
 148        timer_stop(base, ch);
 149        timer_clock(base, ch, tmux, tscl);
 150        timer_count(base, ch, tcnt);
 151        timer_start(base, ch);
 152
 153        reset_timer_masked();
 154        timerinit = 1;
 155
 156        return 0;
 157}
 158
 159void reset_timer(void)
 160{
 161        reset_timer_masked();
 162}
 163
 164unsigned long get_timer(unsigned long base)
 165{
 166        long ret;
 167        unsigned long time = get_timer_masked();
 168        unsigned long hz = TIMER_HZ;
 169
 170        ret = time / hz - base;
 171        return ret;
 172}
 173
 174void set_timer(unsigned long t)
 175{
 176        timestamp = (unsigned long)t;
 177}
 178
 179void reset_timer_masked(void)
 180{
 181        void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
 182        int ch = CONFIG_TIMER_SYS_TICK_CH;
 183
 184        /* reset time */
 185        /* capure current decrementer value time */
 186        lastdec = timer_read(base, ch);
 187        /* start "advancing" time stamp from 0 */
 188        timestamp = 0;
 189}
 190
 191unsigned long get_timer_masked(void)
 192{
 193        void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
 194        int ch = CONFIG_TIMER_SYS_TICK_CH;
 195
 196        unsigned long now = timer_read(base, ch); /* current tick value */
 197
 198        if (now >= lastdec) {                     /* normal mode (non roll) */
 199                /* move stamp fordward with absolute diff ticks */
 200                timestamp += now - lastdec;
 201        } else {
 202                /* we have overflow of the count down timer */
 203                /* nts = ts + ld + (TLV - now)
 204                 * ts=old stamp, ld=time that passed before passing through -1
 205                 * (TLV-now) amount of time after passing though -1
 206                 * nts = new "advancing time stamp"...
 207                 * it could also roll and cause problems.
 208                 */
 209                timestamp += now + TIMER_COUNT - lastdec;
 210        }
 211        /* save last */
 212        lastdec = now;
 213
 214        debug("now=%lu, last=%lu, timestamp=%lu\n", now, lastdec, timestamp);
 215        return (unsigned long)timestamp;
 216}
 217
 218void __udelay(unsigned long usec)
 219{
 220        unsigned long tmo, tmp;
 221
 222        debug("+udelay=%ld\n", usec);
 223
 224        if (!timerinit)
 225                timer_init();
 226
 227        /* if "big" number, spread normalization to seconds */
 228        if (usec >= 1000) {
 229                /* start to normalize for usec to ticks per sec */
 230                tmo  = usec / 1000;
 231                /* find number of "ticks" to wait to achieve target */
 232                tmo *= TIMER_FREQ;
 233                /* finish normalize. */
 234                tmo /= 1000;
 235        /* else small number, don't kill it prior to HZ multiply */
 236        } else {
 237                tmo = usec * TIMER_FREQ;
 238                tmo /= (1000 * 1000);
 239        }
 240
 241        tmp = get_timer_masked();       /* get current timestamp */
 242        debug("A. tmo=%ld, tmp=%ld\n", tmo, tmp);
 243
 244        /* if setting this fordward will roll time stamp */
 245        if (tmp > (tmo + tmp + 1))
 246                /* reset "advancing" timestamp to 0, set lastdec value */
 247                reset_timer_masked();
 248        else
 249                /* set advancing stamp wake up time */
 250                tmo += tmp;
 251
 252        debug("B. tmo=%ld, tmp=%ld\n", tmo, tmp);
 253
 254        /* loop till event */
 255        do {
 256                tmp = get_timer_masked();
 257        } while (tmo > tmp);
 258        debug("-udelay=%ld\n", usec);
 259}
 260
 261void udelay_masked(unsigned long usec)
 262{
 263        unsigned long tmo, endtime;
 264        signed long diff;
 265
 266        /* if "big" number, spread normalization to seconds */
 267        if (usec >= 1000) {
 268                /* start to normalize for usec to ticks per sec */
 269                tmo = usec / 1000;
 270                /* find number of "ticks" to wait to achieve target */
 271                tmo *= TIMER_FREQ;
 272                /* finish normalize. */
 273                tmo /= 1000;
 274        } else { /* else small number, don't kill it prior to HZ multiply */
 275                tmo = usec * TIMER_FREQ;
 276                tmo /= (1000 * 1000);
 277        }
 278
 279        endtime = get_timer_masked() + tmo;
 280
 281        do {
 282                unsigned long now = get_timer_masked();
 283
 284                diff = endtime - now;
 285        } while (diff >= 0);
 286}
 287
 288unsigned long long get_ticks(void)
 289{
 290        return get_timer_masked();
 291}
 292
 293#if defined(CONFIG_ARCH_S5P4418)
 294ulong get_tbclk(void)
 295{
 296        ulong  tbclk = TIMER_FREQ;
 297        return tbclk;
 298}
 299#endif
 300