1181624Skmacy/* 2181624Skmacy * Copyright 2017, Data61 3181624Skmacy * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4181624Skmacy * ABN 41 687 119 230. 5181624Skmacy * 6181624Skmacy * This software may be distributed and modified according to the terms of 7181624Skmacy * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8181624Skmacy * See "LICENSE_BSD2.txt" for details. 9181624Skmacy * 10181624Skmacy * @TAG(DATA61_BSD) 11181624Skmacy */ 12181624Skmacy 13181624Skmacy#include <autoconf.h> 14181624Skmacy#include <platsupport/gen_config.h> 15181624Skmacy#include <errno.h> 16181624Skmacy 17181624Skmacy#include <platsupport/timer.h> 18181624Skmacy#include <platsupport/plat/hpet.h> 19181624Skmacy#include <platsupport/plat/acpi/acpi.h> 20181624Skmacy 21181624Skmacy#include <stdbool.h> 22181624Skmacy#include <stdio.h> 23181624Skmacy#include <stdlib.h> 24181624Skmacy#include <inttypes.h> 25181624Skmacy 26181624Skmacy#include <utils/attribute.h> 27181624Skmacy#include <utils/util.h> 28181624Skmacy#include <utils/fence.h> 29181624Skmacy 30181624Skmacy/* HPET timer config bits - these can't be changed, but allow us to 31181624Skmacy * find out details of the timer */ 32181624Skmacy 33181624Skmacyenum { 34181624Skmacy /* 0 is reserved */ 35181624Skmacy /* 0 if edge triggered, 1 if level triggered. */ 36181624Skmacy TN_INT_TYPE_CNF = 1, 37181624Skmacy /* Set to 1 to cause an interrupt when main timer hits comparator for this timer */ 38181624Skmacy TN_INT_ENB_CNF = 2, 39181624Skmacy /* If this bit is 1 you can write a 1 to it for periodic interrupts, 40181624Skmacy * or a 0 for non-periodic interrupts */ 41181624Skmacy TN_TYPE_CNF = 3, 42181624Skmacy /* If this bit is 1, hardware supports periodic mode for this timer */ 43181624Skmacy TN_PER_INT_CAP = 4, 44181624Skmacy /* 1 = timer is 64 bit, 0 = timer is 32 bit */ 45181624Skmacy TN_SIZE_CAP = 5, 46181624Skmacy /* Writing 1 to this bit allows software to directly set a periodic timers accumulator */ 47181624Skmacy TN_VAL_SET_CNF = 6, 48181624Skmacy /* 7 is reserved */ 49181624Skmacy /* Set this bit to force the timer to be a 32-bit timer (only works on a 64-bit timer) */ 50181624Skmacy TN_32MODE_CNF = 8, 51181624Skmacy /* 5 bit wide field (9:13). Specifies routing for IO APIC if using */ 52181624Skmacy TN_INT_ROUTE_CNF = 9, 53181624Skmacy /* Set this bit to force interrupt delivery to the front side bus, don't use the IO APIC */ 54251767Sgibbs TN_FSB_EN_CNF = 14, 55251767Sgibbs /* If this bit is one, bit TN_FSB_EN_CNF can be set */ 56251767Sgibbs TN_FSB_INT_DEL_CAP = 15, 57251767Sgibbs /* Bits 16:31 are reserved */ 58251767Sgibbs /* Read-only 32-bit field that specifies which routes in the IO APIC this timer can be configured 59181624Skmacy to take */ 60181624Skmacy TN_INT_ROUTE_CAP = 32 61181624Skmacy}; 62181624Skmacy 63181624Skmacy/* General HPET config bits */ 64181624Skmacyenum { 65181624Skmacy /* 1 if main counter is running and interrupts are enabled */ 66181624Skmacy ENABLE_CNF = 0, 67181624Skmacy /* 1 if LegacyReplacementRoute is being used */ 68181624Skmacy LEG_RT_CNF = 1 69181624Skmacy}; 70181624Skmacy 71251767Sgibbs/* MSI registers - used to configure front side bus delivery of the 72181624Skmacy * HPET interrupt. This allows us to avoid writing an I/O APIC driver. 73181624Skmacy * 74181624Skmacy * For details see section 10.10 "APIC message passing mechanism 75181624Skmacy * and protocol (P6 family,pentium processors)" in "Intel 64 and IA-32 76181624Skmacy * Architectures Software Developers Manual, Volume 3 (3A, 3B & 3C), 77181624Skmacy * System Programming Guide" */ 78181624Skmacy/* Message value register layout */ 79181624Skmacyenum { 80181624Skmacy /* 0:7 irq_vector */ 81181624Skmacy IRQ_VECTOR = 0, 82181624Skmacy /* 8:10 */ 83181624Skmacy DELIVERY_MODE = 8, 84181624Skmacy /* 11:13 reserved */ 85181624Skmacy LEVEL_TRIGGER = 14, 86181624Skmacy TRIGGER_MODE = 15, 87181624Skmacy /* 16:32 reserved */ 88181624Skmacy}; 89181624Skmacy 90181624Skmacy/* Message address register layout */ 91181624Skmacyenum { 92181624Skmacy /* 0:1 reserved */ 93181624Skmacy DESTINATION_MODE = 2, 94181624Skmacy REDIRECTION_HINT = 3, 95181624Skmacy /* 4:11 reserved */ 96181624Skmacy /* 12:19 Destination ID */ 97181624Skmacy DESTINATION_ID = 12, 98181624Skmacy /* 20:31 Fixed value 0x0FEE */ 99181624Skmacy FIXED = 20 100181624Skmacy}; 101181624Skmacy 102181624Skmacy#define CAP_ID_REG 0x0 103181624Skmacy#define GENERAL_CONFIG_REG 0x10 104181624Skmacy#define MAIN_COUNTER_REG 0xF0 105181624Skmacy#define TIMERS_OFFSET 0x100 106181624Skmacy 107181624Skmacystatic inline uint64_t *hpet_get_general_config(void *vaddr) 108181624Skmacy{ 109181624Skmacy return (uint64_t*)((uintptr_t)vaddr + GENERAL_CONFIG_REG); 110181624Skmacy} 111181624Skmacy 112181624Skmacystatic inline uint64_t *hpet_get_main_counter(void *vaddr) 113181624Skmacy{ 114181624Skmacy return (uint64_t*)((uintptr_t)vaddr + MAIN_COUNTER_REG); 115181624Skmacy} 116181624Skmacy 117181624Skmacystatic inline uint64_t *hpet_get_cap_id(void *vaddr) 118181624Skmacy{ 119181624Skmacy return (uint64_t*)((uintptr_t)vaddr + CAP_ID_REG); 120181624Skmacy} 121181624Skmacy 122181624Skmacystatic inline hpet_timer_t *hpet_get_hpet_timer(void *vaddr, unsigned int timer) 123181624Skmacy{ 124181624Skmacy return ((hpet_timer_t*)((uintptr_t)vaddr + TIMERS_OFFSET)) + timer; 125181624Skmacy} 126181624Skmacy 127181624Skmacyint hpet_start(const hpet_t *hpet) 128181624Skmacy{ 129181624Skmacy 130181624Skmacy hpet_timer_t *timer = hpet_get_hpet_timer(hpet->base_addr, 0); 131251767Sgibbs /* enable the global timer */ 132251767Sgibbs *hpet_get_general_config(hpet->base_addr) |= BIT(ENABLE_CNF); 133251767Sgibbs 134251767Sgibbs /* make sure the comparator is 0 before we turn time0 on*/ 135251767Sgibbs timer->comparator = 0llu; 136251767Sgibbs COMPILER_MEMORY_RELEASE(); 137251767Sgibbs 138251767Sgibbs /* turn timer0 on */ 139251767Sgibbs timer->config |= BIT(TN_INT_ENB_CNF); 140251767Sgibbs 141181624Skmacy /* ensure the compiler sends the writes to the hardware */ 142181624Skmacy COMPILER_MEMORY_RELEASE(); 143181624Skmacy return 0; 144181624Skmacy} 145181624Skmacy 146181624Skmacyint hpet_stop(const hpet_t *hpet) 147181624Skmacy{ 148181624Skmacy hpet_timer_t *timer = hpet_get_hpet_timer(hpet->base_addr, 0); 149181624Skmacy 150181624Skmacy /* turn off timer0 */ 151181624Skmacy timer->config &= ~(BIT(TN_INT_ENB_CNF)); 152181624Skmacy 153 /* turn the global timer off */ 154 *hpet_get_general_config(hpet->base_addr) &= ~BIT(ENABLE_CNF); 155 156 /* ensure the compiler sends the writes to the hardware */ 157 COMPILER_MEMORY_RELEASE(); 158 return 0; 159} 160 161uint64_t hpet_get_time(const hpet_t *hpet) 162{ 163 uint64_t time; 164 165 do { 166 time = *hpet_get_main_counter(hpet->base_addr); 167 COMPILER_MEMORY_ACQUIRE(); 168 /* race condition on 32-bit systems: check the bottom 32 bits didn't overflow */ 169 } while (CONFIG_WORD_SIZE == 32 && ((uint32_t) (time >> 32llu)) != ((uint32_t *)hpet_get_main_counter(hpet->base_addr))[1]); 170 171 return time * hpet->period_ns; 172} 173 174int hpet_set_timeout(const hpet_t *hpet, uint64_t absolute_ns) 175{ 176 hpet_timer_t *timer = hpet_get_hpet_timer(hpet->base_addr, 0); 177 uint64_t absolute_fs = absolute_ns / hpet->period_ns; 178 179 timer->comparator = absolute_fs; 180 COMPILER_MEMORY_RELEASE(); 181 182 if (hpet_get_time(hpet) > absolute_ns) { 183 return ETIME; 184 } 185 186 return 0; 187} 188 189bool hpet_supports_fsb_delivery(void *vaddr) 190{ 191 hpet_timer_t *timer0 = hpet_get_hpet_timer(vaddr, 0); 192 uint32_t timer0_config_low = timer0->config; 193 return !!(timer0_config_low & BIT(TN_FSB_INT_DEL_CAP)); 194} 195 196uint32_t hpet_ioapic_irq_delivery_mask(void *vaddr) 197{ 198 hpet_timer_t *timer0 = hpet_get_hpet_timer(vaddr, 0); 199 uint32_t irq_mask = timer0->config >> TN_INT_ROUTE_CAP; 200 return irq_mask; 201} 202 203uint32_t hpet_level(void *vaddr) 204{ 205 hpet_timer_t *timer0 = hpet_get_hpet_timer(vaddr, 0); 206 return timer0->config & BIT(TN_INT_TYPE_CNF); 207} 208 209int hpet_init(hpet_t *hpet, hpet_config_t config) 210{ 211 hpet->base_addr = config.vaddr; 212 hpet_timer_t *hpet_timer = hpet_get_hpet_timer(hpet->base_addr, 0); 213 214 uint32_t timer0_config_low = (uint32_t) hpet_timer->config; 215 216 /* check that this timer is edge triggered */ 217 if (timer0_config_low & BIT(TN_INT_TYPE_CNF)) { 218 ZF_LOGE("This driver expects the timer to be edge triggered"); 219 return -1; 220 } 221 222 /* check that this timer is 64 bit */ 223 if (!(timer0_config_low & BIT(TN_SIZE_CAP))) { 224 ZF_LOGE("This driver expects hpet timer0 to be 64bit"); 225 return -1; 226 } 227 228 if (config.ioapic_delivery) { 229 /* Check if this IO/APIC offset is valid */ 230 uint32_t irq_mask = hpet_timer->config >> TN_INT_ROUTE_CAP; 231 if (!(BIT(config.irq) & irq_mask)) { 232 ZF_LOGE("IRQ %d not in the support mask 0x%x", config.irq, irq_mask); 233 return -1; 234 } 235 /* Remove any legacy replacement route so our interrupts go where we want them 236 * NOTE: PIT will cease to function from here on */ 237 *hpet_get_general_config(hpet->base_addr) &= ~BIT(LEG_RT_CNF); 238 /* Make sure we're not deliverying by MSI */ 239 hpet_timer->config &= ~BIT(TN_FSB_EN_CNF); 240 /* Put the IO/APIC offset in (this is called an irq, but in reality it is 241 * an index into whichever IO/APIC the HPET delivers to */ 242 hpet_timer->config &= ~(MASK(5) << TN_INT_ROUTE_CNF); 243 hpet_timer->config |= config.irq << TN_INT_ROUTE_CNF; 244 } else { 245 /* check that this timer supports front size bus delivery */ 246 if (!(timer0_config_low & BIT(TN_FSB_INT_DEL_CAP))) { 247 ZF_LOGE("Requested fsb delivery, but timer0 does not support"); 248 return ENOSYS; 249 } 250 251 /* set timer 0 to delivery interrupts via the front side bus (using MSIs) */ 252 hpet_timer->config |= BIT(TN_FSB_EN_CNF); 253 254 /* set up the message address register and message value register so we receive 255 * MSIs for timer 0*/ 256 hpet_timer->fsb_irr = 257 /* top 32 bits is the message address register */ 258 ((0x0FEEllu << FIXED) << 32llu) 259 /* bottom 32 bits is the message value register */ 260 | config.irq; 261 } 262 COMPILER_MEMORY_RELEASE(); 263 264 /* read the period of the timer (its in femptoseconds) and calculate no of ticks per ns */ 265 uint32_t tick_period_fs = (uint32_t) (*hpet_get_cap_id(hpet->base_addr) >> 32llu); 266 hpet->period_ns = tick_period_fs / 1000000; 267 268 return 0; 269} 270 271int hpet_parse_acpi(acpi_t *acpi, pmem_region_t *region) 272{ 273 if (!acpi || !region) { 274 ZF_LOGE("arguments cannot be NULL"); 275 return EINVAL; 276 } 277 278 acpi_hpet_t *header = (acpi_hpet_t *) acpi_find_region(acpi, ACPI_HPET); 279 if (header == NULL) { 280 ZF_LOGE("Could not find HPET ACPI header"); 281 return ENOSYS; 282 } 283 284 region->base_addr = header->base_address.address; 285 region->length = header->header.length; 286 return 0; 287} 288