194b8a54aSOleksandr Tymoshenko /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
39b7ae691SOleksandr Tymoshenko *
494b8a54aSOleksandr Tymoshenko * Copyright (c) 2017 Tom Jones <tj@enoti.me>
594b8a54aSOleksandr Tymoshenko * All rights reserved.
694b8a54aSOleksandr Tymoshenko *
794b8a54aSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without
894b8a54aSOleksandr Tymoshenko * modification, are permitted provided that the following conditions
994b8a54aSOleksandr Tymoshenko * are met:
1094b8a54aSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright
1194b8a54aSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer.
1294b8a54aSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright
1394b8a54aSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the
1494b8a54aSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution.
1594b8a54aSOleksandr Tymoshenko *
1694b8a54aSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1794b8a54aSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1894b8a54aSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1994b8a54aSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2094b8a54aSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2194b8a54aSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2294b8a54aSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2394b8a54aSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2494b8a54aSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2594b8a54aSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2694b8a54aSOleksandr Tymoshenko * SUCH DAMAGE.
2794b8a54aSOleksandr Tymoshenko *
2894b8a54aSOleksandr Tymoshenko */
2994b8a54aSOleksandr Tymoshenko
3094b8a54aSOleksandr Tymoshenko /*
3194b8a54aSOleksandr Tymoshenko * Copyright (c) 2016 Mark Kettenis
3294b8a54aSOleksandr Tymoshenko *
3394b8a54aSOleksandr Tymoshenko * Permission to use, copy, modify, and distribute this software for any
3494b8a54aSOleksandr Tymoshenko * purpose with or without fee is hereby granted, provided that the above
3594b8a54aSOleksandr Tymoshenko * copyright notice and this permission notice appear in all copies.
3694b8a54aSOleksandr Tymoshenko *
3794b8a54aSOleksandr Tymoshenko * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
3894b8a54aSOleksandr Tymoshenko * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
3994b8a54aSOleksandr Tymoshenko * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
4094b8a54aSOleksandr Tymoshenko * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4194b8a54aSOleksandr Tymoshenko * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4294b8a54aSOleksandr Tymoshenko * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
4394b8a54aSOleksandr Tymoshenko * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4494b8a54aSOleksandr Tymoshenko */
45*fdafd315SWarner Losh
4694b8a54aSOleksandr Tymoshenko #include <sys/param.h>
4794b8a54aSOleksandr Tymoshenko #include <sys/systm.h>
4894b8a54aSOleksandr Tymoshenko #include <sys/bus.h>
4994b8a54aSOleksandr Tymoshenko #include <sys/gpio.h>
5094b8a54aSOleksandr Tymoshenko #include <sys/clock.h>
5194b8a54aSOleksandr Tymoshenko #include <sys/kernel.h>
5294b8a54aSOleksandr Tymoshenko #include <sys/module.h>
5394b8a54aSOleksandr Tymoshenko #include <sys/endian.h>
5494b8a54aSOleksandr Tymoshenko #include <sys/rman.h>
5594b8a54aSOleksandr Tymoshenko #include <sys/types.h>
5694b8a54aSOleksandr Tymoshenko #include <sys/malloc.h>
5794b8a54aSOleksandr Tymoshenko
5894b8a54aSOleksandr Tymoshenko #include <machine/bus.h>
5994b8a54aSOleksandr Tymoshenko #include <machine/resource.h>
6094b8a54aSOleksandr Tymoshenko
6194b8a54aSOleksandr Tymoshenko #include <contrib/dev/acpica/include/acpi.h>
6294b8a54aSOleksandr Tymoshenko #include <contrib/dev/acpica/include/accommon.h>
6394b8a54aSOleksandr Tymoshenko
6494b8a54aSOleksandr Tymoshenko #include <dev/acpica/acpivar.h>
6594b8a54aSOleksandr Tymoshenko #include <dev/gpio/gpiobusvar.h>
6694b8a54aSOleksandr Tymoshenko
6794b8a54aSOleksandr Tymoshenko #include "opt_platform.h"
6894b8a54aSOleksandr Tymoshenko #include "opt_acpi.h"
6994b8a54aSOleksandr Tymoshenko #include "gpio_if.h"
7094b8a54aSOleksandr Tymoshenko
7194b8a54aSOleksandr Tymoshenko #include "chvgpio_reg.h"
7294b8a54aSOleksandr Tymoshenko
7394b8a54aSOleksandr Tymoshenko /*
7494b8a54aSOleksandr Tymoshenko * Macros for driver mutex locking
7594b8a54aSOleksandr Tymoshenko */
7694b8a54aSOleksandr Tymoshenko #define CHVGPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
7794b8a54aSOleksandr Tymoshenko #define CHVGPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
7894b8a54aSOleksandr Tymoshenko #define CHVGPIO_LOCK_INIT(_sc) \
7994b8a54aSOleksandr Tymoshenko mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
8094b8a54aSOleksandr Tymoshenko "chvgpio", MTX_SPIN)
8194b8a54aSOleksandr Tymoshenko #define CHVGPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
8294b8a54aSOleksandr Tymoshenko #define CHVGPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
8394b8a54aSOleksandr Tymoshenko #define CHVGPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
8494b8a54aSOleksandr Tymoshenko
8594b8a54aSOleksandr Tymoshenko struct chvgpio_softc {
8694b8a54aSOleksandr Tymoshenko device_t sc_dev;
8794b8a54aSOleksandr Tymoshenko device_t sc_busdev;
8894b8a54aSOleksandr Tymoshenko struct mtx sc_mtx;
8994b8a54aSOleksandr Tymoshenko
9094b8a54aSOleksandr Tymoshenko ACPI_HANDLE sc_handle;
9194b8a54aSOleksandr Tymoshenko
9294b8a54aSOleksandr Tymoshenko int sc_mem_rid;
9394b8a54aSOleksandr Tymoshenko struct resource *sc_mem_res;
9494b8a54aSOleksandr Tymoshenko
9594b8a54aSOleksandr Tymoshenko int sc_irq_rid;
9694b8a54aSOleksandr Tymoshenko struct resource *sc_irq_res;
9794b8a54aSOleksandr Tymoshenko void *intr_handle;
9894b8a54aSOleksandr Tymoshenko
9994b8a54aSOleksandr Tymoshenko const char *sc_bank_prefix;
10094b8a54aSOleksandr Tymoshenko const int *sc_pins;
10194b8a54aSOleksandr Tymoshenko int sc_npins;
10294b8a54aSOleksandr Tymoshenko int sc_ngroups;
10394b8a54aSOleksandr Tymoshenko const char **sc_pin_names;
10494b8a54aSOleksandr Tymoshenko };
10594b8a54aSOleksandr Tymoshenko
10694b8a54aSOleksandr Tymoshenko static void chvgpio_intr(void *);
10794b8a54aSOleksandr Tymoshenko static int chvgpio_probe(device_t);
10894b8a54aSOleksandr Tymoshenko static int chvgpio_attach(device_t);
10994b8a54aSOleksandr Tymoshenko static int chvgpio_detach(device_t);
11094b8a54aSOleksandr Tymoshenko
11194b8a54aSOleksandr Tymoshenko static inline int
chvgpio_pad_cfg0_offset(int pin)11294b8a54aSOleksandr Tymoshenko chvgpio_pad_cfg0_offset(int pin)
11394b8a54aSOleksandr Tymoshenko {
11494b8a54aSOleksandr Tymoshenko return (CHVGPIO_PAD_CFG0 + 1024 * (pin / 15) + 8 * (pin % 15));
11594b8a54aSOleksandr Tymoshenko }
11694b8a54aSOleksandr Tymoshenko
11794b8a54aSOleksandr Tymoshenko static inline int
chvgpio_read_pad_cfg0(struct chvgpio_softc * sc,int pin)11894b8a54aSOleksandr Tymoshenko chvgpio_read_pad_cfg0(struct chvgpio_softc *sc, int pin)
11994b8a54aSOleksandr Tymoshenko {
12094b8a54aSOleksandr Tymoshenko return bus_read_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin));
12194b8a54aSOleksandr Tymoshenko }
12294b8a54aSOleksandr Tymoshenko
12394b8a54aSOleksandr Tymoshenko static inline void
chvgpio_write_pad_cfg0(struct chvgpio_softc * sc,int pin,uint32_t val)12494b8a54aSOleksandr Tymoshenko chvgpio_write_pad_cfg0(struct chvgpio_softc *sc, int pin, uint32_t val)
12594b8a54aSOleksandr Tymoshenko {
12694b8a54aSOleksandr Tymoshenko bus_write_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin), val);
12794b8a54aSOleksandr Tymoshenko }
12894b8a54aSOleksandr Tymoshenko
12994b8a54aSOleksandr Tymoshenko static inline int
chvgpio_read_pad_cfg1(struct chvgpio_softc * sc,int pin)13094b8a54aSOleksandr Tymoshenko chvgpio_read_pad_cfg1(struct chvgpio_softc *sc, int pin)
13194b8a54aSOleksandr Tymoshenko {
13294b8a54aSOleksandr Tymoshenko return bus_read_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin) + 4);
13394b8a54aSOleksandr Tymoshenko }
13494b8a54aSOleksandr Tymoshenko
13594b8a54aSOleksandr Tymoshenko static device_t
chvgpio_get_bus(device_t dev)13694b8a54aSOleksandr Tymoshenko chvgpio_get_bus(device_t dev)
13794b8a54aSOleksandr Tymoshenko {
13894b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
13994b8a54aSOleksandr Tymoshenko
14094b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
14194b8a54aSOleksandr Tymoshenko
14294b8a54aSOleksandr Tymoshenko return (sc->sc_busdev);
14394b8a54aSOleksandr Tymoshenko }
14494b8a54aSOleksandr Tymoshenko
14594b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_max(device_t dev,int * maxpin)14694b8a54aSOleksandr Tymoshenko chvgpio_pin_max(device_t dev, int *maxpin)
14794b8a54aSOleksandr Tymoshenko {
14894b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
14994b8a54aSOleksandr Tymoshenko
15094b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
15194b8a54aSOleksandr Tymoshenko
15294b8a54aSOleksandr Tymoshenko *maxpin = sc->sc_npins - 1;
15394b8a54aSOleksandr Tymoshenko
15494b8a54aSOleksandr Tymoshenko return (0);
15594b8a54aSOleksandr Tymoshenko }
15694b8a54aSOleksandr Tymoshenko
15794b8a54aSOleksandr Tymoshenko static int
chvgpio_valid_pin(struct chvgpio_softc * sc,int pin)15894b8a54aSOleksandr Tymoshenko chvgpio_valid_pin(struct chvgpio_softc *sc, int pin)
15994b8a54aSOleksandr Tymoshenko {
16094b8a54aSOleksandr Tymoshenko if (pin < 0)
16194b8a54aSOleksandr Tymoshenko return EINVAL;
16294b8a54aSOleksandr Tymoshenko if ((pin / 15) >= sc->sc_ngroups)
16394b8a54aSOleksandr Tymoshenko return EINVAL;
16494b8a54aSOleksandr Tymoshenko if ((pin % 15) >= sc->sc_pins[pin / 15])
16594b8a54aSOleksandr Tymoshenko return EINVAL;
16694b8a54aSOleksandr Tymoshenko return (0);
16794b8a54aSOleksandr Tymoshenko }
16894b8a54aSOleksandr Tymoshenko
16994b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_getname(device_t dev,uint32_t pin,char * name)17094b8a54aSOleksandr Tymoshenko chvgpio_pin_getname(device_t dev, uint32_t pin, char *name)
17194b8a54aSOleksandr Tymoshenko {
17294b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
17394b8a54aSOleksandr Tymoshenko
17494b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
17594b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
17694b8a54aSOleksandr Tymoshenko return (EINVAL);
17794b8a54aSOleksandr Tymoshenko
17894b8a54aSOleksandr Tymoshenko /* return pin name from datasheet */
17994b8a54aSOleksandr Tymoshenko snprintf(name, GPIOMAXNAME, "%s", sc->sc_pin_names[pin]);
18094b8a54aSOleksandr Tymoshenko name[GPIOMAXNAME - 1] = '\0';
18194b8a54aSOleksandr Tymoshenko return (0);
18294b8a54aSOleksandr Tymoshenko }
18394b8a54aSOleksandr Tymoshenko
18494b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)18594b8a54aSOleksandr Tymoshenko chvgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
18694b8a54aSOleksandr Tymoshenko {
18794b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
18894b8a54aSOleksandr Tymoshenko
18994b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
19094b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
19194b8a54aSOleksandr Tymoshenko return (EINVAL);
19294b8a54aSOleksandr Tymoshenko
19394b8a54aSOleksandr Tymoshenko *caps = 0;
19494b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin))
19594b8a54aSOleksandr Tymoshenko *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
19694b8a54aSOleksandr Tymoshenko
19794b8a54aSOleksandr Tymoshenko return (0);
19894b8a54aSOleksandr Tymoshenko }
19994b8a54aSOleksandr Tymoshenko
20094b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)20194b8a54aSOleksandr Tymoshenko chvgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
20294b8a54aSOleksandr Tymoshenko {
20394b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
20494b8a54aSOleksandr Tymoshenko uint32_t val;
20594b8a54aSOleksandr Tymoshenko
20694b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
20794b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
20894b8a54aSOleksandr Tymoshenko return (EINVAL);
20994b8a54aSOleksandr Tymoshenko
21094b8a54aSOleksandr Tymoshenko *flags = 0;
21194b8a54aSOleksandr Tymoshenko
21294b8a54aSOleksandr Tymoshenko /* Get the current pin state */
21394b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK(sc);
21494b8a54aSOleksandr Tymoshenko val = chvgpio_read_pad_cfg0(sc, pin);
21594b8a54aSOleksandr Tymoshenko
21694b8a54aSOleksandr Tymoshenko if (val & CHVGPIO_PAD_CFG0_GPIOCFG_GPIO ||
21794b8a54aSOleksandr Tymoshenko val & CHVGPIO_PAD_CFG0_GPIOCFG_GPO)
21894b8a54aSOleksandr Tymoshenko *flags |= GPIO_PIN_OUTPUT;
21994b8a54aSOleksandr Tymoshenko
22094b8a54aSOleksandr Tymoshenko if (val & CHVGPIO_PAD_CFG0_GPIOCFG_GPIO ||
22194b8a54aSOleksandr Tymoshenko val & CHVGPIO_PAD_CFG0_GPIOCFG_GPI)
22294b8a54aSOleksandr Tymoshenko *flags |= GPIO_PIN_INPUT;
22394b8a54aSOleksandr Tymoshenko
22494b8a54aSOleksandr Tymoshenko val = chvgpio_read_pad_cfg1(sc, pin);
22594b8a54aSOleksandr Tymoshenko
22694b8a54aSOleksandr Tymoshenko CHVGPIO_UNLOCK(sc);
22794b8a54aSOleksandr Tymoshenko return (0);
22894b8a54aSOleksandr Tymoshenko }
22994b8a54aSOleksandr Tymoshenko
23094b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)23194b8a54aSOleksandr Tymoshenko chvgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
23294b8a54aSOleksandr Tymoshenko {
23394b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
23494b8a54aSOleksandr Tymoshenko uint32_t val;
23594b8a54aSOleksandr Tymoshenko uint32_t allowed;
23694b8a54aSOleksandr Tymoshenko
23794b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
23894b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
23994b8a54aSOleksandr Tymoshenko return (EINVAL);
24094b8a54aSOleksandr Tymoshenko
24194b8a54aSOleksandr Tymoshenko allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
24294b8a54aSOleksandr Tymoshenko
24394b8a54aSOleksandr Tymoshenko /*
24494b8a54aSOleksandr Tymoshenko * Only direction flag allowed
24594b8a54aSOleksandr Tymoshenko */
24694b8a54aSOleksandr Tymoshenko if (flags & ~allowed)
24794b8a54aSOleksandr Tymoshenko return (EINVAL);
24894b8a54aSOleksandr Tymoshenko
24994b8a54aSOleksandr Tymoshenko /*
25094b8a54aSOleksandr Tymoshenko * Not both directions simultaneously
25194b8a54aSOleksandr Tymoshenko */
25294b8a54aSOleksandr Tymoshenko if ((flags & allowed) == allowed)
25394b8a54aSOleksandr Tymoshenko return (EINVAL);
25494b8a54aSOleksandr Tymoshenko
25594b8a54aSOleksandr Tymoshenko /* Set the GPIO mode and state */
25694b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK(sc);
25794b8a54aSOleksandr Tymoshenko val = chvgpio_read_pad_cfg0(sc, pin);
25894b8a54aSOleksandr Tymoshenko if (flags & GPIO_PIN_INPUT)
25994b8a54aSOleksandr Tymoshenko val = val & CHVGPIO_PAD_CFG0_GPIOCFG_GPI;
26094b8a54aSOleksandr Tymoshenko if (flags & GPIO_PIN_OUTPUT)
26194b8a54aSOleksandr Tymoshenko val = val & CHVGPIO_PAD_CFG0_GPIOCFG_GPO;
26294b8a54aSOleksandr Tymoshenko chvgpio_write_pad_cfg0(sc, pin, val);
26394b8a54aSOleksandr Tymoshenko CHVGPIO_UNLOCK(sc);
26494b8a54aSOleksandr Tymoshenko
26594b8a54aSOleksandr Tymoshenko return (0);
26694b8a54aSOleksandr Tymoshenko }
26794b8a54aSOleksandr Tymoshenko
26894b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_set(device_t dev,uint32_t pin,unsigned int value)26994b8a54aSOleksandr Tymoshenko chvgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
27094b8a54aSOleksandr Tymoshenko {
27194b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
27294b8a54aSOleksandr Tymoshenko uint32_t val;
27394b8a54aSOleksandr Tymoshenko
27494b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
27594b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
27694b8a54aSOleksandr Tymoshenko return (EINVAL);
27794b8a54aSOleksandr Tymoshenko
27894b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK(sc);
27994b8a54aSOleksandr Tymoshenko val = chvgpio_read_pad_cfg0(sc, pin);
28094b8a54aSOleksandr Tymoshenko if (value == GPIO_PIN_LOW)
28194b8a54aSOleksandr Tymoshenko val = val & ~CHVGPIO_PAD_CFG0_GPIOTXSTATE;
28294b8a54aSOleksandr Tymoshenko else
28394b8a54aSOleksandr Tymoshenko val = val | CHVGPIO_PAD_CFG0_GPIOTXSTATE;
28494b8a54aSOleksandr Tymoshenko chvgpio_write_pad_cfg0(sc, pin, val);
28594b8a54aSOleksandr Tymoshenko CHVGPIO_UNLOCK(sc);
28694b8a54aSOleksandr Tymoshenko
28794b8a54aSOleksandr Tymoshenko return (0);
28894b8a54aSOleksandr Tymoshenko }
28994b8a54aSOleksandr Tymoshenko
29094b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_get(device_t dev,uint32_t pin,unsigned int * value)29194b8a54aSOleksandr Tymoshenko chvgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
29294b8a54aSOleksandr Tymoshenko {
29394b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
29494b8a54aSOleksandr Tymoshenko uint32_t val;
29594b8a54aSOleksandr Tymoshenko
29694b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
29794b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
29894b8a54aSOleksandr Tymoshenko return (EINVAL);
29994b8a54aSOleksandr Tymoshenko
30094b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK(sc);
30194b8a54aSOleksandr Tymoshenko
30294b8a54aSOleksandr Tymoshenko /* Read pin value */
30394b8a54aSOleksandr Tymoshenko val = chvgpio_read_pad_cfg0(sc, pin);
30494b8a54aSOleksandr Tymoshenko if (val & CHVGPIO_PAD_CFG0_GPIORXSTATE)
30594b8a54aSOleksandr Tymoshenko *value = GPIO_PIN_HIGH;
30694b8a54aSOleksandr Tymoshenko else
30794b8a54aSOleksandr Tymoshenko *value = GPIO_PIN_LOW;
30894b8a54aSOleksandr Tymoshenko
30994b8a54aSOleksandr Tymoshenko CHVGPIO_UNLOCK(sc);
31094b8a54aSOleksandr Tymoshenko
31194b8a54aSOleksandr Tymoshenko return (0);
31294b8a54aSOleksandr Tymoshenko }
31394b8a54aSOleksandr Tymoshenko
31494b8a54aSOleksandr Tymoshenko static int
chvgpio_pin_toggle(device_t dev,uint32_t pin)31594b8a54aSOleksandr Tymoshenko chvgpio_pin_toggle(device_t dev, uint32_t pin)
31694b8a54aSOleksandr Tymoshenko {
31794b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
31894b8a54aSOleksandr Tymoshenko uint32_t val;
31994b8a54aSOleksandr Tymoshenko
32094b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
32194b8a54aSOleksandr Tymoshenko if (chvgpio_valid_pin(sc, pin) != 0)
32294b8a54aSOleksandr Tymoshenko return (EINVAL);
32394b8a54aSOleksandr Tymoshenko
32494b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK(sc);
32594b8a54aSOleksandr Tymoshenko
32694b8a54aSOleksandr Tymoshenko /* Toggle the pin */
32794b8a54aSOleksandr Tymoshenko val = chvgpio_read_pad_cfg0(sc, pin);
32894b8a54aSOleksandr Tymoshenko val = val ^ CHVGPIO_PAD_CFG0_GPIOTXSTATE;
32994b8a54aSOleksandr Tymoshenko chvgpio_write_pad_cfg0(sc, pin, val);
33094b8a54aSOleksandr Tymoshenko
33194b8a54aSOleksandr Tymoshenko CHVGPIO_UNLOCK(sc);
33294b8a54aSOleksandr Tymoshenko
33394b8a54aSOleksandr Tymoshenko return (0);
33494b8a54aSOleksandr Tymoshenko }
33594b8a54aSOleksandr Tymoshenko
33694b8a54aSOleksandr Tymoshenko static char *chvgpio_hids[] = {
33794b8a54aSOleksandr Tymoshenko "INT33FF",
33894b8a54aSOleksandr Tymoshenko NULL
33994b8a54aSOleksandr Tymoshenko };
34094b8a54aSOleksandr Tymoshenko
34194b8a54aSOleksandr Tymoshenko static int
chvgpio_probe(device_t dev)34294b8a54aSOleksandr Tymoshenko chvgpio_probe(device_t dev)
34394b8a54aSOleksandr Tymoshenko {
3445efca36fSTakanori Watanabe int rv;
34594b8a54aSOleksandr Tymoshenko
3465efca36fSTakanori Watanabe if (acpi_disabled("chvgpio"))
3475efca36fSTakanori Watanabe return (ENXIO);
3485efca36fSTakanori Watanabe rv = ACPI_ID_PROBE(device_get_parent(dev), dev, chvgpio_hids, NULL);
3495efca36fSTakanori Watanabe if (rv <= 0)
35094b8a54aSOleksandr Tymoshenko device_set_desc(dev, "Intel Cherry View GPIO");
3515efca36fSTakanori Watanabe return (rv);
35294b8a54aSOleksandr Tymoshenko }
35394b8a54aSOleksandr Tymoshenko
35494b8a54aSOleksandr Tymoshenko static int
chvgpio_attach(device_t dev)35594b8a54aSOleksandr Tymoshenko chvgpio_attach(device_t dev)
35694b8a54aSOleksandr Tymoshenko {
35794b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
35894b8a54aSOleksandr Tymoshenko ACPI_STATUS status;
35994b8a54aSOleksandr Tymoshenko int uid;
36094b8a54aSOleksandr Tymoshenko int i;
36194b8a54aSOleksandr Tymoshenko int error;
36294b8a54aSOleksandr Tymoshenko
36394b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
36494b8a54aSOleksandr Tymoshenko sc->sc_dev = dev;
36594b8a54aSOleksandr Tymoshenko sc->sc_handle = acpi_get_handle(dev);
36694b8a54aSOleksandr Tymoshenko
36794b8a54aSOleksandr Tymoshenko status = acpi_GetInteger(sc->sc_handle, "_UID", &uid);
36894b8a54aSOleksandr Tymoshenko if (ACPI_FAILURE(status)) {
36994b8a54aSOleksandr Tymoshenko device_printf(dev, "failed to read _UID\n");
37094b8a54aSOleksandr Tymoshenko return (ENXIO);
37194b8a54aSOleksandr Tymoshenko }
37294b8a54aSOleksandr Tymoshenko
37394b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK_INIT(sc);
37494b8a54aSOleksandr Tymoshenko
37594b8a54aSOleksandr Tymoshenko switch (uid) {
37694b8a54aSOleksandr Tymoshenko case SW_UID:
37794b8a54aSOleksandr Tymoshenko sc->sc_bank_prefix = SW_BANK_PREFIX;
37894b8a54aSOleksandr Tymoshenko sc->sc_pins = chv_southwest_pins;
37994b8a54aSOleksandr Tymoshenko sc->sc_pin_names = chv_southwest_pin_names;
38094b8a54aSOleksandr Tymoshenko break;
38194b8a54aSOleksandr Tymoshenko case N_UID:
38294b8a54aSOleksandr Tymoshenko sc->sc_bank_prefix = N_BANK_PREFIX;
38394b8a54aSOleksandr Tymoshenko sc->sc_pins = chv_north_pins;
38494b8a54aSOleksandr Tymoshenko sc->sc_pin_names = chv_north_pin_names;
38594b8a54aSOleksandr Tymoshenko break;
38694b8a54aSOleksandr Tymoshenko case E_UID:
38794b8a54aSOleksandr Tymoshenko sc->sc_bank_prefix = E_BANK_PREFIX;
38894b8a54aSOleksandr Tymoshenko sc->sc_pins = chv_east_pins;
38994b8a54aSOleksandr Tymoshenko sc->sc_pin_names = chv_east_pin_names;
39094b8a54aSOleksandr Tymoshenko break;
39194b8a54aSOleksandr Tymoshenko case SE_UID:
39294b8a54aSOleksandr Tymoshenko sc->sc_bank_prefix = SE_BANK_PREFIX;
39394b8a54aSOleksandr Tymoshenko sc->sc_pins = chv_southeast_pins;
39494b8a54aSOleksandr Tymoshenko sc->sc_pin_names = chv_southeast_pin_names;
39594b8a54aSOleksandr Tymoshenko break;
39694b8a54aSOleksandr Tymoshenko default:
39794b8a54aSOleksandr Tymoshenko device_printf(dev, "invalid _UID value: %d\n", uid);
39894b8a54aSOleksandr Tymoshenko return (ENXIO);
39994b8a54aSOleksandr Tymoshenko }
40094b8a54aSOleksandr Tymoshenko
40194b8a54aSOleksandr Tymoshenko for (i = 0; sc->sc_pins[i] >= 0; i++) {
40294b8a54aSOleksandr Tymoshenko sc->sc_npins += sc->sc_pins[i];
40394b8a54aSOleksandr Tymoshenko sc->sc_ngroups++;
40494b8a54aSOleksandr Tymoshenko }
40594b8a54aSOleksandr Tymoshenko
40694b8a54aSOleksandr Tymoshenko sc->sc_mem_rid = 0;
40794b8a54aSOleksandr Tymoshenko sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev, SYS_RES_MEMORY,
40894b8a54aSOleksandr Tymoshenko &sc->sc_mem_rid, RF_ACTIVE);
40994b8a54aSOleksandr Tymoshenko if (sc->sc_mem_res == NULL) {
41094b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK_DESTROY(sc);
41194b8a54aSOleksandr Tymoshenko device_printf(dev, "can't allocate memory resource\n");
41294b8a54aSOleksandr Tymoshenko return (ENOMEM);
41394b8a54aSOleksandr Tymoshenko }
41494b8a54aSOleksandr Tymoshenko
41594b8a54aSOleksandr Tymoshenko sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
41694b8a54aSOleksandr Tymoshenko &sc->sc_irq_rid, RF_ACTIVE);
41794b8a54aSOleksandr Tymoshenko
41894b8a54aSOleksandr Tymoshenko if (!sc->sc_irq_res) {
41994b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK_DESTROY(sc);
42094b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY,
42194b8a54aSOleksandr Tymoshenko sc->sc_mem_rid, sc->sc_mem_res);
42294b8a54aSOleksandr Tymoshenko device_printf(dev, "can't allocate irq resource\n");
42394b8a54aSOleksandr Tymoshenko return (ENOMEM);
42494b8a54aSOleksandr Tymoshenko }
42594b8a54aSOleksandr Tymoshenko
42694b8a54aSOleksandr Tymoshenko error = bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
42794b8a54aSOleksandr Tymoshenko NULL, chvgpio_intr, sc, &sc->intr_handle);
42894b8a54aSOleksandr Tymoshenko
42994b8a54aSOleksandr Tymoshenko
43094b8a54aSOleksandr Tymoshenko if (error) {
43194b8a54aSOleksandr Tymoshenko device_printf(sc->sc_dev, "unable to setup irq: error %d\n", error);
43294b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK_DESTROY(sc);
43394b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY,
43494b8a54aSOleksandr Tymoshenko sc->sc_mem_rid, sc->sc_mem_res);
43594b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ,
43694b8a54aSOleksandr Tymoshenko sc->sc_irq_rid, sc->sc_irq_res);
43794b8a54aSOleksandr Tymoshenko return (ENXIO);
43894b8a54aSOleksandr Tymoshenko }
43994b8a54aSOleksandr Tymoshenko
44094b8a54aSOleksandr Tymoshenko /* Mask and ack all interrupts. */
44194b8a54aSOleksandr Tymoshenko bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_MASK, 0);
44294b8a54aSOleksandr Tymoshenko bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 0xffff);
44394b8a54aSOleksandr Tymoshenko
44494b8a54aSOleksandr Tymoshenko sc->sc_busdev = gpiobus_attach_bus(dev);
44594b8a54aSOleksandr Tymoshenko if (sc->sc_busdev == NULL) {
44694b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK_DESTROY(sc);
44794b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY,
44894b8a54aSOleksandr Tymoshenko sc->sc_mem_rid, sc->sc_mem_res);
44994b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ,
45094b8a54aSOleksandr Tymoshenko sc->sc_irq_rid, sc->sc_irq_res);
45194b8a54aSOleksandr Tymoshenko return (ENXIO);
45294b8a54aSOleksandr Tymoshenko }
45394b8a54aSOleksandr Tymoshenko
45494b8a54aSOleksandr Tymoshenko return (0);
45594b8a54aSOleksandr Tymoshenko }
45694b8a54aSOleksandr Tymoshenko
45794b8a54aSOleksandr Tymoshenko static void
chvgpio_intr(void * arg)45894b8a54aSOleksandr Tymoshenko chvgpio_intr(void *arg)
45994b8a54aSOleksandr Tymoshenko {
46094b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc = arg;
46194b8a54aSOleksandr Tymoshenko uint32_t reg;
46294b8a54aSOleksandr Tymoshenko int line;
46394b8a54aSOleksandr Tymoshenko
46494b8a54aSOleksandr Tymoshenko reg = bus_read_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS);
46594b8a54aSOleksandr Tymoshenko for (line = 0; line < 16; line++) {
46694b8a54aSOleksandr Tymoshenko if ((reg & (1 << line)) == 0)
46794b8a54aSOleksandr Tymoshenko continue;
46894b8a54aSOleksandr Tymoshenko bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 1 << line);
46994b8a54aSOleksandr Tymoshenko }
47094b8a54aSOleksandr Tymoshenko }
47194b8a54aSOleksandr Tymoshenko
47294b8a54aSOleksandr Tymoshenko static int
chvgpio_detach(device_t dev)47394b8a54aSOleksandr Tymoshenko chvgpio_detach(device_t dev)
47494b8a54aSOleksandr Tymoshenko {
47594b8a54aSOleksandr Tymoshenko struct chvgpio_softc *sc;
47694b8a54aSOleksandr Tymoshenko sc = device_get_softc(dev);
47794b8a54aSOleksandr Tymoshenko
47894b8a54aSOleksandr Tymoshenko if (sc->sc_busdev)
47994b8a54aSOleksandr Tymoshenko gpiobus_detach_bus(dev);
48094b8a54aSOleksandr Tymoshenko
48194b8a54aSOleksandr Tymoshenko if (sc->intr_handle != NULL)
48294b8a54aSOleksandr Tymoshenko bus_teardown_intr(sc->sc_dev, sc->sc_irq_res, sc->intr_handle);
48394b8a54aSOleksandr Tymoshenko if (sc->sc_irq_res != NULL)
48494b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
48594b8a54aSOleksandr Tymoshenko if (sc->sc_mem_res != NULL)
48694b8a54aSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, sc->sc_mem_res);
48794b8a54aSOleksandr Tymoshenko
48894b8a54aSOleksandr Tymoshenko CHVGPIO_LOCK_DESTROY(sc);
48994b8a54aSOleksandr Tymoshenko
49094b8a54aSOleksandr Tymoshenko return (0);
49194b8a54aSOleksandr Tymoshenko }
49294b8a54aSOleksandr Tymoshenko
49394b8a54aSOleksandr Tymoshenko static device_method_t chvgpio_methods[] = {
49494b8a54aSOleksandr Tymoshenko DEVMETHOD(device_probe, chvgpio_probe),
49594b8a54aSOleksandr Tymoshenko DEVMETHOD(device_attach, chvgpio_attach),
49694b8a54aSOleksandr Tymoshenko DEVMETHOD(device_detach, chvgpio_detach),
49794b8a54aSOleksandr Tymoshenko
49894b8a54aSOleksandr Tymoshenko /* GPIO protocol */
49994b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_get_bus, chvgpio_get_bus),
50094b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_max, chvgpio_pin_max),
50194b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_getname, chvgpio_pin_getname),
50294b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_getflags, chvgpio_pin_getflags),
50394b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_getcaps, chvgpio_pin_getcaps),
50494b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_setflags, chvgpio_pin_setflags),
50594b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_get, chvgpio_pin_get),
50694b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_set, chvgpio_pin_set),
50794b8a54aSOleksandr Tymoshenko DEVMETHOD(gpio_pin_toggle, chvgpio_pin_toggle),
50894b8a54aSOleksandr Tymoshenko
50994b8a54aSOleksandr Tymoshenko DEVMETHOD_END
51094b8a54aSOleksandr Tymoshenko };
51194b8a54aSOleksandr Tymoshenko
51294b8a54aSOleksandr Tymoshenko static driver_t chvgpio_driver = {
51394b8a54aSOleksandr Tymoshenko .name = "gpio",
51494b8a54aSOleksandr Tymoshenko .methods = chvgpio_methods,
51594b8a54aSOleksandr Tymoshenko .size = sizeof(struct chvgpio_softc)
51694b8a54aSOleksandr Tymoshenko };
51794b8a54aSOleksandr Tymoshenko
51884c5f982SJohn Baldwin DRIVER_MODULE(chvgpio, acpi, chvgpio_driver, NULL, NULL);
51994b8a54aSOleksandr Tymoshenko MODULE_DEPEND(chvgpio, acpi, 1, 1, 1);
52094b8a54aSOleksandr Tymoshenko MODULE_DEPEND(chvgpio, gpiobus, 1, 1, 1);
52194b8a54aSOleksandr Tymoshenko
52294b8a54aSOleksandr Tymoshenko MODULE_VERSION(chvgpio, 1);
523