19055c7d1SRyan Harkin /* 2*f2976bddSMax Shvetsov * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. 39055c7d1SRyan Harkin * 482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 59055c7d1SRyan Harkin */ 69055c7d1SRyan Harkin 79055c7d1SRyan Harkin #include <assert.h> 809d40e0eSAntonio Nino Diaz 99055c7d1SRyan Harkin #include <platform_def.h> 1009d40e0eSAntonio Nino Diaz 1109d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h> 1209d40e0eSAntonio Nino Diaz #include <lib/utils_def.h> 139055c7d1SRyan Harkin 149055c7d1SRyan Harkin /*********************************************************** 159055c7d1SRyan Harkin * The delay timer implementation 169055c7d1SRyan Harkin ***********************************************************/ 179fb8af33SRoberto Vargas static const timer_ops_t *timer_ops; 189055c7d1SRyan Harkin 199055c7d1SRyan Harkin /*********************************************************** 209055c7d1SRyan Harkin * Delay for the given number of microseconds. The driver must 219055c7d1SRyan Harkin * be initialized before calling this function. 229055c7d1SRyan Harkin ***********************************************************/ 239055c7d1SRyan Harkin void udelay(uint32_t usec) 249055c7d1SRyan Harkin { 25d47509d6SSathees Balya assert((timer_ops != NULL) && 26d47509d6SSathees Balya (timer_ops->clk_mult != 0U) && 27d47509d6SSathees Balya (timer_ops->clk_div != 0U) && 289fb8af33SRoberto Vargas (timer_ops->get_timer_value != NULL)); 299055c7d1SRyan Harkin 30*f2976bddSMax Shvetsov uint32_t start, delta; 31*f2976bddSMax Shvetsov uint64_t total_delta; 329055c7d1SRyan Harkin 33*f2976bddSMax Shvetsov assert(usec < (UINT64_MAX / timer_ops->clk_div)); 340bcedb22SAntonio Nino Diaz 359fb8af33SRoberto Vargas start = timer_ops->get_timer_value(); 360bcedb22SAntonio Nino Diaz 37e2aec918SJulius Werner /* Add an extra tick to avoid delaying less than requested. */ 389fb8af33SRoberto Vargas total_delta = 39*f2976bddSMax Shvetsov div_round_up((uint64_t)usec * timer_ops->clk_div, 40d47509d6SSathees Balya timer_ops->clk_mult) + 1U; 41*f2976bddSMax Shvetsov /* 42*f2976bddSMax Shvetsov * Precaution for the total_delta ~ UINT32_MAX and the fact that we 43*f2976bddSMax Shvetsov * cannot catch every tick of the timer. 44*f2976bddSMax Shvetsov * For example 100MHz timer over 25MHz APB will miss at least 4 ticks. 45*f2976bddSMax Shvetsov * 1000U is an arbitrary big number which is believed to be sufficient. 46*f2976bddSMax Shvetsov */ 47*f2976bddSMax Shvetsov assert(total_delta < (UINT32_MAX - 1000U)); 480bcedb22SAntonio Nino Diaz 499055c7d1SRyan Harkin do { 500bcedb22SAntonio Nino Diaz /* 510bcedb22SAntonio Nino Diaz * If the timer value wraps around, the subtraction will 520bcedb22SAntonio Nino Diaz * overflow and it will still give the correct result. 53*f2976bddSMax Shvetsov * delta is decreasing counter 540bcedb22SAntonio Nino Diaz */ 55*f2976bddSMax Shvetsov delta = start - timer_ops->get_timer_value(); 560bcedb22SAntonio Nino Diaz 570bcedb22SAntonio Nino Diaz } while (delta < total_delta); 589055c7d1SRyan Harkin } 599055c7d1SRyan Harkin 609055c7d1SRyan Harkin /*********************************************************** 619055c7d1SRyan Harkin * Delay for the given number of milliseconds. The driver must 629055c7d1SRyan Harkin * be initialized before calling this function. 639055c7d1SRyan Harkin ***********************************************************/ 649055c7d1SRyan Harkin void mdelay(uint32_t msec) 659055c7d1SRyan Harkin { 66*f2976bddSMax Shvetsov assert((msec * 1000UL) < UINT32_MAX); 67d47509d6SSathees Balya udelay(msec * 1000U); 689055c7d1SRyan Harkin } 699055c7d1SRyan Harkin 709055c7d1SRyan Harkin /*********************************************************** 719055c7d1SRyan Harkin * Initialize the timer. The fields in the provided timer 729055c7d1SRyan Harkin * ops pointer must be valid. 739055c7d1SRyan Harkin ***********************************************************/ 749055c7d1SRyan Harkin void timer_init(const timer_ops_t *ops_ptr) 759055c7d1SRyan Harkin { 76d47509d6SSathees Balya assert((ops_ptr != NULL) && 77d47509d6SSathees Balya (ops_ptr->clk_mult != 0U) && 78d47509d6SSathees Balya (ops_ptr->clk_div != 0U) && 791496b489SEtienne Carriere (ops_ptr->get_timer_value != NULL)); 809055c7d1SRyan Harkin 819fb8af33SRoberto Vargas timer_ops = ops_ptr; 829055c7d1SRyan Harkin } 83