diff --git a/sys/arm/mv/gpio.c b/sys/arm/mv/gpio.c index f080ac1f5560..6b75d78e7604 100644 --- a/sys/arm/mv/gpio.c +++ b/sys/arm/mv/gpio.c @@ -1,1036 +1,1106 @@ /*- * 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 #define GPIO_MAX_INTR_COUNT 8 #define GPIO_PINS_PER_REG 32 #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 { - struct resource * res[GPIO_MAX_INTR_COUNT + 1]; + struct resource * mem_res; + int mem_rid; + struct resource * irq_res[GPIO_MAX_INTR_COUNT]; + int irq_rid[GPIO_MAX_INTR_COUNT]; void *ih_cookie[GPIO_MAX_INTR_COUNT]; bus_space_tag_t bst; bus_space_handle_t bsh; struct mtx mutex; uint8_t pin_num; /* number of GPIO pins */ uint8_t irq_num; /* number of real IRQs occupied by GPIO controller */ uint8_t use_high; /* Used for debouncing. */ uint32_t debounced_state_lo; uint32_t debounced_state_hi; struct callout **debounce_callouts; int *debounce_counters; }; -extern struct resource_spec mv_gpio_res[]; - static struct mv_gpio_softc *mv_gpio_softc = NULL; static uint32_t gpio_setup[MV_GPIO_MAX_NPINS]; static int mv_gpio_probe(device_t); static int mv_gpio_attach(device_t); static int mv_gpio_intr(void *); static int mv_gpio_init(void); static void mv_gpio_double_edge_init(int pin); static int mv_gpio_debounce_setup(int pin); static int mv_gpio_debounce_init(int pin); static void mv_gpio_debounce_start(int pin); static int mv_gpio_debounce_prepare(int pin); static void mv_gpio_debounce(void *arg); static void mv_gpio_debounced_state_set(int pin, uint8_t new_state); static uint32_t mv_gpio_debounced_state_get(int pin); static void mv_gpio_exec_intr_handlers(uint32_t status, int high); static void mv_gpio_intr_handler(int pin); static uint32_t mv_gpio_reg_read(uint32_t reg); static void mv_gpio_reg_write(uint32_t reg, uint32_t val); static void mv_gpio_reg_set(uint32_t reg, uint32_t val); static void mv_gpio_reg_clear(uint32_t reg, uint32_t val); static void mv_gpio_blink(uint32_t pin, uint8_t enable); static void mv_gpio_polarity(uint32_t pin, uint8_t enable, uint8_t toggle); static void mv_gpio_level(uint32_t pin, uint8_t enable); static void mv_gpio_edge(uint32_t pin, uint8_t enable); static void mv_gpio_out_en(uint32_t pin, uint8_t enable); static void mv_gpio_int_ack(uint32_t pin); static void mv_gpio_value_set(uint32_t pin, uint8_t val); static uint32_t mv_gpio_value_get(uint32_t pin, uint8_t exclude_polar); +static void mv_gpio_intr_mask(int pin); +static void mv_gpio_intr_unmask(int pin); +int mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt, + void (*hand)(void *), void *arg, int pin, int flags, void **cookiep); + +int mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask); +void mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable); +uint8_t mv_gpio_in(uint32_t pin); + #define MV_GPIO_LOCK() mtx_lock_spin(&mv_gpio_softc->mutex) #define MV_GPIO_UNLOCK() mtx_unlock_spin(&mv_gpio_softc->mutex) #define MV_GPIO_ASSERT_LOCKED() mtx_assert(&mv_gpio_softc->mutex, MA_OWNED) static device_method_t mv_gpio_methods[] = { DEVMETHOD(device_probe, mv_gpio_probe), DEVMETHOD(device_attach, mv_gpio_attach), { 0, 0 } }; static driver_t mv_gpio_driver = { "gpio", mv_gpio_methods, sizeof(struct mv_gpio_softc), }; static devclass_t mv_gpio_devclass; DRIVER_MODULE(gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0); typedef int (*gpios_phandler_t)(phandle_t, pcell_t *, int); struct gpio_ctrl_entry { const char *compat; gpios_phandler_t handler; }; static int mv_handle_gpios_prop(phandle_t ctrl, pcell_t *gpios, int len); int gpio_get_config_from_dt(void); struct gpio_ctrl_entry gpio_controllers[] = { { "mrvl,gpio", &mv_handle_gpios_prop }, { NULL, NULL } }; static int mv_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "mrvl,gpio")) return (ENXIO); device_set_desc(dev, "Marvell Integrated GPIO Controller"); return (0); } static int mv_gpio_attach(device_t dev) { - int error, i; + int error, i, size; struct mv_gpio_softc *sc; uint32_t dev_id, rev_id; + pcell_t pincnt = 0; + pcell_t irq_cells = 0; + phandle_t iparent; sc = (struct mv_gpio_softc *)device_get_softc(dev); if (sc == NULL) return (ENXIO); if (mv_gpio_softc != NULL) return (ENXIO); mv_gpio_softc = sc; /* Get chip id and revision */ soc_id(&dev_id, &rev_id); if (dev_id == MV_DEV_88F5182 || dev_id == MV_DEV_88F5281 || dev_id == MV_DEV_MV78100 || dev_id == MV_DEV_MV78100_Z0 ) { sc->pin_num = 32; sc->irq_num = 4; sc->use_high = 0; } else if (dev_id == MV_DEV_88F6281 || dev_id == MV_DEV_88F6282) { sc->pin_num = 50; sc->irq_num = 7; sc->use_high = 1; } else { - device_printf(dev, "unknown chip id=0x%x\n", dev_id); + if (OF_getencprop(ofw_bus_get_node(dev), "pin-count", &pincnt, + sizeof(pcell_t)) >= 0 || + OF_getencprop(ofw_bus_get_node(dev), "ngpios", &pincnt, + sizeof(pcell_t)) >= 0) { + sc->pin_num = pincnt; + device_printf(dev, "%d pins available\n", sc->pin_num); + } else { + device_printf(dev, "ERROR: no pin-count entry found!\n"); + return (ENXIO); + } + } + + /* Find root interrupt controller */ + iparent = ofw_bus_find_iparent(ofw_bus_get_node(dev)); + if (iparent == 0) { + device_printf(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(dev, "DTB: Missing #interrupt-cells " + "property in interrupt parent node\n"); + return (ENXIO); + } + } + + size = OF_getproplen(ofw_bus_get_node(dev), "interrupts"); + if (size != -1) { + size = size / sizeof(pcell_t); + size = size / irq_cells; + sc->irq_num = size; + device_printf(dev, "%d IRQs available\n", sc->irq_num); + } else { + device_printf(dev, "ERROR: no interrupts entry found!\n"); return (ENXIO); } 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); mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN); - error = bus_alloc_resources(dev, mv_gpio_res, sc->res); - if (error) { + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, + RF_ACTIVE); + + if (!sc->mem_res) { mtx_destroy(&sc->mutex); - device_printf(dev, "could not allocate resources\n"); + device_printf(dev, "could not allocate memory window\n"); return (ENXIO); } - sc->bst = rman_get_bustag(sc->res[0]); - sc->bsh = rman_get_bushandle(sc->res[0]); + sc->bst = rman_get_bustag(sc->mem_res); + sc->bsh = rman_get_bushandle(sc->mem_res); + + for (i = 0; i < sc->irq_num; i++) { + sc->irq_rid[i] = i; + sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->irq_rid[i], RF_ACTIVE); + if (!sc->irq_res[i]) { + mtx_destroy(&sc->mutex); + device_printf(dev, + "could not allocate gpio%d interrupt\n", i+1); + return (ENXIO); + } + } /* Disable all interrupts */ bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_EDGE_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_LEV_MASK, 0); if (sc->use_high) { bus_space_write_4(sc->bst, sc->bsh, GPIO_HI_INT_EDGE_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_HI_INT_LEV_MASK, 0); bus_space_write_4(sc->bst, sc->bsh, GPIO_HI_INT_CAUSE, 0); } for (i = 0; i < sc->irq_num; i++) { - if (bus_setup_intr(dev, sc->res[1 + i], + if (bus_setup_intr(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_resources(dev, mv_gpio_res, sc->res); + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid[i], sc->irq_res[i]); device_printf(dev, "could not set up intr %d\n", i); return (ENXIO); } } error = mv_gpio_init(); if (error) { device_printf(dev, "WARNING: failed to initialize GPIO pins, " "error = %d\n", error); } /* Clear interrupt status. */ bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0); return (0); } static int mv_gpio_intr(void *arg) { uint32_t int_cause, gpio_val; uint32_t int_cause_hi, gpio_val_hi; 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(GPIO_INT_CAUSE); int_cause &= mv_gpio_reg_read(GPIO_INT_EDGE_MASK); /* * Level sensitive interrupts are asserted when unmasked GPIO_DATA_IN * register bits are set. */ gpio_val = mv_gpio_reg_read(GPIO_DATA_IN); gpio_val &= mv_gpio_reg_read(GPIO_INT_LEV_MASK); int_cause_hi = 0; gpio_val_hi = 0; if (mv_gpio_softc->use_high) { int_cause_hi = mv_gpio_reg_read(GPIO_HI_INT_CAUSE); int_cause_hi &= mv_gpio_reg_read(GPIO_HI_INT_EDGE_MASK); gpio_val_hi = mv_gpio_reg_read(GPIO_HI_DATA_IN); gpio_val_hi &= mv_gpio_reg_read(GPIO_HI_INT_LEV_MASK); } mv_gpio_exec_intr_handlers(int_cause | gpio_val, 0); if (mv_gpio_softc->use_high) mv_gpio_exec_intr_handlers(int_cause_hi | gpio_val_hi, 1); MV_GPIO_UNLOCK(); return (FILTER_HANDLED); } /* * GPIO interrupt handling */ static struct intr_event *gpio_events[MV_GPIO_MAX_NPINS]; int mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt, void (*hand)(void *), void *arg, int pin, int flags, void **cookiep) { struct intr_event *event; int error; if (pin < 0 || pin >= mv_gpio_softc->pin_num) return (ENXIO); event = gpio_events[pin]; if (event == NULL) { MV_GPIO_LOCK(); if (gpio_setup[pin] & MV_GPIO_IN_DEBOUNCE) { error = mv_gpio_debounce_init(pin); if (error != 0) { MV_GPIO_UNLOCK(); return (error); } } else if (gpio_setup[pin] & MV_GPIO_IN_IRQ_DOUBLE_EDGE) mv_gpio_double_edge_init(pin); MV_GPIO_UNLOCK(); error = intr_event_create(&event, (void *)pin, 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); gpio_events[pin] = event; } intr_event_add_handler(event, name, filt, hand, arg, intr_priority(flags), flags, cookiep); return (0); } void mv_gpio_intr_mask(int pin) { if (pin >= mv_gpio_softc->pin_num) return; MV_GPIO_LOCK(); if (gpio_setup[pin] & (MV_GPIO_IN_IRQ_EDGE | MV_GPIO_IN_IRQ_DOUBLE_EDGE)) mv_gpio_edge(pin, 0); else mv_gpio_level(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(pin); MV_GPIO_UNLOCK(); } void mv_gpio_intr_unmask(int pin) { if (pin >= mv_gpio_softc->pin_num) return; MV_GPIO_LOCK(); if (gpio_setup[pin] & (MV_GPIO_IN_IRQ_EDGE | MV_GPIO_IN_IRQ_DOUBLE_EDGE)) mv_gpio_edge(pin, 1); else mv_gpio_level(pin, 1); MV_GPIO_UNLOCK(); } static void mv_gpio_exec_intr_handlers(uint32_t status, int high) { int i, pin; MV_GPIO_ASSERT_LOCKED(); i = 0; while (status != 0) { if (status & 1) { pin = (high ? (i + GPIO_PINS_PER_REG) : i); if (gpio_setup[pin] & MV_GPIO_IN_DEBOUNCE) mv_gpio_debounce_start(pin); else if (gpio_setup[pin] & MV_GPIO_IN_IRQ_DOUBLE_EDGE) { mv_gpio_polarity(pin, 0, 1); mv_gpio_intr_handler(pin); } else mv_gpio_intr_handler(pin); } status >>= 1; i++; } } static void mv_gpio_intr_handler(int pin) { - struct intr_event *event; +#ifdef INTRNG + struct intr_irqsrc isrc; MV_GPIO_ASSERT_LOCKED(); - event = gpio_events[pin]; - if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) +#ifdef INTR_SOLO + isrc.isrc_filter = NULL; +#endif + isrc.isrc_event = gpio_events[pin]; + + if (isrc.isrc_event == NULL || TAILQ_EMPTY(&isrc.isrc_event->ie_handlers)) return; - intr_event_handle(event, NULL); + intr_isrc_dispatch(&isrc, NULL); +#endif } int mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask) { int error; if (pin >= mv_gpio_softc->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) { error = mv_gpio_debounce_prepare(pin); if (error != 0) return (error); } MV_GPIO_LOCK(); if (mask & MV_GPIO_OUT_BLINK) mv_gpio_blink(pin, flags & MV_GPIO_OUT_BLINK); if (mask & MV_GPIO_IN_POL_LOW) mv_gpio_polarity(pin, flags & MV_GPIO_IN_POL_LOW, 0); if (mask & MV_GPIO_IN_DEBOUNCE) { error = mv_gpio_debounce_setup(pin); if (error) { MV_GPIO_UNLOCK(); return (error); } } gpio_setup[pin] &= ~(mask); gpio_setup[pin] |= (flags & mask); MV_GPIO_UNLOCK(); return (0); } static void mv_gpio_double_edge_init(int pin) { uint8_t raw_read; MV_GPIO_ASSERT_LOCKED(); raw_read = (mv_gpio_value_get(pin, 1) ? 1 : 0); if (raw_read) mv_gpio_polarity(pin, 1, 0); else mv_gpio_polarity(pin, 0, 0); } static int mv_gpio_debounce_setup(int pin) { struct callout *c; MV_GPIO_ASSERT_LOCKED(); c = mv_gpio_softc->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(int pin) { struct callout *c; struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)mv_gpio_softc; 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(int pin) { uint8_t raw_read; int *cnt; MV_GPIO_ASSERT_LOCKED(); cnt = &mv_gpio_softc->debounce_counters[pin]; raw_read = (mv_gpio_value_get(pin, 1) ? 1 : 0); if (raw_read) { mv_gpio_polarity(pin, 1, 0); *cnt = DEBOUNCE_HI_LO_MS / DEBOUNCE_CHECK_MS; } else { mv_gpio_polarity(pin, 0, 0); *cnt = DEBOUNCE_LO_HI_MS / DEBOUNCE_CHECK_MS; } mv_gpio_debounced_state_set(pin, raw_read); return (0); } static void mv_gpio_debounce_start(int pin) { struct callout *c; int *debounced_pin; MV_GPIO_ASSERT_LOCKED(); c = mv_gpio_softc->debounce_callouts[pin]; if (c == NULL) { mv_gpio_int_ack(pin); return; } if (callout_pending(c) || callout_active(c)) { mv_gpio_int_ack(pin); return; } debounced_pin = (int *)malloc(sizeof(int), M_DEVBUF, M_WAITOK); if (debounced_pin == NULL) { mv_gpio_int_ack(pin); return; } *debounced_pin = pin; callout_reset(c, DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, debounced_pin); } static void mv_gpio_debounce(void *arg) { uint8_t raw_read, last_state; int pin; int *debounce_counter; pin = *((int *)arg); MV_GPIO_LOCK(); raw_read = (mv_gpio_value_get(pin, 1) ? 1 : 0); last_state = (mv_gpio_debounced_state_get(pin) ? 1 : 0); debounce_counter = &mv_gpio_softc->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(mv_gpio_softc->debounce_callouts[pin], DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, arg); } else { *debounce_counter = *debounce_counter - 1; if (*debounce_counter != 0) callout_reset(mv_gpio_softc->debounce_callouts[pin], DEBOUNCE_CHECK_TICKS, mv_gpio_debounce, arg); else { mv_gpio_debounced_state_set(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 (((gpio_setup[pin] & MV_GPIO_IN_POL_LOW) && (raw_read == 0)) || (((gpio_setup[pin] & MV_GPIO_IN_POL_LOW) == 0) && raw_read) || (gpio_setup[pin] & MV_GPIO_IN_IRQ_DOUBLE_EDGE)) mv_gpio_intr_handler(pin); /* Toggle polarity for next edge. */ mv_gpio_polarity(pin, 0, 1); free(arg, M_DEVBUF); callout_deactivate(mv_gpio_softc-> debounce_callouts[pin]); } } MV_GPIO_UNLOCK(); } static void mv_gpio_debounced_state_set(int pin, uint8_t new_state) { uint32_t *old_state; MV_GPIO_ASSERT_LOCKED(); if (pin >= GPIO_PINS_PER_REG) { old_state = &mv_gpio_softc->debounced_state_hi; pin -= GPIO_PINS_PER_REG; } else old_state = &mv_gpio_softc->debounced_state_lo; if (new_state) *old_state |= (1 << pin); else *old_state &= ~(1 << pin); } static uint32_t mv_gpio_debounced_state_get(int pin) { uint32_t *state; MV_GPIO_ASSERT_LOCKED(); if (pin >= GPIO_PINS_PER_REG) { state = &mv_gpio_softc->debounced_state_hi; pin -= GPIO_PINS_PER_REG; } else state = &mv_gpio_softc->debounced_state_lo; return (*state & (1 << pin)); } void mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable) { MV_GPIO_LOCK(); mv_gpio_value_set(pin, val); mv_gpio_out_en(pin, enable); MV_GPIO_UNLOCK(); } uint8_t mv_gpio_in(uint32_t pin) { uint8_t state; MV_GPIO_LOCK(); if (gpio_setup[pin] & MV_GPIO_IN_DEBOUNCE) { if (gpio_setup[pin] & MV_GPIO_IN_POL_LOW) state = (mv_gpio_debounced_state_get(pin) ? 0 : 1); else state = (mv_gpio_debounced_state_get(pin) ? 1 : 0); } else if (gpio_setup[pin] & MV_GPIO_IN_IRQ_DOUBLE_EDGE) { if (gpio_setup[pin] & MV_GPIO_IN_POL_LOW) state = (mv_gpio_value_get(pin, 1) ? 0 : 1); else state = (mv_gpio_value_get(pin, 1) ? 1 : 0); } else state = (mv_gpio_value_get(pin, 0) ? 1 : 0); MV_GPIO_UNLOCK(); return (state); } static uint32_t mv_gpio_reg_read(uint32_t reg) { return (bus_space_read_4(mv_gpio_softc->bst, mv_gpio_softc->bsh, reg)); } static void mv_gpio_reg_write(uint32_t reg, uint32_t val) { bus_space_write_4(mv_gpio_softc->bst, mv_gpio_softc->bsh, reg, val); } static void mv_gpio_reg_set(uint32_t reg, uint32_t pin) { uint32_t reg_val; reg_val = mv_gpio_reg_read(reg); reg_val |= GPIO(pin); mv_gpio_reg_write(reg, reg_val); } static void mv_gpio_reg_clear(uint32_t reg, uint32_t pin) { uint32_t reg_val; reg_val = mv_gpio_reg_read(reg); reg_val &= ~(GPIO(pin)); mv_gpio_reg_write(reg, reg_val); } static void mv_gpio_out_en(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_OUT_EN_CTRL; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_OUT_EN_CTRL; if (enable) mv_gpio_reg_clear(reg, pin); else mv_gpio_reg_set(reg, pin); } static void mv_gpio_blink(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_BLINK_EN; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_BLINK_EN; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_polarity(uint32_t pin, uint8_t enable, uint8_t toggle) { uint32_t reg, reg_val; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_IN_POLAR; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_IN_POLAR; if (toggle) { reg_val = mv_gpio_reg_read(reg) & GPIO(pin); if (reg_val) mv_gpio_reg_clear(reg, pin); else mv_gpio_reg_set(reg, pin); } else if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_level(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_INT_LEV_MASK; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_INT_LEV_MASK; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_edge(uint32_t pin, uint8_t enable) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_INT_EDGE_MASK; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_INT_EDGE_MASK; if (enable) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static void mv_gpio_int_ack(uint32_t pin) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_INT_CAUSE; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_INT_CAUSE; mv_gpio_reg_clear(reg, pin); } static uint32_t mv_gpio_value_get(uint32_t pin, uint8_t exclude_polar) { uint32_t reg, polar_reg, reg_val, polar_reg_val; if (pin >= mv_gpio_softc->pin_num) return (0); if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_IN; pin -= GPIO_PINS_PER_REG; polar_reg = GPIO_HI_DATA_IN_POLAR; } else { reg = GPIO_DATA_IN; polar_reg = GPIO_DATA_IN_POLAR; } reg_val = mv_gpio_reg_read(reg); if (exclude_polar) { polar_reg_val = mv_gpio_reg_read(polar_reg); return ((reg_val & GPIO(pin)) ^ (polar_reg_val & GPIO(pin))); } else return (reg_val & GPIO(pin)); } static void mv_gpio_value_set(uint32_t pin, uint8_t val) { uint32_t reg; if (pin >= mv_gpio_softc->pin_num) return; if (pin >= GPIO_PINS_PER_REG) { reg = GPIO_HI_DATA_OUT; pin -= GPIO_PINS_PER_REG; } else reg = GPIO_DATA_OUT; if (val) mv_gpio_reg_set(reg, pin); else mv_gpio_reg_clear(reg, pin); } static int mv_handle_gpios_prop(phandle_t ctrl, pcell_t *gpios, int len) { pcell_t gpio_cells, pincnt; int inc, t, tuples, tuple_size; int dir, flags, pin; u_long gpio_ctrl, size; struct mv_gpio_softc sc; pincnt = 0; if (!OF_hasprop(ctrl, "gpio-controller")) /* Node is not a GPIO controller. */ return (ENXIO); if (OF_getencprop(ctrl, "#gpio-cells", &gpio_cells, sizeof(pcell_t)) < 0) return (ENXIO); if (gpio_cells != 3) return (ENXIO); tuple_size = gpio_cells * sizeof(pcell_t) + sizeof(phandle_t); tuples = len / tuple_size; if (fdt_regsize(ctrl, &gpio_ctrl, &size)) return (ENXIO); if (OF_getencprop(ctrl, "pin-count", &pincnt, sizeof(pcell_t)) < 0) return (ENXIO); sc.pin_num = pincnt; /* * Skip controller reference, since controller's phandle is given * explicitly (in a function argument). */ inc = sizeof(ihandle_t) / sizeof(pcell_t); gpios += inc; for (t = 0; t < tuples; t++) { pin = gpios[0]; dir = gpios[1]; flags = gpios[2]; mv_gpio_configure(pin, flags, ~0); if (dir == 1) /* Input. */ mv_gpio_out_en(pin, 0); else { /* Output. */ if (flags & MV_GPIO_OUT_OPEN_DRAIN) mv_gpio_out(pin, 0, 1); if (flags & MV_GPIO_OUT_OPEN_SRC) mv_gpio_out(pin, 1, 1); } gpios += gpio_cells + inc; } return (0); } #define MAX_PINS_PER_NODE 5 #define GPIOS_PROP_CELLS 4 static int mv_gpio_init(void) { phandle_t child, parent, root, ctrl; pcell_t gpios[MAX_PINS_PER_NODE * GPIOS_PROP_CELLS]; struct gpio_ctrl_entry *e; int len, rv; root = OF_finddevice("/"); len = 0; parent = root; /* Traverse through entire tree to find nodes with 'gpios' prop */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { /* Find a 'leaf'. Start the search from this node. */ while (OF_child(child)) { parent = child; child = OF_child(child); } if ((len = OF_getproplen(child, "gpios")) > 0) { if (len > sizeof(gpios)) return (ENXIO); /* Get 'gpios' property. */ OF_getencprop(child, "gpios", gpios, len); e = (struct gpio_ctrl_entry *)&gpio_controllers; /* Find and call a handler. */ for (; e->compat; e++) { /* * First cell of 'gpios' property should * contain a ref. to a node defining GPIO * controller. */ ctrl = OF_node_from_xref(gpios[0]); if (ofw_bus_node_is_compatible(ctrl, e->compat)) /* Call a handler. */ if ((rv = e->handler(ctrl, (pcell_t *)&gpios, len))) return (rv); } } if (OF_peer(child) == 0) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } return (0); } diff --git a/sys/arm/mv/mvvar.h b/sys/arm/mv/mvvar.h index 5994b15fcba5..46029bfcc133 100644 --- a/sys/arm/mv/mvvar.h +++ b/sys/arm/mv/mvvar.h @@ -1,157 +1,148 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002, 2003 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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/pxa2x0var.h, rev 1 * * $FreeBSD$ */ #ifndef _MVVAR_H_ #define _MVVAR_H_ #include #include #include #include #include #define MV_TYPE_PCI 0 #define MV_TYPE_PCIE 1 #define MV_MODE_ENDPOINT 0 #define MV_MODE_ROOT 1 enum soc_family{ MV_SOC_ARMADA_38X = 0x00, MV_SOC_ARMADA_XP = 0x01, MV_SOC_ARMV5 = 0x02, MV_SOC_UNSUPPORTED = 0xff, }; struct gpio_config { int gc_gpio; /* GPIO number */ uint32_t gc_flags; /* GPIO flags */ int gc_output; /* GPIO output value */ }; struct decode_win { int target; /* Mbus unit ID */ int attr; /* Attributes of the target interface */ vm_paddr_t base; /* Physical base addr */ uint32_t size; vm_paddr_t remap; }; extern const struct gpio_config mv_gpio_config[]; extern const struct decode_win *cpu_wins; extern const struct decode_win *idma_wins; extern const struct decode_win *xor_wins; extern int idma_wins_no; extern int xor_wins_no; -/* Function prototypes */ -int mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt, - void (*hand)(void *), void *arg, int pin, int flags, void **cookiep); -void mv_gpio_intr_mask(int pin); -void mv_gpio_intr_unmask(int pin); -int mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask); -void mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable); -uint8_t mv_gpio_in(uint32_t pin); - int soc_decode_win(void); void soc_id(uint32_t *dev, uint32_t *rev); void soc_dump_decode_win(void); uint32_t soc_power_ctrl_get(uint32_t mask); void soc_power_ctrl_set(uint32_t mask); int decode_win_cpu_set(int target, int attr, vm_paddr_t base, uint32_t size, vm_paddr_t remap); int decode_win_overlap(int, int, const struct decode_win *); int win_cpu_can_remap(int); void decode_win_pcie_setup(u_long); void ddr_disable(int i); int ddr_is_active(int i); uint32_t ddr_base(int i); uint32_t ddr_size(int i); uint32_t ddr_attr(int i); uint32_t ddr_target(int i); uint32_t cpu_extra_feat(void); uint32_t get_tclk(void); uint32_t get_cpu_freq(void); uint32_t get_l2clk(void); uint32_t read_cpu_ctrl(uint32_t); void write_cpu_ctrl(uint32_t, uint32_t); uint32_t read_cpu_mp_clocks(uint32_t reg); void write_cpu_mp_clocks(uint32_t reg, uint32_t val); uint32_t read_cpu_misc(uint32_t reg); void write_cpu_misc(uint32_t reg, uint32_t val); int mv_pcib_bar_win_set(device_t dev, uint32_t base, uint32_t size, uint32_t remap, int winno, int busno); int mv_pcib_cpu_win_remap(device_t dev, uint32_t remap, uint32_t size); void mv_mask_endpoint_irq(uintptr_t nb, int unit); void mv_unmask_endpoint_irq(uintptr_t nb, int unit); int mv_drbl_get_next_irq(int dir, int unit); void mv_drbl_mask_all(int unit); void mv_drbl_mask_irq(uint32_t irq, int dir, int unit); void mv_drbl_unmask_irq(uint32_t irq, int dir, int unit); void mv_drbl_set_mask(uint32_t val, int dir, int unit); uint32_t mv_drbl_get_mask(int dir, int unit); void mv_drbl_set_cause(uint32_t val, int dir, int unit); uint32_t mv_drbl_get_cause(int dir, int unit); void mv_drbl_set_msg(uint32_t val, int mnr, int dir, int unit); uint32_t mv_drbl_get_msg(int mnr, int dir, int unit); int mv_msi_data(int irq, uint64_t *addr, uint32_t *data); struct devmap_entry; int mv_pci_devmap(phandle_t, struct devmap_entry *, vm_offset_t, vm_offset_t); int fdt_localbus_devmap(phandle_t, struct devmap_entry *, int, int *); enum soc_family mv_check_soc_family(void); uint32_t get_tclk_armadaxp(void); uint32_t get_tclk_armada38x(void); uint32_t get_cpu_freq_armadaxp(void); uint32_t get_cpu_freq_armada38x(void); #endif /* _MVVAR_H_ */