1/*
2 * Copyright 2020, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <stdio.h>
14#include <assert.h>
15#include <errno.h>
16#include <stdlib.h>
17#include <string.h>
18#include <utils/util.h>
19#include <inttypes.h>
20#include <utils/fence.h>
21#include <utils/arch/io.h>
22
23#include <platsupport/timer.h>
24#include <platsupport/plat/timer.h>
25
26#define CLEANUP_FAIL_TEXT "Failed to cleanup the TTC after failing to initialise it"
27
28#define CLKCTRL_EXT_NEDGE           BIT(6)
29#define CLKCTRL_EXT_SRC_EN          BIT(5)
30#define CLKCTRL_PRESCALE_VAL(N)     (((N) & 0xf) << 1) /* rate = clk_src/[2^(N+1)] */
31#define CLKCTRL_GET_PRESCALE_VAL(v) (((v) >> 1) & 0xf)
32#define CLKCTRL_PRESCALE_EN         BIT(0)
33#define CLKCTRL_PRESCALE_MASK       (CLKCTRL_PRESCALE_VAL(0xf) | CLKCTRL_PRESCALE_EN)
34
35/* Waveform polarity: When this bit is high, the
36 * waveform output goes from high to low on
37 * Match_1 interrupt and returns high on overflow
38 * or interval interrupt; when low, the waveform
39 * goes from low to high on Match_1 interrupt and
40 * returns low on overflow or interval interrupt */
41#define CNTCTRL_WAVE_POL BIT(6)
42/* Output waveform enable, active low. */
43#define CNTCTRL_WAVE_EN  BIT(5)
44/* Setting this bit high resets the counter value and
45 * restarts counting; the RST bit is automatically
46 * cleared on restart. */
47#define CNTCTRL_RST      BIT(4)
48/* Register Match mode: when Match is set, an
49 * interrupt is generated when the count value
50 * matches one of the three match registers and the
51 * corresponding bit is set in the Interrupt Enable
52 * register.
53 */
54#define CNTCTRL_MATCH    BIT(3)
55/* Register Match mode: when Match is set, an
56 * interrupt is generated when the count value
57 * matches one of the three match registers and the
58 * corresponding bit is set in the Interrupt Enable
59 * register. */
60#define CNTCTRL_DECR     BIT(2)
61/* When this bit is high, the timer is in Interval
62 * Mode, and the counter generates interrupts at
63 * regular intervals; when low, the timer is in
64 * overflow mode. */
65#define CNTCTRL_INT      BIT(1)
66/* Disable counter: when this bit is high, the counter
67 * is stopped, holding its last value until reset,
68 *  restarted or enabled again. */
69#define CNTCTRL_STOP     BIT(0)
70
71/* Event timer overflow interrupt */
72#define INT_EVENT_OVR    BIT(5)
73/* Counter overflow */
74#define INT_CNT_OVR      BIT(4)
75/* Match 3 interrupt */
76#define INT_MATCH2       BIT(3)
77/* Match 2 interrupt */
78#define INT_MATCH1       BIT(2)
79/* Match 1 interrupt */
80#define INT_MATCH0       BIT(1)
81/* Interval interrupt */
82#define INT_INTERVAL     BIT(0)
83
84/* Event Control Timer register: controls the behavior of the internal counter */
85
86/* Specifies how to handle overflow at the internal counter (during the counting phase
87 * of the external pulse)
88 *
89 * - When 0: Overflow causes E_En to be 0 (see E_En bit description)
90 * - When 1: Overflow causes the internal counter to wrap around and continues incrementing
91 */
92#define EVCTRL_OVR       BIT(2)
93/* Specifies the counting phase of the external pulse */
94#define EVCTRL_LO        BIT(1)
95/* When 0, immediately resets the internal counter to 0, and stops incrementing*/
96#define EVCTRL_EN        BIT(0)
97
98#define PRESCALE_MAX       0xf
99#define PCLK_FREQ          111110000U
100
101#ifdef CONFIG_PLAT_ZYNQMP
102#define CNT_WIDTH 32
103#define CNT_MAX ((1ULL << CNT_WIDTH) - 1)
104#else
105#define CNT_WIDTH 16
106#define CNT_MAX (BIT(CNT_WIDTH) - 1)
107#endif
108
109
110/* Byte offsets into a field of ttc_tmr_regs_t for each ttc */
111#define TTCX_TIMER1_OFFSET 0x0
112#define TTCX_TIMER2_OFFSET 0x4
113#define TTCX_TIMER3_OFFSET 0x8
114
115#define TTCX_TIMER1_IRQ_POS 0
116#define TTCX_TIMER2_IRQ_POS 1
117#define TTCX_TIMER3_IRQ_POS 2
118
119struct ttc_tmr_regs {
120    /* Controls prescaler, selects clock input, edge */
121    uint32_t clk_ctrl[3];   /* +0x00 */
122    /* Enables counter, sets mode of operation, sets up/down
123     * counting, enables matching, enables waveform output */
124    uint32_t cnt_ctrl[3];   /* +0x0C */
125    /* Returns current counter value */
126    uint32_t cnt_val[3];    /* +0x18 */
127    /* Sets interval value - If interval is enabled, this is the maximum value
128     * that the counter will count up to or down from */
129    uint32_t interval[3];   /* +0x24 */
130    /* Sets match values, total 3 */
131    uint32_t match[3][3];   /* +0x30 */
132    /* Shows current interrupt status */
133    uint32_t int_sts[3];    /* +0x54 */
134    /* Enable interrupts */
135    uint32_t int_en[3];     /* +0x60 */
136    /* Enable event timer, stop timer, sets phrase */
137    uint32_t event_ctrl[3]; /* +0x6C */
138    /* Shows width of external pulse */
139    uint32_t event[3];      /* +0x78 */
140};
141typedef volatile struct ttc_tmr_regs ttc_tmr_regs_t;
142
143static freq_t _ttc_clk_get_freq(clk_t *clk);
144static freq_t _ttc_clk_set_freq(clk_t *clk, freq_t hz);
145static void _ttc_clk_recal(clk_t *clk);
146static clk_t *_ttc_clk_init(clk_t *clk);
147
148static inline ttc_tmr_regs_t *ttc_get_regs(ttc_t *ttc)
149{
150    return ttc->regs;
151}
152
153static inline size_t ttc_get_timer_shift(ttc_id_t id)
154{
155    switch (id) {
156    case TTC0_TIMER1:
157    case TTC1_TIMER1:
158#ifdef CONFIG_PLAT_ZYNQMP
159    case TTC2_TIMER1:
160    case TTC3_TIMER1:
161#endif
162        return TTCX_TIMER1_OFFSET;
163    case TTC0_TIMER2:
164    case TTC1_TIMER2:
165#ifdef CONFIG_PLAT_ZYNQMP
166    case TTC2_TIMER2:
167    case TTC3_TIMER2:
168#endif
169        return TTCX_TIMER2_OFFSET;
170    case TTC0_TIMER3:
171    case TTC1_TIMER3:
172#ifdef CONFIG_PLAT_ZYNQMP
173    case TTC2_TIMER3:
174    case TTC3_TIMER3:
175#endif
176        return TTCX_TIMER3_OFFSET;
177    default:
178        ZF_LOGF("Invalid ttc_id_t!");
179        return 0;
180    }
181}
182
183static inline unsigned ttc_get_irq_pos(ttc_id_t id)
184{
185    switch (id) {
186    case TTC0_TIMER1:
187    case TTC1_TIMER1:
188#ifdef CONFIG_PLAT_ZYNQMP
189    case TTC2_TIMER1:
190    case TTC3_TIMER1:
191#endif
192        return TTCX_TIMER1_IRQ_POS;
193    case TTC0_TIMER2:
194    case TTC1_TIMER2:
195#ifdef CONFIG_PLAT_ZYNQMP
196    case TTC2_TIMER2:
197    case TTC3_TIMER2:
198#endif
199        return TTCX_TIMER2_IRQ_POS;
200    case TTC0_TIMER3:
201    case TTC1_TIMER3:
202#ifdef CONFIG_PLAT_ZYNQMP
203    case TTC2_TIMER3:
204    case TTC3_TIMER3:
205#endif
206        return TTCX_TIMER3_IRQ_POS;
207    default:
208        ZF_LOGF("Invalid ttc_id_t!");
209        return 0;
210    }
211}
212
213static inline char *ttc_get_device_path(ttc_id_t id)
214{
215    switch (id) {
216    case TTC0_TIMER1:
217    case TTC0_TIMER2:
218    case TTC0_TIMER3:
219        return TTC0_PATH;
220    case TTC1_TIMER1:
221    case TTC1_TIMER2:
222    case TTC1_TIMER3:
223        return TTC1_PATH;
224#ifdef CONFIG_PLAT_ZYNQMP
225    case TTC2_TIMER1:
226    case TTC2_TIMER2:
227    case TTC2_TIMER3:
228        return TTC2_PATH;
229    case TTC3_TIMER1:
230    case TTC3_TIMER2:
231    case TTC3_TIMER3:
232        return TTC3_PATH;
233#endif /* CONFIG_PLAT_ZYNQMP */
234    default:
235        ZF_LOGF("Invalid ttc_id_t!");
236        return NULL;
237    }
238}
239
240/****************** Clocks ******************/
241
242static ttc_t *ttc_clk_get_priv(clk_t *clk)
243{
244    return (ttc_t *)clk->priv;
245}
246
247/* FPGA PL Clocks */
248static freq_t _ttc_clk_get_freq(clk_t *clk)
249{
250    ttc_t *ttc = ttc_clk_get_priv(clk);
251    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
252    uint32_t clk_ctrl;
253    freq_t fin, fout;
254    /* Get the parent frequency */
255    if (clk->parent) {
256        fin = clk_get_freq(clk->parent);
257    } else {
258        fin = PCLK_FREQ;
259    }
260    /* Calculate fout */
261    clk_ctrl = *regs->clk_ctrl;
262    if (clk_ctrl & CLKCTRL_PRESCALE_EN) {
263        fout = fin >> (CLKCTRL_GET_PRESCALE_VAL(clk_ctrl) + 1);
264    } else {
265        fout = fin;
266    }
267    /* Return */
268    return fout;
269}
270
271static freq_t _ttc_clk_set_freq(clk_t *clk, freq_t hz)
272{
273    ttc_t *ttc = ttc_clk_get_priv(clk);
274    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
275    uint32_t v;
276    freq_t fin;
277    int ps;
278    /* Determine input clock frequency */
279    if (clk->parent) {
280        fin = clk_get_freq(clk->parent);
281    } else {
282        fin = PCLK_FREQ;
283    }
284    /* Find a prescale value */
285    for (ps = 0; fin > hz; ps++, fin >>= 1);
286    if (ps > PRESCALE_MAX) {
287        return 0;
288    }
289    /* Configure the timer */
290    v = regs->clk_ctrl[0] & ~CLKCTRL_PRESCALE_MASK;
291    if (ps > 0) {
292        v |= CLKCTRL_PRESCALE_EN | CLKCTRL_PRESCALE_VAL(ps - 1);
293    } else {
294        v &= ~CLKCTRL_PRESCALE_EN;
295    }
296    *regs->clk_ctrl = v;
297    return clk_get_freq(clk);
298}
299
300static void _ttc_clk_recal(clk_t *clk UNUSED)
301{
302    assert(0);
303}
304
305static clk_t *_ttc_clk_init(clk_t *clk)
306{
307    return clk;
308}
309
310static inline freq_t _ttc_get_freq(ttc_t *ttc)
311{
312    return ttc->freq;
313}
314
315static inline freq_t _ttc_set_freq(ttc_t *ttc, freq_t hz)
316{
317    ttc->freq = clk_set_freq(&ttc->clk, hz);
318    return ttc->freq;
319}
320
321static inline bool _ttc_check_interrupt(ttc_t *ttc)
322{
323    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
324    /* The int_sts register is being accessed through typedef ttc_tmr_regs_t
325     * which is marked volatile, so the compiler will not elide this read.
326     */
327    uint32_t res = *regs->int_sts;
328    /* There are no data dependencies within this function that imply that the
329     * CPU cannot reorder this read in the pipeline. Use a CPU read barrier to
330     * inform the CPU that it should stall reads until this read has completed.
331     */
332    THREAD_MEMORY_RELEASE();
333
334    return !!res;
335}
336
337/********************************************/
338
339/* Computes the optimal clock frequency for interrupting after
340 * a given period of time. This will be the highest frequency
341 * such that starting from 0, the timer counter will reach its
342 * maximum value in AT MOST the specified time.
343 *
344 * If no such frequency is supported by the clock (ie. the
345 * requested time is too high) this returns ETIME. Returns 0
346 * on success.
347 *
348 * If a frequency is found, the clock is reprogrammed to run
349 * at that frequency. The number of ticks it will take for the
350 * requested time to pass (ie. the interval) is computed and
351 * returned via an argument (interval). */
352static inline int _ttc_set_freq_for_ns(ttc_t *ttc, uint64_t ns, uint64_t *interval)
353{
354    freq_t fin, f;
355    uint64_t interval_value;
356
357    /* Set the clock source frequency
358     * 1 / (fin / max_cnt) > interval
359     * fin < max_cnt / interval */
360    f = freq_cycles_and_ns_to_hz(CNT_MAX, ns);
361    fin = _ttc_set_freq(ttc, f);
362    if (fin > f) {
363        /* This happens when the requested time is so long that the clock can't
364         * run slow enough. In this case, the clock driver reported the minimum
365         * rate it can run at, and we can use that to calculate a maximum time.
366         */
367        ZF_LOGE("Timeout too big for timer, max %"PRIu64", got %"PRIu64"\n",
368                freq_cycles_and_hz_to_ns(CNT_MAX, fin), ns);
369
370        return ETIME;
371    }
372
373    interval_value = freq_ns_and_hz_to_cycles(ns, fin);
374
375    assert(interval_value <= CNT_MAX);
376
377    if (interval) {
378        *interval = interval_value;
379    }
380
381    return 0;
382}
383
384int ttc_start(ttc_t *ttc)
385{
386    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
387    *regs->cnt_ctrl &= ~CNTCTRL_STOP;
388    return 0;
389}
390
391int ttc_stop(ttc_t *ttc)
392{
393    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
394    *regs->cnt_ctrl |= CNTCTRL_STOP;
395    return 0;
396}
397
398void ttc_freerun(ttc_t *ttc)
399{
400    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
401    *regs->cnt_ctrl = CNTCTRL_RST;
402    *regs->int_en = INT_EVENT_OVR | INT_CNT_OVR;
403}
404
405/* Set up the ttc to fire an interrupt every ns nanoseconds.
406 * The first such interrupt may arrive before ns nanoseconds
407 * have passed since calling. */
408static int _ttc_periodic(ttc_tmr_regs_t *regs, uint64_t interval)
409{
410    *regs->interval = interval;
411
412    /* Interval mode: Continuously count from 0 to value in interval register,
413     * triggering an interval interrupt and resetting the counter to 0
414     * whenever the counter passes through 0. */
415    *regs->cnt_ctrl |= CNTCTRL_INT;
416
417    /* The INTERVAL interrupt is used in periodic mode. The only source of
418     * interrupts will be when the counter passes through 0 after reaching
419     * the value in the interval register. */
420    *regs->int_en = INT_INTERVAL;
421
422    return 0;
423}
424
425static void ttc_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
426{
427    assert(data != NULL);
428    ttc_t *ttc = data;
429    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
430
431    bool interrupt_pending = _ttc_check_interrupt(ttc);
432
433    if (ttc->is_timestamp) {
434        /* Check if we already updated the timestamp when reading the time,
435         * the interrupt status register should be empty if we did */
436        if (interrupt_pending) {
437            ttc->hi_time += ttc_ticks_to_ns(ttc, CNT_MAX);
438        }
439    } else {
440        /* The MATCH0 interrupt is used in oneshot mode. It is enabled when a
441         * oneshot function is called, and disabled here so only one interrupt
442         * is triggered per call. */
443        *regs->int_en &= ~INT_MATCH0;
444    }
445
446    /* Acknowledge the interrupt and call the user callback if any */
447    ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the interrupt from the TTC");
448    if (ttc->user_callback) {
449        if (ttc->is_timestamp) {
450            ttc->user_callback(ttc->user_callback_token, LTIMER_OVERFLOW_EVENT);
451        } else {
452            ttc->user_callback(ttc->user_callback_token, LTIMER_TIMEOUT_EVENT);
453        }
454    }
455}
456
457uint64_t ttc_ticks_to_ns(ttc_t *ttc, uint32_t ticks)
458{
459    if (!ttc) {
460        return 0;
461    }
462    uint32_t fin = _ttc_get_freq(ttc);
463    return freq_cycles_and_hz_to_ns(ticks, fin);
464}
465
466uint64_t ttc_get_time(ttc_t *ttc)
467{
468    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
469    uint32_t cnt = *regs->cnt_val;
470    bool interrupt_pending = _ttc_check_interrupt(ttc);
471    /* Check if there is an interrupt pending, i.e. counter overflowed */
472    if (interrupt_pending) {
473        /* Re-read the counter again */
474        cnt = *regs->cnt_val;
475        /* Bump the hi_time counter now, as there may be latency in serving the interrupt */
476        ttc->hi_time += ttc_ticks_to_ns(ttc, CNT_MAX);
477    }
478    uint32_t fin = _ttc_get_freq(ttc);
479    return ttc->hi_time + freq_cycles_and_hz_to_ns(cnt, fin);
480}
481
482/* Set up the ttc to fire an interrupt ns nanoseconds after this
483 * function is called. */
484static int _ttc_oneshot_relative(ttc_tmr_regs_t *regs, uint64_t interval)
485{
486
487    /* In overflow mode the ttc will continuously count up to 0xffff and reset to 0.
488     * The ttc will be programmed to interrupt when the counter reaches
489     * current_time + interval, allowing the addition to wrap around (16 bits).
490     */
491
492#ifdef CONFIG_PLAT_ZYNQMP
493    *regs->match[0] = (interval + *regs->cnt_val);
494#else
495    *regs->match[0] = (interval + *regs->cnt_val) % BIT(CNT_WIDTH);
496#endif
497
498    /* Overflow mode: Continuously count from 0 to 0xffff (this is a 16 bit ttc).
499     * In this mode no interrval interrupts. A match interrupt (MATCH0) will be used
500     * in this mode. */
501    *regs->cnt_ctrl &= ~CNTCTRL_INT;
502
503    /* The MATCH0 interrupt is used in oneshot mode. The only source of interrupts
504     * will be when the counter passes through the value in the match[0] register.
505     * This interrupt is disabled in the irq handler so it is only triggered once.
506     */
507    *regs->int_en = INT_MATCH0;
508
509    return 0;
510}
511
512int ttc_set_timeout(ttc_t *ttc, uint64_t ns, bool periodic)
513{
514    if (ttc == NULL) {
515        return EINVAL;
516    }
517
518    /* Program the clock and compute the interval value */
519    uint64_t interval;
520    int error = _ttc_set_freq_for_ns(ttc, ns, &interval);
521    if (error) {
522        return error;
523    }
524
525    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
526    if (periodic) {
527        return _ttc_periodic(regs, interval);
528    } else {
529        return _ttc_oneshot_relative(regs, interval);
530    }
531}
532
533static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token)
534{
535    assert(token != NULL);
536    ttc_t *ttc = token;
537    ttc->regs = ps_pmem_map(&ttc->io_ops, pmem, false, PS_MEM_NORMAL);
538    if (ttc->regs == NULL) {
539        ZF_LOGE("Failed to map in registers for the TTC");
540        return EIO;
541    }
542    /* This sets the base of the ttc_tmr_regs_t pointer to
543     * an offset into the ttc's mmio region such that
544     * ((ttc_tmr_regs_t*)vaddr)->clk_ctrl
545     * (and all other registers) refers to the address of the
546     * register relevant for the specified ttc device. */
547    ttc->regs += ttc_get_timer_shift(ttc->id);
548    ttc->timer_pmem = pmem;
549    return 0;
550}
551
552static int allocate_irq_callback(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token)
553{
554    assert(token != NULL);
555    ttc_t *ttc = token;
556    assert(num_irqs == IRQS_PER_TTC);
557    /* Get the corresponding IRQ position and register the IRQ if it matches */
558    unsigned irq_pos = ttc_get_irq_pos(ttc->id);
559    if (irq_pos == curr_num) {
560        ttc->irq_id = ps_irq_register(&ttc->io_ops.irq_ops, irq, ttc_handle_irq, ttc);
561        if (ttc->irq_id < 0) {
562            ZF_LOGE("Failed to register the IRQ for TTC timer %d", ttc->id);
563            return EIO;
564        }
565    }
566    return 0;
567}
568
569int ttc_init(ttc_t *ttc, ttc_config_t config)
570{
571    if (ttc == NULL) {
572        ZF_LOGE("ttc is NULL");
573        return EINVAL;
574    }
575
576    /* Initialise all the struct members */
577    ttc->io_ops = config.io_ops;
578    ttc->user_callback = config.user_callback;
579    ttc->user_callback_token = config.user_callback_token;
580    ttc->irq_id = PS_INVALID_IRQ_ID;
581    ttc->is_timestamp = config.is_timestamp;
582    ttc->id = config.id;
583
584    char *device_path = ttc_get_device_path(config.id);
585
586    /* Read the timer's path in the DTB */
587    ps_fdt_cookie_t *cookie = NULL;
588    int error = ps_fdt_read_path(&ttc->io_ops.io_fdt, &ttc->io_ops.malloc_ops, device_path, &cookie);
589    if (error) {
590        ZF_LOGF_IF(ps_fdt_cleanup_cookie(&ttc->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
591        ZF_LOGF_IF(ttc_destroy(ttc), CLEANUP_FAIL_TEXT);
592        return ENODEV;
593    }
594
595    /* Walk the registers and allocate them */
596    error = ps_fdt_walk_registers(&ttc->io_ops.io_fdt, cookie, allocate_register_callback, ttc);
597    if (error) {
598        ZF_LOGF_IF(ps_fdt_cleanup_cookie(&ttc->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
599        ZF_LOGF_IF(ttc_destroy(ttc), CLEANUP_FAIL_TEXT);
600        return ENODEV;
601    }
602
603    /* Walk the interrupts and allocate the corresponding interrupt for this timer */
604    error = ps_fdt_walk_irqs(&ttc->io_ops.io_fdt, cookie, allocate_irq_callback, ttc);
605    if (error) {
606        ZF_LOGF_IF(ps_fdt_cleanup_cookie(&ttc->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
607        ZF_LOGF_IF(ttc_destroy(ttc), CLEANUP_FAIL_TEXT);
608        return ENODEV;
609    }
610
611    /* Configure clock source */
612    memset(&ttc->clk, 0, sizeof(ttc->clk));
613    ttc->clk.name = STRINGIFY(config.id);
614    ttc->clk.get_freq = _ttc_clk_get_freq;
615    ttc->clk.set_freq = _ttc_clk_set_freq;
616    ttc->clk.recal = _ttc_clk_recal;
617    ttc->clk.init = _ttc_clk_init;
618    ttc->clk.priv = ttc;
619
620    if (config.clk_src) {
621        clk_register_child(config.clk_src, &ttc->clk);
622    }
623    ttc->freq = clk_get_freq(&ttc->clk);
624
625    ttc_tmr_regs_t *regs = ttc_get_regs(ttc);
626    *regs->int_en = 0;
627    FORCE_READ(regs->int_sts); /* Clear on read */
628    *regs->cnt_ctrl = CNTCTRL_RST | CNTCTRL_STOP | CNTCTRL_INT | CNTCTRL_MATCH;
629    *regs->clk_ctrl = 0;
630    *regs->int_en = INT_INTERVAL;
631    *regs->interval = CNT_MAX;
632
633    return 0;
634}
635
636int ttc_destroy(ttc_t *ttc)
637{
638    assert(ttc != NULL);
639
640    if (ttc->regs) {
641        ZF_LOGF_IF(ttc_stop(ttc), "Failed to stop the TTC before de-allocating it");
642        ps_io_unmap(&ttc->io_ops.io_mapper, ttc->regs, (size_t) ttc->timer_pmem.length);
643    }
644
645    if (ttc->irq_id != PS_INVALID_IRQ_ID) {
646        ZF_LOGF_IF(ps_irq_unregister(&ttc->io_ops.irq_ops, ttc->irq_id), "Failed to unregister IRQ");
647    }
648
649    return 0;
650}
651