xref: /freebsd-src/sys/dev/gpio/chvgpio.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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