1/* 2 * Copyright 2019, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 128 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#include <autoconf.h> 13#include <stdio.h> 14#include <assert.h> 15 16#include <utils/util.h> 17#include <utils/time.h> 18#include <utils/frequency.h> 19 20#include <platsupport/ltimer.h> 21#include <platsupport/arch/generic_timer.h> 22#include <platsupport/io.h> 23 24#include "../../ltimer.h" 25 26typedef struct { 27 uint32_t freq; // frequency of the generic timer 28 uint64_t period; // period of a current periodic timeout, in ns 29 irq_id_t timer_irq_id; 30 timer_callback_data_t callback_data; 31 ltimer_callback_fn_t user_callback; 32 void *user_callback_token; 33 ps_io_ops_t ops; 34} generic_ltimer_t; 35 36static size_t get_num_irqs(void *data) 37{ 38 return 1; 39} 40 41static int get_nth_irq(void *data, size_t n, ps_irq_t *irq) 42{ 43 assert(n < get_num_irqs(data)); 44 irq->type = PS_PER_CPU; 45 irq->cpu.number = GENERIC_TIMER_PCNT_IRQ; 46 irq->cpu.trigger = 0; 47 irq->cpu.cpu_idx = 0; 48 49 return 0; 50} 51 52static size_t get_num_pmems(void *data) 53{ 54 return 0; 55} 56 57static int get_nth_pmem(void *data, size_t n, pmem_region_t *region) 58{ 59 return -1; 60} 61 62static int get_time(void *data, uint64_t *time) 63{ 64 assert(data != NULL); 65 assert(time != NULL); 66 67 generic_ltimer_t *ltimer = data; 68 uint64_t ticks = generic_timer_get_ticks(); 69 *time = freq_cycles_and_hz_to_ns(ticks, ltimer->freq); 70 return 0; 71} 72 73int set_timeout(void *data, uint64_t ns, timeout_type_t type) 74{ 75 generic_ltimer_t *ltimer = data; 76 if (type == TIMEOUT_PERIODIC) { 77 ltimer->period = ns; 78 } else { 79 ltimer->period = 0; 80 } 81 82 uint64_t time; 83 int error = get_time(data, &time); 84 if (type != TIMEOUT_ABSOLUTE) { 85 if (error) { 86 return error; 87 } 88 ns += time; 89 } 90 91 if (time > ns) { 92 return ETIME; 93 } 94 generic_timer_set_compare(freq_ns_and_hz_to_cycles(ns, ltimer->freq)); 95 96 return 0; 97} 98 99static int handle_irq(void *data, ps_irq_t *irq) 100{ 101 if (irq->type != PS_PER_CPU && 102 irq->cpu.number != GENERIC_TIMER_PCNT_IRQ && 103 irq->cpu.trigger != 0 && 104 irq->cpu.cpu_idx != 0) { 105 return EINVAL; 106 } 107 108 generic_ltimer_t *ltimer = data; 109 if (ltimer->period) { 110 set_timeout(data, ltimer->period, TIMEOUT_PERIODIC); 111 } else { 112 generic_timer_set_compare(UINT64_MAX); 113 } 114 115 /* Interrupts are only generated for the timeout portion */ 116 if (ltimer->user_callback) { 117 ltimer->user_callback(ltimer->user_callback_token, LTIMER_TIMEOUT_EVENT); 118 } 119 return 0; 120} 121 122static int get_resolution(void *data, uint64_t *resolution) 123{ 124 return ENOSYS; 125} 126 127static int reset(void *data) 128{ 129 generic_ltimer_t *generic_ltimer = data; 130 generic_ltimer->period = 0; 131 generic_timer_set_compare(UINT64_MAX); 132 return 0; 133} 134 135static void destroy(void *data) 136{ 137 int error; 138 generic_ltimer_t *generic_ltimer = data; 139 generic_timer_disable(); 140 if (generic_ltimer->callback_data.irq) { 141 ps_free(&generic_ltimer->ops.malloc_ops, sizeof(ps_irq_t), generic_ltimer->callback_data.irq); 142 } 143 if (generic_ltimer->timer_irq_id > PS_INVALID_IRQ_ID) { 144 error = ps_irq_unregister(&generic_ltimer->ops.irq_ops, generic_ltimer->timer_irq_id); 145 ZF_LOGF_IF(error, "Failed to unregister IRQ ID"); 146 } 147 ps_free(&generic_ltimer->ops.malloc_ops, sizeof(generic_ltimer_t), generic_ltimer); 148} 149 150int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_data) 151{ 152 if (ltimer == NULL) { 153 ZF_LOGE("ltimer cannot be NULL"); 154 return EINVAL; 155 } 156 157 if (!config_set(CONFIG_EXPORT_PCNT_USER)) { 158 ZF_LOGE("Generic timer not exported!"); 159 return ENXIO; 160 } 161 162 ltimer_default_describe(ltimer, ops); 163 ltimer->get_time = get_time; 164 ltimer->get_resolution = get_resolution; 165 ltimer->set_timeout = set_timeout; 166 ltimer->reset = reset; 167 ltimer->destroy = destroy; 168 169 int error = ps_calloc(&ops.malloc_ops, 1, sizeof(generic_ltimer_t), <imer->data); 170 if (error) { 171 return error; 172 } 173 assert(ltimer->data != NULL); 174 generic_ltimer_t *generic_ltimer = ltimer->data; 175 176 generic_ltimer->ops = ops; 177 generic_ltimer->user_callback = callback; 178 generic_ltimer->user_callback_token = callback_data; 179 180 generic_ltimer->freq = generic_timer_get_freq(); 181 if (generic_ltimer->freq == 0) { 182 ZF_LOGE("Couldn't read timer frequency"); 183 return ENXIO; 184 } 185 186 generic_timer_set_compare(UINT64_MAX); 187 generic_timer_enable(); 188 189 /* register the IRQ we need */ 190 error = ps_calloc(&ops.malloc_ops, 1, sizeof(ps_irq_t), (void **) &generic_ltimer->callback_data.irq); 191 if (error) { 192 destroy(ltimer->data); 193 return error; 194 } 195 generic_ltimer->callback_data.ltimer = ltimer; 196 generic_ltimer->callback_data.irq_handler = handle_irq; 197 error = get_nth_irq(ltimer->data, 0, generic_ltimer->callback_data.irq); 198 if (error) { 199 destroy(ltimer->data); 200 return error; 201 } 202 generic_ltimer->timer_irq_id = ps_irq_register(&ops.irq_ops, *generic_ltimer->callback_data.irq, 203 handle_irq_wrapper, &generic_ltimer->callback_data); 204 if (generic_ltimer->timer_irq_id < 0) { 205 destroy(ltimer->data); 206 return EIO; 207 } 208 209 return 0; 210} 211 212int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops) 213{ 214 if (ltimer == NULL) { 215 ZF_LOGE("Timer is NULL!"); 216 return EINVAL; 217 } 218 219 ltimer->get_num_irqs = get_num_irqs; 220 ltimer->get_nth_irq = get_nth_irq; 221 ltimer->get_num_pmems = get_num_pmems; 222 ltimer->get_nth_pmem = get_nth_pmem; 223 return 0; 224} 225