1/* 2 * Copyright 2017, 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 <utils/util.h> 18#include <inttypes.h> 19 20#include <platsupport/timer.h> 21#include <platsupport/plat/timer.h> 22 23/* enable bit */ 24#define PVT_E_BIT 31 25 26/* enable auto-reload for periodic mode */ 27#define PVT_PERIODIC_E_BIT 30 28 29/* 0-28 bits are for value, n + 1 trigger mode */ 30#define PVT_VAL_MASK 0x1fffffff 31#define PVT_VAL_BITS 29 32 33/* write 1 to clear interruts */ 34#define PCR_INTR_CLR_BIT 30 35 36/* counter value: decrements from PVT */ 37#define PCR_VAL_MASK 0x1fffffff 38/* counter width is 29 bits */ 39 40/* The NV-TMR timers are based on a 1us upcounter. This 1us upcounter literally 41 * counts up once every 1us, so the frequency here can only be 1us. 42 * 43 * You are required to initialize the upcounter with a divisor that will cause 44 * it to count up at 1us, and no other upcount value is supported. 45 * 46 * We set the divisor below in nv_get_timer(). The divisor divides the 47 * "clk_m" input clock (which operates at 12MHz) down to 1us. 48 */ 49#define CLK_FREQ_MHZ 1 50#define CLK_FREQ_HZ CLK_FREQ_MHZ * US_IN_S 51 52int nv_tmr_start(nv_tmr_t *tmr) 53{ 54 tmr->tmr_map->pcr |= BIT(PCR_INTR_CLR_BIT); 55 tmr->tmr_map->pvt |= BIT(PVT_E_BIT); 56 return 0; 57} 58 59int nv_tmr_stop(nv_tmr_t *tmr) 60{ 61 tmr->tmr_map->pvt &= ~(BIT(PVT_E_BIT)); 62 return 0; 63} 64 65void nv_tmr_destroy(nv_tmr_t *tmr) 66{ 67 if (tmr->reg_base) { 68 ps_pmem_unmap(&tmr->ops, tmr->timer_pmem, (void *) tmr->reg_base); 69 } 70 71 if (tmr->irq_id > PS_INVALID_IRQ_ID) { 72 int error = ps_irq_unregister(&tmr->ops.irq_ops, tmr->irq_id); 73 ZF_LOGF_IF(error, "Failed to unregister an IRQ"); 74 } 75} 76 77#define INVALID_PVT_VAL 0x80000000 78 79static uint32_t get_ticks(uint64_t ns) 80{ 81 uint64_t microsecond = ns / 1000ull; 82 uint64_t ticks = microsecond * CLK_FREQ_MHZ; 83 if (ticks >= BIT(PVT_VAL_BITS)) { 84 ZF_LOGE("ns too high %"PRIu64"\n", ns); 85 return INVALID_PVT_VAL; 86 } 87 return (uint32_t)ticks; 88} 89 90int nv_tmr_set_timeout(nv_tmr_t *tmr, bool periodic, uint64_t ns) 91{ 92 uint32_t ticks = get_ticks(ns); 93 if (ticks == INVALID_PVT_VAL) { 94 ZF_LOGE("Invalid PVT val"); 95 return EINVAL; 96 } 97 /* ack any pending irqs */ 98 tmr->tmr_map->pcr |= BIT(PCR_INTR_CLR_BIT); 99 tmr->tmr_map->pvt = BIT(PVT_E_BIT) | ticks 100 | ((periodic) ? BIT(PVT_PERIODIC_E_BIT) : 0); 101 return 0; 102} 103 104void nv_tmr_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data) 105{ 106 nv_tmr_t *tmr = data; 107 tmr->tmr_map->pcr |= BIT(PCR_INTR_CLR_BIT); 108 if (!(tmr->tmr_map->pvt & BIT(PVT_PERIODIC_E_BIT))) { 109 tmr->tmr_map->pvt &= ~(BIT(PVT_E_BIT)); 110 } 111 ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the timer's interrupts"); 112 if (tmr->user_callback) { 113 /* Call the ltimer's user callback */ 114 tmr->user_callback(tmr->user_callback_token, LTIMER_TIMEOUT_EVENT); 115 } 116} 117 118uint64_t nv_tmr_get_time(nv_tmr_t *tmr) 119{ 120 return (uint64_t)tmr->tmrus_map->cntr_1us * NS_IN_US; 121} 122 123#define TMRUS_USEC_CFG_DEFAULT 11 124 125static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token) 126{ 127 assert(token != NULL); 128 nv_tmr_t *tmr = token; 129 /* There's only one register region to map, map it in */ 130 assert(num_regs == 1 && curr_num == 0); 131 tmr->reg_base = (uintptr_t) ps_pmem_map(&tmr->ops, pmem, false, PS_MEM_NORMAL); 132 if (tmr->reg_base == 0) { 133 return EIO; 134 } 135 tmr->timer_pmem = pmem; 136 return 0; 137} 138 139static int allocate_irq_callback(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token) 140{ 141 assert(token != NULL); 142 nv_tmr_t *tmr = token; 143 /* Skip all interrupts except the first */ 144 if (curr_num != 0) { 145 return 0; 146 } 147 148 tmr->irq_id = ps_irq_register(&tmr->ops.irq_ops, irq, nv_tmr_handle_irq, tmr); 149 if (tmr->irq_id < 0) { 150 return EIO; 151 } 152 153 return 0; 154} 155 156int nv_tmr_init(nv_tmr_t *tmr, ps_io_ops_t ops, char *device_path, ltimer_callback_fn_t user_callback, 157 void *user_callback_token) 158{ 159 if (!tmr || !device_path) { 160 return EINVAL; 161 } 162 163 /* setup the private structure */ 164 tmr->ops = ops; 165 tmr->user_callback = user_callback; 166 tmr->user_callback_token = user_callback_token; 167 tmr->irq_id = PS_INVALID_IRQ_ID; 168 169 /* read the timer's path in the DTB */ 170 ps_fdt_cookie_t *cookie = NULL; 171 int error = ps_fdt_read_path(&ops.io_fdt, &ops.malloc_ops, device_path, &cookie); 172 if (error) { 173 nv_tmr_destroy(tmr); 174 return ENODEV; 175 } 176 177 /* walk the registers and allocate them */ 178 error = ps_fdt_walk_registers(&ops.io_fdt, cookie, allocate_register_callback, tmr); 179 if (error) { 180 nv_tmr_destroy(tmr); 181 return ENODEV; 182 } 183 184 /* walk the interrupts and allocate the first */ 185 error = ps_fdt_walk_irqs(&ops.io_fdt, cookie, allocate_irq_callback, tmr); 186 if (error) { 187 nv_tmr_destroy(tmr); 188 return ENODEV; 189 } 190 191 error = ps_fdt_cleanup_cookie(&ops.malloc_ops, cookie); 192 if (error) { 193 nv_tmr_destroy(tmr); 194 return ENODEV; 195 } 196 197 tmr->tmr_map = (void *)(tmr->reg_base + NV_TMR_ID_OFFSET); 198 tmr->tmrus_map = (void *)(tmr->reg_base + TMRUS_OFFSET); 199 tmr->tmr_shared_map = (void *) tmr->reg_base + TMR_SHARED_OFFSET; 200 201 tmr->tmr_map->pvt = 0; 202 tmr->tmr_map->pcr = BIT(PCR_INTR_CLR_BIT); 203 204 /* The following #ifdef is for some platform specific init for tk1, tx1, tx2 205 * currently this custom init is only one line each hence using the ifdef. 206 * If the platform specific code becomes any larger, then it should be considered 207 * moving into a per platform nv_timer_plat_init function 208 */ 209#ifdef CONFIG_PLAT_TX2 210 /* Route the interrupt to the correct shared interrupt number. */ 211 tmr->tmr_shared_map->TKEIE[NV_TMR_ID] = BIT(NV_TMR_ID); 212#else 213 /* Just unconditionally set the divisor as if "clk_m" is always 12MHz, 214 * because it actually is always 12MHz on TK1 or TX1. 215 * 216 * Nvidia manual, section 5.2.2, Table 14: 217 * "clk_m: This clock (with DFT control) runs at 12 MHz, 13 MHz, 16.8 MHz, 218 * 19.2 MHz, 26 MHz, 38.4 MHz, or 48 MHz. Only 12 MHz is currently 219 * supported." 220 */ 221 tmr->tmrus_map->usec_cfg = TMRUS_USEC_CFG_DEFAULT; 222#endif 223 224 return 0; 225} 226