diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c --- a/sys/arm/arm/generic_timer.c +++ b/sys/arm/arm/generic_timer.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -58,10 +59,7 @@ #include #include #include - -#if defined(__arm__) #include /* For arm_set_delay */ -#endif #if defined(__aarch64__) #include @@ -78,6 +76,8 @@ #include #endif +#include "pic_if.h" + #define GT_PHYS_SECURE 0 #define GT_PHYS_NONSECURE 1 #define GT_VIRT 2 @@ -104,10 +104,14 @@ struct resource *res; void *ihl; struct arm_tmr_softc *sc; +#if defined(__aarch64__) + struct intr_irqsrc isrc; +#endif u_int flags; #define TMR_IRQ_PHYS (1 << 0) #define TMR_IRQ_ET (1 << 1) #define TMR_IRQ_CHILD (1 << 2) + u_int irq; }; struct arm_tmr_softc { @@ -116,6 +120,9 @@ uint32_t clkfreq; struct eventtimer et; bool physical; +#if defined(__aarch64__) + struct rman intr_rman; +#endif }; static struct arm_tmr_softc *arm_tmr_sc = NULL; @@ -128,6 +135,10 @@ { -1, 0 } }; +struct arm_tmr_ivar { + struct resource_list rl; +}; + static uint32_t arm_tmr_fill_vdso_timehands(struct vdso_timehands *vdso_th, struct timecounter *tc); static void arm_tmr_do_delay(int usec, void *); @@ -363,17 +374,32 @@ { struct arm_tmr_softc *sc; struct arm_tmr_irq *irq; + struct trapframe *tf; int ctrl; - bool physical; + bool physical, mask; irq = (struct arm_tmr_irq *)arg; - physical = (irq->flags & TMR_IRQ_PHYS) != 0; + if ((irq->flags & TMR_IRQ_CHILD) != 0) { + /* The child should manage masking the interrupt */ + mask = false; + tf = curthread->td_intr_frame; + if (intr_isrc_dispatch(&irq->isrc, tf) != 0) { + printf("Stray timer irq %u\n", irq->irq); + mask = true; + } + } else { + mask = true; + } + + if (mask) { + physical = (irq->flags & TMR_IRQ_PHYS) != 0; - ctrl = get_ctrl(physical); - if (ctrl & GT_CTRL_INT_STAT) { - ctrl |= GT_CTRL_INT_MASK; - set_ctrl(ctrl, physical); + ctrl = get_ctrl(physical); + if (ctrl & GT_CTRL_INT_STAT) { + ctrl |= GT_CTRL_INT_MASK; + set_ctrl(ctrl, physical); + } } if ((irq->flags & TMR_IRQ_ET) != 0) { @@ -457,11 +483,33 @@ } #endif +#if defined(__aarch64__) +static void +arm_tmr_add_vtimer(device_t dev) +{ + struct arm_tmr_ivar *devi; + device_t child; + + child = device_add_child(dev, "vtimer", -1); + if (child == NULL) { + device_printf(dev, "Could not add vtimer child\n"); + return; + } + + devi = malloc(sizeof(*devi), M_DEVBUF, M_WAITOK | M_ZERO); + resource_list_init(&devi->rl); + device_set_ivars(child, devi); + + BUS_SET_RESOURCE(dev, child, SYS_RES_IRQ, 0, GT_VIRT, 1); +} +#endif + static int arm_tmr_attach(device_t dev) { struct arm_tmr_softc *sc; struct resource *res[GT_IRQ_COUNT]; + const char *name; #ifdef FDT phandle_t node; pcell_t clock; @@ -509,11 +557,12 @@ for (i = 0; i < GT_IRQ_COUNT; i++) { sc->irqs[i].res = res[i]; sc->irqs[i].sc = sc; + sc->irqs[i].irq = i; } #ifdef __aarch64__ /* Use the virtual timer if we have one. */ - if (sc->irqs[GT_VIRT].res != NULL) { + if (sc->irqs[GT_VIRT].res != NULL && !has_hyp()) { sc->physical = false; } else #endif @@ -545,6 +594,24 @@ } } +#if defined(__aarch64__) + name = device_get_nameunit(dev); + for (i = 0; i < GT_IRQ_COUNT; i++) { + intr_isrc_register(&sc->irqs[i].isrc, dev, 0, "%st%u", name, i); + } + intr_pic_register(dev, 0); + sc->intr_rman.rm_type = RMAN_ARRAY; + sc->intr_rman.rm_descr = "Timer Interrupts"; + if (rman_init(&sc->intr_rman) != 0 || + rman_manage_region(&sc->intr_rman, 0, ~0) != 0) + panic("%s: failed to set up rman.", __func__); + + /* Add a vtimer child */ + if (sc->physical) + arm_tmr_add_vtimer(dev); + bus_generic_attach(dev); +#endif + /* Disable the virtual timer until we are ready */ if (sc->irqs[GT_VIRT].res != NULL) arm_tmr_disable(false); @@ -574,9 +641,183 @@ return (0); } +#if defined(__aarch64__) +struct intr_map_data_timer { + struct intr_map_data hdr; + u_int irq; +}; + +static int +arm_tmr_set_resource(device_t dev, device_t child, int type, int rid, + rman_res_t start, rman_res_t count) +{ + struct intr_map_data_timer *irq_data; + struct arm_tmr_ivar *devi; + struct resource_list_entry *rle; + u_int irq; + + if (type != SYS_RES_IRQ) + return (EINVAL); + if (count != 1) + return (EINVAL); + + irq_data = (struct intr_map_data_timer *)intr_alloc_map_data( + INTR_MAP_DATA_PLAT_1, sizeof(*irq_data), M_WAITOK | M_ZERO); + irq_data->irq = start; + irq = intr_map_irq(dev, 0, (struct intr_map_data *)irq_data); + + devi = device_get_ivars(child); + rle = resource_list_add(&devi->rl, type, rid, irq, + irq + count - 1, count); + if (rle == NULL) + return (ENXIO); + + return (0); +} + +static struct resource * +arm_tmr_alloc_resource(device_t bus, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct arm_tmr_softc *sc; + struct resource_list_entry *rle; + struct resource_list *rl; + struct resource *rv; + int isdefault; + + if (type != SYS_RES_IRQ) + return (NULL); + if (device_get_parent(child) != bus) + return (NULL); + + rle = NULL; + isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1); + if (isdefault) { + rl = BUS_GET_RESOURCE_LIST(bus, child); + if (rl == NULL) + return (NULL); + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) + return (NULL); + if (rle->res != NULL) + panic("%s: resource entry is busy", __func__); + start = rle->start; + count = rle->count; + end = rle->end; + } + sc = device_get_softc(bus); + rv = rman_reserve_resource(&sc->intr_rman, start, end, count, flags, + child); + if (rv == NULL) + return (NULL); + rman_set_rid(rv, *rid); + if ((flags & RF_ACTIVE) != 0 && + bus_activate_resource(child, type, *rid, rv) != 0) { + rman_release_resource(rv); + return (NULL); + } + + return (rv); +} + +static struct resource_list * +arm_tmr_get_resource_list(device_t bus __unused, device_t child) +{ + struct arm_tmr_ivar *devi; + + devi = device_get_ivars(child); + return (&devi->rl); +} + +static int +arm_tmr_print_child(device_t dev, device_t child) +{ + struct arm_tmr_ivar *devi; + int retval; + + devi = device_get_ivars(child); + + retval = bus_print_child_header(dev, child); + resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%jd"); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static void +arm_tmr_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct arm_tmr_softc *sc; + int i; + + printf("%s\n", __func__); + sc = device_get_softc(dev); + for (i = 0; i < nitems(sc->irqs); i++) { + if (isrc == &sc->irqs[i].isrc) { + sc->irqs[i].flags &= ~TMR_IRQ_CHILD; + return; + } + } + + panic("%s: Invalid interrupt", __func__); +} + +static void +arm_tmr_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct arm_tmr_softc *sc; + int i; + + printf("%s\n", __func__); + sc = device_get_softc(dev); + for (i = 0; i < nitems(sc->irqs); i++) { + if (isrc == &sc->irqs[i].isrc) { + sc->irqs[i].flags |= TMR_IRQ_CHILD; + return; + } + } + + panic("%s: Invalid interrupt", __func__); +} + +static int +arm_tmr_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct intr_map_data_timer *irq_data; + struct arm_tmr_softc *sc; + + if (data->type != INTR_MAP_DATA_PLAT_1) + return (EINVAL); + + sc = device_get_softc(dev); + irq_data = (struct intr_map_data_timer *)data; + MPASS(irq_data->irq < nitems(sc->irqs)); + + *isrcp = &sc->irqs[irq_data->irq].isrc; + return (0); +} +#endif + static device_method_t arm_tmr_methods[] = { DEVMETHOD(device_attach, arm_tmr_attach), +#if defined(__aarch64__) + /* Bus interface */ + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_config_intr, bus_generic_config_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_set_resource, arm_tmr_set_resource), + DEVMETHOD(bus_alloc_resource, arm_tmr_alloc_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_get_resource_list, arm_tmr_get_resource_list), + DEVMETHOD(bus_print_child, arm_tmr_print_child), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, arm_tmr_disable_intr), + DEVMETHOD(pic_enable_intr, arm_tmr_enable_intr), + DEVMETHOD(pic_map_intr, arm_tmr_map_intr), +#endif DEVMETHOD_END, };