Index: stable/12/sys/arm/mv/armada38x/armada38x_rtc.c =================================================================== --- stable/12/sys/arm/mv/armada38x/armada38x_rtc.c (revision 367215) +++ stable/12/sys/arm/mv/armada38x/armada38x_rtc.c (revision 367216) @@ -1,325 +1,370 @@ /*- * Copyright (c) 2015 Semihalf. * Copyright (c) 2015 Stormshield. * 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 #include #include #include "clock_if.h" #define RTC_RES_US 1000000 #define HALF_OF_SEC_NS 500000000 #define RTC_STATUS 0x0 #define RTC_TIME 0xC #define RTC_TEST_CONFIG 0x1C #define RTC_IRQ_1_CONFIG 0x4 #define RTC_IRQ_2_CONFIG 0x8 #define RTC_ALARM_1 0x10 #define RTC_ALARM_2 0x14 #define RTC_CLOCK_CORR 0x18 #define RTC_NOMINAL_TIMING 0x2000 #define RTC_NOMINAL_TIMING_MASK 0x7fff #define RTC_STATUS_ALARM1_MASK 0x1 #define RTC_STATUS_ALARM2_MASK 0x2 #define MV_RTC_LOCK(sc) mtx_lock_spin(&(sc)->mutex) #define MV_RTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->mutex) -#define RTC_BRIDGE_TIMING_CTRL 0x0 -#define RTC_WRCLK_PERIOD_SHIFT 0 -#define RTC_WRCLK_PERIOD_MASK 0x00000003FF -#define RTC_WRCLK_PERIOD_MAX 0x3FF -#define RTC_READ_OUTPUT_DELAY_SHIFT 26 -#define RTC_READ_OUTPUT_DELAY_MASK 0x007C000000 -#define RTC_READ_OUTPUT_DELAY_MAX 0x1F +#define A38X_RTC_BRIDGE_TIMING_CTRL 0x0 +#define A38X_RTC_WRCLK_PERIOD_SHIFT 0 +#define A38X_RTC_WRCLK_PERIOD_MASK 0x00000003FF +#define A38X_RTC_WRCLK_PERIOD_MAX 0x3FF +#define A38X_RTC_READ_OUTPUT_DELAY_SHIFT 26 +#define A38X_RTC_READ_OUTPUT_DELAY_MASK 0x007C000000 +#define A38X_RTC_READ_OUTPUT_DELAY_MAX 0x1F +#define A8K_RTC_BRIDGE_TIMING_CTRL0 0x0 +#define A8K_RTC_WRCLK_PERIOD_SHIFT 0 +#define A8K_RTC_WRCLK_PERIOD_MASK 0x000000FFFF +#define A8K_RTC_WRCLK_PERIOD_VAL 0x3FF +#define A8K_RTC_WRCLK_SETUP_SHIFT 16 +#define A8K_RTC_WRCLK_SETUP_MASK 0x00FFFF0000 +#define A8K_RTC_WRCLK_SETUP_VAL 29 +#define A8K_RTC_BRIDGE_TIMING_CTRL1 0x4 +#define A8K_RTC_READ_OUTPUT_DELAY_SHIFT 0 +#define A8K_RTC_READ_OUTPUT_DELAY_MASK 0x000000FFFF +#define A8K_RTC_READ_OUTPUT_DELAY_VAL 0x3F + + #define RTC_RES 0 #define RTC_SOC_RES 1 static struct resource_spec res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { -1, 0 } }; struct mv_rtc_softc { device_t dev; struct resource *res[2]; struct mtx mutex; + int rtc_type; }; static int mv_rtc_probe(device_t dev); static int mv_rtc_attach(device_t dev); static int mv_rtc_detach(device_t dev); static int mv_rtc_gettime(device_t dev, struct timespec *ts); static int mv_rtc_settime(device_t dev, struct timespec *ts); static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off); static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val); -static inline void mv_rtc_configure_bus(struct mv_rtc_softc *sc); +static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc); +static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc); static device_method_t mv_rtc_methods[] = { DEVMETHOD(device_probe, mv_rtc_probe), DEVMETHOD(device_attach, mv_rtc_attach), DEVMETHOD(device_detach, mv_rtc_detach), DEVMETHOD(clock_gettime, mv_rtc_gettime), DEVMETHOD(clock_settime, mv_rtc_settime), { 0, 0 }, }; static driver_t mv_rtc_driver = { "rtc", mv_rtc_methods, sizeof(struct mv_rtc_softc), }; +#define RTC_A38X 1 +#define RTC_A8K 2 + static struct ofw_compat_data mv_rtc_compat[] = { - {"marvell,armada-380-rtc", true}, - {"marvell,armada-8k-rtc", true}, - {NULL, false}, + {"marvell,armada-380-rtc", RTC_A38X}, + {"marvell,armada-8k-rtc", RTC_A8K}, + {NULL, 0}, }; static devclass_t mv_rtc_devclass; DRIVER_MODULE(a38x_rtc, simplebus, mv_rtc_driver, mv_rtc_devclass, 0, 0); static void mv_rtc_reset(device_t dev) { struct mv_rtc_softc *sc; sc = device_get_softc(dev); /* Reset Test register */ mv_rtc_reg_write(sc, RTC_TEST_CONFIG, 0); DELAY(500000); /* Reset Time register */ mv_rtc_reg_write(sc, RTC_TIME, 0); DELAY(62); /* Reset Status register */ mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK)); DELAY(62); /* Turn off Int1 and Int2 sources & clear the Alarm count */ mv_rtc_reg_write(sc, RTC_IRQ_1_CONFIG, 0); mv_rtc_reg_write(sc, RTC_IRQ_2_CONFIG, 0); mv_rtc_reg_write(sc, RTC_ALARM_1, 0); mv_rtc_reg_write(sc, RTC_ALARM_2, 0); /* Setup nominal register access timing */ mv_rtc_reg_write(sc, RTC_CLOCK_CORR, RTC_NOMINAL_TIMING); /* Reset Time register */ mv_rtc_reg_write(sc, RTC_TIME, 0); DELAY(10); /* Reset Status register */ mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK)); DELAY(50); } static int mv_rtc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data) return (ENXIO); device_set_desc(dev, "Marvell Integrated RTC"); return (BUS_PROBE_DEFAULT); } static int mv_rtc_attach(device_t dev) { struct mv_rtc_softc *sc; int unit, ret; unit = device_get_unit(dev); sc = device_get_softc(dev); sc->dev = dev; + sc->rtc_type = ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data; - clock_register(dev, RTC_RES_US); - mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN); ret = bus_alloc_resources(dev, res_spec, sc->res); - if (ret != 0) { device_printf(dev, "could not allocate resources\n"); mtx_destroy(&sc->mutex); return (ENXIO); } - mv_rtc_configure_bus(sc); + switch (sc->rtc_type) { + case RTC_A38X: + mv_rtc_configure_bus_a38x(sc); + break; + case RTC_A8K: + mv_rtc_configure_bus_a8k(sc); + break; + default: + panic("Unknown RTC type: %d", sc->rtc_type); + } + clock_register(dev, RTC_RES_US); + return (0); } static int mv_rtc_detach(device_t dev) { struct mv_rtc_softc *sc; sc = device_get_softc(dev); mtx_destroy(&sc->mutex); bus_release_resources(dev, res_spec, sc->res); return (0); } static int mv_rtc_gettime(device_t dev, struct timespec *ts) { struct mv_rtc_softc *sc; uint32_t val, val_check; sc = device_get_softc(dev); MV_RTC_LOCK(sc); /* - * According to HW Errata if more than one second between - * two time reads is detected, then read once again + * According to HW Errata, if more than one second is detected + * between two time reads, then at least one of the reads gave + * an invalid value. */ - val = mv_rtc_reg_read(sc, RTC_TIME); - val_check = mv_rtc_reg_read(sc, RTC_TIME); - if (val_check - val > 1) + do { + val = mv_rtc_reg_read(sc, RTC_TIME); + DELAY(100); val_check = mv_rtc_reg_read(sc, RTC_TIME); + } while ((val_check - val) > 1); MV_RTC_UNLOCK(sc); ts->tv_sec = val_check; /* RTC resolution is 1 sec */ ts->tv_nsec = 0; return (0); } static int mv_rtc_settime(device_t dev, struct timespec *ts) { struct mv_rtc_softc *sc; sc = device_get_softc(dev); /* RTC resolution is 1 sec */ if (ts->tv_nsec >= HALF_OF_SEC_NS) ts->tv_sec++; ts->tv_nsec = 0; MV_RTC_LOCK(sc); if ((mv_rtc_reg_read(sc, RTC_CLOCK_CORR) & RTC_NOMINAL_TIMING_MASK) != RTC_NOMINAL_TIMING) { /* RTC was not resetted yet */ mv_rtc_reset(dev); } /* * According to errata FE-3124064, Write to RTC TIME register * may fail. As a workaround, before writing to RTC TIME register, * issue a dummy write of 0x0 twice to RTC Status register. */ mv_rtc_reg_write(sc, RTC_STATUS, 0x0); mv_rtc_reg_write(sc, RTC_STATUS, 0x0); mv_rtc_reg_write(sc, RTC_TIME, ts->tv_sec); - MV_RTC_UNLOCK(sc); return (0); } static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off) { return (bus_read_4(sc->res[RTC_RES], off)); } /* * According to the datasheet, the OS should wait 5us after every * register write to the RTC hard macro so that the required update * can occur without holding off the system bus */ static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->res[RTC_RES], off, val); DELAY(5); return (0); } static inline void -mv_rtc_configure_bus(struct mv_rtc_softc *sc) +mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc) { int val; - val = bus_read_4(sc->res[RTC_SOC_RES], RTC_BRIDGE_TIMING_CTRL); - val &= ~(RTC_WRCLK_PERIOD_MASK | RTC_READ_OUTPUT_DELAY_MASK); - val |= RTC_WRCLK_PERIOD_MAX << RTC_WRCLK_PERIOD_SHIFT; - val |= RTC_READ_OUTPUT_DELAY_MAX << RTC_READ_OUTPUT_DELAY_SHIFT; - bus_write_4(sc->res[RTC_SOC_RES], RTC_BRIDGE_TIMING_CTRL, val); + val = bus_read_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL); + val &= ~(A38X_RTC_WRCLK_PERIOD_MASK | A38X_RTC_READ_OUTPUT_DELAY_MASK); + val |= A38X_RTC_WRCLK_PERIOD_MAX << A38X_RTC_WRCLK_PERIOD_SHIFT; + val |= A38X_RTC_READ_OUTPUT_DELAY_MAX << A38X_RTC_READ_OUTPUT_DELAY_SHIFT; + bus_write_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL, val); +} + +static inline void +mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc) +{ + int val; + + val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0); + val &= ~(A8K_RTC_WRCLK_PERIOD_MASK | A8K_RTC_WRCLK_SETUP_MASK); + val |= A8K_RTC_WRCLK_PERIOD_VAL << A8K_RTC_WRCLK_PERIOD_SHIFT; + val |= A8K_RTC_WRCLK_SETUP_VAL << A8K_RTC_WRCLK_SETUP_SHIFT; + bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val); + + val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0); + val &= ~A8K_RTC_READ_OUTPUT_DELAY_MASK; + val |= A8K_RTC_READ_OUTPUT_DELAY_VAL << A8K_RTC_READ_OUTPUT_DELAY_SHIFT; + bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val); } Index: stable/12/sys/arm/mv/gpio.c =================================================================== --- stable/12/sys/arm/mv/gpio.c (revision 367215) +++ stable/12/sys/arm/mv/gpio.c (revision 367216) @@ -1,1222 +1,1216 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Benno Rice. * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. * Copyright (c) 2017 Semihalf. * All rights reserved. * * Adapted and extended for Marvell SoCs by Semihalf. * * 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 ``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 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. * * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_gpio.c, rev 1 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" -#ifdef __aarch64__ -#include "opt_soc.h" -#endif #define GPIO_MAX_INTR_COUNT 8 #define GPIO_PINS_PER_REG 32 #define GPIO_GENERIC_CAP (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ GPIO_PIN_TRISTATE | GPIO_PIN_PULLUP | \ GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | \ GPIO_PIN_INVOUT) #define DEBOUNCE_CHECK_MS 1 #define DEBOUNCE_LO_HI_MS 2 #define DEBOUNCE_HI_LO_MS 2 #define DEBOUNCE_CHECK_TICKS ((hz / 1000) * DEBOUNCE_CHECK_MS) struct mv_gpio_softc { device_t dev; device_t sc_busdev; struct resource * mem_res; int mem_rid; struct resource * irq_res[GPIO_MAX_INTR_COUNT]; int irq_rid[GPIO_MAX_INTR_COUNT]; struct intr_event * gpio_events[MV_GPIO_MAX_NPINS]; void *ih_cookie[GPIO_MAX_INTR_COUNT]; bus_space_tag_t bst; bus_space_handle_t bsh; uint32_t offset; struct mtx mutex; uint8_t pin_num; /* number of GPIO pins */ uint8_t irq_num; /* number of real IRQs occupied by GPIO controller */ struct gpio_pin gpio_setup[MV_GPIO_MAX_NPINS]; /* Used for debouncing. */ uint32_t debounced_state_lo; uint32_t debounced_state_hi; struct callout **debounce_callouts; int *debounce_counters; }; struct mv_gpio_pindev { device_t dev; int pin; }; static int mv_gpio_probe(device_t); static int mv_gpio_attach(device_t); static int mv_gpio_intr(device_t, void *); static void mv_gpio_double_edge_init(device_t, int); static int mv_gpio_debounce_setup(device_t, int); static int mv_gpio_debounce_prepare(device_t, int); static int mv_gpio_debounce_init(device_t, int); static void mv_gpio_debounce_start(device_t, int); static void mv_gpio_debounce(void *); static void mv_gpio_debounced_state_set(device_t, int, uint8_t); static uint32_t mv_gpio_debounced_state_get(device_t, int); static void mv_gpio_exec_intr_handlers(device_t, uint32_t, int); static void mv_gpio_intr_handler(device_t, int); static uint32_t mv_gpio_reg_read(device_t, uint32_t); static void mv_gpio_reg_write(device_t, uint32_t, uint32_t); static void mv_gpio_reg_set(device_t, uint32_t, uint32_t); static void mv_gpio_reg_clear(device_t, uint32_t, uint32_t); static void mv_gpio_blink(device_t, uint32_t, uint8_t); static void mv_gpio_polarity(device_t, uint32_t, uint8_t, uint8_t); static void mv_gpio_level(device_t, uint32_t, uint8_t); static void mv_gpio_edge(device_t, uint32_t, uint8_t); static void mv_gpio_out_en(device_t, uint32_t, uint8_t); static void mv_gpio_int_ack(struct mv_gpio_pindev *); static void mv_gpio_value_set(device_t, uint32_t, uint8_t); static uint32_t mv_gpio_value_get(device_t, uint32_t, uint8_t); static void mv_gpio_intr_mask(struct mv_gpio_pindev *); static void mv_gpio_intr_unmask(struct mv_gpio_pindev *); void mv_gpio_finish_intrhandler(struct mv_gpio_pindev *); int mv_gpio_setup_intrhandler(device_t, const char *, driver_filter_t *, void (*)(void *), void *, int, int, void **); int mv_gpio_configure(device_t, uint32_t, uint32_t, uint32_t); void mv_gpio_out(device_t, uint32_t, uint8_t, uint8_t); uint8_t mv_gpio_in(device_t, uint32_t); /* * GPIO interface */ static device_t mv_gpio_get_bus(device_t); static int mv_gpio_pin_max(device_t, int *); static int mv_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int mv_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int mv_gpio_pin_getname(device_t, uint32_t, char *); static int mv_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int mv_gpio_pin_set(device_t, uint32_t, unsigned int); static int mv_gpio_pin_get(device_t, uint32_t, unsigned int *); static int mv_gpio_pin_toggle(device_t, uint32_t); static int mv_gpio_map_gpios(device_t, phandle_t, phandle_t, int, pcell_t *, uint32_t *, uint32_t *); #define MV_GPIO_LOCK() mtx_lock_spin(&sc->mutex) #define MV_GPIO_UNLOCK() mtx_unlock_spin(&sc->mutex) #define MV_GPIO_ASSERT_LOCKED() mtx_assert(&sc->mutex, MA_OWNED) static device_method_t mv_gpio_methods[] = { DEVMETHOD(device_probe, mv_gpio_probe), DEVMETHOD(device_attach, mv_gpio_attach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, mv_gpio_get_bus), DEVMETHOD(gpio_pin_max, mv_gpio_pin_max), DEVMETHOD(gpio_pin_getname, mv_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, mv_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, mv_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, mv_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, mv_gpio_pin_get), DEVMETHOD(gpio_pin_set, mv_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, mv_gpio_pin_toggle), DEVMETHOD(gpio_map_gpios, mv_gpio_map_gpios), DEVMETHOD_END }; static driver_t mv_gpio_driver = { "gpio", mv_gpio_methods, sizeof(struct mv_gpio_softc), }; static devclass_t mv_gpio_devclass; EARLY_DRIVER_MODULE(mv_gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); struct ofw_compat_data compat_data[] = { { "mrvl,gpio", 1 }, { "marvell,orion-gpio", 1 }, -#ifdef SOC_MARVELL_8K - { "marvell,armada-8k-gpio", 1 }, -#endif { NULL, 0 } }; static int mv_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Marvell Integrated GPIO Controller"); return (0); } static int mv_gpio_setup_interrupts(struct mv_gpio_softc *sc, phandle_t node) { phandle_t iparent; pcell_t irq_cells; int i, size; /* Find root interrupt controller */ iparent = ofw_bus_find_iparent(node); if (iparent == 0) { device_printf(sc->dev, "No interrupt-parrent found. " "Error in DTB\n"); return (ENXIO); } else { /* While at parent - store interrupt cells prop */ if (OF_searchencprop(OF_node_from_xref(iparent), "#interrupt-cells", &irq_cells, sizeof(irq_cells)) == -1) { device_printf(sc->dev, "DTB: Missing #interrupt-cells " "property in interrupt parent node\n"); return (ENXIO); } } size = OF_getproplen(node, "interrupts"); if (size != -1) { size = size / sizeof(pcell_t); size = size / irq_cells; sc->irq_num = size; device_printf(sc->dev, "%d IRQs available\n", sc->irq_num); } else { device_printf(sc->dev, "ERROR: no interrupts entry found!\n"); return (ENXIO); } for (i = 0; i < sc->irq_num; i++) { sc->irq_rid[i] = i; sc->irq_res[i] = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irq_rid[i], RF_ACTIVE); if (!sc->irq_res[i]) { mtx_destroy(&sc->mutex); device_printf(sc->dev, "could not allocate gpio%d interrupt\n", i+1); return (ENXIO); } } device_printf(sc->dev, "Disable interrupts (offset = %x + EDGE(0x18)\n", sc->offset); /* Disable all interrupts */ bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_EDGE_MASK, 0); device_printf(sc->dev, "Disable interrupts (offset = %x + LEV(0x1C))\n", sc->offset); bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_LEV_MASK, 0); for (i = 0; i < sc->irq_num; i++) { device_printf(sc->dev, "Setup intr %d\n", i); if (bus_setup_intr(sc->dev, sc->irq_res[i], INTR_TYPE_MISC, (driver_filter_t *)mv_gpio_intr, NULL, sc, &sc->ih_cookie[i]) != 0) { mtx_destroy(&sc->mutex); bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid[i], sc->irq_res[i]); device_printf(sc->dev, "could not set up intr %d\n", i); return (ENXIO); } } /* Clear interrupt status. */ device_printf(sc->dev, "Clear int status (offset = %x)\n", sc->offset); bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_CAUSE, 0); sc->debounce_callouts = (struct callout **)malloc(sc->pin_num * sizeof(struct callout *), M_DEVBUF, M_WAITOK | M_ZERO); if (sc->debounce_callouts == NULL) return (ENOMEM); sc->debounce_counters = (int *)malloc(sc->pin_num * sizeof(int), M_DEVBUF, M_WAITOK); if (sc->debounce_counters == NULL) return (ENOMEM); return (0); } static int mv_gpio_attach(device_t dev) { int i, rv; struct mv_gpio_softc *sc; phandle_t node; pcell_t pincnt = 0; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (sc == NULL) return (ENXIO); node = ofw_bus_get_node(dev); sc->dev = dev; if (OF_getencprop(node, "pin-count", &pincnt, sizeof(pcell_t)) >= 0 || OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t)) >= 0) { sc->pin_num = MIN(pincnt, MV_GPIO_MAX_NPINS); if (bootverbose) device_printf(dev, "%d pins available\n", sc->pin_num); } else { device_printf(dev, "ERROR: no pin-count or ngpios entry found!\n"); return (ENXIO); } if (OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset)) == -1) sc->offset = 0; /* Assign generic capabilities to every gpio pin */ for(i = 0; i < sc->pin_num; i++) sc->gpio_setup[i].gp_caps = GPIO_GENERIC_CAP; mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN); sc->mem_rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE | RF_SHAREABLE ); if (!sc->mem_res) { mtx_destroy(&sc->mutex); device_printf(dev, "could not allocate memory window\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->mem_res); sc->bsh = rman_get_bushandle(sc->mem_res); rv = mv_gpio_setup_interrupts(sc, node); if (rv != 0) return (rv); sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { mtx_destroy(&sc->mutex); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid[i], sc->irq_res[i]); return (ENXIO); } return (0); } static int mv_gpio_intr(device_t dev, void *arg) { uint32_t int_cause, gpio_val; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_LOCK(); /* * According to documentation, edge sensitive interrupts are asserted * when unmasked GPIO_INT_CAUSE register bits are set. */ int_cause = mv_gpio_reg_read(dev, GPIO_INT_CAUSE); int_cause &= mv_gpio_reg_read(dev, GPIO_INT_EDGE_MASK); /* * Level sensitive interrupts are asserted when unmasked GPIO_DATA_IN * register bits are set. */ gpio_val = mv_gpio_reg_read(dev, GPIO_DATA_IN); gpio_val &= mv_gpio_reg_read(dev, GPIO_INT_LEV_MASK); mv_gpio_exec_intr_handlers(dev, int_cause | gpio_val, 0); MV_GPIO_UNLOCK(); return (FILTER_HANDLED); } /* * GPIO interrupt handling */ void mv_gpio_finish_intrhandler(struct mv_gpio_pindev *s) { /* When we acheive full interrupt support * This function will be opposite to * mv_gpio_setup_intrhandler */ /* Now it exists only to remind that * there should be place to free mv_gpio_pindev * allocated by mv_gpio_setup_intrhandler */ free(s, M_DEVBUF); } int mv_gpio_setup_intrhandler(device_t dev, const char *name, driver_filter_t *filt, void (*hand)(void *), void *arg, int pin, int flags, void **cookiep) { struct intr_event *event; int error; struct mv_gpio_pindev *s; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); s = malloc(sizeof(struct mv_gpio_pindev), M_DEVBUF, M_NOWAIT | M_ZERO); if (pin < 0 || pin >= sc->pin_num) return (ENXIO); event = sc->gpio_events[pin]; if (event == NULL) { MV_GPIO_LOCK(); if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE) { error = mv_gpio_debounce_init(dev, pin); if (error != 0) { MV_GPIO_UNLOCK(); return (error); } } else if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE) mv_gpio_double_edge_init(dev, pin); MV_GPIO_UNLOCK(); error = intr_event_create(&event, (void *)s, 0, pin, (void (*)(void *))mv_gpio_intr_mask, (void (*)(void *))mv_gpio_intr_unmask, (void (*)(void *))mv_gpio_int_ack, NULL, "gpio%d:", pin); if (error != 0) return (error); sc->gpio_events[pin] = event; } intr_event_add_handler(event, name, filt, hand, arg, intr_priority(flags), flags, cookiep); return (0); } static void mv_gpio_intr_mask(struct mv_gpio_pindev *s) { struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(s->dev); if (s->pin >= sc->pin_num) return; MV_GPIO_LOCK(); if (sc->gpio_setup[s->pin].gp_flags & (MV_GPIO_IN_IRQ_EDGE | MV_GPIO_IN_IRQ_DOUBLE_EDGE)) mv_gpio_edge(s->dev, s->pin, 0); else mv_gpio_level(s->dev, s->pin, 0); /* * The interrupt has to be acknowledged before scheduling an interrupt * thread. This way we allow for interrupt source to trigger again * (which can happen with shared IRQs e.g. PCI) while processing the * current event. */ mv_gpio_int_ack(s); MV_GPIO_UNLOCK(); return; } static void mv_gpio_intr_unmask(struct mv_gpio_pindev *s) { struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(s->dev); if (s->pin >= sc->pin_num) return; MV_GPIO_LOCK(); if (sc->gpio_setup[s->pin].gp_flags & (MV_GPIO_IN_IRQ_EDGE | MV_GPIO_IN_IRQ_DOUBLE_EDGE)) mv_gpio_edge(s->dev, s->pin, 1); else mv_gpio_level(s->dev, s->pin, 1); MV_GPIO_UNLOCK(); return; } static void mv_gpio_exec_intr_handlers(device_t dev, uint32_t status, int high) { int i, pin; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); i = 0; while (status != 0) { if (status & 1) { pin = (high ? (i + GPIO_PINS_PER_REG) : i); if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE) mv_gpio_debounce_start(dev, pin); else if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE) { mv_gpio_polarity(dev, pin, 0, 1); mv_gpio_intr_handler(dev, pin); } else mv_gpio_intr_handler(dev, pin); } status >>= 1; i++; } } static void mv_gpio_intr_handler(device_t dev, int pin) { #ifdef INTRNG struct intr_irqsrc isrc; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); #ifdef INTR_SOLO isrc.isrc_filter = NULL; #endif isrc.isrc_event = sc->gpio_events[pin]; if (isrc.isrc_event == NULL || CK_SLIST_EMPTY(&isrc.isrc_event->ie_handlers)) return; intr_isrc_dispatch(&isrc, NULL); #endif } int mv_gpio_configure(device_t dev, uint32_t pin, uint32_t flags, uint32_t mask) { int error; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); error = 0; if (pin >= sc->pin_num) return (EINVAL); /* check flags consistency */ if (((flags & mask) & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) return (EINVAL); if (mask & MV_GPIO_IN_DEBOUNCE) { if (sc->irq_num == 0) return (EINVAL); error = mv_gpio_debounce_prepare(dev, pin); if (error != 0) return (error); } MV_GPIO_LOCK(); if ((mask & flags) & GPIO_PIN_INPUT) mv_gpio_out_en(dev, pin, 0); if ((mask & flags) & GPIO_PIN_OUTPUT) { if ((flags & mask) & GPIO_PIN_OPENDRAIN) mv_gpio_value_set(dev, pin, 0); else mv_gpio_value_set(dev, pin, 1); mv_gpio_out_en(dev, pin, 1); } if (mask & MV_GPIO_OUT_BLINK) mv_gpio_blink(dev, pin, flags & MV_GPIO_OUT_BLINK); if (mask & MV_GPIO_IN_POL_LOW) mv_gpio_polarity(dev, pin, flags & MV_GPIO_IN_POL_LOW, 0); if (mask & MV_GPIO_IN_DEBOUNCE) { error = mv_gpio_debounce_setup(dev, pin); if (error) { MV_GPIO_UNLOCK(); return (error); } } sc->gpio_setup[pin].gp_flags &= ~(mask); sc->gpio_setup[pin].gp_flags |= (flags & mask); MV_GPIO_UNLOCK(); return (0); } static void mv_gpio_double_edge_init(device_t dev, int pin) { uint8_t raw_read; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); raw_read = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0); if (raw_read) mv_gpio_polarity(dev, pin, 1, 0); else mv_gpio_polarity(dev, pin, 0, 0); } static int mv_gpio_debounce_setup(device_t dev, int pin) { struct callout *c; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); c = sc->debounce_callouts[pin]; if (c == NULL) return (ENXIO); if (callout_active(c)) callout_deactivate(c); callout_stop(c); return (0); } static int mv_gpio_debounce_prepare(device_t dev, int pin) { struct callout *c; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); c = sc->debounce_callouts[pin]; if (c == NULL) { c = (struct callout *)malloc(sizeof(struct callout), M_DEVBUF, M_WAITOK); sc->debounce_callouts[pin] = c; if (c == NULL) return (ENOMEM); callout_init(c, 1); } return (0); } static int mv_gpio_debounce_init(device_t dev, int pin) { uint8_t raw_read; int *cnt; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); cnt = &sc->debounce_counters[pin]; raw_read = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0); if (raw_read) { mv_gpio_polarity(dev, pin, 1, 0); *cnt = DEBOUNCE_HI_LO_MS / DEBOUNCE_CHECK_MS; } else { mv_gpio_polarity(dev, pin, 0, 0); *cnt = DEBOUNCE_LO_HI_MS / DEBOUNCE_CHECK_MS; } mv_gpio_debounced_state_set(dev, pin, raw_read); return (0); } static void mv_gpio_debounce_start(device_t dev, int pin) { struct callout *c; struct mv_gpio_pindev s = {dev, pin}; struct mv_gpio_pindev *sd; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); c = sc->debounce_callouts[pin]; if (c == NULL) { mv_gpio_int_ack(&s); return; } if (callout_pending(c) || callout_active(c)) { mv_gpio_int_ack(&s); return; } sd = (struct mv_gpio_pindev *)malloc(sizeof(struct mv_gpio_pindev), M_DEVBUF, M_WAITOK); if (sd == NULL) { mv_gpio_int_ack(&s); return; } sd->pin = pin; sd->dev = dev; callout_reset(c, DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, sd); } static void mv_gpio_debounce(void *arg) { uint8_t raw_read, last_state; int pin; device_t dev; int *debounce_counter; struct mv_gpio_softc *sc; struct mv_gpio_pindev *s; s = (struct mv_gpio_pindev *)arg; dev = s->dev; pin = s->pin; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_LOCK(); raw_read = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0); last_state = (mv_gpio_debounced_state_get(dev, pin) ? 1 : 0); debounce_counter = &sc->debounce_counters[pin]; if (raw_read == last_state) { if (last_state) *debounce_counter = DEBOUNCE_HI_LO_MS / DEBOUNCE_CHECK_MS; else *debounce_counter = DEBOUNCE_LO_HI_MS / DEBOUNCE_CHECK_MS; callout_reset(sc->debounce_callouts[pin], DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, arg); } else { *debounce_counter = *debounce_counter - 1; if (*debounce_counter != 0) callout_reset(sc->debounce_callouts[pin], DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, arg); else { mv_gpio_debounced_state_set(dev, pin, raw_read); if (last_state) *debounce_counter = DEBOUNCE_HI_LO_MS / DEBOUNCE_CHECK_MS; else *debounce_counter = DEBOUNCE_LO_HI_MS / DEBOUNCE_CHECK_MS; if (((sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) && (raw_read == 0)) || (((sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) == 0) && raw_read) || (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE)) mv_gpio_intr_handler(dev, pin); /* Toggle polarity for next edge. */ mv_gpio_polarity(dev, pin, 0, 1); free(arg, M_DEVBUF); callout_deactivate(sc->debounce_callouts[pin]); } } MV_GPIO_UNLOCK(); } static void mv_gpio_debounced_state_set(device_t dev, int pin, uint8_t new_state) { uint32_t *old_state; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); if (pin >= GPIO_PINS_PER_REG) { old_state = &sc->debounced_state_hi; pin -= GPIO_PINS_PER_REG; } else old_state = &sc->debounced_state_lo; if (new_state) *old_state |= (1 << pin); else *old_state &= ~(1 << pin); } static uint32_t mv_gpio_debounced_state_get(device_t dev, int pin) { uint32_t *state; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); if (pin >= GPIO_PINS_PER_REG) { state = &sc->debounced_state_hi; pin -= GPIO_PINS_PER_REG; } else state = &sc->debounced_state_lo; return (*state & (1 << pin)); } void mv_gpio_out(device_t dev, uint32_t pin, uint8_t val, uint8_t enable) { struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_LOCK(); mv_gpio_value_set(dev, pin, val); mv_gpio_out_en(dev, pin, enable); MV_GPIO_UNLOCK(); } uint8_t mv_gpio_in(device_t dev, uint32_t pin) { uint8_t state; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE) { if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) state = (mv_gpio_debounced_state_get(dev, pin) ? 0 : 1); else state = (mv_gpio_debounced_state_get(dev, pin) ? 1 : 0); } else if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_IRQ_DOUBLE_EDGE) { if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) state = (mv_gpio_value_get(dev, pin, 1) ? 0 : 1); else state = (mv_gpio_value_get(dev, pin, 1) ? 1 : 0); } else state = (mv_gpio_value_get(dev, pin, 0) ? 1 : 0); return (state); } static uint32_t mv_gpio_reg_read(device_t dev, uint32_t reg) { struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); return (bus_space_read_4(sc->bst, sc->bsh, sc->offset + reg)); } static void mv_gpio_reg_write(device_t dev, uint32_t reg, uint32_t val) { struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); bus_space_write_4(sc->bst, sc->bsh, sc->offset + reg, val); } static void mv_gpio_reg_set(device_t dev, uint32_t reg, uint32_t pin) { uint32_t reg_val; reg_val = mv_gpio_reg_read(dev, reg); reg_val |= GPIO(pin); mv_gpio_reg_write(dev, reg, reg_val); } static void mv_gpio_reg_clear(device_t dev, uint32_t reg, uint32_t pin) { uint32_t reg_val; reg_val = mv_gpio_reg_read(dev, reg); reg_val &= ~(GPIO(pin)); mv_gpio_reg_write(dev, reg, reg_val); } static void mv_gpio_out_en(device_t dev, uint32_t pin, uint8_t enable) { uint32_t reg; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (pin >= sc->pin_num) return; reg = GPIO_DATA_OUT_EN_CTRL; if (enable) mv_gpio_reg_clear(dev, reg, pin); else mv_gpio_reg_set(dev, reg, pin); } static void mv_gpio_blink(device_t dev, uint32_t pin, uint8_t enable) { uint32_t reg; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (pin >= sc->pin_num) return; reg = GPIO_BLINK_EN; if (enable) mv_gpio_reg_set(dev, reg, pin); else mv_gpio_reg_clear(dev, reg, pin); } static void mv_gpio_polarity(device_t dev, uint32_t pin, uint8_t enable, uint8_t toggle) { uint32_t reg, reg_val; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (pin >= sc->pin_num) return; reg = GPIO_DATA_IN_POLAR; if (toggle) { reg_val = mv_gpio_reg_read(dev, reg) & GPIO(pin); if (reg_val) mv_gpio_reg_clear(dev, reg, pin); else mv_gpio_reg_set(dev, reg, pin); } else if (enable) mv_gpio_reg_set(dev, reg, pin); else mv_gpio_reg_clear(dev, reg, pin); } static void mv_gpio_level(device_t dev, uint32_t pin, uint8_t enable) { uint32_t reg; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (pin >= sc->pin_num) return; reg = GPIO_INT_LEV_MASK; if (enable) mv_gpio_reg_set(dev, reg, pin); else mv_gpio_reg_clear(dev, reg, pin); } static void mv_gpio_edge(device_t dev, uint32_t pin, uint8_t enable) { uint32_t reg; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (pin >= sc->pin_num) return; reg = GPIO_INT_EDGE_MASK; if (enable) mv_gpio_reg_set(dev, reg, pin); else mv_gpio_reg_clear(dev, reg, pin); } static void mv_gpio_int_ack(struct mv_gpio_pindev *s) { uint32_t reg, pin; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(s->dev); pin = s->pin; if (pin >= sc->pin_num) return; reg = GPIO_INT_CAUSE; mv_gpio_reg_clear(s->dev, reg, pin); } static uint32_t mv_gpio_value_get(device_t dev, uint32_t pin, uint8_t exclude_polar) { uint32_t reg, polar_reg, reg_val, polar_reg_val; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (pin >= sc->pin_num) return (0); reg = GPIO_DATA_IN; polar_reg = GPIO_DATA_IN_POLAR; reg_val = mv_gpio_reg_read(dev, reg); if (exclude_polar) { polar_reg_val = mv_gpio_reg_read(dev, polar_reg); return ((reg_val & GPIO(pin)) ^ (polar_reg_val & GPIO(pin))); } else return (reg_val & GPIO(pin)); } static void mv_gpio_value_set(device_t dev, uint32_t pin, uint8_t val) { uint32_t reg; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); MV_GPIO_ASSERT_LOCKED(); if (pin >= sc->pin_num) return; reg = GPIO_DATA_OUT; if (val) mv_gpio_reg_set(dev, reg, pin); else mv_gpio_reg_clear(dev, reg, pin); } /* * GPIO interface methods */ static int mv_gpio_pin_max(device_t dev, int *maxpin) { struct mv_gpio_softc *sc; if (maxpin == NULL) return (EINVAL); sc = device_get_softc(dev); *maxpin = sc->pin_num; return (0); } static int mv_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct mv_gpio_softc *sc = device_get_softc(dev); if (caps == NULL) return (EINVAL); if (pin >= sc->pin_num) return (EINVAL); MV_GPIO_LOCK(); *caps = sc->gpio_setup[pin].gp_caps; MV_GPIO_UNLOCK(); return (0); } static int mv_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct mv_gpio_softc *sc = device_get_softc(dev); if (flags == NULL) return (EINVAL); if (pin >= sc->pin_num) return (EINVAL); MV_GPIO_LOCK(); *flags = sc->gpio_setup[pin].gp_flags; MV_GPIO_UNLOCK(); return (0); } static int mv_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct mv_gpio_softc *sc = device_get_softc(dev); if (name == NULL) return (EINVAL); if (pin >= sc->pin_num) return (EINVAL); MV_GPIO_LOCK(); memcpy(name, sc->gpio_setup[pin].gp_name, GPIOMAXNAME); MV_GPIO_UNLOCK(); return (0); } static int mv_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { int ret; struct mv_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->pin_num) return (EINVAL); /* Check for unwanted flags. */ if ((flags & sc->gpio_setup[pin].gp_caps) != flags) return (EINVAL); ret = mv_gpio_configure(dev, pin, flags, ~0); return (ret); } static int mv_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct mv_gpio_softc *sc = device_get_softc(dev); if (pin >= sc->pin_num) return (EINVAL); MV_GPIO_LOCK(); mv_gpio_value_set(dev, pin, value); MV_GPIO_UNLOCK(); return (0); } static int mv_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) { struct mv_gpio_softc *sc = device_get_softc(dev); if (value == NULL) return (EINVAL); if (pin >= sc->pin_num) return (EINVAL); MV_GPIO_LOCK(); *value = mv_gpio_in(dev, pin); MV_GPIO_UNLOCK(); return (0); } static int mv_gpio_pin_toggle(device_t dev, uint32_t pin) { struct mv_gpio_softc *sc = device_get_softc(dev); uint32_t value; if (pin >= sc->pin_num) return (EINVAL); MV_GPIO_LOCK(); value = mv_gpio_in(dev, pin); value = (~value) & 1; mv_gpio_value_set(dev, pin, value); MV_GPIO_UNLOCK(); return (0); } static device_t mv_gpio_get_bus(device_t dev) { struct mv_gpio_softc *sc = device_get_softc(dev); return (sc->sc_busdev); } static int mv_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { struct mv_gpio_softc *sc = device_get_softc(bus); if (gpios[0] >= sc->pin_num) return (EINVAL); *pin = gpios[0]; *flags = gpios[1]; mv_gpio_configure(bus, *pin, *flags, ~0); return (0); } Index: stable/12/sys/arm/mv/mvebu_gpio.c =================================================================== --- stable/12/sys/arm/mv/mvebu_gpio.c (nonexistent) +++ stable/12/sys/arm/mv/mvebu_gpio.c (revision 367216) @@ -0,0 +1,869 @@ +/*- + * Copyright (c) 2020 Michal Meloun + * + * 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$"); + +/* + * ARMADA 8040 GPIO driver. + */ +#include "opt_platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "pic_if.h" +#include "syscon_if.h" + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ + device_get_nameunit(_sc->dev), "mvebu_gpio", MTX_DEF) +#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx); +#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED); +#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED); + +#define GPIO_DATA_OUT 0x00 +#define GPIO_CONTROL 0x04 +#define GPIO_BLINK_ENA 0x08 +#define GPIO_DATA_IN_POL 0x0C +#define GPIO_DATA_IN 0x10 +#define GPIO_INT_CAUSE 0x14 +#define GPIO_INT_MASK 0x18 +#define GPIO_INT_LEVEL_MASK 0x1C +#define GPIO_CONTROL_SET 0x28 +#define GPIO_CONTROL_CLR 0x2C +#define GPIO_DATA_SET 0x30 +#define GPIO_DATA_CLR 0x34 + +#define GPIO_BIT(_p) ((_p) % 32) +#define GPIO_REGNUM(_p) ((_p) / 32) + +#define MV_GPIO_MAX_NIRQS 4 +#define MV_GPIO_MAX_NPINS 32 + +#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg)) +#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val)) + +struct mvebu_gpio_irqsrc { + struct intr_irqsrc isrc; + u_int irq; + bool is_level; + bool is_inverted; +}; + +struct mvebu_gpio_softc; +struct mvebu_gpio_irq_cookie { + struct mvebu_gpio_softc *sc; + int bank_num; +}; + +struct mvebu_gpio_softc { + device_t dev; + device_t busdev; + struct mtx mtx; + struct syscon *syscon; + uint32_t offset; + struct resource *irq_res[MV_GPIO_MAX_NIRQS]; + void *irq_ih[MV_GPIO_MAX_NIRQS]; + struct mvebu_gpio_irq_cookie irq_cookies[MV_GPIO_MAX_NIRQS]; + int gpio_npins; + struct gpio_pin gpio_pins[MV_GPIO_MAX_NPINS]; + struct mvebu_gpio_irqsrc *isrcs; +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,armada-8k-gpio", 1}, + {NULL, 0} +}; + +/* -------------------------------------------------------------------------- + * + * GPIO + * + */ +static inline void +gpio_write(struct mvebu_gpio_softc *sc, bus_size_t reg, + struct gpio_pin *pin, uint32_t val) +{ + uint32_t tmp; + int bit; + + bit = GPIO_BIT(pin->gp_pin); + tmp = 0x100 << bit; /* mask */ + tmp |= (val & 1) << bit; /* value */ + SYSCON_WRITE_4(sc->syscon, sc->offset + GPIO_REGNUM(pin->gp_pin) + reg, + tmp); +} + +static inline uint32_t +gpio_read(struct mvebu_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin) +{ + int bit; + uint32_t val; + + bit = GPIO_BIT(pin->gp_pin); + val = SYSCON_READ_4(sc->syscon, + sc->offset + GPIO_REGNUM(pin->gp_pin) + reg); + return (val >> bit) & 1; +} + +static void +mvebu_gpio_pin_configure(struct mvebu_gpio_softc *sc, struct gpio_pin *pin, + unsigned int flags) +{ + + if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0) + return; + + /* Manage input/output */ + pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + if (flags & GPIO_PIN_OUTPUT) { + pin->gp_flags |= GPIO_PIN_OUTPUT; + gpio_write(sc, GPIO_CONTROL_SET, pin, 1); + } else { + pin->gp_flags |= GPIO_PIN_INPUT; + gpio_write(sc, GPIO_CONTROL_CLR, pin, 1); + } +} + +static device_t +mvebu_gpio_get_bus(device_t dev) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + return (sc->busdev); +} + +static int +mvebu_gpio_pin_max(device_t dev, int *maxpin) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + *maxpin = sc->gpio_npins - 1; + return (0); +} + +static int +mvebu_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + *caps = sc->gpio_pins[pin].gp_caps; + + return (0); +} + +static int +mvebu_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + *flags = sc->gpio_pins[pin].gp_flags; + + return (0); +} + +static int +mvebu_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME); + + return (0); +} + +static int +mvebu_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + + mvebu_gpio_pin_configure(sc, &sc->gpio_pins[pin], flags); + + return (0); +} + +static int +mvebu_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + if (value != 0) + gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1); + else + gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1); + + return (0); +} + +static int +mvebu_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *val = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[pin]); + *val ^= gpio_read(sc, GPIO_DATA_IN_POL, &sc->gpio_pins[pin]); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +mvebu_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct mvebu_gpio_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + if (pin >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + mvebu_gpio_pin_get(sc->dev, pin, &val); + if (val != 0) + gpio_write(sc, GPIO_DATA_CLR, &sc->gpio_pins[pin], 1); + else + gpio_write(sc, GPIO_DATA_SET, &sc->gpio_pins[pin], 1); + GPIO_UNLOCK(sc); + + return (0); +} + + +/* -------------------------------------------------------------------------- + * + * Interrupts + * + */ +static inline void +intr_modify(struct mvebu_gpio_softc *sc, bus_addr_t reg, + struct mvebu_gpio_irqsrc *mgi, uint32_t val, uint32_t mask) +{ + int bit; + + bit = GPIO_BIT(mgi->irq); + GPIO_LOCK(sc); + val = SYSCON_MODIFY_4(sc->syscon, + sc->offset + GPIO_REGNUM(mgi->irq) + reg, val, mask); + GPIO_UNLOCK(sc); +} + +static inline void +mvebu_gpio_isrc_mask(struct mvebu_gpio_softc *sc, + struct mvebu_gpio_irqsrc *mgi, uint32_t val) +{ + + if (mgi->is_level) + intr_modify(sc, GPIO_INT_LEVEL_MASK, mgi, val, 1); + else + intr_modify(sc, GPIO_INT_MASK, mgi, val, 1); +} + +static inline void +mvebu_gpio_isrc_eoi(struct mvebu_gpio_softc *sc, + struct mvebu_gpio_irqsrc *mgi) +{ + + if (!mgi->is_level) + intr_modify(sc, GPIO_INT_CAUSE, mgi, 1, 1); +} + + +static int +mvebu_gpio_pic_attach(struct mvebu_gpio_softc *sc) +{ + int rv; + uint32_t irq; + const char *name; + + sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < sc->gpio_npins; irq++) { + sc->isrcs[irq].irq = irq; + sc->isrcs[irq].is_level = false; + sc->isrcs[irq].is_inverted = false; + rv = intr_isrc_register(&sc->isrcs[irq].isrc, + sc->dev, 0, "%s,%u", name, irq); + if (rv != 0) + return (rv); /* XXX deregister ISRCs */ + } + if (intr_pic_register(sc->dev, + OF_xref_from_node(ofw_bus_get_node(sc->dev))) == NULL) + return (ENXIO); + + return (0); +} + +static int +mvebu_gpio_pic_detach(struct mvebu_gpio_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + + +static void +mvebu_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + mvebu_gpio_isrc_mask(sc, mgi, 0); +} + +static void +mvebu_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + mvebu_gpio_isrc_mask(sc, mgi, 1); +} + +static int +mvebu_gpio_pic_map_fdt(struct mvebu_gpio_softc *sc, u_int ncells, + pcell_t *cells, u_int *irqp, bool *invertedp, bool *levelp) +{ + bool inverted, level; + + /* + * The first cell is the interrupt number. + * The second cell is used to specify flags: + * bits[3:0] trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + */ + if (ncells != 2 || cells[0] >= sc->gpio_npins) + return (EINVAL); + + + switch (cells[1]) { + case 1: + inverted = false; + level = false; + break; + case 2: + inverted = true; + level = false; + break; + case 4: + inverted = false; + level = true; + break; + case 8: + inverted = true; + level = true; + break; + default: + return (EINVAL); + } + *irqp = cells[0]; + if (invertedp != NULL) + *invertedp = inverted; + if (levelp != NULL) + *levelp = level; + return (0); +} + + +static int +mvebu_gpio_pic_map_gpio(struct mvebu_gpio_softc *sc, u_int gpio_pin_num, + u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, bool *invertedp, + bool *levelp) +{ + bool inverted, level; + + if (gpio_pin_num >= sc->gpio_npins) + return (EINVAL); + + switch (intr_mode) { + case GPIO_INTR_LEVEL_LOW: + inverted = true; + level = true; + break; + case GPIO_INTR_LEVEL_HIGH: + inverted = false; + level = true; + break; + case GPIO_INTR_CONFORM: + case GPIO_INTR_EDGE_RISING: + inverted = false; + level = false; + break; + case GPIO_INTR_EDGE_FALLING: + inverted = true; + level = false; + break; + default: + return (EINVAL); + } + *irqp = gpio_pin_num; + if (invertedp != NULL) + *invertedp = inverted; + if (levelp != NULL) + *levelp = level; + return (0); +} + +static int +mvebu_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + int rv; + u_int irq; + struct mvebu_gpio_softc *sc; + + sc = device_get_softc(dev); + + if (data->type == INTR_MAP_DATA_FDT) { + struct intr_map_data_fdt *daf; + + daf = (struct intr_map_data_fdt *)data; + rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq, + NULL, NULL); + } else if (data->type == INTR_MAP_DATA_GPIO) { + struct intr_map_data_gpio *dag; + + dag = (struct intr_map_data_gpio *)data; + rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num, + dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, NULL, NULL); + } else + return (ENOTSUP); + + if (rv == 0) + *isrcp = &sc->isrcs[irq].isrc; + return (rv); +} + +static void +mvebu_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + if (mgi->is_level) + mvebu_gpio_isrc_eoi(sc, mgi); +} + +static void +mvebu_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + mvebu_gpio_isrc_mask(sc, mgi, 1); +} + +static void +mvebu_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + + mvebu_gpio_isrc_mask(sc, mgi, 0); + if (mgi->is_level) + mvebu_gpio_isrc_eoi(sc, mgi); +} + +static int +mvebu_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + u_int irq; + bool inverted, level; + int rv; + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + + if (data == NULL) + return (ENOTSUP); + + /* Get and check config for an interrupt. */ + if (data->type == INTR_MAP_DATA_FDT) { + struct intr_map_data_fdt *daf; + + daf = (struct intr_map_data_fdt *)data; + rv = mvebu_gpio_pic_map_fdt(sc, daf->ncells, daf->cells, &irq, + &inverted, &level); + } else if (data->type == INTR_MAP_DATA_GPIO) { + struct intr_map_data_gpio *dag; + + dag = (struct intr_map_data_gpio *)data; + rv = mvebu_gpio_pic_map_gpio(sc, dag->gpio_pin_num, + dag->gpio_pin_flags, dag->gpio_intr_mode, &irq, + &inverted, &level); + } else + return (ENOTSUP); + + if (rv != 0) + return (EINVAL); + + /* + * If this is a setup for another handler, + * only check that its configuration match. + */ + if (isrc->isrc_handlers != 0) + return ( + mgi->is_level == level && mgi->is_inverted == inverted ? + 0 : EINVAL); + + mgi->is_level = level; + mgi->is_inverted = inverted; + intr_modify(sc, GPIO_DATA_IN_POL, mgi, inverted ? 1 : 0, 1); + mvebu_gpio_pic_enable_intr(dev, isrc); + + return (0); +} + +static int +mvebu_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mvebu_gpio_softc *sc; + struct mvebu_gpio_irqsrc *mgi; + + sc = device_get_softc(dev); + mgi = (struct mvebu_gpio_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) + mvebu_gpio_isrc_mask(sc, mgi, 0); + return (0); +} + +/* -------------------------------------------------------------------------- + * + * Bus + * + */ + +static int +mvebu_gpio_intr(void *arg) +{ + u_int i, lvl, edge; + struct mvebu_gpio_softc *sc; + struct trapframe *tf; + struct mvebu_gpio_irqsrc *mgi; + struct mvebu_gpio_irq_cookie *cookie; + + + cookie = (struct mvebu_gpio_irq_cookie *)arg; + sc = cookie->sc; + tf = curthread->td_intr_frame; + + for (i = 0; i < sc->gpio_npins; i++) { + lvl = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]); + lvl &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]); + edge = gpio_read(sc, GPIO_DATA_IN, &sc->gpio_pins[i]); + edge &= gpio_read(sc, GPIO_INT_LEVEL_MASK, &sc->gpio_pins[i]); + if (edge == 0 || lvl == 0) + continue; + + mgi = &sc->isrcs[i]; + if (!mgi->is_level) + mvebu_gpio_isrc_eoi(sc, mgi); + if (intr_isrc_dispatch(&mgi->isrc, tf) != 0) { + mvebu_gpio_isrc_mask(sc, mgi, 0); + if (mgi->is_level) + mvebu_gpio_isrc_eoi(sc, mgi); + device_printf(sc->dev, + "Stray irq %u disabled\n", mgi->irq); + } + } + return (FILTER_HANDLED); +} + +static int +mvebu_gpio_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell Integrated GPIO Controller"); + return (0); +} + +static int +mvebu_gpio_detach(device_t dev) +{ + struct mvebu_gpio_softc *sc; + int i; + + sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized")); + + for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) { + if (sc->irq_ih[i] != NULL) + bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]); + } + + if (sc->isrcs != NULL) + mvebu_gpio_pic_detach(sc); + + gpiobus_detach_bus(dev); + + for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) { + if (sc->irq_res[i] != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, + sc->irq_res[i]); + } + GPIO_LOCK_DESTROY(sc); + + return(0); +} + +static int +mvebu_gpio_attach(device_t dev) +{ + struct mvebu_gpio_softc *sc; + phandle_t node; + struct gpio_pin *pin; + pcell_t pincnt; + int i, rv, rid; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + GPIO_LOCK_INIT(sc); + + pincnt = 0; + rv = OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t)); + if (rv < 0) { + device_printf(dev, + "ERROR: no pin-count or ngpios entry found!\n"); + return (ENXIO); + } + + sc->gpio_npins = MIN(pincnt, MV_GPIO_MAX_NPINS); + if (bootverbose) + device_printf(dev, + "%d pins available\n", sc->gpio_npins); + + rv = OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset)); + if (rv == -1) { + device_printf(dev, "ERROR: no 'offset' property found!\n"); + return (ENXIO); + } + + if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 || + sc->syscon == NULL) { + device_printf(dev, "ERROR: cannot get syscon handle!\n"); + return (ENXIO); + } + + /* Allocate interrupts. */ + for (i = 0; i < MV_GPIO_MAX_NIRQS; i++) { + sc->irq_cookies[i].sc = sc; + sc->irq_cookies[i].bank_num = i; + rid = i; + sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &rid, RF_ACTIVE); + if (sc->irq_res[i] == NULL) + break; + if ((bus_setup_intr(dev, sc->irq_res[i], + INTR_TYPE_MISC | INTR_MPSAFE, mvebu_gpio_intr, NULL, + &sc->irq_cookies[i], &sc->irq_ih[i]))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + mvebu_gpio_detach(dev); + return (ENXIO); + } + } + + /* Init GPIO pins */ + for (i = 0; i < sc->gpio_npins; i++) { + pin = sc->gpio_pins + i; + pin->gp_pin = i; + if (sc->irq_res[0] != NULL) + pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | + GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING; + else + pin->gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + pin->gp_flags = + gpio_read(sc, GPIO_CONTROL, &sc->gpio_pins[i]) != 0 ? + GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; + snprintf(pin->gp_name, GPIOMAXNAME, "gpio%d", i); + + /* Init HW */ + gpio_write(sc, GPIO_INT_MASK, pin, 0); + gpio_write(sc, GPIO_INT_LEVEL_MASK, pin, 0); + gpio_write(sc, GPIO_INT_CAUSE, pin, 1); + gpio_write(sc, GPIO_DATA_IN_POL, pin, 1); + gpio_write(sc, GPIO_BLINK_ENA, pin, 0); + } + + if (sc->irq_res[0] != NULL) { + rv = mvebu_gpio_pic_attach(sc); + if (rv != 0) { + device_printf(dev, "WARNING: unable to attach PIC\n"); + mvebu_gpio_detach(dev); + return (rv); + } + } + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) { + mvebu_gpio_detach(dev); + return (ENXIO); + } + + return (bus_generic_attach(dev)); +} + +static int +mvebu_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, + int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + + if (gcells != 2) + return (ERANGE); + *pin = gpios[0]; + *flags= gpios[1]; + return (0); +} + +static phandle_t +mvebu_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t mvebu_gpio_methods[] = { + DEVMETHOD(device_probe, mvebu_gpio_probe), + DEVMETHOD(device_attach, mvebu_gpio_attach), + DEVMETHOD(device_detach, mvebu_gpio_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, mvebu_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, mvebu_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, mvebu_gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, mvebu_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, mvebu_gpio_pic_teardown_intr), + DEVMETHOD(pic_post_filter, mvebu_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, mvebu_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, mvebu_gpio_pic_pre_ithread), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, mvebu_gpio_get_bus), + DEVMETHOD(gpio_pin_max, mvebu_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, mvebu_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, mvebu_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, mvebu_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, mvebu_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, mvebu_gpio_pin_get), + DEVMETHOD(gpio_pin_set, mvebu_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, mvebu_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, mvebu_gpio_map_gpios), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, mvebu_gpio_get_node), + + DEVMETHOD_END +}; + +static devclass_t mvebu_gpio_devclass; +static DEFINE_CLASS_0(gpio, mvebu_gpio_driver, mvebu_gpio_methods, + sizeof(struct mvebu_gpio_softc)); +EARLY_DRIVER_MODULE(gpio, simplebus, mvebu_gpio_driver, + mvebu_gpio_devclass, NULL, NULL, + BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); Property changes on: stable/12/sys/arm/mv/mvebu_gpio.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/12/sys/conf/files.arm64 =================================================================== --- stable/12/sys/conf/files.arm64 (revision 367215) +++ stable/12/sys/conf/files.arm64 (revision 367216) @@ -1,372 +1,373 @@ # $FreeBSD$ cloudabi32_vdso.o optional compat_cloudabi32 \ dependency "$S/contrib/cloudabi/cloudabi_vdso_armv6_on_64bit.S" \ compile-with "${CC} -x assembler-with-cpp -m32 -shared -nostdinc -nostdlib -Wl,-T$S/compat/cloudabi/cloudabi_vdso.lds $S/contrib/cloudabi/cloudabi_vdso_armv6_on_64bit.S -o ${.TARGET}" \ no-obj no-implicit-rule \ clean "cloudabi32_vdso.o" # cloudabi32_vdso_blob.o optional compat_cloudabi32 \ dependency "cloudabi32_vdso.o" \ compile-with "${OBJCOPY} --input-target binary --output-target elf64-littleaarch64 --binary-architecture aarch64 cloudabi32_vdso.o ${.TARGET}" \ no-implicit-rule \ clean "cloudabi32_vdso_blob.o" # cloudabi64_vdso.o optional compat_cloudabi64 \ dependency "$S/contrib/cloudabi/cloudabi_vdso_aarch64.S" \ compile-with "${CC} -x assembler-with-cpp -shared -nostdinc -nostdlib -Wl,-T$S/compat/cloudabi/cloudabi_vdso.lds $S/contrib/cloudabi/cloudabi_vdso_aarch64.S -o ${.TARGET}" \ no-obj no-implicit-rule \ clean "cloudabi64_vdso.o" # cloudabi64_vdso_blob.o optional compat_cloudabi64 \ dependency "cloudabi64_vdso.o" \ compile-with "${OBJCOPY} --input-target binary --output-target elf64-littleaarch64 --binary-architecture aarch64 cloudabi64_vdso.o ${.TARGET}" \ no-implicit-rule \ clean "cloudabi64_vdso_blob.o" # # Allwinner common files arm/allwinner/a10_timer.c optional a10_timer fdt arm/allwinner/a10_codec.c optional sound a10_codec arm/allwinner/a31_dmac.c optional a31_dmac arm/allwinner/sunxi_dma_if.m optional a31_dmac arm/allwinner/aw_cir.c optional evdev aw_cir fdt arm/allwinner/aw_gpio.c optional gpio aw_gpio fdt arm/allwinner/aw_mmc.c optional mmc aw_mmc fdt | mmccam aw_mmc fdt arm/allwinner/aw_nmi.c optional aw_nmi fdt \ compile-with "${NORMAL_C} -I$S/gnu/dts/include" arm/allwinner/aw_pwm.c optional aw_pwm fdt arm/allwinner/aw_rsb.c optional aw_rsb fdt arm/allwinner/aw_rtc.c optional aw_rtc fdt arm/allwinner/aw_sid.c optional aw_sid nvmem fdt arm/allwinner/aw_spi.c optional aw_spi fdt arm/allwinner/aw_syscon.c optional aw_syscon ext_resources syscon fdt arm/allwinner/aw_thermal.c optional aw_thermal nvmem fdt arm/allwinner/aw_usbphy.c optional ehci aw_usbphy fdt arm/allwinner/aw_wdog.c optional aw_wdog fdt arm/allwinner/axp81x.c optional axp81x fdt arm/allwinner/if_awg.c optional awg ext_resources syscon aw_sid nvmem fdt # Allwinner clock driver arm/allwinner/clkng/aw_ccung.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_frac.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_m.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_mipi.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_nkmp.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_nm.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_nmm.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_np.c optional aw_ccu fdt arm/allwinner/clkng/aw_clk_prediv_mux.c optional aw_ccu fdt arm/allwinner/clkng/ccu_a64.c optional soc_allwinner_a64 aw_ccu fdt arm/allwinner/clkng/ccu_h3.c optional soc_allwinner_h5 aw_ccu fdt arm/allwinner/clkng/ccu_h6.c optional soc_allwinner_h6 aw_ccu fdt arm/allwinner/clkng/ccu_h6_r.c optional soc_allwinner_h6 aw_ccu fdt arm/allwinner/clkng/ccu_sun8i_r.c optional aw_ccu fdt arm/allwinner/clkng/ccu_de2.c optional aw_ccu fdt # Allwinner padconf files arm/allwinner/a64/a64_padconf.c optional soc_allwinner_a64 fdt arm/allwinner/a64/a64_r_padconf.c optional soc_allwinner_a64 fdt arm/allwinner/h3/h3_padconf.c optional soc_allwinner_h5 fdt arm/allwinner/h3/h3_r_padconf.c optional soc_allwinner_h5 fdt arm/allwinner/h6/h6_padconf.c optional soc_allwinner_h6 fdt arm/allwinner/h6/h6_r_padconf.c optional soc_allwinner_h6 fdt arm/annapurna/alpine/alpine_ccu.c optional al_ccu fdt arm/annapurna/alpine/alpine_nb_service.c optional al_nb_service fdt arm/annapurna/alpine/alpine_pci.c optional al_pci fdt arm/annapurna/alpine/alpine_pci_msix.c optional al_pci fdt arm/annapurna/alpine/alpine_serdes.c optional al_serdes fdt \ no-depend \ compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}" arm/arm/generic_timer.c standard arm/arm/gic.c standard arm/arm/gic_acpi.c optional acpi arm/arm/gic_fdt.c optional fdt arm/arm/pmu.c standard arm/broadcom/bcm2835/bcm2835_audio.c optional sound vchiq fdt \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" arm/broadcom/bcm2835/bcm2835_bsc.c optional bcm2835_bsc fdt arm/broadcom/bcm2835/bcm2835_clkman.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_cpufreq.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_dma.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_fbd.c optional vt soc_brcm_bcm2837 fdt | vt soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_ft5406.c optional evdev bcm2835_ft5406 fdt arm/broadcom/bcm2835/bcm2835_gpio.c optional gpio soc_brcm_bcm2837 fdt | gpio soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_intr.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_mbox.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_rng.c optional random soc_brcm_bcm2837 fdt | random soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_sdhci.c optional sdhci soc_brcm_bcm2837 fdt | sdhci soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_sdhost.c optional sdhci soc_brcm_bcm2837 fdt | sdhci soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_spi.c optional bcm2835_spi fdt arm/broadcom/bcm2835/bcm2835_vcbus.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_vcio.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2835_wdog.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2836.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt soc_brcm_bcm2837 | dwcotg fdt soc_brcm_bcm2838 arm/mv/a37x0_gpio.c optional a37x0_gpio gpio fdt arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt arm/mv/gpio.c optional mv_gpio fdt +arm/mv/mvebu_gpio.c optional mv_gpio fdt arm/mv/mvebu_pinctrl.c optional mvebu_pinctrl fdt arm/mv/mv_ap806_clock.c optional SOC_MARVELL_8K fdt arm/mv/mv_ap806_gicp.c optional mv_ap806_gicp fdt arm/mv/mv_ap806_sei.c optional mv_ap806_sei fdt arm/mv/mv_cp110_clock.c optional SOC_MARVELL_8K fdt arm/mv/mv_cp110_icu.c optional mv_cp110_icu fdt arm/mv/mv_cp110_icu_bus.c optional mv_cp110_icu fdt arm/mv/mv_thermal.c optional SOC_MARVELL_8K mv_thermal fdt arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt arm/xilinx/uart_dev_cdnc.c optional uart soc_xilinx_zynq arm64/acpica/acpi_iort.c optional acpi arm64/acpica/acpi_machdep.c optional acpi arm64/acpica/OsdEnvironment.c optional acpi arm64/acpica/acpi_wakeup.c optional acpi arm64/acpica/pci_cfgreg.c optional acpi pci arm64/arm64/autoconf.c standard arm64/arm64/bus_machdep.c standard arm64/arm64/bus_space_asm.S standard arm64/arm64/busdma_bounce.c standard arm64/arm64/busdma_machdep.c standard arm64/arm64/bzero.S standard arm64/arm64/clock.c standard arm64/arm64/copyinout.S standard arm64/arm64/copystr.c standard arm64/arm64/cpu_errata.c standard arm64/arm64/cpufunc_asm.S standard arm64/arm64/db_disasm.c optional ddb arm64/arm64/db_interface.c optional ddb arm64/arm64/db_trace.c optional ddb arm64/arm64/debug_monitor.c optional ddb arm64/arm64/disassem.c optional ddb arm64/arm64/dump_machdep.c standard arm64/arm64/efirt_machdep.c optional efirt arm64/arm64/elf32_machdep.c optional compat_freebsd32 arm64/arm64/elf_machdep.c standard arm64/arm64/exception.S standard arm64/arm64/freebsd32_machdep.c optional compat_freebsd32 arm64/arm64/gicv3_its.c optional intrng fdt arm64/arm64/gic_v3.c standard arm64/arm64/gic_v3_acpi.c optional acpi arm64/arm64/gic_v3_fdt.c optional fdt arm64/arm64/identcpu.c standard arm64/arm64/in_cksum.c optional inet | inet6 arm64/arm64/locore.S standard no-obj arm64/arm64/machdep.c standard arm64/arm64/mem.c standard arm64/arm64/memcpy.S standard arm64/arm64/memmove.S standard arm64/arm64/minidump_machdep.c standard arm64/arm64/mp_machdep.c optional smp arm64/arm64/nexus.c standard arm64/arm64/ofw_machdep.c optional fdt arm64/arm64/pmap.c standard arm64/arm64/stack_machdep.c optional ddb | stack arm64/arm64/support.S standard arm64/arm64/swtch.S standard arm64/arm64/sys_machdep.c standard arm64/arm64/trap.c standard arm64/arm64/uio_machdep.c standard arm64/arm64/uma_machdep.c standard arm64/arm64/undefined.c standard arm64/arm64/unwind.c optional ddb | kdtrace_hooks | stack arm64/arm64/vfp.c standard arm64/arm64/vm_machdep.c standard arm64/cavium/thunder_pcie_fdt.c optional soc_cavm_thunderx pci fdt arm64/cavium/thunder_pcie_pem.c optional soc_cavm_thunderx pci arm64/cavium/thunder_pcie_pem_fdt.c optional soc_cavm_thunderx pci fdt arm64/cavium/thunder_pcie_common.c optional soc_cavm_thunderx pci arm64/cloudabi32/cloudabi32_sysvec.c optional compat_cloudabi32 arm64/cloudabi64/cloudabi64_sysvec.c optional compat_cloudabi64 arm64/coresight/coresight.c standard arm64/coresight/coresight_if.m standard arm64/coresight/coresight-cmd.c standard arm64/coresight/coresight-cpu-debug.c standard arm64/coresight/coresight-dynamic-replicator.c standard arm64/coresight/coresight-etm4x.c standard arm64/coresight/coresight-funnel.c standard arm64/coresight/coresight-tmc.c standard arm64/qualcomm/qcom_gcc.c optional qcom_gcc fdt contrib/vchiq/interface/compat/vchi_bsd.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -Wno-unused -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_arm.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -Wno-unused -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_connected.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_core.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_kmod.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_shim.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" contrib/vchiq/interface/vchiq_arm/vchiq_util.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq" crypto/armv8/armv8_crypto.c optional armv8crypto armv8_crypto_wrap.o optional armv8crypto \ dependency "$S/crypto/armv8/armv8_crypto_wrap.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc:N-mgeneral-regs-only} -I$S/crypto/armv8/ ${WERROR} ${NO_WCAST_QUAL} ${PROF} -march=armv8-a+crypto ${.IMPSRC}" \ no-implicit-rule \ clean "armv8_crypto_wrap.o" crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb dev/acpica/acpi_bus_if.m optional acpi dev/acpica/acpi_if.m optional acpi dev/acpica/acpi_pci_link.c optional acpi pci dev/acpica/acpi_pcib.c optional acpi pci dev/acpica/acpi_pxm.c optional acpi dev/ahci/ahci_generic.c optional ahci dev/altera/dwc/if_dwc_socfpga.c optional fdt dwc_socfpga dev/axgbe/if_axgbe.c optional axgbe dev/axgbe/xgbe-desc.c optional axgbe dev/axgbe/xgbe-dev.c optional axgbe dev/axgbe/xgbe-drv.c optional axgbe dev/axgbe/xgbe-mdio.c optional axgbe dev/cpufreq/cpufreq_dt.c optional cpufreq fdt dev/ice/if_ice_iflib.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_lib.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_osdep.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_resmgr.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_strings.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_iflib_recovery_txrx.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_iflib_txrx.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_common.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_controlq.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_dcb.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_flex_pipe.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_flow.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_nvm.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_sched.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_sriov.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" dev/ice/ice_switch.c optional ice pci \ compile-with "${NORMAL_C} -I$S/dev/ice" ice_ddp.c optional ice_ddp \ compile-with "${AWK} -f $S/tools/fw_stub.awk ice_ddp.fw:ice_ddp:0x01031000 -mice_ddp -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "ice_ddp.c" ice_ddp.fwo optional ice_ddp \ dependency "ice_ddp.fw" \ compile-with "${NORMAL_FWO}" \ no-implicit-rule \ clean "ice_ddp.fwo" ice_ddp.fw optional ice_ddp \ dependency "$S/contrib/dev/ice/ice-1.3.16.0.pkg" \ compile-with "${CP} $S/contrib/dev/ice/ice-1.3.16.0.pkg ice_ddp.fw" \ no-obj no-implicit-rule \ clean "ice_ddp.fw" dev/iicbus/sy8106a.c optional sy8106a fdt 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/hwpmc/hwpmc_arm64.c optional hwpmc dev/hwpmc/hwpmc_arm64_md.c optional hwpmc dev/mbox/mbox_if.m optional soc_brcm_bcm2837 dev/mmc/host/dwmmc.c optional dwmmc fdt dev/mmc/host/dwmmc_altera.c optional dwmmc fdt dwmmc_altera dev/mmc/host/dwmmc_hisi.c optional dwmmc fdt soc_hisi_hi6220 dev/mmc/host/dwmmc_rockchip.c optional dwmmc fdt soc_rockchip_rk3328 dev/neta/if_mvneta_fdt.c optional neta fdt dev/neta/if_mvneta.c optional neta mdio mii dev/ofw/ofw_cpu.c optional fdt dev/ofw/ofwpci.c optional fdt pci dev/pci/pci_host_generic.c optional pci dev/pci/pci_host_generic_acpi.c optional pci acpi dev/pci/pci_host_generic_fdt.c optional pci fdt dev/pci/pci_dw_mv.c optional pci fdt dev/pci/pci_dw.c optional pci fdt dev/pci/pci_dw_if.m optional pci fdt dev/psci/psci.c standard dev/psci/psci_arm64.S standard dev/psci/smccc.c standard dev/safexcel/safexcel.c optional safexcel fdt dev/sdhci/sdhci_xenon.c optional sdhci_xenon sdhci fdt dev/uart/uart_cpu_arm64.c optional uart dev/uart/uart_dev_mu.c optional uart uart_mu dev/uart/uart_dev_pl011.c optional uart pl011 dev/usb/controller/dwc_otg_hisi.c optional dwcotg fdt soc_hisi_hi6220 dev/usb/controller/ehci_mv.c optional ehci_mv fdt dev/usb/controller/generic_ehci.c optional ehci dev/usb/controller/generic_ehci_acpi.c optional ehci acpi dev/usb/controller/generic_ehci_fdt.c optional ehci fdt dev/usb/controller/generic_ohci.c optional ohci fdt dev/usb/controller/generic_usb_if.m optional ohci fdt dev/usb/controller/musb_otg_allwinner.c optional musb fdt soc_allwinner_a64 dev/usb/controller/usb_nop_xceiv.c optional fdt ext_resources dev/usb/controller/generic_xhci.c optional xhci fdt dev/vnic/mrml_bridge.c optional vnic fdt dev/vnic/nic_main.c optional vnic pci dev/vnic/nicvf_main.c optional vnic pci pci_iov dev/vnic/nicvf_queues.c optional vnic pci pci_iov dev/vnic/thunder_bgx_fdt.c optional vnic fdt dev/vnic/thunder_bgx.c optional vnic pci dev/vnic/thunder_mdio_fdt.c optional vnic fdt dev/vnic/thunder_mdio.c optional vnic dev/vnic/lmac_if.m optional inet | inet6 | vnic kern/kern_clocksource.c standard kern/msi_if.m optional intrng kern/pic_if.m optional intrng kern/subr_devmap.c standard kern/subr_intr.c optional intrng kern/subr_physmem.c standard libkern/bcmp.c standard libkern/ffs.c standard libkern/ffsl.c standard libkern/ffsll.c standard libkern/fls.c standard libkern/flsl.c standard libkern/flsll.c standard libkern/memcmp.c standard libkern/memset.c standard libkern/arm64/crc32c_armv8.S standard cddl/dev/dtrace/aarch64/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}" cddl/dev/dtrace/aarch64/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}" cddl/dev/fbt/aarch64/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" # RockChip Drivers arm64/rockchip/rk3399_emmcphy.c optional fdt rk_emmcphy soc_rockchip_rk3399 arm64/rockchip/rk_dwc3.c optional fdt rk_dwc3 soc_rockchip_rk3399 arm64/rockchip/rk_i2c.c optional fdt rk_i2c soc_rockchip_rk3328 | fdt rk_i2c soc_rockchip_rk3399 arm64/rockchip/rk805.c optional fdt rk805 soc_rockchip_rk3328 | fdt rk805 soc_rockchip_rk3399 arm64/rockchip/rk_grf.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/rk_pinctrl.c optional fdt rk_pinctrl soc_rockchip_rk3328 | fdt rk_pinctrl soc_rockchip_rk3399 arm64/rockchip/rk_gpio.c optional fdt rk_gpio soc_rockchip_rk3328 | fdt rk_gpio soc_rockchip_rk3399 arm64/rockchip/rk_iodomain.c optional fdt rk_iodomain arm64/rockchip/rk_spi.c optional fdt rk_spi arm64/rockchip/rk_usb2phy.c optional fdt rk_usb2phy soc_rockchip_rk3328 | soc_rockchip_rk3399 arm64/rockchip/rk_typec_phy.c optional fdt rk_typec_phy soc_rockchip_rk3399 arm64/rockchip/if_dwc_rk.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 arm64/rockchip/rk_tsadc_if.m optional fdt soc_rockchip_rk3399 arm64/rockchip/rk_tsadc.c optional fdt soc_rockchip_rk3399 arm64/rockchip/rk_pcie.c optional fdt pci soc_rockchip_rk3399 arm64/rockchip/rk_pcie_phy.c optional fdt pci soc_rockchip_rk3399 arm64/rockchip/rk_pwm.c optional fdt rk_pwm dev/dwc/if_dwc.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 dev/dwc/if_dwc_if.m optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 # RockChip Clock support arm64/rockchip/clk/rk_cru.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_armclk.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_composite.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_fract.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_gate.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_mux.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk_clk_pll.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk3328_cru.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk3399_cru.c optional fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk3399_pmucru.c optional fdt soc_rockchip_rk3399 Index: stable/12/sys/dev/pci/pci_dw_mv.c =================================================================== --- stable/12/sys/dev/pci/pci_dw_mv.c (revision 367215) +++ stable/12/sys/dev/pci/pci_dw_mv.c (revision 367216) @@ -1,328 +1,323 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Michal Meloun * * 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. * */ /* Armada 8k DesignWare PCIe driver */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "pci_dw_if.h" #define MV_GLOBAL_CONTROL_REG 0x8000 #define PCIE_APP_LTSSM_EN (1 << 2) -//#define PCIE_DEVICE_TYPE_SHIFT 4 -//#define PCIE_DEVICE_TYPE_MASK 0xF -//#define PCIE_DEVICE_TYPE_RC 0x4/ #define MV_GLOBAL_STATUS_REG 0x8008 #define MV_STATUS_RDLH_LINK_UP (1 << 1) #define MV_STATUS_PHY_LINK_UP (1 << 9) - #define MV_INT_CAUSE1 0x801C #define MV_INT_MASK1 0x8020 #define INT_A_ASSERT_MASK (1 << 9) #define INT_B_ASSERT_MASK (1 << 10) #define INT_C_ASSERT_MASK (1 << 11) #define INT_D_ASSERT_MASK (1 << 12) #define MV_INT_CAUSE2 0x8024 #define MV_INT_MASK2 0x8028 #define MV_ERR_INT_CAUSE 0x802C #define MV_ERR_INT_MASK 0x8030 #define MV_ARCACHE_TRC_REG 0x8050 #define MV_AWCACHE_TRC_REG 0x8054 #define MV_ARUSER_REG 0x805C #define MV_AWUSER_REG 0x8060 - - #define MV_MAX_LANES 8 - - struct pci_mv_softc { struct pci_dw_softc dw_sc; device_t dev; phandle_t node; struct resource *irq_res; void *intr_cookie; phy_t phy[MV_MAX_LANES]; clk_t clk_core; clk_t clk_reg; }; /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"marvell,armada8k-pcie", 1}, {NULL, 0}, }; - static int pci_mv_phy_init(struct pci_mv_softc *sc) { int i, rv; for (i = 0; i < MV_MAX_LANES; i++) { rv = phy_get_by_ofw_idx(sc->dev, sc->node, i, &(sc->phy[i])); if (rv != 0 && rv != ENOENT) { - device_printf(sc->dev, "Cannot get phy[%d]\n", i); - goto fail; - } - if (sc->phy[i] == NULL) - continue; - rv = phy_enable(sc->phy[i]); - if (rv != 0) { - device_printf(sc->dev, "Cannot enable phy[%d]\n", i); - goto fail; - } - } - return (0); + device_printf(sc->dev, "Cannot get phy[%d]\n", i); +/* XXX revert when phy driver will be implemented */ +#if 0 + goto fail; +#else + continue; +#endif + } + if (sc->phy[i] == NULL) + continue; + rv = phy_enable(sc->phy[i]); + if (rv != 0) { + device_printf(sc->dev, "Cannot enable phy[%d]\n", i); + goto fail; + } + } + return (0); fail: for (i = 0; i < MV_MAX_LANES; i++) { if (sc->phy[i] == NULL) continue; phy_release(sc->phy[i]); } return (rv); } static void pci_mv_init(struct pci_mv_softc *sc) { uint32_t reg; /* Set device configuration to RC */ reg = pci_dw_dbi_rd4(sc->dev, MV_GLOBAL_CONTROL_REG); reg &= ~0x000000F0; reg |= 0x000000040; pci_dw_dbi_wr4(sc->dev, MV_GLOBAL_CONTROL_REG, reg); /* AxCache master transaction attribures */ pci_dw_dbi_wr4(sc->dev, MV_ARCACHE_TRC_REG, 0x3511); pci_dw_dbi_wr4(sc->dev, MV_AWCACHE_TRC_REG, 0x5311); /* AxDomain master transaction attribures */ pci_dw_dbi_wr4(sc->dev, MV_ARUSER_REG, 0x0002); pci_dw_dbi_wr4(sc->dev, MV_AWUSER_REG, 0x0002); /* Enable all INTx interrupt (virtuual) pins */ reg = pci_dw_dbi_rd4(sc->dev, MV_INT_MASK1); reg |= INT_A_ASSERT_MASK | INT_B_ASSERT_MASK | INT_C_ASSERT_MASK | INT_D_ASSERT_MASK; pci_dw_dbi_wr4(sc->dev, MV_INT_MASK1, reg); /* Enable local interrupts */ pci_dw_dbi_wr4(sc->dev, DW_MSI_INTR0_MASK, 0xFFFFFFFF); pci_dw_dbi_wr4(sc->dev, MV_INT_MASK1, 0xFFFFFFFF); - pci_dw_dbi_wr4(sc->dev, MV_INT_MASK2, 0xFFFFFFFF); + pci_dw_dbi_wr4(sc->dev, MV_INT_MASK2, 0xFFFFFFFD); pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE1, 0xFFFFFFFF); pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE2, 0xFFFFFFFF); /* Errors have own interrupt, not yet populated in DTt */ pci_dw_dbi_wr4(sc->dev, MV_ERR_INT_MASK, 0); } + static int pci_mv_intr(void *arg) { struct pci_mv_softc *sc = arg; uint32_t cause1, cause2; /* Ack all interrups */ cause1 = pci_dw_dbi_rd4(sc->dev, MV_INT_CAUSE1); cause2 = pci_dw_dbi_rd4(sc->dev, MV_INT_CAUSE2); - if (cause1 == 0 || cause2 == 0) - return(FILTER_STRAY); pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE1, cause1); pci_dw_dbi_wr4(sc->dev, MV_INT_CAUSE2, cause2); return (FILTER_HANDLED); } static int pci_mv_get_link(device_t dev, bool *status) { uint32_t reg; reg = pci_dw_dbi_rd4(dev, MV_GLOBAL_STATUS_REG); if ((reg & (MV_STATUS_RDLH_LINK_UP | MV_STATUS_PHY_LINK_UP)) == (MV_STATUS_RDLH_LINK_UP | MV_STATUS_PHY_LINK_UP)) *status = true; else *status = false; return (0); } static int pci_mv_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Marvell Armada8K PCI-E Controller"); return (BUS_PROBE_DEFAULT); } static int pci_mv_attach(device_t dev) { struct pci_mv_softc *sc; phandle_t node; int rv; int rid; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->dev = dev; sc->node = node; rid = 0; sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->dw_sc.dbi_res == NULL) { device_printf(dev, "Cannot allocate DBI memory\n"); rv = ENXIO; goto out; } /* PCI interrupt */ rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); rv = ENXIO; goto out; } /* Clocks */ rv = clk_get_by_ofw_name(sc->dev, 0, "core", &sc->clk_core); if (rv != 0) { device_printf(sc->dev, "Cannot get 'core' clock\n"); rv = ENXIO; goto out; } rv = clk_get_by_ofw_name(sc->dev, 0, "reg", &sc->clk_reg); if (rv != 0) { device_printf(sc->dev, "Cannot get 'reg' clock\n"); rv = ENXIO; goto out; } rv = clk_enable(sc->clk_core); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'core' clock\n"); rv = ENXIO; goto out; } rv = clk_enable(sc->clk_reg); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'reg' clock\n"); rv = ENXIO; goto out; } rv = pci_mv_phy_init(sc); if (rv) goto out; rv = pci_dw_init(dev); if (rv != 0) goto out; pci_mv_init(sc); /* Setup interrupt */ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, pci_mv_intr, NULL, sc, &sc->intr_cookie)) { device_printf(dev, "cannot setup interrupt handler\n"); rv = ENXIO; goto out; } return (bus_generic_attach(dev)); out: /* XXX Cleanup */ return (rv); } static device_method_t pci_mv_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_mv_probe), DEVMETHOD(device_attach, pci_mv_attach), DEVMETHOD(pci_dw_get_link, pci_mv_get_link), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, pci_mv_driver, pci_mv_methods, sizeof(struct pci_mv_softc), pci_dw_driver); static devclass_t pci_mv_devclass; DRIVER_MODULE( pci_mv, simplebus, pci_mv_driver, pci_mv_devclass, - NULL, NULL); \ No newline at end of file + NULL, NULL); Index: stable/12/sys/modules/dtb/mv/Makefile =================================================================== --- stable/12/sys/modules/dtb/mv/Makefile (revision 367215) +++ stable/12/sys/modules/dtb/mv/Makefile (revision 367216) @@ -1,13 +1,17 @@ # $FreeBSD$ # All the dts files for Marvell systems we support. .if ${MACHINE_ARCH} == "armv7" DTS= \ armada-388-clearfog.dts \ armada-388-gp.dts .elif ${MACHINE_ARCH} == "aarch64" DTS= \ - marvell/armada-3720-espressobin.dts + marvell/armada-3720-espressobin.dts \ + marvell/armada-8040-clearfog-gt-8k.dts \ + marvell/armada-8040-db \ + marvell/armada-8040-mcbin.dts \ + marvell/armada-8040-mcbin-singleshot.dts .endif .include Index: stable/12 =================================================================== --- stable/12 (revision 367215) +++ stable/12 (revision 367216) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r362384-362386,362392