Index: head/sys/arm64/conf/GENERIC =================================================================== --- head/sys/arm64/conf/GENERIC +++ head/sys/arm64/conf/GENERIC @@ -257,6 +257,7 @@ device ls1046_gpio # LS1046A GPIO controller device mv_gpio # Marvell GPIO controller device mvebu_pinctrl # Marvell Pinmux Controller +device pl061 # Arm PL061 GPIO controller device rk_gpio # RockChip GPIO Controller device rk_pinctrl # RockChip Pinmux Controller Index: head/sys/conf/files.arm64 =================================================================== --- head/sys/conf/files.arm64 +++ head/sys/conf/files.arm64 @@ -305,6 +305,9 @@ dev/iicbus/twsi/mv_twsi.c optional twsi fdt dev/iicbus/twsi/a10_twsi.c optional twsi fdt dev/iicbus/twsi/twsi.c optional twsi fdt +dev/gpio/pl061.c optional pl061 gpio +dev/gpio/pl061_acpi.c optional pl061 gpio acpi +dev/gpio/pl061_fdt.c optional pl061 gpio fdt dev/hwpmc/hwpmc_arm64.c optional hwpmc dev/hwpmc/hwpmc_arm64_md.c optional hwpmc dev/mbox/mbox_if.m optional soc_brcm_bcm2837 Index: head/sys/dev/gpio/pl061.h =================================================================== --- head/sys/dev/gpio/pl061.h +++ head/sys/dev/gpio/pl061.h @@ -0,0 +1,59 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Amazon.com, Inc. or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __DEV_PL061_H__ +#define __DEV_PL061_H__ + +DECLARE_CLASS(pl061_driver); + +#define PL061_NUM_GPIO 8 + +struct pl061_pin_irqsrc { + struct intr_irqsrc isrc; + uint32_t irq; + uint32_t mode; +}; + +struct pl061_softc { + device_t sc_dev; + device_t sc_busdev; + struct mtx sc_mtx; + struct resource *sc_mem_res; + struct resource *sc_irq_res; + void *sc_irq_hdlr; + int sc_mem_rid; + int sc_irq_rid; + struct pl061_pin_irqsrc sc_isrcs[PL061_NUM_GPIO]; +}; + +int pl061_attach(device_t); +int pl061_detach(device_t); + +#endif /* __DEV_PL061_H__ */ Index: head/sys/dev/gpio/pl061.c =================================================================== --- head/sys/dev/gpio/pl061.c +++ head/sys/dev/gpio/pl061.c @@ -0,0 +1,580 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Amazon.com, Inc. or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "pl061.h" + +#include "gpio_if.h" +#include "pic_if.h" + +#define PL061_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) +#define PL061_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) +#define PL061_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) +#define PL061_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define PL061_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) + +#if 0 +#define dprintf(fmt, args...) do { \ + printf(fmt, ##args); \ +} while (0) +#else +#define dprintf(fmt, args...) +#endif + +#define PL061_PIN_TO_ADDR(pin) (1 << (pin + 2)) +#define PL061_DATA 0x3FC +#define PL061_DIR 0x400 +#define PL061_INTSENSE 0x404 +#define PL061_INTBOTHEDGES 0x408 +#define PL061_INTEVENT 0x40C +#define PL061_INTMASK 0x410 +#define PL061_RAWSTATUS 0x414 +#define PL061_STATUS 0x418 +#define PL061_INTCLR 0x41C +#define PL061_MODECTRL 0x420 + +#define PL061_ALLOWED_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_INTR_EDGE_BOTH | \ + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \ + GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW ) + +#define PIC_INTR_ISRC(sc, irq) (&(sc->sc_isrcs[irq].isrc)) + +static device_t +pl061_get_bus(device_t dev) +{ + struct pl061_softc *sc; + + sc = device_get_softc(dev); + return (sc->sc_busdev); +} + +static int +pl061_pin_max(device_t dev, int *maxpin) +{ + *maxpin = PL061_NUM_GPIO - 1; + return (0); +} + +static int +pl061_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct pl061_softc *sc; + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + snprintf(name, GPIOMAXNAME, "p%u", pin); + name[GPIOMAXNAME - 1] = '\0'; + + return (0); +} + +static int +pl061_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct pl061_softc *sc; + uint8_t mask = 1 << pin; + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + PL061_LOCK(sc); + *flags = 0; + + if (mask & bus_read_1(sc->sc_mem_res, PL061_DIR)) + *flags |= GPIO_PIN_OUTPUT; + else + *flags |= GPIO_PIN_INPUT; + + PL061_UNLOCK(sc); + return (0); +} + +static int +pl061_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct pl061_softc *sc; + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + *caps = PL061_ALLOWED_CAPS; + + return (0); +} + +static void +mask_and_set(struct pl061_softc *sc, long a, uint8_t m, uint8_t b) +{ + uint8_t tmp; + + tmp = bus_read_1(sc->sc_mem_res, a); + tmp &= ~m; + tmp |= b; + bus_write_1(sc->sc_mem_res, a, tmp); + dprintf("%s: writing %#x to register %#lx\n", __func__, tmp, a); +} + +static int +pl061_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct pl061_softc *sc; + uint8_t mask = 1 << pin; + const uint32_t in_out = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + if (flags & ~PL061_ALLOWED_CAPS) + return (EINVAL); + + /* can't be both input and output */ + if ((flags & in_out) == in_out) + return (EINVAL); + + + PL061_LOCK(sc); + mask_and_set(sc, PL061_DIR, mask, flags & GPIO_PIN_OUTPUT ? mask : 0); + PL061_UNLOCK(sc); + return (0); +} + +static int +pl061_pin_get(device_t dev, uint32_t pin, uint32_t *value) +{ + struct pl061_softc *sc; + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + PL061_LOCK(sc); + if (bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin))) + *value = GPIO_PIN_HIGH; + else + *value = GPIO_PIN_LOW; + PL061_UNLOCK(sc); + + return (0); +} + +static int +pl061_pin_set(device_t dev, uint32_t pin, uint32_t value) +{ + struct pl061_softc *sc; + uint8_t d = (value == GPIO_PIN_HIGH) ? 0xff : 0x00; + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + PL061_LOCK(sc); + bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d); + PL061_UNLOCK(sc); + + return (0); +} + +static int +pl061_pin_toggle(device_t dev, uint32_t pin) +{ + struct pl061_softc *sc; + uint8_t d; + + sc = device_get_softc(dev); + if (pin >= PL061_NUM_GPIO) + return (EINVAL); + + PL061_LOCK(sc); + d = ~bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin)); + bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d); + PL061_UNLOCK(sc); + + return (0); +} + +static void +pl061_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct pl061_softc *sc; + uint8_t mask; + + sc = device_get_softc(dev); + mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq; + + dprintf("%s: calling disable interrupt %#x\n", __func__, mask); + PL061_LOCK(sc); + mask_and_set(sc, PL061_INTMASK, mask, 0); + PL061_UNLOCK(sc); +} + + + +static void +pl061_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct pl061_softc *sc; + uint8_t mask; + + sc = device_get_softc(dev); + mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq; + + + dprintf("%s: calling enable interrupt %#x\n", __func__, mask); + PL061_LOCK(sc); + mask_and_set(sc, PL061_INTMASK, mask, mask); + PL061_UNLOCK(sc); +} + +static int +pl061_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct pl061_softc *sc; + struct intr_map_data_gpio *gdata; + uint32_t irq; + + sc = device_get_softc(dev); + if (data->type != INTR_MAP_DATA_GPIO) + return (ENOTSUP); + + gdata = (struct intr_map_data_gpio *)data; + irq = gdata->gpio_pin_num; + if (irq >= PL061_NUM_GPIO) { + device_printf(dev, "invalid interrupt number %u\n", irq); + return (EINVAL); + } + + dprintf("%s: calling map interrupt %u\n", __func__, irq); + *isrcp = PIC_INTR_ISRC(sc, irq); + + return (0); +} + +static int +pl061_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct pl061_softc *sc; + struct intr_map_data_gpio *gdata; + struct pl061_pin_irqsrc *irqsrc; + uint32_t mode; + uint8_t mask; + + if (data == NULL) + return (ENOTSUP); + + sc = device_get_softc(dev); + gdata = (struct intr_map_data_gpio *)data; + irqsrc = (struct pl061_pin_irqsrc *)isrc; + + mode = gdata->gpio_intr_mode; + mask = 1 << gdata->gpio_pin_num; + + dprintf("%s: calling setup interrupt %u mode %#x\n", __func__, + irqsrc->irq, mode); + if (irqsrc->irq != gdata->gpio_pin_num) { + dprintf("%s: interrupts don't match\n", __func__); + return (EINVAL); + } + + if (isrc->isrc_handlers != 0) { + dprintf("%s: handler already attached\n", __func__); + return (irqsrc->mode == mode ? 0 : EINVAL); + } + irqsrc->mode = mode; + + PL061_LOCK(sc); + + if (mask & GPIO_INTR_EDGE_BOTH) { + mask_and_set(sc, PL061_INTBOTHEDGES, mask, mask); + mask_and_set(sc, PL061_INTSENSE, mask, 0); + } else if (mask & GPIO_INTR_EDGE_RISING) { + mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0); + mask_and_set(sc, PL061_INTSENSE, mask, 0); + mask_and_set(sc, PL061_INTEVENT, mask, mask); + } else if (mask & GPIO_INTR_EDGE_FALLING) { + mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0); + mask_and_set(sc, PL061_INTSENSE, mask, 0); + mask_and_set(sc, PL061_INTEVENT, mask, 0); + } else if (mask & GPIO_INTR_LEVEL_HIGH) { + mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0); + mask_and_set(sc, PL061_INTSENSE, mask, mask); + mask_and_set(sc, PL061_INTEVENT, mask, mask); + } else if (mask & GPIO_INTR_LEVEL_LOW) { + mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0); + mask_and_set(sc, PL061_INTSENSE, mask, mask); + mask_and_set(sc, PL061_INTEVENT, mask, 0); + } + PL061_UNLOCK(sc); + return (0); +} + +static int +pl061_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct pl061_softc *sc; + struct pl061_pin_irqsrc *irqsrc; + uint8_t mask; + + irqsrc = (struct pl061_pin_irqsrc *)isrc; + mask = 1 << irqsrc->irq; + dprintf("%s: calling teardown interrupt %#x\n", __func__, mask); + + sc = device_get_softc(dev); + if (isrc->isrc_handlers == 0) { + irqsrc->mode = GPIO_INTR_CONFORM; + PL061_LOCK(sc); + mask_and_set(sc, PL061_INTMASK, mask, 0); + PL061_UNLOCK(sc); + } + return (0); +} + +static void +pl061_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct pl061_softc *sc; + uint8_t mask; + + sc = device_get_softc(dev); + mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq; + dprintf("%s: calling post filter %#x\n", __func__, mask); + + bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask); +} + +static void +pl061_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct pl061_softc *sc; + uint8_t mask; + + sc = device_get_softc(dev); + mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq; + dprintf("%s: calling post ithread %#x\n", __func__, mask); + bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask); + + pl061_pic_enable_intr(dev, isrc); +} + +static void +pl061_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + pl061_pic_disable_intr(dev, isrc); +} + +static int +pl061_intr(void *arg) +{ + struct pl061_softc *sc; + struct trapframe *tf; + uint8_t status; + int pin; + + sc = (struct pl061_softc *)arg; + tf = curthread->td_intr_frame; + + status = bus_read_1(sc->sc_mem_res, PL061_STATUS); + + while (status != 0) { + pin = ffs(status) - 1; + status &= ~(1 << pin); + + if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, pin), tf) != 0) + device_printf(sc->sc_dev, "spurious interrupt %d\n", + pin); + + dprintf("got IRQ on %d\n", pin); + + } + return (FILTER_HANDLED); +} + +int +pl061_attach(device_t dev) +{ + struct pl061_softc *sc; + int ret; + int irq; + const char *name; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_mem_rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "can't allocate memory resource\n"); + return (ENXIO); + } + + sc->sc_irq_rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid, RF_ACTIVE); + + if (sc->sc_irq_res == NULL) { + device_printf(dev, "can't allocate IRQ resource\n"); + goto free_mem; + } + + ret = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + pl061_intr, NULL, sc, &sc->sc_irq_hdlr); + if (ret) { + device_printf(dev, "can't setup IRQ\n"); + goto free_pic; + } + + name = device_get_nameunit(dev); + + for (irq = 0; irq < PL061_NUM_GPIO; irq++) { + if (bootverbose) { + device_printf(dev, + "trying to register pin %d name %s\n", irq, name); + } + sc->sc_isrcs[irq].irq = irq; + sc->sc_isrcs[irq].mode = GPIO_INTR_CONFORM; + ret = intr_isrc_register(PIC_INTR_ISRC(sc, irq), dev, 0, + "%s", name); + if (ret) { + device_printf(dev, "can't register isrc %d\n", ret); + goto free_isrc; + } + } + + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { + device_printf(dev, "couldn't attach gpio bus\n"); + goto free_isrc; + } + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN); + + return (0); + +free_isrc: + /* + * XXX isrc_release_counters() not implemented + * for (irq = 0; irq < PL061_NUM_GPIO; irq++) + * intr_isrc_deregister(PIC_INTR_ISRC(sc, irq)); + */ + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, + sc->sc_irq_res); +free_pic: + /* + * XXX intr_pic_deregister: not implemented + * intr_pic_deregister(dev, 0); + */ + +free_mem: + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + + return (ENXIO); + +} + +int +pl061_detach(device_t dev) +{ + struct pl061_softc *sc; + sc = device_get_softc(dev); + + if (sc->sc_busdev) + gpiobus_detach_bus(dev); + + if (sc->sc_irq_hdlr != NULL) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdlr); + + if (sc->sc_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, + sc->sc_irq_res); + + if (sc->sc_mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + PL061_LOCK_DESTROY(sc); + return (0); +} + +static device_method_t pl061_methods[] = { + /* Device interface */ + DEVMETHOD(device_attach, pl061_attach), + DEVMETHOD(device_detach, pl061_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, pl061_get_bus), + DEVMETHOD(gpio_pin_max, pl061_pin_max), + DEVMETHOD(gpio_pin_getname, pl061_pin_getname), + DEVMETHOD(gpio_pin_getflags, pl061_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, pl061_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, pl061_pin_setflags), + DEVMETHOD(gpio_pin_get, pl061_pin_get), + DEVMETHOD(gpio_pin_set, pl061_pin_set), + DEVMETHOD(gpio_pin_toggle, pl061_pin_toggle), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, pl061_pic_disable_intr), + DEVMETHOD(pic_enable_intr, pl061_pic_enable_intr), + DEVMETHOD(pic_map_intr, pl061_pic_map_intr), + DEVMETHOD(pic_setup_intr, pl061_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, pl061_pic_teardown_intr), + DEVMETHOD(pic_post_filter, pl061_pic_post_filter), + DEVMETHOD(pic_post_ithread, pl061_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, pl061_pic_pre_ithread), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(pl061, pl061_driver, pl061_methods, sizeof(struct pl061_softc)); Index: head/sys/dev/gpio/pl061_acpi.c =================================================================== --- head/sys/dev/gpio/pl061_acpi.c +++ head/sys/dev/gpio/pl061_acpi.c @@ -0,0 +1,103 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Amazon.com, Inc. or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_acpi.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "pl061.h" + +static char *gpio_ids[] = { "ARMH0061", NULL }; + +static int +pl061_acpi_probe(device_t dev) +{ + int rv; + + if (acpi_disabled("gpio")) + return (ENXIO); + + rv = ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids, NULL); + + if (rv <= 0) + device_set_desc(dev, "Arm PL061 GPIO Controller"); + + return (rv); +} + +static int +pl061_acpi_attach(device_t dev) +{ + int error; + + error = pl061_attach(dev); + if (error != 0) + return (error); + + if (!intr_pic_register(dev, ACPI_INTR_XREF)) { + device_printf(dev, "couldn't register PIC\n"); + pl061_detach(dev); + error = ENXIO; + } + + return (error); +} + +static device_method_t pl061_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pl061_acpi_probe), + DEVMETHOD(device_attach, pl061_acpi_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(pl061, pl061_acpi_driver, pl061_acpi_methods, + sizeof(struct pl061_softc), pl061_driver); + +static devclass_t pl061_devclass; + +DRIVER_MODULE(pl061, acpi, pl061_driver, pl061_devclass, NULL, NULL); +MODULE_DEPEND(pl061, acpi, 1, 1, 1); +MODULE_DEPEND(pl061, gpiobus, 1, 1, 1); Index: head/sys/dev/gpio/pl061_fdt.c =================================================================== --- head/sys/dev/gpio/pl061_fdt.c +++ head/sys/dev/gpio/pl061_fdt.c @@ -0,0 +1,97 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Andrew Turner + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "pl061.h" + +static int +pl061_fdt_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "arm,pl061")) + return (ENXIO); + + device_set_desc(dev, "Arm PL061 GPIO Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +pl061_fdt_attach(device_t dev) +{ + int error; + + error = pl061_attach(dev); + if (error != 0) + return (error); + + if (!intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)))) { + device_printf(dev, "couldn't register PIC\n"); + pl061_detach(dev); + error = ENXIO; + } + + return (error); +} + +static device_method_t pl061_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pl061_fdt_probe), + DEVMETHOD(device_attach, pl061_fdt_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(gpio, pl061_fdt_driver, pl061_fdt_methods, + sizeof(struct pl061_softc), pl061_driver); + +static devclass_t pl061_devclass; + +EARLY_DRIVER_MODULE(pl061, ofwbus, pl061_fdt_driver, pl061_devclass, NULL, NULL, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); +MODULE_DEPEND(pl061, gpiobus, 1, 1, 1);