1/* 2 * Copyright 2019, 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 <platsupport/ltimer.h> 14#include <platsupport/io.h> 15#include <utils/io.h> 16#include <platsupport/plat/meson_timer.h> 17 18#include "../../ltimer.h" 19 20enum { 21 MESON_TIMER = 0, 22 NUM_TIMERS = 1 23}; 24 25typedef struct { 26 meson_timer_t meson_timer; 27 void *meson_timer_vaddr; 28 irq_id_t timer_irq_id; 29 timer_callback_data_t callback_data; 30 ltimer_callback_fn_t user_callback; 31 void *user_callback_token; 32 ps_io_ops_t ops; 33} odroidc2_ltimer_t; 34 35static pmem_region_t pmems[] = { 36 { 37 .type = PMEM_TYPE_DEVICE, 38 .base_addr = TIMER_MAP_BASE, 39 .length = PAGE_SIZE_4K 40 } 41}; 42 43static ps_irq_t irqs[] = { 44 { 45 .type = PS_TRIGGER, 46 .trigger.number = TIMER_A_IRQ, 47 .trigger.trigger = 1 /* edge-triggered */ 48 } 49}; 50 51static size_t get_num_irqs(void *data) 52{ 53 return sizeof(irqs) / sizeof(irqs[0]); 54} 55 56static int get_nth_irq(void *data, size_t n, ps_irq_t *irq) 57{ 58 assert(n < get_num_irqs(data)); 59 *irq = irqs[n]; 60 return 0; 61} 62 63static size_t get_num_pmems(void *data) 64{ 65 return sizeof(pmems) / sizeof(pmems[0]); 66} 67 68static int get_nth_pmem(void *data, size_t n, pmem_region_t *region) 69{ 70 assert(n < get_num_pmems(data)); 71 *region = pmems[n]; 72 return 0; 73} 74 75static int handle_irq(void *data, ps_irq_t *irq) 76{ 77 assert(data != NULL); 78 odroidc2_ltimer_t *odroidc2_timer = data; 79 80 if (irq->irq.number != TIMER_A_IRQ) { 81 ZF_LOGE("Got IRQ from unkown source?"); 82 return EINVAL; 83 } 84 85 if (odroidc2_timer->user_callback) { 86 /* The only interrupt event that we'll get is a timeout event */ 87 odroidc2_timer->user_callback(odroidc2_timer->user_callback_token, LTIMER_TIMEOUT_EVENT); 88 } 89 90 return 0; 91} 92 93static int get_time(void *data, uint64_t *time) 94{ 95 assert(time); 96 assert(data); 97 98 odroidc2_ltimer_t *odroidc2_timer = data; 99 100 *time = meson_get_time(&odroidc2_timer->meson_timer); 101 return 0; 102} 103 104static int get_resolution(void *data, uint64_t *resolution) 105{ 106 return ENOSYS; 107} 108 109static int set_timeout(void *data, uint64_t ns, timeout_type_t type) 110{ 111 assert(data != NULL); 112 odroidc2_ltimer_t *odroidc2_timer = data; 113 bool periodic = false; 114 uint16_t timeout; 115 uint64_t current_time; 116 117 switch (type) { 118 case TIMEOUT_ABSOLUTE: 119 current_time = meson_get_time(&odroidc2_timer->meson_timer); 120 if (current_time > ns) { 121 ZF_LOGE("Timeout in the past: now %lu, timeout %lu", current_time, ns); 122 return ETIME; 123 } else if ((ns - current_time) / NS_IN_MS > UINT16_MAX) { 124 ZF_LOGE("Timeout too far in the future"); 125 return ETIME; 126 } 127 128 timeout = (ns - current_time) / NS_IN_MS; 129 break; 130 case TIMEOUT_PERIODIC: 131 periodic = true; 132 /* Fall through */ 133 case TIMEOUT_RELATIVE: 134 if (ns / NS_IN_MS > UINT16_MAX) { 135 ZF_LOGE("Timeout too far in the future"); 136 return ETIME; 137 } 138 139 timeout = ns / NS_IN_MS; 140 break; 141 } 142 143 /* The timer tick includes 0, i.e. one unit of time greater than the value written */ 144 timeout = MIN(timeout, (uint16_t)(timeout - 1)); 145 meson_set_timeout(&odroidc2_timer->meson_timer, timeout, periodic); 146 return 0; 147} 148 149static int reset(void *data) 150{ 151 assert(data != NULL); 152 odroidc2_ltimer_t *odroidc2_timer = data; 153 154 meson_stop_timer(&odroidc2_timer->meson_timer); 155 return 0; 156} 157 158static void destroy(void *data) 159{ 160 assert(data); 161 odroidc2_ltimer_t *odroidc2_timer = data; 162 163 meson_stop_timer(&odroidc2_timer->meson_timer); 164 165 if (odroidc2_timer->meson_timer_vaddr) { 166 ps_pmem_unmap(&odroidc2_timer->ops, pmems[0], odroidc2_timer->meson_timer_vaddr); 167 } 168 169 if (odroidc2_timer->timer_irq_id > PS_INVALID_IRQ_ID) { 170 int error = ps_irq_unregister(&odroidc2_timer->ops.irq_ops, odroidc2_timer->timer_irq_id); 171 ZF_LOGF_IF(error, "Failed to unregister IRQ"); 172 } 173 174 ps_free(&odroidc2_timer->ops.malloc_ops, sizeof(odroidc2_ltimer_t), odroidc2_timer); 175 return; 176} 177 178int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token) 179{ 180 if (ltimer == NULL) { 181 ZF_LOGE("ltimer cannot be NULL"); 182 return EINVAL; 183 } 184 185 int error = ltimer_default_describe(ltimer, ops); 186 if (error) { 187 return error; 188 } 189 190 ltimer->get_time = get_time; 191 ltimer->get_resolution = get_resolution; 192 ltimer->set_timeout = set_timeout; 193 ltimer->reset = reset; 194 ltimer->destroy = destroy; 195 196 error = ps_calloc(&ops.malloc_ops, 1, sizeof(odroidc2_ltimer_t), <imer->data); 197 if (error) { 198 return error; 199 } 200 assert(ltimer->data != NULL); 201 202 odroidc2_ltimer_t *odroidc2_timer = ltimer->data; 203 204 odroidc2_timer->ops = ops; 205 odroidc2_timer->user_callback = callback; 206 odroidc2_timer->user_callback_token = callback_token; 207 odroidc2_timer->timer_irq_id = PS_INVALID_IRQ_ID; 208 209 odroidc2_timer->meson_timer_vaddr = ps_pmem_map(&ops, pmems[0], false, PS_MEM_NORMAL); 210 if (odroidc2_timer->meson_timer_vaddr == NULL) { 211 destroy(ltimer->data); 212 return EINVAL; 213 } 214 215 odroidc2_timer->callback_data.ltimer = ltimer; 216 odroidc2_timer->callback_data.irq_handler = handle_irq; 217 odroidc2_timer->callback_data.irq = &irqs[0]; 218 219 odroidc2_timer->timer_irq_id = ps_irq_register(&ops.irq_ops, irqs[0], handle_irq_wrapper, 220 &odroidc2_timer->callback_data); 221 if (odroidc2_timer->timer_irq_id < 0) { 222 destroy(ltimer->data); 223 return EIO; 224 } 225 226 meson_timer_config_t meson_config = { 227 .vaddr = odroidc2_timer->meson_timer_vaddr 228 }; 229 meson_init(&odroidc2_timer->meson_timer, meson_config); 230 return 0; 231} 232 233int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops) 234{ 235 if (ltimer == NULL) { 236 ZF_LOGE("Timer is NULL!"); 237 return EINVAL; 238 } 239 240 *ltimer = (ltimer_t) { 241 .get_num_irqs = get_num_irqs, 242 .get_nth_irq = get_nth_irq, 243 .get_num_pmems = get_num_pmems, 244 .get_nth_pmem = get_nth_pmem 245 }; 246 247 return 0; 248} 249