Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/arm/generic_timer.c
Show First 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | |||||
#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */ | #define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */ | ||||
#define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */ | #define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */ | ||||
#define GT_CNTKCTL_EVNTI (0xf << 4) /* Virtual counter event bits */ | #define GT_CNTKCTL_EVNTI (0xf << 4) /* Virtual counter event bits */ | ||||
#define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */ | #define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */ | ||||
#define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */ | #define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */ | ||||
#define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */ | #define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */ | ||||
#define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */ | #define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */ | ||||
struct arm_tmr_softc; | |||||
struct arm_tmr_irq { | |||||
struct resource *res; | |||||
void *ihl; | |||||
int rid; | |||||
}; | |||||
struct arm_tmr_softc { | struct arm_tmr_softc { | ||||
struct resource *res[GT_IRQ_COUNT]; | struct arm_tmr_irq irqs[GT_IRQ_COUNT]; | ||||
void *ihl[GT_IRQ_COUNT]; | |||||
uint64_t (*get_cntxct)(bool); | uint64_t (*get_cntxct)(bool); | ||||
uint32_t clkfreq; | uint32_t clkfreq; | ||||
struct eventtimer et; | struct eventtimer et; | ||||
bool physical; | bool physical; | ||||
}; | }; | ||||
static struct arm_tmr_softc *arm_tmr_sc = NULL; | static struct arm_tmr_softc *arm_tmr_sc = NULL; | ||||
#ifdef DEV_ACPI | |||||
static struct resource_spec timer_acpi_spec[] = { | |||||
{ SYS_RES_IRQ, GT_PHYS_SECURE, RF_ACTIVE | RF_OPTIONAL }, | |||||
{ SYS_RES_IRQ, GT_PHYS_NONSECURE, RF_ACTIVE }, | |||||
{ SYS_RES_IRQ, GT_VIRT, RF_ACTIVE }, | |||||
{ SYS_RES_IRQ, GT_HYP_PHYS, RF_ACTIVE | RF_OPTIONAL }, | |||||
{ -1, 0 } | |||||
}; | |||||
#endif | |||||
static const struct arm_tmr_irq_defs { | static const struct arm_tmr_irq_defs { | ||||
int idx; | int idx; | ||||
const char *name; | const char *name; | ||||
int flags; | int flags; | ||||
} arm_tmr_irq_defs[] = { | } arm_tmr_irq_defs[] = { | ||||
{ | { | ||||
.idx = GT_PHYS_SECURE, | .idx = GT_PHYS_SECURE, | ||||
.name = "sec-phys", | .name = "sec-phys", | ||||
▲ Show 20 Lines • Show All 268 Lines • ▼ Show 20 Lines | arm_tmr_intr(void *arg) | ||||
} | } | ||||
if (sc->et.et_active) | if (sc->et.et_active) | ||||
sc->et.et_event_cb(&sc->et, sc->et.et_arg); | sc->et.et_event_cb(&sc->et, sc->et.et_arg); | ||||
return (FILTER_HANDLED); | return (FILTER_HANDLED); | ||||
} | } | ||||
static int | |||||
arm_tmr_attach_irq(device_t dev, struct arm_tmr_softc *sc, | |||||
const struct arm_tmr_irq_defs *irq_def, int rid, int flags) | |||||
{ | |||||
sc->irqs[irq_def->idx].res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | |||||
&rid, flags); | |||||
if (sc->irqs[irq_def->idx].res == NULL) { | |||||
if (bootverbose || (flags & RF_OPTIONAL) == 0) { | |||||
device_printf(dev, | |||||
"could not allocate irq for %s interrupt '%s'\n", | |||||
(flags & RF_OPTIONAL) != 0 ? "optional" : | |||||
"required", irq_def->name); | |||||
} | |||||
if ((flags & RF_OPTIONAL) == 0) | |||||
return (ENXIO); | |||||
} else if (bootverbose) { | |||||
device_printf(dev, "allocated irq for '%s'\n", irq_def->name); | |||||
} | |||||
return (0); | |||||
} | |||||
#ifdef FDT | #ifdef FDT | ||||
static int | static int | ||||
arm_tmr_fdt_probe(device_t dev) | arm_tmr_fdt_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if (has_names) { | ||||
/* | /* | ||||
* Warn about failing to activate if we did actually | * Warn about failing to activate if we did actually | ||||
* have the name present. | * have the name present. | ||||
*/ | */ | ||||
flags &= ~RF_OPTIONAL; | flags &= ~RF_OPTIONAL; | ||||
} | } | ||||
sc->res[irq_def->idx] = bus_alloc_resource_any(dev, | error = arm_tmr_attach_irq(dev, sc, irq_def, rid, flags); | ||||
SYS_RES_IRQ, &rid, flags); | if (error != 0) | ||||
if (sc->res[irq_def->idx] == NULL) { | |||||
device_printf(dev, | |||||
"could not allocate irq for %s interrupt '%s'\n", | |||||
(flags & RF_OPTIONAL) != 0 ? "optional" : | |||||
"required", irq_def->name); | |||||
if ((flags & RF_OPTIONAL) == 0) { | |||||
error = ENXIO; | |||||
goto out; | goto out; | ||||
} | } | ||||
continue; | |||||
} | |||||
if (bootverbose) { | |||||
device_printf(dev, | |||||
"allocated irq for '%s'\n", irq_def->name); | |||||
} | |||||
} | |||||
error = arm_tmr_attach(dev); | error = arm_tmr_attach(dev); | ||||
out: | out: | ||||
if (error != 0) { | if (error != 0) { | ||||
for (i = 0; i < GT_IRQ_COUNT; i++) { | for (i = 0; i < GT_IRQ_COUNT; i++) { | ||||
if (sc->res[i] != NULL) { | if (sc->irqs[i].res != NULL) { | ||||
/* | |||||
* rid may not match the index into sc->res in | |||||
* a number of cases; e.g., optional sec-phys or | |||||
* interrupt-names specifying them in a | |||||
* different order than expected. | |||||
*/ | |||||
bus_release_resource(dev, SYS_RES_IRQ, | bus_release_resource(dev, SYS_RES_IRQ, | ||||
rman_get_rid(sc->res[i]), sc->res[i]); | sc->irqs[i].rid, sc->irqs[i].res); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | arm_tmr_acpi_probe(device_t dev) | ||||
device_set_desc(dev, "ARM Generic Timer"); | device_set_desc(dev, "ARM Generic Timer"); | ||||
return (BUS_PROBE_NOWILDCARD); | return (BUS_PROBE_NOWILDCARD); | ||||
} | } | ||||
static int | static int | ||||
arm_tmr_acpi_attach(device_t dev) | arm_tmr_acpi_attach(device_t dev) | ||||
{ | { | ||||
const struct arm_tmr_irq_defs *irq_def; | |||||
struct arm_tmr_softc *sc; | struct arm_tmr_softc *sc; | ||||
int error; | int error; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
if (bus_alloc_resources(dev, timer_acpi_spec, sc->res)) { | for (int i = 0; i < nitems(arm_tmr_irq_defs); i++) { | ||||
device_printf(dev, "could not allocate resources\n"); | irq_def = &arm_tmr_irq_defs[i]; | ||||
return (ENXIO); | error = arm_tmr_attach_irq(dev, sc, irq_def, irq_def->idx, | ||||
irq_def->flags); | |||||
if (error != 0) | |||||
goto out; | |||||
} | } | ||||
error = arm_tmr_attach(dev); | error = arm_tmr_attach(dev); | ||||
if (error != 0) | out: | ||||
bus_release_resources(dev, timer_acpi_spec, sc->res); | if (error != 0) { | ||||
for (int i = 0; i < GT_IRQ_COUNT; i++) { | |||||
if (sc->irqs[i].res != NULL) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, | |||||
sc->irqs[i].rid, sc->irqs[i].res); | |||||
} | |||||
} | |||||
} | |||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
static int | static int | ||||
arm_tmr_attach(device_t dev) | arm_tmr_attach(device_t dev) | ||||
{ | { | ||||
struct arm_tmr_softc *sc; | struct arm_tmr_softc *sc; | ||||
Show All 37 Lines | if (sc->clkfreq == 0) { | ||||
device_printf(dev, "No clock frequency specified\n"); | device_printf(dev, "No clock frequency specified\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Confirm that non-optional irqs were allocated before coming in. */ | /* Confirm that non-optional irqs were allocated before coming in. */ | ||||
for (i = 0; i < nitems(arm_tmr_irq_defs); i++) { | for (i = 0; i < nitems(arm_tmr_irq_defs); i++) { | ||||
irq_def = &arm_tmr_irq_defs[i]; | irq_def = &arm_tmr_irq_defs[i]; | ||||
MPASS(sc->res[irq_def->idx] != NULL || | MPASS(sc->irqs[irq_def->idx].res != NULL || | ||||
(irq_def->flags & RF_OPTIONAL) != 0); | (irq_def->flags & RF_OPTIONAL) != 0); | ||||
} | } | ||||
#ifdef __aarch64__ | #ifdef __aarch64__ | ||||
/* Use the virtual timer if we have one. */ | /* Use the virtual timer if we have one. */ | ||||
if (sc->res[GT_VIRT] != NULL) { | if (sc->irqs[GT_VIRT].res != NULL) { | ||||
kevans: This is guaranteed by virtue of the fact that GT_VIRT is not RF_OPTIONAL, we can do this… | |||||
Done Inline ActionsI'll replace with HAS_HYP as we need to use the physical timer in the kernel with bhyve. andrew: I'll replace with `HAS_HYP` as we need to use the physical timer in the kernel with bhyve. | |||||
sc->physical = false; | sc->physical = false; | ||||
first_timer = GT_VIRT; | first_timer = GT_VIRT; | ||||
last_timer = GT_VIRT; | last_timer = GT_VIRT; | ||||
} else | } else | ||||
#endif | #endif | ||||
/* Otherwise set up the secure and non-secure physical timers. */ | /* Otherwise set up the secure and non-secure physical timers. */ | ||||
{ | { | ||||
sc->physical = true; | sc->physical = true; | ||||
first_timer = GT_PHYS_SECURE; | first_timer = GT_PHYS_SECURE; | ||||
last_timer = GT_PHYS_NONSECURE; | last_timer = GT_PHYS_NONSECURE; | ||||
} | } | ||||
arm_tmr_sc = sc; | arm_tmr_sc = sc; | ||||
/* Setup secure, non-secure and virtual IRQs handler */ | /* Setup secure, non-secure and virtual IRQs handler */ | ||||
for (i = first_timer; i <= last_timer; i++) { | for (i = first_timer; i <= last_timer; i++) { | ||||
Not Done Inline ActionsIterate over all nitems(sc->irqs), filter on actual rid kevans: Iterate over all nitems(sc->irqs), filter on actual rid | |||||
/* If we do not have the interrupt, skip it. */ | /* If we do not have the interrupt, skip it. */ | ||||
if (sc->res[i] == NULL) | if (sc->irqs[i].res == NULL) | ||||
continue; | continue; | ||||
error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK, | error = bus_setup_intr(dev, sc->irqs[i].res, INTR_TYPE_CLK, | ||||
arm_tmr_intr, NULL, sc, &sc->ihl[i]); | arm_tmr_intr, NULL, sc, &sc->irqs[i].ihl); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "Unable to alloc int resource.\n"); | device_printf(dev, "Unable to alloc int resource.\n"); | ||||
for (int j = first_timer; j < i; j++) | for (int j = first_timer; j < i; j++) | ||||
Not Done Inline Actions0 to i kevans: 0 to i | |||||
bus_teardown_intr(dev, sc->res[j], &sc->ihl[j]); | bus_teardown_intr(dev, sc->irqs[j].res, | ||||
&sc->irqs[j].ihl); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
} | } | ||||
/* Disable the virtual timer until we are ready */ | /* Disable the virtual timer until we are ready */ | ||||
if (sc->res[GT_VIRT] != NULL) | if (sc->irqs[GT_VIRT].res != NULL) | ||||
Not Done Inline ActionsDitto above; we can do this unconditionally, we know it's present. kevans: Ditto above; we can do this unconditionally, we know it's present. | |||||
arm_tmr_disable(false); | arm_tmr_disable(false); | ||||
/* And the physical */ | /* And the physical */ | ||||
if ((sc->res[GT_PHYS_SECURE] != NULL || | if ((sc->irqs[GT_PHYS_SECURE].res != NULL || | ||||
sc->res[GT_PHYS_NONSECURE] != NULL) && HAS_PHYS) | sc->irqs[GT_PHYS_NONSECURE].res != NULL) && HAS_PHYS) | ||||
Not Done Inline ActionsThese could be reduced to just sc->physical && HAS_PHYS or even just HAS_PHYS; we're guaranteed at least the phys timer (if not the sec-phys timer) kevans: These could be reduced to just `sc->physical && HAS_PHYS` or even just `HAS_PHYS`; we're… | |||||
Done Inline ActionsI'll replace the resource checks with a KASSERT. sc->physical is incorrect here because firmware may leave the physical timer enabled so this is careful to disable the physical timer if present. andrew: I'll replace the resource checks with a `KASSERT`. `sc->physical` is incorrect here because… | |||||
arm_tmr_disable(true); | arm_tmr_disable(true); | ||||
arm_tmr_timecount.tc_frequency = sc->clkfreq; | arm_tmr_timecount.tc_frequency = sc->clkfreq; | ||||
tc_init(&arm_tmr_timecount); | tc_init(&arm_tmr_timecount); | ||||
sc->et.et_name = "ARM MPCore Eventtimer"; | sc->et.et_name = "ARM MPCore Eventtimer"; | ||||
sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; | sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; | ||||
sc->et.et_quality = 1000; | sc->et.et_quality = 1000; | ||||
▲ Show 20 Lines • Show All 112 Lines • Show Last 20 Lines |
This is guaranteed by virtue of the fact that GT_VIRT is not RF_OPTIONAL, we can do this unconditionally for arm64 (and the other block unconditionally for arm)