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