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