1/* 2 * ARM Cortex M3/M4/M7 SysTick timer driver 3 * (C) Copyright 2017 Renesas Electronics Europe Ltd 4 * 5 * Based on arch/arm/mach-stm32/stm32f1/timer.c 6 * (C) Copyright 2015 7 * Kamil Lulko, <kamil.lulko@gmail.com> 8 * 9 * Copyright 2015 ATS Advanced Telematics Systems GmbH 10 * Copyright 2015 Konsulko Group, Matt Porter <mporter@konsulko.com> 11 * 12 * SPDX-License-Identifier: GPL-2.0+ 13 * 14 * The SysTick timer is a 24-bit count down timer. The clock can be either the 15 * CPU clock or a reference clock. Since the timer will wrap around very quickly 16 * when using the CPU clock, and we do not handle the timer interrupts, it is 17 * expected that this driver is only ever used with a slow reference clock. 18 * 19 * The number of reference clock ticks that correspond to 10ms is normally 20 * defined in the SysTick Calibration register's TENMS field. However, on some 21 * devices this is wrong, so this driver allows the clock rate to be defined 22 * using CONFIG_SYS_HZ_CLOCK. 23 */ 24 25#include <common.h> 26#include <asm/io.h> 27 28DECLARE_GLOBAL_DATA_PTR; 29 30/* SysTick Base Address - fixed for all Cortex M3, M4 and M7 devices */ 31#define SYSTICK_BASE 0xE000E010 32 33struct cm3_systick { 34 uint32_t ctrl; 35 uint32_t reload_val; 36 uint32_t current_val; 37 uint32_t calibration; 38}; 39 40#define TIMER_MAX_VAL 0x00FFFFFF 41#define SYSTICK_CTRL_EN BIT(0) 42/* Clock source: 0 = Ref clock, 1 = CPU clock */ 43#define SYSTICK_CTRL_CPU_CLK BIT(2) 44#define SYSTICK_CAL_NOREF BIT(31) 45#define SYSTICK_CAL_SKEW BIT(30) 46#define SYSTICK_CAL_TENMS_MASK 0x00FFFFFF 47 48/* read the 24-bit timer */ 49static ulong read_timer(void) 50{ 51 struct cm3_systick *systick = (struct cm3_systick *)SYSTICK_BASE; 52 53 /* The timer counts down, therefore convert to an incrementing timer */ 54 return TIMER_MAX_VAL - readl(&systick->current_val); 55} 56 57int timer_init(void) 58{ 59 struct cm3_systick *systick = (struct cm3_systick *)SYSTICK_BASE; 60 u32 cal; 61 62 writel(TIMER_MAX_VAL, &systick->reload_val); 63 /* Any write to current_val reg clears it to 0 */ 64 writel(0, &systick->current_val); 65 66 cal = readl(&systick->calibration); 67 if (cal & SYSTICK_CAL_NOREF) 68 /* Use CPU clock, no interrupts */ 69 writel(SYSTICK_CTRL_EN | SYSTICK_CTRL_CPU_CLK, &systick->ctrl); 70 else 71 /* Use external clock, no interrupts */ 72 writel(SYSTICK_CTRL_EN, &systick->ctrl); 73 74 /* 75 * If the TENMS field is inexact or wrong, specify the clock rate using 76 * CONFIG_SYS_HZ_CLOCK. 77 */ 78#if defined(CONFIG_SYS_HZ_CLOCK) 79 gd->arch.timer_rate_hz = CONFIG_SYS_HZ_CLOCK; 80#else 81 gd->arch.timer_rate_hz = (cal & SYSTICK_CAL_TENMS_MASK) * 100; 82#endif 83 84 gd->arch.tbl = 0; 85 gd->arch.tbu = 0; 86 gd->arch.lastinc = read_timer(); 87 88 return 0; 89} 90 91/* return milli-seconds timer value */ 92ulong get_timer(ulong base) 93{ 94 unsigned long long t = get_ticks() * 1000; 95 96 return (ulong)((t / gd->arch.timer_rate_hz)) - base; 97} 98 99unsigned long long get_ticks(void) 100{ 101 u32 now = read_timer(); 102 103 if (now >= gd->arch.lastinc) 104 gd->arch.tbl += (now - gd->arch.lastinc); 105 else 106 gd->arch.tbl += (TIMER_MAX_VAL - gd->arch.lastinc) + now; 107 108 gd->arch.lastinc = now; 109 110 return gd->arch.tbl; 111} 112 113ulong get_tbclk(void) 114{ 115 return gd->arch.timer_rate_hz; 116} 117