blob: 3ef56f144e80dbd9eb2c64d394ad38d0649705d4 [file] [log] [blame]
Mario Six2c217492018-08-06 10:23:38 +02001// 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>
Simon Glassc30b7ad2019-11-14 12:57:41 -070011#include <irq_func.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060012#include <log.h>
Simon Glassc3e44302019-11-14 12:57:11 -070013#include <status_led.h>
Simon Glass036a0172019-11-14 12:57:27 -070014#include <time.h>
Mario Six2c217492018-08-06 10:23:38 +020015#include <timer.h>
16#include <watchdog.h>
17
18DECLARE_GLOBAL_DATA_PTR;
19
20/**
21 * struct mpc83xx_timer_priv - Private data structure for MPC83xx timer driver
22 * @decrementer_count: Value to which the decrementer register should be re-set
23 * to when a timer interrupt occurs, thus determines the
24 * interrupt frequency (value for 1e6/HZ microseconds)
25 * @timestamp: Counter for the number of timer interrupts that have
26 * occurred (i.e. can be used to trigger events
27 * periodically in the timer interrupt)
28 */
29struct mpc83xx_timer_priv {
30 uint decrementer_count;
31 ulong timestamp;
32};
33
34/*
35 * Bitmask for enabling the time base in the SPCR (System Priority
36 * Configuration Register)
37 */
38static const u32 SPCR_TBEN_MASK = BIT(31 - 9);
39
40/**
41 * get_dec() - Get the value of the decrementer register
42 *
43 * Return: The value of the decrementer register
44 */
45static inline unsigned long get_dec(void)
46{
47 unsigned long val;
48
49 asm volatile ("mfdec %0" : "=r" (val) : );
50
51 return val;
52}
53
54/**
55 * set_dec() - Set the value of the decrementer register
56 * @val: The value of the decrementer register to be set
57 */
58static inline void set_dec(unsigned long val)
59{
60 if (val)
61 asm volatile ("mtdec %0"::"r" (val));
62}
63
64/**
65 * mftbu() - Get value of TBU (upper time base) register
66 *
67 * Return: Value of the TBU register
68 */
69static inline u32 mftbu(void)
70{
71 u32 rval;
72
73 asm volatile("mftbu %0" : "=r" (rval));
74 return rval;
75}
76
77/**
78 * mftb() - Get value of TBL (lower time base) register
79 *
80 * Return: Value of the TBL register
81 */
82static inline u32 mftb(void)
83{
84 u32 rval;
85
86 asm volatile("mftb %0" : "=r" (rval));
87 return rval;
88}
89
90/*
91 * TODO(mario.six@gdsys.cc): This should really be done by timer_init, and the
92 * interrupt init should go into a interrupt driver.
93 */
94int interrupt_init(void)
95{
96 immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
97 struct udevice *csb;
98 struct udevice *board;
99 struct udevice *timer;
100 struct mpc83xx_timer_priv *timer_priv;
101 struct clk clock;
102 int ret;
103
104 ret = uclass_first_device_err(UCLASS_TIMER, &timer);
105 if (ret) {
106 debug("%s: Could not find timer device (error: %d)",
107 __func__, ret);
108 return ret;
109 }
110
111 timer_priv = dev_get_priv(timer);
112
113 if (board_get(&board)) {
114 debug("%s: board device could not be fetched.\n", __func__);
115 return -ENOENT;
116 }
117
118 ret = uclass_get_device_by_phandle(UCLASS_SIMPLE_BUS, board,
119 "csb", &csb);
120 if (ret) {
121 debug("%s: Could not retrieve CSB device (error: %d)",
122 __func__, ret);
123 return ret;
124 }
125
126 ret = clk_get_by_index(csb, 0, &clock);
127 if (ret) {
128 debug("%s: Could not retrieve clock (error: %d)",
129 __func__, ret);
130 return ret;
131 }
132
133 timer_priv->decrementer_count = (clk_get_rate(&clock) / 4)
134 / CONFIG_SYS_HZ;
135 /* Enable e300 time base */
136 setbits_be32(&immr->sysconf.spcr, SPCR_TBEN_MASK);
137
138 set_dec(timer_priv->decrementer_count);
139
140 /* Switch on interrupts */
141 set_msr(get_msr() | MSR_EE);
142
143 return 0;
144}
145
146/**
147 * timer_interrupt() - Handler for the timer interrupt
148 * @regs: Array of register values
149 */
150void timer_interrupt(struct pt_regs *regs)
151{
152 struct udevice *timer = gd->timer;
153 struct mpc83xx_timer_priv *priv;
154
155 /*
156 * During initialization, gd->timer might not be set yet, but the timer
157 * interrupt may already be enabled. In this case, wait for the
158 * initialization to complete
159 */
160 if (!timer)
161 return;
162
163 priv = dev_get_priv(timer);
164
165 /* Restore Decrementer Count */
166 set_dec(priv->decrementer_count);
167
168 priv->timestamp++;
169
170#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG)
171 if ((timestamp % (CONFIG_SYS_WATCHDOG_FREQ)) == 0)
172 WATCHDOG_RESET();
173#endif /* CONFIG_WATCHDOG || CONFIG_HW_WATCHDOG */
174
175#ifdef CONFIG_LED_STATUS
176 status_led_tick(priv->timestamp);
177#endif /* CONFIG_LED_STATUS */
Mario Six2c217492018-08-06 10:23:38 +0200178}
179
180void wait_ticks(ulong ticks)
181{
182 ulong end = get_ticks() + ticks;
183
184 while (end > get_ticks())
185 WATCHDOG_RESET();
186}
187
188static 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
207static 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
232static const struct timer_ops mpc83xx_timer_ops = {
233 .get_count = mpc83xx_timer_get_count,
234};
235
236static const struct udevice_id mpc83xx_timer_ids[] = {
237 { .compatible = "fsl,mpc83xx-timer" },
238 { /* sentinel */ }
239};
240
241U_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,
Mario Six2c217492018-08-06 10:23:38 +0200247 .priv_auto_alloc_size = sizeof(struct mpc83xx_timer_priv),
248};