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 19 DECLARE_GLOBAL_DATA_PTR; 20 21 unsigned long get_current_tick(void); 22 static void reset_timer_masked(void); 23 24 /* macro to read the 16 bit timer */ 25 static 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 */ 38 static 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 45 int 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 */ 65 unsigned 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 85 unsigned 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 */ 101 void __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 110 static 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 */ 123 unsigned 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 */ 132 unsigned long get_tbclk(void) 133 { 134 return CONFIG_SYS_HZ; 135 } 136