blob: 004f9cc4b15c84a5d9b434dc4d5805cb0855b8a1 [file] [log] [blame]
/*
*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of The Linux Foundation nor the names of its
*       contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <asm/io.h>
#include <common.h>
#include <asm/types.h>
#include <watchdog.h>
#include <fdtdec.h>
static unsigned long long timestamp __attribute__((section(".data")));
static unsigned long long lastinc __attribute__((section(".data")));
#define GPT_FREQ_HZ (ipq_timer.gpt_freq_hz)
#define GPT_FREQ_KHZ (GPT_FREQ_HZ / 1000)
#define GPT_FREQ (GPT_FREQ_KHZ / 1000)
#define TIMER_LOAD_VAL (ipq_timer.timer_load_val)
DECLARE_GLOBAL_DATA_PTR;
/* Information about timer */
static struct ipq_timer_platdata {
unsigned int gcnt_base;
unsigned int gcnt_cntcv_lo;
unsigned int gcnt_cntcv_hi;
unsigned int gpt_freq_hz;
unsigned long long timer_load_val;
} ipq_timer __attribute__((section(".data")));
/**
* timer_init - initialize timer
*/
int timer_init(void)
{
int nodeoff;
nodeoff = fdt_path_offset(gd->fdt_blob, "/timer");
if (nodeoff < 0) {
WATCHDOG_RESET();
}
ipq_timer.gcnt_base = fdtdec_get_uint(gd->fdt_blob, nodeoff,
"gcnt_base", -1);
ipq_timer.gcnt_cntcv_lo = fdtdec_get_uint(gd->fdt_blob, nodeoff,
"gcnt_cntcv_lo", -1);
ipq_timer.gcnt_cntcv_hi = fdtdec_get_uint(gd->fdt_blob, nodeoff,
"gcnt_cntcv_hi", -1);
ipq_timer.gpt_freq_hz = fdtdec_get_uint(gd->fdt_blob, nodeoff,
"gpt_freq_hz", -1);
ipq_timer.timer_load_val = fdtdec_get_uint64(gd->fdt_blob, nodeoff,
"timer_load_val", -1);
if (ipq_timer.gcnt_base != -1)
writel(1, ipq_timer.gcnt_base);
return 0;
}
/**
* get_timer - returns time lapsed
* @base: base/start time
*
* Returns time lapsed, since the specified base time value.
*/
ulong get_timer(ulong base)
{
return get_timer_masked() - base;
}
/*
* read_counter - returns 64-bit counter value
* Reads Timer HI and Timer LO value register
*/
static unsigned long long read_counter(void)
{
unsigned long vect_hi1, vect_hi2;
unsigned long vect_low;
if(ipq_timer.gcnt_cntcv_hi != 0){
repeat:
vect_hi1 = readl(ipq_timer.gcnt_cntcv_hi);
vect_low = readl(ipq_timer.gcnt_cntcv_lo);
vect_hi2 = readl(ipq_timer.gcnt_cntcv_hi);
if (vect_hi1 != vect_hi2)
goto repeat;
return ((unsigned long long)vect_hi1 << 32 | vect_low);
}
else{
return (readl(ipq_timer.gcnt_cntcv_lo));
}
}
/* get_ticks - returns the timer coutner */
uint64_t notrace get_ticks(void)
{
return read_counter();
}
/* get_ticks - returns the timer frequency */
unsigned long get_tbclk(void)
{
return GPT_FREQ_HZ;
}
/**
* __udelay - generates micro second delay.
* @usec: delay duration in microseconds
*
* With 32KHz clock, minimum possible delay is 31.25 Micro seconds and
* its multiples. In Rumi GPT clock is 32 KHz
*/
void __udelay(unsigned long usec)
{
unsigned long long val;
unsigned long long now;
unsigned long long last;
unsigned long long runcount;
if (usec == 0)
return;
val = (usec * GPT_FREQ_KHZ) / 1000;
if (val == 0 )
val = 1; /* Wait for atleast 1 tick */
last = read_counter();
do {
now = read_counter();
if (last > now)
runcount = (TIMER_LOAD_VAL - last) + now;
else
runcount = now - last;
} while (runcount < val);
}
static inline unsigned long long gpt_to_sys_freq(unsigned long long gpt)
{
/*
* get_timer() expects the timer increments to be in terms
* of CONFIG_SYS_HZ. Convert GPT timer values to CONFIG_SYS_HZ
* units.
*/
return (gpt) / (GPT_FREQ_HZ / CONFIG_SYS_HZ);
}
/**
* get_timer_masked - returns current ticks
*
* Returns the current timer ticks, since boot.
*/
ulong get_timer_masked(void)
{
unsigned long long now;
unsigned long long counter_val;
counter_val = read_counter();
now = gpt_to_sys_freq(counter_val);
if (lastinc <= now) { /* normal mode (non roll) */
/* normal mode */
timestamp += now - lastinc;
/* move stamp forward with absolute diff ticks */
} else {
/* we have overflow of the count down timer */
timestamp += now + (gpt_to_sys_freq(TIMER_LOAD_VAL) - lastinc);
}
lastinc = now;
return (ulong)timestamp;
}