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/* Implementation of a logical timer for HiFive Unleashed platform. 13 * 14 * We use two pwms: one for the time and the other for timeouts. 15 */ 16#include <platsupport/timer.h> 17#include <platsupport/ltimer.h> 18#include <platsupport/plat/pwm.h> 19#include <platsupport/pmem.h> 20#include <utils/util.h> 21 22#include "../../ltimer.h" 23 24enum { 25 COUNTER_TIMER, 26 TIMEOUT_TIMER, 27 NUM_TIMERS 28}; 29 30typedef struct { 31 pwm_t pwm; 32 void *vaddr; 33} pwm_ltimer_t; 34 35typedef struct { 36 pwm_ltimer_t pwm_ltimers[NUM_TIMERS]; 37 irq_id_t timer_irq_ids[NUM_TIMERS]; 38 timer_callback_data_t callback_datas[NUM_TIMERS]; 39 ltimer_callback_fn_t user_callback; 40 void *user_callback_token; 41 ps_io_ops_t ops; 42} hifive_timers_t; 43 44static ps_irq_t irqs[] = { 45 { 46 .type = PS_INTERRUPT, 47 .irq.number = PWM0_INTERRUPT0 48 49 }, 50 { 51 .type = PS_INTERRUPT, 52 .irq.number = PWM1_INTERRUPT0 53 }, 54}; 55 56static pmem_region_t pmems[] = { 57 { 58 .type = PMEM_TYPE_DEVICE, 59 .base_addr = PWM0_PADDR, 60 .length = PAGE_SIZE_4K 61 }, 62 { 63 .type = PMEM_TYPE_DEVICE, 64 .base_addr = PWM1_PADDR, 65 .length = PAGE_SIZE_4K 66 } 67}; 68 69#define N_IRQS ARRAY_SIZE(irqs) 70#define N_PMEMS ARRAY_SIZE(pmems) 71 72 size_t get_num_irqs(void *data) 73{ 74 return N_IRQS; 75} 76 77static int get_nth_irq(void *data, size_t n, ps_irq_t *irq) 78{ 79 assert(n < N_IRQS); 80 81 *irq = irqs[n]; 82 return 0; 83} 84 85static size_t get_num_pmems(void *data) 86{ 87 return N_PMEMS; 88} 89 90static int get_nth_pmem(void *data, size_t n, pmem_region_t *paddr) 91{ 92 assert(n < N_PMEMS); 93 *paddr = pmems[n]; 94 return 0; 95} 96 97static int ltimer_handle_irq(void *data, ps_irq_t *irq) 98{ 99 assert(data != NULL); 100 hifive_timers_t *timers = data; 101 long irq_number = irq->irq.number; 102 ltimer_event_t event; 103 if (irq_number == PWM0_INTERRUPT0) { 104 pwm_handle_irq(&timers->pwm_ltimers[COUNTER_TIMER].pwm, irq->irq.number); 105 event = LTIMER_OVERFLOW_EVENT; 106 } else if (irq_number == PWM1_INTERRUPT0) { 107 pwm_handle_irq(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm, irq->irq.number); 108 event = LTIMER_TIMEOUT_EVENT; 109 } else { 110 ZF_LOGE("Invalid IRQ number: %d received.\n", irq_number); 111 } 112 113 if (timers->user_callback) { 114 timers->user_callback(timers->user_callback_token, event); 115 } 116 117 return 0; 118} 119 120static int get_time(void *data, uint64_t *time) 121{ 122 assert(data != NULL); 123 assert(time != NULL); 124 hifive_timers_t *timers = data; 125 126 *time = pwm_get_time(&timers->pwm_ltimers[COUNTER_TIMER].pwm); 127 return 0; 128} 129 130static int get_resolution(void *data, uint64_t *resolution) 131{ 132 return ENOSYS; 133} 134 135static int set_timeout(void *data, uint64_t ns, timeout_type_t type) 136{ 137 assert(data != NULL); 138 hifive_timers_t *timers = data; 139 140 switch (type) { 141 case TIMEOUT_ABSOLUTE: { 142 uint64_t time = pwm_get_time(&timers->pwm_ltimers[COUNTER_TIMER].pwm); 143 if (time >= ns) { 144 return ETIME; 145 } 146 return pwm_set_timeout(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm, ns - time, false); 147 } 148 case TIMEOUT_RELATIVE: 149 return pwm_set_timeout(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm, ns, false); 150 case TIMEOUT_PERIODIC: 151 return pwm_set_timeout(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm, ns, true); 152 } 153 154 return EINVAL; 155} 156 157static int reset(void *data) 158{ 159 assert(data != NULL); 160 hifive_timers_t *timers = data; 161 pwm_stop(&timers->pwm_ltimers[COUNTER_TIMER].pwm); 162 pwm_start(&timers->pwm_ltimers[COUNTER_TIMER].pwm); 163 pwm_stop(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm); 164 pwm_start(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm); 165 return 0; 166} 167 168static void destroy(void *data) 169{ 170 assert(data); 171 hifive_timers_t *timers = data; 172 for (int i = 0; i < NUM_TIMERS; i++) { 173 if (timers->pwm_ltimers[i].vaddr) { 174 pwm_stop(&timers->pwm_ltimers[i].pwm); 175 ps_pmem_unmap(&timers->ops, pmems[i], timers->pwm_ltimers[i].vaddr); 176 } 177 if (timers->timer_irq_ids[i] > PS_INVALID_IRQ_ID) { 178 int error = ps_irq_unregister(&timers->ops.irq_ops, timers->timer_irq_ids[i]); 179 ZF_LOGF_IF(error, "Failed to unregister IRQ"); 180 } 181 } 182 ps_free(&timers->ops.malloc_ops, sizeof(timers), timers); 183} 184 185static int create_ltimer(ltimer_t *ltimer, ps_io_ops_t ops) 186{ 187 assert(ltimer != NULL); 188 ltimer->get_time = get_time; 189 ltimer->get_resolution = get_resolution; 190 ltimer->set_timeout = set_timeout; 191 ltimer->reset = reset; 192 ltimer->destroy = destroy; 193 194 int error = ps_calloc(&ops.malloc_ops, 1, sizeof(hifive_timers_t), <imer->data); 195 if (error) { 196 return error; 197 } 198 assert(ltimer->data != NULL); 199 200 return 0; 201} 202 203static int init_ltimer(ltimer_t *ltimer) 204{ 205 assert(ltimer != NULL); 206 hifive_timers_t *timers = ltimer->data; 207 208 /* setup pwm */ 209 pwm_config_t config_counter = { 210 .vaddr = timers->pwm_ltimers[COUNTER_TIMER].vaddr, 211 .mode = UPCOUNTER, 212 }; 213 pwm_config_t config_timeout = { 214 .vaddr = timers->pwm_ltimers[TIMEOUT_TIMER].vaddr, 215 .mode = TIMEOUT, 216 }; 217 218 pwm_init(&timers->pwm_ltimers[COUNTER_TIMER].pwm, config_counter); 219 pwm_init(&timers->pwm_ltimers[TIMEOUT_TIMER].pwm, config_timeout); 220 pwm_start(&timers->pwm_ltimers[COUNTER_TIMER].pwm); 221 return 0; 222} 223 224int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token) 225{ 226 227 int error = ltimer_default_describe(ltimer, ops); 228 if (error) { 229 return error; 230 } 231 232 error = create_ltimer(ltimer, ops); 233 if (error) { 234 return error; 235 } 236 237 hifive_timers_t *timers = ltimer->data; 238 timers->ops = ops; 239 timers->user_callback = callback; 240 timers->user_callback_token = callback_token; 241 for (int i = 0; i < NUM_TIMERS; i++) { 242 timers->timer_irq_ids[i] = PS_INVALID_IRQ_ID; 243 } 244 245 for (int i = 0; i < NUM_TIMERS; i++) { 246 /* map the registers we need */ 247 timers->pwm_ltimers[i].vaddr = ps_pmem_map(&ops, pmems[i], false, PS_MEM_NORMAL); 248 if (timers->pwm_ltimers[i].vaddr == NULL) { 249 destroy(ltimer->data); 250 return ENOMEM; 251 } 252 253 /* register the IRQs we need */ 254 timers->callback_datas[i].ltimer = ltimer; 255 timers->callback_datas[i].irq = &irqs[i]; 256 timers->callback_datas[i].irq_handler = ltimer_handle_irq; 257 timers->timer_irq_ids[i] = ps_irq_register(&ops.irq_ops, irqs[i], handle_irq_wrapper, 258 &timers->callback_datas[i]); 259 if (timers->timer_irq_ids[i] < 0) { 260 destroy(ltimer->data); 261 return EIO; 262 } 263 } 264 265 error = init_ltimer(ltimer); 266 if (error) { 267 destroy(ltimer->data); 268 return error; 269 } 270 271 /* success! */ 272 return 0; 273} 274 275int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops) 276{ 277 if (ltimer == NULL) { 278 ZF_LOGE("Timer is NULL!"); 279 return EINVAL; 280 } 281 282 ltimer->get_num_irqs = get_num_irqs; 283 ltimer->get_nth_irq = get_nth_irq; 284 ltimer->get_num_pmems = get_num_pmems; 285 ltimer->get_nth_pmem = get_nth_pmem; 286 return 0; 287} 288