1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7#include <config.h> 8#include <types.h> 9#include <machine/io.h> 10#include <kernel/vspace.h> 11#include <arch/machine.h> 12#include <arch/kernel/vspace.h> 13#include <plat/machine.h> 14#include <linker.h> 15#include <plat/machine/devices_gen.h> 16#include <plat/machine/hardware.h> 17 18#define TIOCP_CFG_SOFTRESET BIT(0) 19 20#define TIER_MATCH_ENABLE BIT(0) 21#define TIER_OVERFLOW_ENABLE BIT(1) 22#define TIER_COMPARE_ENABLE BIT(2) 23 24#define TCLR_STARTTIMER BIT(0) 25#define TCLR_AUTORELOAD BIT(1) 26#define TCLR_PRESCALE_ENABLE BIT(5) 27#define TCLR_COMPAREENABLE BIT(6) 28 29timer_t *timer = (timer_t *) TIMER_PPTR; 30 31#define WDT_REG(base, off) ((volatile uint32_t *)((base) + (off))) 32#define WDT_REG_WWPS 0x34 33#define WDT_REG_WSPR 0x48 34#define WDT_WWPS_PEND_WSPR BIT(4) 35 36#define SET_REGISTER(reg, mask) \ 37 do { \ 38 reg |= mask; \ 39 } while((reg & mask) != mask); 40 41static BOOT_CODE void disableWatchdog(void) 42{ 43 uint32_t wdt = WDT1_PPTR; 44 45 // am335x ref man, sec 20.4.3.8 46 *WDT_REG(wdt, WDT_REG_WSPR) = 0xaaaa; 47 while ((*WDT_REG(wdt, WDT_REG_WWPS) & WDT_WWPS_PEND_WSPR)) { 48 continue; 49 } 50 *WDT_REG(wdt, WDT_REG_WSPR) = 0x5555; 51 while ((*WDT_REG(wdt, WDT_REG_WWPS) & WDT_WWPS_PEND_WSPR)) { 52 continue; 53 } 54} 55 56/* 57 * Enable DMTIMER clocks, otherwise their registers wont be accessible. 58 * This could be moved out of kernel. 59 */ 60static BOOT_CODE void enableTimers(void) 61{ 62 uint32_t cmper = CMPER_PPTR; 63 64 /* XXX repeat this for DMTIMER4..7 */ 65 /* select clock - Timer 3 */ 66 *CMPER_REG(cmper, CMPER_CLKSEL_TIMER3) = CMPER_CKLSEL_MOSC; 67 while ((*CMPER_REG(cmper, CMPER_CLKSEL_TIMER3) & RESERVED) != CMPER_CKLSEL_MOSC) { 68 continue; 69 } 70 71 /* enable clock */ 72 *CMPER_REG(cmper, CMPER_TIMER3_CLKCTRL) = CMPER_CLKCTRL_ENABLE; 73 while ((*CMPER_REG(cmper, CMPER_TIMER3_CLKCTRL) & RESERVED) != CMPER_CLKCTRL_ENABLE) { 74 continue; 75 } 76 77 /* select clock - Timer 4 */ 78 *CMPER_REG(cmper, CMPER_CLKSEL_TIMER4) = CMPER_CKLSEL_MOSC; 79 while ((*CMPER_REG(cmper, CMPER_CLKSEL_TIMER4) & RESERVED) != CMPER_CKLSEL_MOSC) { 80 continue; 81 } 82 83 /* enable clock */ 84 *CMPER_REG(cmper, CMPER_TIMER4_CLKCTRL) = CMPER_CLKCTRL_ENABLE; 85 while ((*CMPER_REG(cmper, CMPER_TIMER4_CLKCTRL) & RESERVED) != CMPER_CLKCTRL_ENABLE) { 86 continue; 87 } 88} 89#ifdef CONFIG_KERNEL_MCS 90 91/* The idea here is to use the functionality of overflow interrupts to calculate time 92 and use match interrupts for setting a deadline. */ 93 94uint32_t high_bits = 0; 95BOOT_CODE void initTimer(void) 96{ 97 int timeout; 98 disableWatchdog(); 99 enableTimers(); 100 101 /* Configure dmtimer0 as kernel timer */ 102 SET_REGISTER(timer->cfg, TIOCP_CFG_SOFTRESET); 103 104 /* disable */ 105 SET_REGISTER(timer->tclr, 0u); 106 107 /* wait for reset */ 108 for (timeout = 10000; (timer->cfg & TIOCP_CFG_SOFTRESET) && timeout > 0; timeout--); 109 110 if (!timeout) { 111 printf("init timer failed\n"); 112 return; 113 } 114 115 maskInterrupt(/*disable*/ true, KERNEL_TIMER_IRQ); 116 117 /* Set the reload value */ 118 SET_REGISTER(timer->tldr, 0u); 119 120 /* Enables interrupt on overflow and match */ 121 SET_REGISTER(timer->tier, (TIER_OVERFLOW_ENABLE | TIER_MATCH_ENABLE)); 122 123 /* Clear the read register */ 124 SET_REGISTER(timer->tcrr, 0u); 125 126 /* start the timer */ 127 SET_REGISTER(timer->tclr, (TCLR_AUTORELOAD | TCLR_STARTTIMER | TCLR_COMPAREENABLE)); 128} 129 130#else /* CONFIG_KERNEL_MCS */ 131 132 133/* Configure dmtimer0 as kernel preemption timer */ 134BOOT_CODE void initTimer(void) 135{ 136 int timeout; 137 138 disableWatchdog(); 139 enableTimers(); 140 141 timer->cfg = TIOCP_CFG_SOFTRESET; 142 143 for (timeout = 10000; (timer->cfg & TIOCP_CFG_SOFTRESET) && timeout > 0; timeout--) 144 ; 145 if (!timeout) { 146 printf("init timer failed\n"); 147 return; 148 } 149 150 maskInterrupt(/*disable*/ true, KERNEL_TIMER_IRQ); 151 152 /* Set the reload value */ 153 timer->tldr = 0xFFFFFFFFUL - TIMER_RELOAD; 154 155 /* Enables interrupt on overflow */ 156 timer->tier = TIER_OVERFLOW_ENABLE; 157 158 /* Clear the read register */ 159 timer->tcrr = 0xFFFFFFFFUL - TIMER_RELOAD; 160 161 /* Set autoreload and start the timer */ 162 timer->tclr = TCLR_AUTORELOAD | TCLR_STARTTIMER; 163} 164#endif /* CONFIG_KERNEL_MCS */ 165