1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2009 Samsung Electronics 4 * Heungjun Kim <riverful.kim@samsung.com> 5 * Inki Dae <inki.dae@samsung.com> 6 * Minkyu Kang <mk7.kang@samsung.com> 7 */ 8 9#include <common.h> 10#include <div64.h> 11#include <asm/io.h> 12#include <asm/arch/pwm.h> 13#include <asm/arch/clk.h> 14 15/* Use the old PWM interface for now */ 16#undef CONFIG_DM_PWM 17#include <pwm.h> 18 19DECLARE_GLOBAL_DATA_PTR; 20 21unsigned long get_current_tick(void); 22static void reset_timer_masked(void); 23 24/* macro to read the 16 bit timer */ 25static inline struct s5p_timer *s5p_get_base_timer(void) 26{ 27 return (struct s5p_timer *)samsung_get_base_timer(); 28} 29 30/** 31 * Read the countdown timer. 32 * 33 * This operates at 1MHz and counts downwards. It will wrap about every 34 * hour (2^32 microseconds). 35 * 36 * @return current value of timer 37 */ 38static unsigned long timer_get_us_down(void) 39{ 40 struct s5p_timer *const timer = s5p_get_base_timer(); 41 42 return readl(&timer->tcnto4); 43} 44 45int timer_init(void) 46{ 47 /* PWM Timer 4 */ 48 pwm_init(4, MUX_DIV_4, 0); 49 pwm_config(4, 100000, 100000); 50 pwm_enable(4); 51 52 /* Use this as the current monotonic time in us */ 53 gd->arch.timer_reset_value = 0; 54 55 /* Use this as the last timer value we saw */ 56 gd->arch.lastinc = timer_get_us_down(); 57 reset_timer_masked(); 58 59 return 0; 60} 61 62/* 63 * timer without interrupts 64 */ 65unsigned long get_timer(unsigned long base) 66{ 67 unsigned long long time_ms; 68 69 ulong now = timer_get_us_down(); 70 71 /* 72 * Increment the time by the amount elapsed since the last read. 73 * The timer may have wrapped around, but it makes no difference to 74 * our arithmetic here. 75 */ 76 gd->arch.timer_reset_value += gd->arch.lastinc - now; 77 gd->arch.lastinc = now; 78 79 /* Divide by 1000 to convert from us to ms */ 80 time_ms = gd->arch.timer_reset_value; 81 do_div(time_ms, 1000); 82 return time_ms - base; 83} 84 85unsigned long __attribute__((no_instrument_function)) timer_get_us(void) 86{ 87 static unsigned long base_time_us; 88 89 struct s5p_timer *const timer = 90 (struct s5p_timer *)samsung_get_base_timer(); 91 unsigned long now_downward_us = readl(&timer->tcnto4); 92 93 if (!base_time_us) 94 base_time_us = now_downward_us; 95 96 /* Note that this timer counts downward. */ 97 return base_time_us - now_downward_us; 98} 99 100/* delay x useconds */ 101void __udelay(unsigned long usec) 102{ 103 unsigned long count_value; 104 105 count_value = timer_get_us_down(); 106 while ((int)(count_value - timer_get_us_down()) < (int)usec) 107 ; 108} 109 110static void reset_timer_masked(void) 111{ 112 struct s5p_timer *const timer = s5p_get_base_timer(); 113 114 /* reset time */ 115 gd->arch.lastinc = readl(&timer->tcnto4); 116 gd->arch.tbl = 0; 117} 118 119/* 120 * This function is derived from PowerPC code (read timebase as long long). 121 * On ARM it just returns the timer value. 122 */ 123unsigned long long get_ticks(void) 124{ 125 return get_timer(0); 126} 127 128/* 129 * This function is derived from PowerPC code (timebase clock frequency). 130 * On ARM it returns the number of timer ticks per second. 131 */ 132unsigned long get_tbclk(void) 133{ 134 return CONFIG_SYS_HZ; 135} 136