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 <stdio.h> 14#include <assert.h> 15#include <errno.h> 16#include <stdlib.h> 17#include <stdint.h> 18 19#include <utils/util.h> 20 21#include <platsupport/mach/epit.h> 22#include <platsupport/plat/timer.h> 23 24#define CLEANUP_FAIL_TEXT "Failed to cleanup the EPIT after failing to initialise it" 25 26/* EPIT CONTROL REGISTER BITS */ 27typedef enum { 28 /* 29 * This bit enables the EPIT. 30 */ 31 EN = 0, 32 33 /* 34 * By setting this bit, then when EPIT is disabled (EN=0), then 35 * both Main Counter and Prescaler Counter freeze their count at 36 * current count values. 37 */ 38 ENMOD = 1, 39 40 /* 41 * This bit enables the generation of interrupt when a compare 42 * event occurs 43 */ 44 OCIEN = 2, 45 46 /* 47 * This bit is cleared by hardware reset. It controls whether the 48 * counter runs in free running mode OR set and forget mode. 49 */ 50 RLD = 3, 51 52 /* 53 * Bits 4 - 15 determine the prescaler value by which the clock is divided 54 * before it goes into the counter. 55 * 56 * The prescaler used is the value in these bits + 1. ie: 57 * 58 * 0x00 divide by 1 59 * 0x01 divide by 2 60 * 0x10 divide by 3 61 * . 62 * . 63 * . 64 * 0xFFF divide by 4096 65 * 66 */ 67 PRESCALER = 4, 68 69 /* 70 * This bit controls the counter data when the modulus register is 71 * written. When this bit is set, all writes to the load register 72 * will overwrite the counter contents and the counter will 73 * subsequently start counting down from the programmed value. 74 */ 75 IOVW = 17, 76 77 /* 78 * These bits select the clock input used to run the counter. After 79 * reset, the system functional clock is selected. The input clock 80 * can also be turned off if these bits are set to 00. This field 81 * value should only be changed when the EPIT is disabled. 82 */ 83 CLKSRC = 24 84} epit_control_reg; 85 86enum IPGConstants { 87 IPG_CLK = 1, IPG_CLK_HIGHFREQ = 2, IPG_CLK_32K = 3 88}; 89 90/* Memory map for EPIT (Enhanced Periodic Interrupt Timer). */ 91struct epit_map { 92 /* epit control register */ 93 uint32_t epitcr; 94 /* epit status register */ 95 uint32_t epitsr; 96 /* epit load register */ 97 uint32_t epitlr; 98 /* epit compare register */ 99 uint32_t epitcmpr; 100 /* epit counter register */ 101 uint32_t epitcnt; 102}; 103 104int epit_stop(epit_t *epit) 105{ 106 /* Disable timer irq. */ 107 epit->epit_map->epitcr &= ~(BIT(EN)); 108 return 0; 109} 110 111int epit_set_timeout_ticks(epit_t *epit, uint64_t counterValue, bool periodic) 112{ 113 ZF_LOGF_IF(epit == NULL, "invalid epit provided"); 114 ZF_LOGF_IF(epit->epit_map == NULL, "uninitialised epit provided"); 115 116 if (counterValue >= (1ULL << 32)) { 117 /* Counter too large to be stored in 32 bits. */ 118 ZF_LOGW("counterValue too high, going to be capping it\n"); 119 counterValue = UINT32_MAX; 120 } 121 122 /* configure it and turn it on */ 123 uint32_t reload_val = periodic ? BIT(RLD) : 0; 124 epit->epit_map->epitcr = reload_val | (IPG_CLK << CLKSRC) | /* Clock source = IPG */ 125 (epit->prescaler << PRESCALER) | /* Set the prescaler */ 126 BIT(IOVW) | /* Overwrite counter immediately on write */ 127 BIT(OCIEN) | /* Enable interrupt on comparison event */ 128 BIT(ENMOD) | /* Count from modulus on restart */ 129 BIT(EN); 130 131 /* hardware has a race condition where the epitlr won't be set properly 132 * - keep trying until it works 133 */ 134 epit->epit_map->epitlr = counterValue; 135 while (epit->epit_map->epitlr != counterValue) { 136 epit->epit_map->epitlr = counterValue; 137 } 138 139 return 0; 140} 141 142int epit_set_timeout(epit_t *epit, uint64_t ns, bool periodic) 143{ 144 if (epit->is_timestamp) { 145 ZF_LOGW("Using the EPIT as a timeout timer when it is configured as a timestamp timer"); 146 } 147 ZF_LOGF_IF(epit == NULL, "invalid epit provided"); 148 ZF_LOGF_IF(epit->epit_map == NULL, "uninitialised epit provided"); 149 /* Set counter modulus - this effectively sets the timeouts to us but doesn't 150 * overflow as fast. */ 151 uint64_t counterValue = (uint64_t)(IPG_FREQ / (epit->prescaler + 1)) * (ns / 1000ULL); 152 153 return epit_set_timeout_ticks(epit, counterValue, periodic); 154} 155 156uint64_t epit_get_time(epit_t *epit) 157{ 158 if (!epit->is_timestamp) { 159 ZF_LOGW("Using the EPIT as a timestamp timer when it is configured as a timeout timer"); 160 } 161 ZF_LOGF_IF(epit == NULL, "invalid epit provided"); 162 ZF_LOGF_IF(epit->epit_map == NULL, "uninitialised epit provided"); 163 uint64_t ticks = (epit->high_bits + !!epit->epit_map->epitsr) << 32llu; 164 ticks += (UINT32_MAX - epit->epit_map->epitcnt); 165 return (ticks * 1000llu) / (IPG_FREQ / (epit->prescaler + 1)); 166} 167 168static void epit_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data) 169{ 170 assert(data != NULL); 171 epit_t *epit = data; 172 if (epit->epit_map->epitsr) { 173 /* ack the irq */ 174 epit->epit_map->epitsr = 1; 175 if (epit->is_timestamp) { 176 epit->high_bits++; 177 } 178 } 179 /* acknowledge the interrupt and call the user callback if any */ 180 ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the interrupt from the EPIT"); 181 if (epit->user_callback) { 182 if (epit->is_timestamp) { 183 epit->user_callback(epit->user_callback_token, LTIMER_OVERFLOW_EVENT); 184 } else { 185 epit->user_callback(epit->user_callback_token, LTIMER_TIMEOUT_EVENT); 186 } 187 } 188} 189 190static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token) 191{ 192 assert(token != NULL); 193 /* Should only be called once. I.e. only one register field */ 194 assert(curr_num == 0); 195 epit_t *epit = token; 196 epit->epit_map = (volatile struct epit_map *) ps_pmem_map(&epit->io_ops, pmem, false, PS_MEM_NORMAL); 197 if (!epit->epit_map) { 198 ZF_LOGE("Failed to map in registers for the EPIT"); 199 return EIO; 200 } 201 epit->timer_pmem = pmem; 202 return 0; 203} 204 205static int allocate_irq_callback(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token) 206{ 207 assert(token != NULL); 208 /* Should only be called once. I.e. only one interrupt field */ 209 assert(curr_num == 0); 210 epit_t *epit = token; 211 epit->irq_id = ps_irq_register(&epit->io_ops.irq_ops, irq, epit_handle_irq, epit); 212 if (epit->irq_id < 0) { 213 ZF_LOGE("Failed to register the EPIT interrupt with the IRQ interface"); 214 return EIO; 215 } 216 return 0; 217} 218 219int epit_init(epit_t *epit, epit_config_t config) 220{ 221 if (epit == NULL) { 222 ZF_LOGE("Epit cannot be NULL, must be preallocated"); 223 return EINVAL; 224 } 225 226 /* Initialise the structure */ 227 epit->io_ops = config.io_ops; 228 epit->user_callback = config.user_callback; 229 epit->user_callback_token = config.user_callback_token; 230 epit->irq_id = PS_INVALID_IRQ_ID; 231 epit->prescaler = config.prescaler; 232 epit->is_timestamp = config.is_timestamp; 233 234 /* Read the timer's path in the DTB */ 235 ps_fdt_cookie_t *cookie = NULL; 236 int error = ps_fdt_read_path(&epit->io_ops.io_fdt, &epit->io_ops.malloc_ops, config.device_path, &cookie); 237 if (error) { 238 ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT); 239 ZF_LOGF_IF(epit_destroy(epit), CLEANUP_FAIL_TEXT); 240 return ENODEV; 241 } 242 243 /* Walk the registers and allocate them */ 244 error = ps_fdt_walk_registers(&epit->io_ops.io_fdt, cookie, allocate_register_callback, epit); 245 if (error) { 246 ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT); 247 ZF_LOGF_IF(epit_destroy(epit), CLEANUP_FAIL_TEXT); 248 return ENODEV; 249 } 250 251 /* Walk the interrupts and allocate the first */ 252 error = ps_fdt_walk_irqs(&epit->io_ops.io_fdt, cookie, allocate_irq_callback, epit); 253 if (error) { 254 ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT); 255 ZF_LOGF_IF(epit_destroy(epit), CLEANUP_FAIL_TEXT); 256 return ENODEV; 257 } 258 259 ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), 260 "Failed to cleanup the FDT cookie after initialising the GPT"); 261 262 /* Disable EPIT. */ 263 epit->epit_map->epitcr = 0; 264 265 /* Interrupt when compare with 0. */ 266 epit->epit_map->epitcmpr = 0; 267 268 return 0; 269} 270 271int epit_destroy(epit_t *epit) 272{ 273 if (epit->epit_map) { 274 ZF_LOGF_IF(epit_stop(epit), "Failed to stop the GPT before de-allocating it"); 275 ps_io_unmap(&epit->io_ops.io_mapper, (void *) epit->epit_map, (size_t) epit->timer_pmem.length); 276 } 277 278 if (epit->irq_id != PS_INVALID_IRQ_ID) { 279 ZF_LOGF_IF(ps_irq_unregister(&epit->io_ops.irq_ops, epit->irq_id), "Failed to unregister IRQ"); 280 } 281 282 return 0; 283} 284