"Das U-Boot" Source Tree
at master 253 lines 5.7 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2018 4 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc 5 */ 6 7#include <config.h> 8#include <clk.h> 9#include <dm.h> 10#include <irq_func.h> 11#include <log.h> 12#include <status_led.h> 13#include <sysinfo.h> 14#include <time.h> 15#include <timer.h> 16#include <watchdog.h> 17#include <asm/global_data.h> 18#include <asm/ptrace.h> 19#include <linux/bitops.h> 20 21DECLARE_GLOBAL_DATA_PTR; 22 23#ifndef CFG_SYS_WATCHDOG_FREQ 24#define CFG_SYS_WATCHDOG_FREQ (CONFIG_SYS_HZ / 2) 25#endif 26 27/** 28 * struct mpc83xx_timer_priv - Private data structure for MPC83xx timer driver 29 * @decrementer_count: Value to which the decrementer register should be re-set 30 * to when a timer interrupt occurs, thus determines the 31 * interrupt frequency (value for 1e6/HZ microseconds) 32 * @timestamp: Counter for the number of timer interrupts that have 33 * occurred (i.e. can be used to trigger events 34 * periodically in the timer interrupt) 35 */ 36struct mpc83xx_timer_priv { 37 uint decrementer_count; 38 ulong timestamp; 39}; 40 41/* 42 * Bitmask for enabling the time base in the SPCR (System Priority 43 * Configuration Register) 44 */ 45static const u32 SPCR_TBEN_MASK = BIT(31 - 9); 46 47/** 48 * get_dec() - Get the value of the decrementer register 49 * 50 * Return: The value of the decrementer register 51 */ 52static inline unsigned long get_dec(void) 53{ 54 unsigned long val; 55 56 asm volatile ("mfdec %0" : "=r" (val) : ); 57 58 return val; 59} 60 61/** 62 * set_dec() - Set the value of the decrementer register 63 * @val: The value of the decrementer register to be set 64 */ 65static inline void set_dec(unsigned long val) 66{ 67 if (val) 68 asm volatile ("mtdec %0"::"r" (val)); 69} 70 71/** 72 * mftbu() - Get value of TBU (upper time base) register 73 * 74 * Return: Value of the TBU register 75 */ 76static inline u32 mftbu(void) 77{ 78 u32 rval; 79 80 asm volatile("mftbu %0" : "=r" (rval)); 81 return rval; 82} 83 84/** 85 * mftb() - Get value of TBL (lower time base) register 86 * 87 * Return: Value of the TBL register 88 */ 89static inline u32 mftb(void) 90{ 91 u32 rval; 92 93 asm volatile("mftb %0" : "=r" (rval)); 94 return rval; 95} 96 97/* 98 * TODO(mario.six@gdsys.cc): This should really be done by timer_init, and the 99 * interrupt init should go into a interrupt driver. 100 */ 101int interrupt_init(void) 102{ 103 immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; 104 struct udevice *csb; 105 struct udevice *sysinfo; 106 struct udevice *timer; 107 struct mpc83xx_timer_priv *timer_priv; 108 struct clk clock; 109 int ret; 110 111 ret = uclass_first_device_err(UCLASS_TIMER, &timer); 112 if (ret) { 113 debug("%s: Could not find timer device (error: %d)", 114 __func__, ret); 115 return ret; 116 } 117 118 timer_priv = dev_get_priv(timer); 119 120 if (sysinfo_get(&sysinfo)) { 121 debug("%s: sysinfo device could not be fetched.\n", __func__); 122 return -ENOENT; 123 } 124 125 ret = uclass_get_device_by_phandle(UCLASS_SIMPLE_BUS, sysinfo, 126 "csb", &csb); 127 if (ret) { 128 debug("%s: Could not retrieve CSB device (error: %d)", 129 __func__, ret); 130 return ret; 131 } 132 133 ret = clk_get_by_index(csb, 0, &clock); 134 if (ret) { 135 debug("%s: Could not retrieve clock (error: %d)", 136 __func__, ret); 137 return ret; 138 } 139 140 timer_priv->decrementer_count = (clk_get_rate(&clock) / 4) 141 / CONFIG_SYS_HZ; 142 /* Enable e300 time base */ 143 setbits_be32(&immr->sysconf.spcr, SPCR_TBEN_MASK); 144 145 set_dec(timer_priv->decrementer_count); 146 147 /* Switch on interrupts */ 148 set_msr(get_msr() | MSR_EE); 149 150 return 0; 151} 152 153/** 154 * timer_interrupt() - Handler for the timer interrupt 155 * @regs: Array of register values 156 */ 157void timer_interrupt(struct pt_regs *regs) 158{ 159 struct udevice *timer = gd->timer; 160 struct mpc83xx_timer_priv *priv; 161 162 /* 163 * During initialization, gd->timer might not be set yet, but the timer 164 * interrupt may already be enabled. In this case, wait for the 165 * initialization to complete 166 */ 167 if (!timer) 168 return; 169 170 priv = dev_get_priv(timer); 171 172 /* Restore Decrementer Count */ 173 set_dec(priv->decrementer_count); 174 175 priv->timestamp++; 176 177#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG) 178 if (CFG_SYS_WATCHDOG_FREQ && (priv->timestamp % (CFG_SYS_WATCHDOG_FREQ)) == 0) 179 schedule(); 180#endif /* CONFIG_WATCHDOG || CONFIG_HW_WATCHDOG */ 181 182#ifdef CONFIG_LED_STATUS 183 status_led_tick(priv->timestamp); 184#endif /* CONFIG_LED_STATUS */ 185} 186 187void wait_ticks(ulong ticks) 188{ 189 ulong end = get_ticks() + ticks; 190 191 while (end > get_ticks()) 192 schedule(); 193} 194 195static u64 mpc83xx_timer_get_count(struct udevice *dev) 196{ 197 u32 tbu, tbl; 198 199 /* 200 * To make sure that no tbl overflow occurred between reading tbl and 201 * tbu, read tbu again, and compare it with the previously read tbu 202 * value: If they're different, a tbl overflow has occurred. 203 */ 204 do { 205 tbu = mftbu(); 206 tbl = mftb(); 207 } while (tbu != mftbu()); 208 209 return (uint64_t)tbu << 32 | tbl; 210} 211 212static int mpc83xx_timer_probe(struct udevice *dev) 213{ 214 struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); 215 struct clk clock; 216 int ret; 217 218 ret = interrupt_init(); 219 if (ret) { 220 debug("%s: interrupt_init failed (err = %d)\n", 221 dev->name, ret); 222 return ret; 223 } 224 225 ret = clk_get_by_index(dev, 0, &clock); 226 if (ret) { 227 debug("%s: Could not retrieve clock (err = %d)\n", 228 dev->name, ret); 229 return ret; 230 } 231 232 uc_priv->clock_rate = (clk_get_rate(&clock) + 3L) / 4L; 233 234 return 0; 235} 236 237static const struct timer_ops mpc83xx_timer_ops = { 238 .get_count = mpc83xx_timer_get_count, 239}; 240 241static const struct udevice_id mpc83xx_timer_ids[] = { 242 { .compatible = "fsl,mpc83xx-timer" }, 243 { /* sentinel */ } 244}; 245 246U_BOOT_DRIVER(mpc83xx_timer) = { 247 .name = "mpc83xx_timer", 248 .id = UCLASS_TIMER, 249 .of_match = mpc83xx_timer_ids, 250 .probe = mpc83xx_timer_probe, 251 .ops = &mpc83xx_timer_ops, 252 .priv_auto = sizeof(struct mpc83xx_timer_priv), 253};