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#include <stdio.h> 13#include <assert.h> 14 15#include <utils/util.h> 16#include <utils/time.h> 17 18#include <platsupport/ltimer.h> 19#include <platsupport/plat/sp804.h> 20#include <platsupport/io.h> 21 22#include "../../ltimer.h" 23 24/* 25 * We use two sp804 timers: one to keep track of an absolute time, the other for timeouts. 26 */ 27 28typedef struct { 29 /* fvp sp804 have 2 timers per frame, we just use one per each */ 30 sp804_t sp804_timeout; 31 sp804_t sp804_timestamp; 32 ps_io_ops_t ops; 33} fvp_ltimer_t; 34 35static int get_time(void *data, uint64_t *time) 36{ 37 fvp_ltimer_t *fvp_ltimer = data; 38 assert(data != NULL); 39 assert(time != NULL); 40 41 *time = sp804_get_time(&fvp_ltimer->sp804_timestamp); 42 return 0; 43} 44 45int set_timeout(void *data, uint64_t ns, timeout_type_t type) 46{ 47 if (type == TIMEOUT_ABSOLUTE) { 48 uint64_t time; 49 int error = get_time(data, &time); 50 if (error) { 51 return error; 52 } 53 if (time > ns) { 54 return ETIME; 55 } 56 ns -= time; 57 } 58 59 fvp_ltimer_t *fvp_ltimer = data; 60 return sp804_set_timeout(&fvp_ltimer->sp804_timeout, ns, type == TIMEOUT_PERIODIC, true); 61} 62 63static int reset(void *data) 64{ 65 fvp_ltimer_t *fvp_ltimer = data; 66 /* restart the rtc */ 67 sp804_stop(&fvp_ltimer->sp804_timeout); 68 sp804_start(&fvp_ltimer->sp804_timeout); 69 return 0; 70} 71 72static void destroy(void *data) 73{ 74 assert(data != NULL); 75 fvp_ltimer_t *fvp_ltimer = data; 76 sp804_destroy(&fvp_ltimer->sp804_timeout); 77 sp804_destroy(&fvp_ltimer->sp804_timestamp); 78 ps_free(&fvp_ltimer->ops.malloc_ops, sizeof(fvp_ltimer_t), fvp_ltimer); 79} 80 81int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token) 82{ 83 int error; 84 85 if (ltimer == NULL) { 86 ZF_LOGE("ltimer cannot be NULL"); 87 return EINVAL; 88 } 89 90 error = create_ltimer_simple( 91 ltimer, ops, sizeof(fvp_ltimer_t), 92 get_time, set_timeout, reset, destroy 93 ); 94 if (error) { 95 ZF_LOGE("Failed to create ltimer simple"); 96 return error; 97 } 98 99 fvp_ltimer_t *fvp_ltimer = ltimer->data; 100 fvp_ltimer->ops = ops; 101 102 /* set up an SP804 for timeouts */ 103 sp804_config_t sp804_config = { 104 .fdt_path = SP804_TIMER1_PATH, 105 .user_cb_fn = callback, 106 .user_cb_token = callback_token, 107 .user_cb_event = LTIMER_TIMEOUT_EVENT 108 }; 109 110 error = sp804_init(&fvp_ltimer->sp804_timeout, ops, sp804_config); 111 if (error) { 112 ZF_LOGE("Failed to init timeout timer"); 113 destroy(&fvp_ltimer); 114 return error; 115 } 116 117 error = sp804_start(&fvp_ltimer->sp804_timeout); 118 if (error) { 119 ZF_LOGE("Failed to start timeout timer"); 120 destroy(&fvp_ltimer); 121 return error; 122 } 123 124 /* another for timestamps */ 125 sp804_config.fdt_path = SP804_TIMER2_PATH; 126 sp804_config.user_cb_event = LTIMER_OVERFLOW_EVENT; 127 128 error = sp804_init(&fvp_ltimer->sp804_timestamp, ops, sp804_config); 129 if (error) { 130 ZF_LOGE("Failed to init timestamp timer"); 131 destroy(&fvp_ltimer); 132 return error; 133 } 134 135 error = sp804_start(&fvp_ltimer->sp804_timestamp); 136 if (error) { 137 ZF_LOGE("Failed to start timestamp timer"); 138 destroy(&fvp_ltimer); 139 return error; 140 } 141 142 error = sp804_set_timeout_ticks(&fvp_ltimer->sp804_timestamp, UINT32_MAX, true, true); 143 if (error) { 144 ZF_LOGE("Failed to set timeout ticks for timer"); 145 destroy(&fvp_ltimer); 146 return error; 147 } 148 149 return 0; 150} 151 152/* This function is intended to be deleted, 153 * this is just left here for now so that stuff can compile */ 154int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops) 155{ 156 ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid"); 157 return EINVAL; 158} 159