150276Speter// SPDX-License-Identifier: GPL-2.0+ 2262629Sdelphij/* 350276Speter * (C) Copyright 2016 Nexell 450276Speter * Hyunseok, Jung <hsjung@nexell.co.kr> 550276Speter */ 650276Speter 750276Speter#include <common.h> 850276Speter#include <log.h> 950276Speter 1050276Speter#include <asm/io.h> 1150276Speter#include <asm/arch/nexell.h> 1250276Speter#include <asm/arch/clk.h> 1350276Speter#if defined(CONFIG_ARCH_S5P4418) 1450276Speter#include <asm/arch/reset.h> 1550276Speter#endif 1650276Speter 1750276Speter#if (CONFIG_TIMER_SYS_TICK_CH > 3) 1850276Speter#error Not support timer channel. Please use "0~3" channels. 1950276Speter#endif 2050276Speter 2150276Speter/* global variables to save timer count 2250276Speter * 2350276Speter * Section ".data" must be used because BSS is not available before relocation, 2450276Speter * in board_init_f(), respectively! I.e. global variables can not be used! 2550276Speter */ 2650276Speterstatic unsigned long timestamp __section(".data"); 2750276Speterstatic unsigned long lastdec __section(".data"); 2850276Speterstatic int timerinit __section(".data"); 2950276Speter 3050276Speter/* macro to hw timer tick config */ 3150276Speterstatic long TIMER_FREQ = 1000000; 3250276Speterstatic long TIMER_HZ = 1000000 / CONFIG_SYS_HZ; 3350276Speterstatic long TIMER_COUNT = 0xFFFFFFFF; 3450276Speter 3550276Speter#define REG_TCFG0 (0x00) 3650276Speter#define REG_TCFG1 (0x04) 3750276Speter#define REG_TCON (0x08) 3850276Speter#define REG_TCNTB0 (0x0C) 3950276Speter#define REG_TCMPB0 (0x10) 4050276Speter#define REG_TCNT0 (0x14) 4150276Speter#define REG_CSTAT (0x44) 42262629Sdelphij 4350276Speter#define TCON_BIT_AUTO (1 << 3) 4476726Speter#define TCON_BIT_INVT (1 << 2) 4576726Speter#define TCON_BIT_UP (1 << 1) 4650276Speter#define TCON_BIT_RUN (1 << 0) 4750276Speter#define TCFG0_BIT_CH(ch) ((ch) == 0 || (ch) == 1 ? 0 : 8) 4876726Speter#define TCFG1_BIT_CH(ch) ((ch) * 4) 4976726Speter#define TCON_BIT_CH(ch) ((ch) ? (ch) * 4 + 4 : 0) 5076726Speter#define TINT_CH(ch) (ch) 5176726Speter#define TINT_CSTAT_BIT_CH(ch) ((ch) + 5) 5250276Speter#define TINT_CSTAT_MASK (0x1F) 5350276Speter#define TIMER_TCNT_OFFS (0xC) 5476726Speter 5576726Spetervoid reset_timer_masked(void); 5650276Speterunsigned long get_timer_masked(void); 5750276Speter 5876726Speter/* 5976726Speter * Timer HW 60262629Sdelphij */ 6150276Speterstatic inline void timer_clock(void __iomem *base, int ch, int mux, int scl) 62262629Sdelphij{ 6350276Speter u32 val = readl(base + REG_TCFG0) & ~(0xFF << TCFG0_BIT_CH(ch)); 64262629Sdelphij 65262629Sdelphij writel(val | ((scl - 1) << TCFG0_BIT_CH(ch)), base + REG_TCFG0); 66262629Sdelphij val = readl(base + REG_TCFG1) & ~(0xF << TCFG1_BIT_CH(ch)); 67262629Sdelphij writel(val | (mux << TCFG1_BIT_CH(ch)), base + REG_TCFG1); 68262629Sdelphij} 69262629Sdelphij 70262629Sdelphijstatic inline void timer_count(void __iomem *base, int ch, unsigned int cnt) 71262629Sdelphij{ 72262629Sdelphij writel((cnt - 1), base + REG_TCNTB0 + (TIMER_TCNT_OFFS * ch)); 73262629Sdelphij writel((cnt - 1), base + REG_TCMPB0 + (TIMER_TCNT_OFFS * ch)); 74262629Sdelphij} 75262629Sdelphij 76262629Sdelphijstatic inline void timer_start(void __iomem *base, int ch) 7750276Speter{ 7850276Speter int on = 0; 7976726Speter u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch); 8076726Speter 8150276Speter writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch), 8250276Speter base + REG_CSTAT); 83262629Sdelphij val = readl(base + REG_TCON) & ~(0xE << TCON_BIT_CH(ch)); 8450276Speter writel(val | (TCON_BIT_UP << TCON_BIT_CH(ch)), base + REG_TCON); 8576726Speter 8676726Speter val &= ~(TCON_BIT_UP << TCON_BIT_CH(ch)); 8776726Speter val |= ((TCON_BIT_AUTO | TCON_BIT_RUN) << TCON_BIT_CH(ch)); 8876726Speter writel(val, base + REG_TCON); 8976726Speter dmb(); 9050276Speter} 9150276Speter 9276726Speterstatic inline void timer_stop(void __iomem *base, int ch) 9376726Speter{ 9450276Speter int on = 0; 9550276Speter u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch); 9650276Speter 9776726Speter writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch), 9850276Speter base + REG_CSTAT); 99262629Sdelphij val = readl(base + REG_TCON) & ~(TCON_BIT_RUN << TCON_BIT_CH(ch)); 10097049Speter writel(val, base + REG_TCON); 10176726Speter} 10276726Speter 10376726Speterstatic inline unsigned long timer_read(void __iomem *base, int ch) 10450276Speter{ 10576726Speter unsigned long ret; 10676726Speter 10750276Speter ret = TIMER_COUNT - readl(base + REG_TCNT0 + (TIMER_TCNT_OFFS * ch)); 10876726Speter return ret; 10976726Speter} 11076726Speter 11176726Speterint timer_init(void) 11276726Speter{ 11376726Speter struct clk *clk = NULL; 11476726Speter char name[16] = "pclk"; 11550276Speter int ch = CONFIG_TIMER_SYS_TICK_CH; 11676726Speter unsigned long rate, tclk = 0; 11776726Speter unsigned long mout, thz, cmp = -1UL; 11876726Speter int tcnt, tscl = 0, tmux = 0; 11976726Speter int mux = 0, scl = 0; 12097049Speter void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER; 12197049Speter 12250276Speter if (timerinit) 12350276Speter return 0; 12476726Speter 12576726Speter /* get with PCLK */ 12650276Speter clk = clk_get(name); 12750276Speter rate = clk_get_rate(clk); 12850276Speter for (mux = 0; mux < 5; mux++) { 129262629Sdelphij mout = rate / (1 << mux), scl = mout / TIMER_FREQ, 13097049Speter thz = mout / scl; 13176726Speter if (!(mout % TIMER_FREQ) && 256 > scl) { 13276726Speter tclk = thz, tmux = mux, tscl = scl; 13376726Speter break; 13450276Speter } 13576726Speter if (scl > 256) 13676726Speter continue; 13776726Speter if (abs(thz - TIMER_FREQ) >= cmp) 13850276Speter continue; 13976726Speter tclk = thz, tmux = mux, tscl = scl; 14076726Speter cmp = abs(thz - TIMER_FREQ); 14176726Speter } 14276726Speter tcnt = tclk; /* Timer Count := 1 Mhz counting */ 14350276Speter 14476726Speter TIMER_FREQ = tcnt; /* Timer Count := 1 Mhz counting */ 14576726Speter TIMER_HZ = TIMER_FREQ / CONFIG_SYS_HZ; 14676726Speter tcnt = TIMER_COUNT == 0xFFFFFFFF ? TIMER_COUNT + 1 : tcnt; 14776726Speter 14876726Speter timer_stop(base, ch); 14976726Speter timer_clock(base, ch, tmux, tscl); 150174993Srafan timer_count(base, ch, tcnt); 15176726Speter timer_start(base, ch); 15276726Speter 15376726Speter reset_timer_masked(); 15476726Speter timerinit = 1; 15576726Speter 15650276Speter return 0; 15750276Speter} 15850276Speter 15997049Spetervoid reset_timer(void) 16050276Speter{ 16150276Speter reset_timer_masked(); 16276726Speter} 16376726Speter 16450276Speterunsigned long get_timer(unsigned long base) 16550276Speter{ 16676726Speter long ret; 16797049Speter unsigned long time = get_timer_masked(); 168262629Sdelphij unsigned long hz = TIMER_HZ; 16976726Speter 17076726Speter ret = time / hz - base; 17176726Speter return ret; 17297049Speter} 17350276Speter 17450276Spetervoid set_timer(unsigned long t) 17576726Speter{ 17676726Speter timestamp = (unsigned long)t; 17750276Speter} 17850276Speter 179166124Srafanvoid reset_timer_masked(void) 18076726Speter{ 18176726Speter void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER; 18250276Speter int ch = CONFIG_TIMER_SYS_TICK_CH; 183262629Sdelphij 18450276Speter /* reset time */ 185166124Srafan /* capure current decrementer value time */ 186262629Sdelphij lastdec = timer_read(base, ch); 187262629Sdelphij /* start "advancing" time stamp from 0 */ 188262629Sdelphij timestamp = 0; 189184989Srafan} 190166124Srafan 191262629Sdelphijunsigned long get_timer_masked(void) 192262629Sdelphij{ 193262629Sdelphij void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER; 194166124Srafan int ch = CONFIG_TIMER_SYS_TICK_CH; 195262629Sdelphij 196262629Sdelphij unsigned long now = timer_read(base, ch); /* current tick value */ 197262629Sdelphij 198262629Sdelphij if (now >= lastdec) { /* normal mode (non roll) */ 199262629Sdelphij /* move stamp fordward with absolute diff ticks */ 200166124Srafan timestamp += now - lastdec; 20150276Speter } else { 202166124Srafan /* we have overflow of the count down timer */ 20350276Speter /* nts = ts + ld + (TLV - now) 204166124Srafan * ts=old stamp, ld=time that passed before passing through -1 205166124Srafan * (TLV-now) amount of time after passing though -1 206166124Srafan * nts = new "advancing time stamp"... 207166124Srafan * it could also roll and cause problems. 208166124Srafan */ 209166124Srafan timestamp += now + TIMER_COUNT - lastdec; 210166124Srafan } 21150276Speter /* save last */ 212166124Srafan lastdec = now; 213166124Srafan 214166124Srafan debug("now=%lu, last=%lu, timestamp=%lu\n", now, lastdec, timestamp); 215166124Srafan return (unsigned long)timestamp; 21650276Speter} 217166124Srafan 218166124Srafanvoid __udelay(unsigned long usec) 21950276Speter{ 220166124Srafan unsigned long tmo, tmp; 221166124Srafan 222166124Srafan debug("+udelay=%ld\n", usec); 223166124Srafan 224166124Srafan if (!timerinit) 225166124Srafan timer_init(); 226166124Srafan 227166124Srafan /* if "big" number, spread normalization to seconds */ 228166124Srafan if (usec >= 1000) { 229166124Srafan /* start to normalize for usec to ticks per sec */ 23050276Speter tmo = usec / 1000; 231166124Srafan /* find number of "ticks" to wait to achieve target */ 232166124Srafan tmo *= TIMER_FREQ; 233166124Srafan /* finish normalize. */ 234166124Srafan tmo /= 1000; 235166124Srafan /* else small number, don't kill it prior to HZ multiply */ 236166124Srafan } else { 237166124Srafan tmo = usec * TIMER_FREQ; 238166124Srafan tmo /= (1000 * 1000); 239166124Srafan } 240166124Srafan 241166124Srafan tmp = get_timer_masked(); /* get current timestamp */ 242262629Sdelphij debug("A. tmo=%ld, tmp=%ld\n", tmo, tmp); 243166124Srafan 244166124Srafan /* if setting this fordward will roll time stamp */ 245166124Srafan if (tmp > (tmo + tmp + 1)) 246166124Srafan /* reset "advancing" timestamp to 0, set lastdec value */ 247166124Srafan reset_timer_masked(); 248166124Srafan else 249184989Srafan /* set advancing stamp wake up time */ 25076726Speter tmo += tmp; 25176726Speter 25250276Speter 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