Index: head/sys/arm/mv/gpio.c =================================================================== --- head/sys/arm/mv/gpio.c (revision 332019) +++ head/sys/arm/mv/gpio.c (revision 332020) @@ -1,661 +1,1036 @@ /*- * 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]; 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); +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); +static uint32_t mv_gpio_value_get(uint32_t pin, uint8_t exclude_polar); +#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; struct mv_gpio_softc *sc; uint32_t dev_id, rev_id; 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); 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) { + mtx_destroy(&sc->mutex); device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); - /* Disable and clear all interrupts */ + /* 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); - bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0); - if (sc->pin_num > GPIO_PINS_PER_REG) { + 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], - INTR_TYPE_MISC, mv_gpio_intr, NULL, + 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); device_printf(dev, "could not set up intr %d\n", i); return (ENXIO); } } - return (mv_gpio_init()); + 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 = 0; - int i; + 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 &= int_cause; - if (mv_gpio_softc->pin_num > GPIO_PINS_PER_REG) { + 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 &= int_cause_hi; + gpio_val_hi &= mv_gpio_reg_read(GPIO_HI_INT_LEV_MASK); } - i = 0; - while (gpio_val != 0) { - if (gpio_val & 1) - mv_gpio_intr_handler(i); - gpio_val >>= 1; - i++; - } + mv_gpio_exec_intr_handlers(int_cause | gpio_val, 0); - if (mv_gpio_softc->pin_num > GPIO_PINS_PER_REG) { - i = 0; - while (gpio_val_hi != 0) { - if (gpio_val_hi & 1) - mv_gpio_intr_handler(i + GPIO_PINS_PER_REG); - gpio_val_hi >>= 1; - i++; - } - } + 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; - if (gpio_setup[pin] & MV_GPIO_IN_IRQ_EDGE) + 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; - if (gpio_setup[pin] & MV_GPIO_IN_IRQ_EDGE) + 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; + MV_GPIO_ASSERT_LOCKED(); + event = gpio_events[pin]; if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) return; intr_event_handle(event, NULL); } -static int -mv_gpio_configure(uint32_t pin, uint32_t flags) +int +mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask) { + int error; if (pin >= mv_gpio_softc->pin_num) return (EINVAL); - if (flags & MV_GPIO_OUT_BLINK) - mv_gpio_blink(pin, 1); - if (flags & MV_GPIO_IN_POL_LOW) - mv_gpio_polarity(pin, 1); - if (flags & MV_GPIO_IN_IRQ_EDGE) - mv_gpio_edge(pin, 1); - if (flags & MV_GPIO_IN_IRQ_LEVEL) - mv_gpio_level(pin, 1); + /* check flags consistency */ + if (((flags & mask) & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) + return (EINVAL); - gpio_setup[pin] = flags; + 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; - return (mv_gpio_value_get(pin) ? 1 : 0); + 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) +mv_gpio_polarity(uint32_t pin, uint8_t enable, uint8_t toggle) { - uint32_t reg; + 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 (enable) + 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) +mv_gpio_value_get(uint32_t pin, uint8_t exclude_polar) { - uint32_t reg, reg_val; + 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; - } else + 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); - return (reg_val & GPIO(pin)); + 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); + 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); } Index: head/sys/arm/mv/mvreg.h =================================================================== --- head/sys/arm/mv/mvreg.h (revision 332019) +++ head/sys/arm/mv/mvreg.h (revision 332020) @@ -1,450 +1,452 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD. * All rights reserved. * * Developed 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. * 3. Neither the name of MARVELL nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MVREG_H_ #define _MVREG_H_ #include #if defined(SOC_MV_DISCOVERY) #define IRQ_CAUSE_ERROR 0x0 #define IRQ_CAUSE 0x4 #define IRQ_CAUSE_HI 0x8 #define IRQ_MASK_ERROR 0xC #define IRQ_MASK 0x10 #define IRQ_MASK_HI 0x14 #define IRQ_CAUSE_SELECT 0x18 #define FIQ_MASK_ERROR 0x1C #define FIQ_MASK 0x20 #define FIQ_MASK_HI 0x24 #define FIQ_CAUSE_SELECT 0x28 #define ENDPOINT_IRQ_MASK_ERROR(n) 0x2C #define ENDPOINT_IRQ_MASK(n) 0x30 #define ENDPOINT_IRQ_MASK_HI(n) 0x34 #define ENDPOINT_IRQ_CAUSE_SELECT 0x38 #else #define IRQ_CAUSE 0x0 #define IRQ_MASK 0x4 #define FIQ_MASK 0x8 #define ENDPOINT_IRQ_MASK(n) 0xC #define IRQ_CAUSE_HI 0x10 #define IRQ_MASK_HI 0x14 #define FIQ_MASK_HI 0x18 #define ENDPOINT_IRQ_MASK_HI(n) 0x1C #define ENDPOINT_IRQ_MASK_ERROR(n) (-1) #define IRQ_CAUSE_ERROR (-1) /* Fake defines for unified */ #define IRQ_MASK_ERROR (-1) /* interrupt controller code */ #endif #define MAIN_IRQ_NUM 116 #define ERR_IRQ_NUM 32 #define ERR_IRQ (MAIN_IRQ_NUM) #define MSI_IRQ (ERR_IRQ + ERR_IRQ_NUM) #define MSI_IRQ_NUM 32 #define IRQ_CPU_SELF 0x00000001 #define BRIDGE_IRQ_CAUSE_ARMADAXP 0x68 #define IRQ_TIMER0_ARMADAXP 0x00000001 #define IRQ_TIMER1_ARMADAXP 0x00000002 #define IRQ_TIMER_WD_ARMADAXP 0x00000004 #define BRIDGE_IRQ_CAUSE 0x10 #define IRQ_CPU_SELF 0x00000001 #define IRQ_TIMER0 0x00000002 #define IRQ_TIMER1 0x00000004 #define IRQ_TIMER_WD 0x00000008 #define BRIDGE_IRQ_MASK 0x14 #define IRQ_CPU_MASK 0x00000001 #define IRQ_TIMER0_MASK 0x00000002 #define IRQ_TIMER1_MASK 0x00000004 #define IRQ_TIMER_WD_MASK 0x00000008 #define IRQ_CPU_SELF_CLR (~IRQ_CPU_SELF) #define IRQ_TIMER0_CLR (~IRQ_TIMER0) #define IRQ_TIMER_WD_CLR (~IRQ_TIMER_WD) #define IRQ_TIMER0_CLR_ARMADAXP (~IRQ_TIMER0_ARMADAXP) #define IRQ_TIMER_WD_CLR_ARMADAXP (~IRQ_TIMER_WD_ARMADAXP) /* * System reset */ #define RSTOUTn_MASK_ARMV7 0x60 #define SYSTEM_SOFT_RESET_ARMV7 0x64 #define SOFT_RST_OUT_EN_ARMV7 0x00000001 #define SYS_SOFT_RST_ARMV7 0x00000001 #define RSTOUTn_MASK 0x8 #define SOFT_RST_OUT_EN 0x00000004 #define SYSTEM_SOFT_RESET 0xc #define SYS_SOFT_RST 0x00000001 #define RSTOUTn_MASK_WD 0x400 #define WD_RSTOUTn_MASK 0x4 #define WD_GLOBAL_MASK 0x00000100 #define WD_CPU0_MASK 0x00000001 #define WD_RST_OUT_EN 0x00000002 /* * Power Control */ #if defined(SOC_MV_KIRKWOOD) #define CPU_PM_CTRL 0x18 #else #define CPU_PM_CTRL 0x1C #endif #define CPU_PM_CTRL_NONE 0 #define CPU_PM_CTRL_ALL ~0x0 #if defined(SOC_MV_KIRKWOOD) #define CPU_PM_CTRL_GE0 (1 << 0) #define CPU_PM_CTRL_PEX0_PHY (1 << 1) #define CPU_PM_CTRL_PEX0 (1 << 2) #define CPU_PM_CTRL_USB0 (1 << 3) #define CPU_PM_CTRL_SDIO (1 << 4) #define CPU_PM_CTRL_TSU (1 << 5) #define CPU_PM_CTRL_DUNIT (1 << 6) #define CPU_PM_CTRL_RUNIT (1 << 7) #define CPU_PM_CTRL_XOR0 (1 << 8) #define CPU_PM_CTRL_AUDIO (1 << 9) #define CPU_PM_CTRL_SATA0 (1 << 14) #define CPU_PM_CTRL_SATA1 (1 << 15) #define CPU_PM_CTRL_XOR1 (1 << 16) #define CPU_PM_CTRL_CRYPTO (1 << 17) #define CPU_PM_CTRL_GE1 (1 << 19) #define CPU_PM_CTRL_TDM (1 << 20) #define CPU_PM_CTRL_XOR (CPU_PM_CTRL_XOR0 | CPU_PM_CTRL_XOR1) #define CPU_PM_CTRL_USB(u) (CPU_PM_CTRL_USB0) #define CPU_PM_CTRL_SATA (CPU_PM_CTRL_SATA0 | CPU_PM_CTRL_SATA1) #define CPU_PM_CTRL_GE(u) (CPU_PM_CTRL_GE1 * (u) | CPU_PM_CTRL_GE0 * \ (1 - (u))) #define CPU_PM_CTRL_IDMA (CPU_PM_CTRL_NONE) #elif defined(SOC_MV_DISCOVERY) #define CPU_PM_CTRL_GE0 (1 << 1) #define CPU_PM_CTRL_GE1 (1 << 2) #define CPU_PM_CTRL_PEX00 (1 << 5) #define CPU_PM_CTRL_PEX01 (1 << 6) #define CPU_PM_CTRL_PEX02 (1 << 7) #define CPU_PM_CTRL_PEX03 (1 << 8) #define CPU_PM_CTRL_PEX10 (1 << 9) #define CPU_PM_CTRL_PEX11 (1 << 10) #define CPU_PM_CTRL_PEX12 (1 << 11) #define CPU_PM_CTRL_PEX13 (1 << 12) #define CPU_PM_CTRL_SATA0_PHY (1 << 13) #define CPU_PM_CTRL_SATA0 (1 << 14) #define CPU_PM_CTRL_SATA1_PHY (1 << 15) #define CPU_PM_CTRL_SATA1 (1 << 16) #define CPU_PM_CTRL_USB0 (1 << 17) #define CPU_PM_CTRL_USB1 (1 << 18) #define CPU_PM_CTRL_USB2 (1 << 19) #define CPU_PM_CTRL_IDMA (1 << 20) #define CPU_PM_CTRL_XOR (1 << 21) #define CPU_PM_CTRL_CRYPTO (1 << 22) #define CPU_PM_CTRL_DEVICE (1 << 23) #define CPU_PM_CTRL_USB(u) (1 << (17 + (u))) #define CPU_PM_CTRL_SATA (CPU_PM_CTRL_SATA0 | CPU_PM_CTRL_SATA1) #define CPU_PM_CTRL_GE(u) (CPU_PM_CTRL_GE1 * (u) | CPU_PM_CTRL_GE0 * \ (1 - (u))) #else #define CPU_PM_CTRL_CRYPTO (CPU_PM_CTRL_NONE) #define CPU_PM_CTRL_IDMA (CPU_PM_CTRL_NONE) #define CPU_PM_CTRL_XOR (CPU_PM_CTRL_NONE) #define CPU_PM_CTRL_SATA (CPU_PM_CTRL_NONE) #define CPU_PM_CTRL_USB(u) (CPU_PM_CTRL_NONE) #define CPU_PM_CTRL_GE(u) (CPU_PM_CTRL_NONE) #endif /* * Timers */ #define CPU_TIMERS_BASE 0x300 #define CPU_TIMER_CONTROL 0x0 #define CPU_TIMER0_EN 0x00000001 #define CPU_TIMER0_AUTO 0x00000002 #define CPU_TIMER1_EN 0x00000004 #define CPU_TIMER1_AUTO 0x00000008 #define CPU_TIMER2_EN 0x00000010 #define CPU_TIMER2_AUTO 0x00000020 #define CPU_TIMER_WD_EN 0x00000100 #define CPU_TIMER_WD_AUTO 0x00000200 /* 25MHz mode is Armada XP - specific */ #define CPU_TIMER_WD_25MHZ_EN 0x00000400 #define CPU_TIMER0_25MHZ_EN 0x00000800 #define CPU_TIMER1_25MHZ_EN 0x00001000 #define CPU_TIMER0_REL 0x10 #define CPU_TIMER0 0x14 /* * SATA */ #define SATA_CHAN_NUM 2 #define EDMA_REGISTERS_OFFSET 0x2000 #define EDMA_REGISTERS_SIZE 0x2000 #define SATA_EDMA_BASE(ch) (EDMA_REGISTERS_OFFSET + \ ((ch) * EDMA_REGISTERS_SIZE)) /* SATAHC registers */ #define SATA_CR 0x000 /* Configuration Reg. */ #define SATA_CR_NODMABS (1 << 8) #define SATA_CR_NOEDMABS (1 << 9) #define SATA_CR_NOPRDPBS (1 << 10) #define SATA_CR_COALDIS(ch) (1 << (24 + ch)) /* Interrupt Coalescing Threshold Reg. */ #define SATA_ICTR 0x00C #define SATA_ICTR_MAX ((1 << 8) - 1) /* Interrupt Time Threshold Reg. */ #define SATA_ITTR 0x010 #define SATA_ITTR_MAX ((1 << 24) - 1) #define SATA_ICR 0x014 /* Interrupt Cause Reg. */ #define SATA_ICR_DMADONE(ch) (1 << (ch)) #define SATA_ICR_COAL (1 << 4) #define SATA_ICR_DEV(ch) (1 << (8 + ch)) #define SATA_MICR 0x020 /* Main Interrupt Cause Reg. */ #define SATA_MICR_ERR(ch) (1 << (2 * ch)) #define SATA_MICR_DONE(ch) (1 << ((2 * ch) + 1)) #define SATA_MICR_DMADONE(ch) (1 << (4 + ch)) #define SATA_MICR_COAL (1 << 8) #define SATA_MIMR 0x024 /* Main Interrupt Mask Reg. */ /* Shadow registers */ #define SATA_SHADOWR_BASE(ch) (SATA_EDMA_BASE(ch) + 0x100) #define SATA_SHADOWR_CONTROL(ch) (SATA_EDMA_BASE(ch) + 0x120) /* SATA registers */ #define SATA_SATA_SSTATUS(ch) (SATA_EDMA_BASE(ch) + 0x300) #define SATA_SATA_SERROR(ch) (SATA_EDMA_BASE(ch) + 0x304) #define SATA_SATA_SCONTROL(ch) (SATA_EDMA_BASE(ch) + 0x308) #define SATA_SATA_FISICR(ch) (SATA_EDMA_BASE(ch) + 0x364) /* EDMA registers */ #define SATA_EDMA_CFG(ch) (SATA_EDMA_BASE(ch) + 0x000) #define SATA_EDMA_CFG_QL128 (1 << 19) #define SATA_EDMA_CFG_HQCACHE (1 << 22) #define SATA_EDMA_IECR(ch) (SATA_EDMA_BASE(ch) + 0x008) #define SATA_EDMA_IEMR(ch) (SATA_EDMA_BASE(ch) + 0x00C) #define SATA_EDMA_REQBAHR(ch) (SATA_EDMA_BASE(ch) + 0x010) #define SATA_EDMA_REQIPR(ch) (SATA_EDMA_BASE(ch) + 0x014) #define SATA_EDMA_REQOPR(ch) (SATA_EDMA_BASE(ch) + 0x018) #define SATA_EDMA_RESBAHR(ch) (SATA_EDMA_BASE(ch) + 0x01C) #define SATA_EDMA_RESIPR(ch) (SATA_EDMA_BASE(ch) + 0x020) #define SATA_EDMA_RESOPR(ch) (SATA_EDMA_BASE(ch) + 0x024) #define SATA_EDMA_CMD(ch) (SATA_EDMA_BASE(ch) + 0x028) #define SATA_EDMA_CMD_ENABLE (1 << 0) #define SATA_EDMA_CMD_DISABLE (1 << 1) #define SATA_EDMA_CMD_RESET (1 << 2) #define SATA_EDMA_STATUS(ch) (SATA_EDMA_BASE(ch) + 0x030) #define SATA_EDMA_STATUS_IDLE (1 << 7) /* Offset to extract input slot from REQIPR register */ #define SATA_EDMA_REQIS_OFS 5 /* Offset to extract input slot from RESOPR register */ #define SATA_EDMA_RESOS_OFS 3 /* * GPIO */ #define GPIO_DATA_OUT 0x00 #define GPIO_DATA_OUT_EN_CTRL 0x04 #define GPIO_BLINK_EN 0x08 #define GPIO_DATA_IN_POLAR 0x0c #define GPIO_DATA_IN 0x10 #define GPIO_INT_CAUSE 0x14 #define GPIO_INT_EDGE_MASK 0x18 #define GPIO_INT_LEV_MASK 0x1c #define GPIO_HI_DATA_OUT 0x40 #define GPIO_HI_DATA_OUT_EN_CTRL 0x44 #define GPIO_HI_BLINK_EN 0x48 #define GPIO_HI_DATA_IN_POLAR 0x4c #define GPIO_HI_DATA_IN 0x50 #define GPIO_HI_INT_CAUSE 0x54 #define GPIO_HI_INT_EDGE_MASK 0x58 #define GPIO_HI_INT_LEV_MASK 0x5c #define GPIO(n) (1 << (n)) #define MV_GPIO_MAX_NPINS 64 -#define MV_GPIO_IN_NONE 0x0 -#define MV_GPIO_IN_POL_LOW (1 << 16) -#define MV_GPIO_IN_IRQ_EDGE (2 << 16) -#define MV_GPIO_IN_IRQ_LEVEL (4 << 16) -#define MV_GPIO_OUT_NONE 0x0 -#define MV_GPIO_OUT_BLINK 0x1 -#define MV_GPIO_OUT_OPEN_DRAIN 0x2 -#define MV_GPIO_OUT_OPEN_SRC 0x4 +#define MV_GPIO_IN_NONE 0x0 +#define MV_GPIO_IN_POL_LOW (1 << 16) +#define MV_GPIO_IN_IRQ_EDGE (2 << 16) +#define MV_GPIO_IN_IRQ_LEVEL (4 << 16) +#define MV_GPIO_IN_IRQ_DOUBLE_EDGE (8 << 16) +#define MV_GPIO_IN_DEBOUNCE (16 << 16) +#define MV_GPIO_OUT_NONE 0x0 +#define MV_GPIO_OUT_BLINK 0x1 +#define MV_GPIO_OUT_OPEN_DRAIN 0x2 +#define MV_GPIO_OUT_OPEN_SRC 0x4 #define IS_GPIO_IRQ(irq) ((irq) >= NIRQ && (irq) < NIRQ + MV_GPIO_MAX_NPINS) #define GPIO2IRQ(gpio) ((gpio) + NIRQ) #define IRQ2GPIO(irq) ((irq) - NIRQ) #if defined(SOC_MV_ORION) #define SAMPLE_AT_RESET 0x10 #elif defined(SOC_MV_KIRKWOOD) #define SAMPLE_AT_RESET 0x30 #endif #define SAMPLE_AT_RESET_ARMADA38X 0x400 #define SAMPLE_AT_RESET_LO 0x30 #define SAMPLE_AT_RESET_HI 0x34 /* * Clocks */ #if defined(SOC_MV_ORION) #define TCLK_MASK 0x00000300 #define TCLK_SHIFT 0x08 #elif defined(SOC_MV_DISCOVERY) #define TCLK_MASK 0x00000180 #define TCLK_SHIFT 0x07 #endif #define TCLK_MASK_ARMADA38X 0x00008000 #define TCLK_SHIFT_ARMADA38X 15 #define TCLK_100MHZ 100000000 #define TCLK_125MHZ 125000000 #define TCLK_133MHZ 133333333 #define TCLK_150MHZ 150000000 #define TCLK_166MHZ 166666667 #define TCLK_200MHZ 200000000 #define TCLK_250MHZ 250000000 #define TCLK_300MHZ 300000000 #define TCLK_667MHZ 667000000 #define A38X_CPU_DDR_CLK_MASK 0x00007c00 #define A38X_CPU_DDR_CLK_SHIFT 10 /* * CPU Cache Configuration */ #define CPU_CONFIG 0x00000000 #define CPU_CONFIG_IC_PREF 0x00010000 #define CPU_CONFIG_DC_PREF 0x00020000 #define CPU_CONTROL 0x00000004 #define CPU_CONTROL_L2_SIZE 0x00200000 /* Only on Discovery */ #define CPU_CONTROL_L2_MODE 0x00020000 /* Only on Discovery */ #define CPU_L2_CONFIG 0x00000028 /* Only on Kirkwood */ #define CPU_L2_CONFIG_MODE 0x00000010 /* Only on Kirkwood */ /* * PCI Express port control (CPU Control registers) */ #define CPU_CONTROL_PCIE_DISABLE(n) (1 << (3 * (n))) /* * Vendor ID */ #define PCI_VENDORID_MRVL 0x11AB #define PCI_VENDORID_MRVL2 0x1B4B /* * Chip ID */ #define MV_DEV_88F5181 0x5181 #define MV_DEV_88F5182 0x5182 #define MV_DEV_88F5281 0x5281 #define MV_DEV_88F6281 0x6281 #define MV_DEV_88F6282 0x6282 #define MV_DEV_88F6781 0x6781 #define MV_DEV_88F6828 0x6828 #define MV_DEV_88F6820 0x6820 #define MV_DEV_88F6810 0x6810 #define MV_DEV_MV78100_Z0 0x6381 #define MV_DEV_MV78100 0x7810 #define MV_DEV_MV78130 0x7813 #define MV_DEV_MV78160 0x7816 #define MV_DEV_MV78230 0x7823 #define MV_DEV_MV78260 0x7826 #define MV_DEV_MV78460 0x7846 #define MV_DEV_88RC8180 0x8180 #define MV_DEV_88RC9480 0x9480 #define MV_DEV_88RC9580 0x9580 #define MV_DEV_FAMILY_MASK 0xff00 #define MV_DEV_DISCOVERY 0x7800 #define MV_DEV_ARMADA38X 0x6800 /* * Doorbell register control */ #define MV_DRBL_PCIE_TO_CPU 0 #define MV_DRBL_CPU_TO_PCIE 1 #define MV_DRBL_CAUSE(d,u) (0x10 * (u) + 0x8 * (d)) #define MV_DRBL_MASK(d,u) (0x10 * (u) + 0x8 * (d) + 0x4) #define MV_DRBL_MSG(m,d,u) (0x10 * (u) + 0x8 * (d) + 0x4 * (m) + 0x30) /* * SCU */ #define MV_SCU_BASE (MV_BASE + 0xc000) #define MV_SCU_REGS_LEN 0x100 #define MV_SCU_REG_CTRL 0x00 #define MV_SCU_REG_CONFIG 0x04 #define MV_SCU_ENABLE (1 << 0) #define MV_SCU_SL_L2_ENABLE (1 << 3) #define SCU_CFG_REG_NCPU_MASK 0x3 /* * PMSU */ #define MV_PMSU_BASE (MV_BASE + 0x22000) #define MV_PMSU_REGS_LEN 0x1000 #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) (((cpu) * 0x100) + 0x124) /* * CPU RESET */ #define MV_CPU_RESET_BASE (MV_BASE + 0x20800) #define MV_CPU_RESET_REGS_LEN 0x8 #define CPU_RESET_OFFSET(cpu) ((cpu) * 0x8) #define CPU_RESET_ASSERT 0x1 #define MV_MBUS_CTRL_BASE (MV_BASE + 0x20420) #define MV_MBUS_CTRL_REGS_LEN 0x10 #endif /* _MVREG_H_ */ Index: head/sys/arm/mv/mvvar.h =================================================================== --- head/sys/arm/mv/mvvar.h (revision 332019) +++ head/sys/arm/mv/mvvar.h (revision 332020) @@ -1,156 +1,157 @@ /*- * 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_ */