xref: /freebsd/sys/dev/ftgpio/ftgpio.c (revision 2c1b8eb29d92d0911e7493c6a16be04ff6064ad1)
11b10e191SStéphane Rochoy /*-
21b10e191SStéphane Rochoy  * SPDX-License-Identifier: BSD-2-Clause
31b10e191SStéphane Rochoy  *
41b10e191SStéphane Rochoy  * Copyright (c) 2016-2023 Stormshield
51b10e191SStéphane Rochoy  *
61b10e191SStéphane Rochoy  * Redistribution and use in source and binary forms, with or without
71b10e191SStéphane Rochoy  * modification, are permitted provided that the following conditions
81b10e191SStéphane Rochoy  * are met:
91b10e191SStéphane Rochoy  * 1. Redistributions of source code must retain the above copyright
101b10e191SStéphane Rochoy  *    notice, this list of conditions and the following disclaimer.
111b10e191SStéphane Rochoy  * 2. Redistributions in binary form must reproduce the above copyright
121b10e191SStéphane Rochoy  *    notice, this list of conditions and the following disclaimer in the
131b10e191SStéphane Rochoy  *    documentation and/or other materials provided with the distribution.
141b10e191SStéphane Rochoy  *
151b10e191SStéphane Rochoy  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161b10e191SStéphane Rochoy  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171b10e191SStéphane Rochoy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181b10e191SStéphane Rochoy  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191b10e191SStéphane Rochoy  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201b10e191SStéphane Rochoy  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211b10e191SStéphane Rochoy  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221b10e191SStéphane Rochoy  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231b10e191SStéphane Rochoy  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241b10e191SStéphane Rochoy  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251b10e191SStéphane Rochoy  * SUCH DAMAGE.
261b10e191SStéphane Rochoy  */
271b10e191SStéphane Rochoy 
281b10e191SStéphane Rochoy #include <sys/cdefs.h>
291b10e191SStéphane Rochoy 
301b10e191SStéphane Rochoy #include <sys/param.h>
311b10e191SStéphane Rochoy #include <sys/systm.h>
321b10e191SStéphane Rochoy 
331b10e191SStéphane Rochoy #include <sys/bus.h>
341b10e191SStéphane Rochoy #include <sys/eventhandler.h>
351b10e191SStéphane Rochoy #include <sys/gpio.h>
361b10e191SStéphane Rochoy #include <sys/kernel.h>
371b10e191SStéphane Rochoy #include <sys/lock.h>
381b10e191SStéphane Rochoy #include <sys/module.h>
391b10e191SStéphane Rochoy #include <sys/mutex.h>
401b10e191SStéphane Rochoy 
411b10e191SStéphane Rochoy #include <machine/bus.h>
421b10e191SStéphane Rochoy 
431b10e191SStéphane Rochoy #include <dev/gpio/gpiobusvar.h>
441b10e191SStéphane Rochoy #include <dev/superio/superio.h>
451b10e191SStéphane Rochoy 
461b10e191SStéphane Rochoy #include "gpio_if.h"
471b10e191SStéphane Rochoy 
481b10e191SStéphane Rochoy #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,	\
491b10e191SStéphane Rochoy 		device_get_nameunit(dev), NULL, MTX_DEF)
501b10e191SStéphane Rochoy #define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
511b10e191SStéphane Rochoy #define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
521b10e191SStéphane Rochoy #define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
531b10e191SStéphane Rochoy #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
541b10e191SStéphane Rochoy #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
551b10e191SStéphane Rochoy 
561b10e191SStéphane Rochoy /* Global register set */
571b10e191SStéphane Rochoy #define GPIO4_ENABLE 0x28
581b10e191SStéphane Rochoy #define GPIO3_ENABLE 0x29
591b10e191SStéphane Rochoy #define FULL_UR5_UR6 0x2A
601b10e191SStéphane Rochoy #define GPIO1_ENABLE 0x2B
611b10e191SStéphane Rochoy #define GPIO2_ENABLE 0x2C
621b10e191SStéphane Rochoy 
631b10e191SStéphane Rochoy /* Logical Device Numbers. */
641b10e191SStéphane Rochoy #define FTGPIO_LDN_GPIO			0x06
651b10e191SStéphane Rochoy 
661b10e191SStéphane Rochoy #define FTGPIO_MAX_GROUP 6
671b10e191SStéphane Rochoy #define FTGPIO_MAX_PIN   52
681b10e191SStéphane Rochoy 
691b10e191SStéphane Rochoy #define FTGPIO_IS_VALID_PIN(_p)  ((_p) >= 0 && (_p) <= FTGPIO_MAX_PIN)
701b10e191SStéphane Rochoy #define FTGPIO_PIN_GETINDEX(_p) ((_p) & 7)
711b10e191SStéphane Rochoy #define FTGPIO_PIN_GETGROUP(_p) ((_p) >> 3)
721b10e191SStéphane Rochoy 
731b10e191SStéphane Rochoy #define FTGPIO_GPIO_CAPS (GPIO_PIN_INPUT  | GPIO_PIN_OUTPUT    | GPIO_PIN_INVIN | \
741b10e191SStéphane Rochoy                           GPIO_PIN_INVOUT | GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)
751b10e191SStéphane Rochoy 
761b10e191SStéphane Rochoy #define GET_BIT(_v, _b) (((_v) >> (_b)) & 1)
771b10e191SStéphane Rochoy 
781b10e191SStéphane Rochoy #define FTGPIO_VERBOSE_PRINTF(dev, ...)         \
791b10e191SStéphane Rochoy 	do {                                        \
801b10e191SStéphane Rochoy 		if (__predict_false(bootverbose))       \
811b10e191SStéphane Rochoy 			device_printf(dev, __VA_ARGS__);    \
821b10e191SStéphane Rochoy 	} while (0)
831b10e191SStéphane Rochoy 
841b10e191SStéphane Rochoy /*
851b10e191SStéphane Rochoy  * Note that the values are important.
861b10e191SStéphane Rochoy  * They match actual register offsets.
871b10e191SStéphane Rochoy  * See p71 and p72 of F81865's datasheet.
881b10e191SStéphane Rochoy  */
891b10e191SStéphane Rochoy #define REG_OUTPUT_ENABLE         0 /* Not for GPIO0 */
901b10e191SStéphane Rochoy #define REG_OUTPUT_DATA           1
911b10e191SStéphane Rochoy #define REG_PIN_STATUS            2
921b10e191SStéphane Rochoy #define REG_DRIVE_ENABLE          3
931b10e191SStéphane Rochoy #define REG_MODE_SELECT_1         4 /* Only for GPIO0 */
941b10e191SStéphane Rochoy #define REG_MODE_SELECT_2         5 /* Only for GPIO0 */
951b10e191SStéphane Rochoy #define REG_PULSE_WIDTH_SELECT_1  6 /* Only for GPIO0 */
961b10e191SStéphane Rochoy #define REG_PULSE_WIDTH_SELECT_2  7 /* Only for GPIO0 */
971b10e191SStéphane Rochoy #define REG_INTERRUPT_ENABLE      8 /* Only for GPIO0 */
981b10e191SStéphane Rochoy #define REG_INTERRUPT_STATUS      9 /* Only for GPIO0 */
991b10e191SStéphane Rochoy 
1001b10e191SStéphane Rochoy struct ftgpio_device {
1011b10e191SStéphane Rochoy 	uint16_t    devid;
1021b10e191SStéphane Rochoy 	const char *descr;
1031b10e191SStéphane Rochoy } ftgpio_devices[] = {
1041b10e191SStéphane Rochoy 	{
1051b10e191SStéphane Rochoy 		.devid = 0x0704,
106*2c1b8eb2SStéphane Rochoy 		.descr = "Fintek F81865",
1071b10e191SStéphane Rochoy 	},
1081b10e191SStéphane Rochoy };
1091b10e191SStéphane Rochoy 
1101b10e191SStéphane Rochoy struct ftgpio_softc {
1111b10e191SStéphane Rochoy 	device_t			dev;
1121b10e191SStéphane Rochoy 	device_t			busdev;
1131b10e191SStéphane Rochoy 	struct mtx			mtx;
1141b10e191SStéphane Rochoy 	struct gpio_pin		pins[FTGPIO_MAX_PIN + 1];
1151b10e191SStéphane Rochoy };
1161b10e191SStéphane Rochoy 
1171b10e191SStéphane Rochoy static uint8_t
ftgpio_group_get_ioreg(struct ftgpio_softc * sc,uint8_t reg,unsigned group)1181b10e191SStéphane Rochoy ftgpio_group_get_ioreg(struct ftgpio_softc *sc, uint8_t reg, unsigned group)
1191b10e191SStéphane Rochoy {
1201b10e191SStéphane Rochoy 	uint8_t ioreg;
1211b10e191SStéphane Rochoy 
1221b10e191SStéphane Rochoy 	KASSERT((group == 0 && REG_OUTPUT_DATA <= reg && reg <= REG_INTERRUPT_STATUS) || \
1231b10e191SStéphane Rochoy 	        (group >= 1 && reg <= REG_DRIVE_ENABLE),
1241b10e191SStéphane Rochoy 		("%s: invalid register %u for group %u", __func__, reg, group));
1251b10e191SStéphane Rochoy 	ioreg = (((0xf - group) << 4) + reg);
1261b10e191SStéphane Rochoy 	return (ioreg);
1271b10e191SStéphane Rochoy }
1281b10e191SStéphane Rochoy 
1291b10e191SStéphane Rochoy static uint8_t
ftgpio_group_get_output(struct ftgpio_softc * sc,unsigned group)1301b10e191SStéphane Rochoy ftgpio_group_get_output(struct ftgpio_softc *sc, unsigned group)
1311b10e191SStéphane Rochoy {
1321b10e191SStéphane Rochoy 	uint8_t ioreg, val;
1331b10e191SStéphane Rochoy 
1341b10e191SStéphane Rochoy 	ioreg = ftgpio_group_get_ioreg(sc, REG_OUTPUT_DATA, group);
1351b10e191SStéphane Rochoy 	val   = superio_read(sc->dev, ioreg);
1361b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u output is 0x%x (ioreg=0x%x)\n",
1371b10e191SStéphane Rochoy 		group, val, ioreg);
1381b10e191SStéphane Rochoy 	return (val);
1391b10e191SStéphane Rochoy }
1401b10e191SStéphane Rochoy 
1411b10e191SStéphane Rochoy static void
ftgpio_group_set_output(struct ftgpio_softc * sc,unsigned group,uint8_t group_value)1421b10e191SStéphane Rochoy ftgpio_group_set_output(struct ftgpio_softc *sc, unsigned group, uint8_t group_value)
1431b10e191SStéphane Rochoy {
1441b10e191SStéphane Rochoy 	uint8_t ioreg;
1451b10e191SStéphane Rochoy 
1461b10e191SStéphane Rochoy 	ioreg = ftgpio_group_get_ioreg(sc, REG_OUTPUT_DATA, group);
1471b10e191SStéphane Rochoy 	superio_write(sc->dev, ioreg, group_value);
1481b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "set group GPIO%u output to 0x%x (ioreg=0x%x)\n",
1491b10e191SStéphane Rochoy 		group, group_value, ioreg);
1501b10e191SStéphane Rochoy }
1511b10e191SStéphane Rochoy 
1521b10e191SStéphane Rochoy static uint8_t
ftgpio_group_get_status(struct ftgpio_softc * sc,unsigned group)1531b10e191SStéphane Rochoy ftgpio_group_get_status(struct ftgpio_softc *sc, unsigned group)
1541b10e191SStéphane Rochoy {
1551b10e191SStéphane Rochoy 	uint8_t ioreg;
1561b10e191SStéphane Rochoy 
1571b10e191SStéphane Rochoy 	ioreg = ftgpio_group_get_ioreg(sc, REG_PIN_STATUS, group);
1581b10e191SStéphane Rochoy 	return (superio_read(sc->dev, ioreg));
1591b10e191SStéphane Rochoy }
1601b10e191SStéphane Rochoy 
1611b10e191SStéphane Rochoy static void
ftgpio_pin_write(struct ftgpio_softc * sc,uint32_t pin_num,bool pin_value)1621b10e191SStéphane Rochoy ftgpio_pin_write(struct ftgpio_softc *sc, uint32_t pin_num, bool pin_value)
1631b10e191SStéphane Rochoy {
1641b10e191SStéphane Rochoy 	uint32_t pin_flags;
1651b10e191SStéphane Rochoy 	uint8_t  val;
1661b10e191SStéphane Rochoy 	unsigned group, index;
1671b10e191SStéphane Rochoy 
1681b10e191SStéphane Rochoy 	GPIO_ASSERT_LOCKED(sc);
1691b10e191SStéphane Rochoy 	index     = FTGPIO_PIN_GETINDEX(pin_num);
1701b10e191SStéphane Rochoy 	group     = FTGPIO_PIN_GETGROUP(pin_num);
1711b10e191SStéphane Rochoy 	pin_flags = sc->pins[pin_num].gp_flags;
1721b10e191SStéphane Rochoy 	if ((pin_flags & (GPIO_PIN_OUTPUT)) == 0) {
1731b10e191SStéphane Rochoy 		FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> is not configured for output\n",
1741b10e191SStéphane Rochoy 			pin_num, group, index);
1751b10e191SStéphane Rochoy 		return;
1761b10e191SStéphane Rochoy 	}
1771b10e191SStéphane Rochoy 
1781b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "set pin %u<GPIO%u%u> to %s\n",
1791b10e191SStéphane Rochoy 		pin_num, group, index, (pin_value ? "on" : "off"));
1801b10e191SStéphane Rochoy 
1811b10e191SStéphane Rochoy 	val = ftgpio_group_get_output(sc, group);
1821b10e191SStéphane Rochoy 	if (!pin_value != !(pin_flags & GPIO_PIN_INVOUT))
1831b10e191SStéphane Rochoy 		val |=  (1 << index);
1841b10e191SStéphane Rochoy 	else
1851b10e191SStéphane Rochoy 		val &= ~(1 << index);
1861b10e191SStéphane Rochoy 	ftgpio_group_set_output(sc, group, val);
1871b10e191SStéphane Rochoy }
1881b10e191SStéphane Rochoy 
1891b10e191SStéphane Rochoy static bool
ftgpio_pin_read(struct ftgpio_softc * sc,uint32_t pin_num)1901b10e191SStéphane Rochoy ftgpio_pin_read(struct ftgpio_softc *sc, uint32_t pin_num)
1911b10e191SStéphane Rochoy {
1921b10e191SStéphane Rochoy 	uint32_t pin_flags;
1931b10e191SStéphane Rochoy 	unsigned group, index;
1941b10e191SStéphane Rochoy 	uint8_t  val;
1951b10e191SStéphane Rochoy 	bool     pin_value;
1961b10e191SStéphane Rochoy 
1971b10e191SStéphane Rochoy 	GPIO_ASSERT_LOCKED(sc);
1981b10e191SStéphane Rochoy 	group     = FTGPIO_PIN_GETGROUP(pin_num);
1991b10e191SStéphane Rochoy 	index     = FTGPIO_PIN_GETINDEX(pin_num);
2001b10e191SStéphane Rochoy 	pin_flags = sc->pins[pin_num].gp_flags;
2011b10e191SStéphane Rochoy 	if ((pin_flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) == 0) {
2021b10e191SStéphane Rochoy 		FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> is not configured for input or output\n",
2031b10e191SStéphane Rochoy 			pin_num, group, index);
2041b10e191SStéphane Rochoy 		return (false);
2051b10e191SStéphane Rochoy 	}
2061b10e191SStéphane Rochoy 
2071b10e191SStéphane Rochoy 	if (pin_flags & GPIO_PIN_OUTPUT)
2081b10e191SStéphane Rochoy 		val = ftgpio_group_get_output(sc, group);
2091b10e191SStéphane Rochoy 	else
2101b10e191SStéphane Rochoy 		val = ftgpio_group_get_status(sc, group);
2111b10e191SStéphane Rochoy 	pin_value = GET_BIT(val, index);
2121b10e191SStéphane Rochoy 
2131b10e191SStéphane Rochoy 	if (((pin_flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INVOUT)) == (GPIO_PIN_OUTPUT|GPIO_PIN_INVOUT)) ||
2141b10e191SStéphane Rochoy 	    ((pin_flags & (GPIO_PIN_INPUT |GPIO_PIN_INVIN )) == (GPIO_PIN_INPUT |GPIO_PIN_INVIN)))
2151b10e191SStéphane Rochoy 		pin_value = !pin_value;
2161b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> is %s\n",
2171b10e191SStéphane Rochoy 		pin_num, group, index, (pin_value ? "on" : "off"));
2181b10e191SStéphane Rochoy 
2191b10e191SStéphane Rochoy 	return (pin_value);
2201b10e191SStéphane Rochoy }
2211b10e191SStéphane Rochoy 
2221b10e191SStéphane Rochoy static void
ftgpio_pin_set_drive(struct ftgpio_softc * sc,uint32_t pin_num,bool pin_drive)2231b10e191SStéphane Rochoy ftgpio_pin_set_drive(struct ftgpio_softc *sc, uint32_t pin_num, bool pin_drive)
2241b10e191SStéphane Rochoy {
2251b10e191SStéphane Rochoy 	unsigned group, index;
2261b10e191SStéphane Rochoy 	uint8_t  group_drive, ioreg;
2271b10e191SStéphane Rochoy 
2281b10e191SStéphane Rochoy 	index       = FTGPIO_PIN_GETINDEX(pin_num);
2291b10e191SStéphane Rochoy 	group       = FTGPIO_PIN_GETGROUP(pin_num);
2301b10e191SStéphane Rochoy 	ioreg		= ftgpio_group_get_ioreg(sc, REG_DRIVE_ENABLE, group);
2311b10e191SStéphane Rochoy 	group_drive = superio_read(sc->dev, ioreg);
2321b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u drive is 0x%x (ioreg=0x%x)\n",
2331b10e191SStéphane Rochoy 		group, group_drive, ioreg);
2341b10e191SStéphane Rochoy 
2351b10e191SStéphane Rochoy 	if (pin_drive)
2361b10e191SStéphane Rochoy 		group_drive |= (1 << index);   /* push pull */
2371b10e191SStéphane Rochoy 	else
2381b10e191SStéphane Rochoy 		group_drive &= ~(1 << index);  /* open drain */
2391b10e191SStéphane Rochoy 	superio_write(sc->dev, ioreg, group_drive);
2401b10e191SStéphane Rochoy }
2411b10e191SStéphane Rochoy 
2421b10e191SStéphane Rochoy static bool
ftgpio_pin_is_pushpull(struct ftgpio_softc * sc,uint32_t pin_num)2431b10e191SStéphane Rochoy ftgpio_pin_is_pushpull(struct ftgpio_softc *sc, uint32_t pin_num)
2441b10e191SStéphane Rochoy {
2451b10e191SStéphane Rochoy 	unsigned group, index;
2461b10e191SStéphane Rochoy 	uint8_t  group_drive, ioreg;
2471b10e191SStéphane Rochoy 	bool is_pushpull;
2481b10e191SStéphane Rochoy 
2491b10e191SStéphane Rochoy 	index       = FTGPIO_PIN_GETINDEX(pin_num);
2501b10e191SStéphane Rochoy 	group       = FTGPIO_PIN_GETGROUP(pin_num);
2511b10e191SStéphane Rochoy 
2521b10e191SStéphane Rochoy 	ioreg		= ftgpio_group_get_ioreg(sc, REG_DRIVE_ENABLE, group);
2531b10e191SStéphane Rochoy 	group_drive = superio_read(sc->dev, ioreg);
2541b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u drive is 0x%x (ioreg=0x%x)\n",
2551b10e191SStéphane Rochoy 		group, group_drive, ioreg);
2561b10e191SStéphane Rochoy 
2571b10e191SStéphane Rochoy 	is_pushpull = group_drive & (1 << index);
2581b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> drive is %s\n",
2591b10e191SStéphane Rochoy 		pin_num, group, index, (is_pushpull ? "pushpull" : "opendrain"));
2601b10e191SStéphane Rochoy 
2611b10e191SStéphane Rochoy 	return (is_pushpull);
2621b10e191SStéphane Rochoy }
2631b10e191SStéphane Rochoy 
2641b10e191SStéphane Rochoy static void
ftgpio_pin_set_io(struct ftgpio_softc * sc,uint32_t pin_num,bool pin_io)2651b10e191SStéphane Rochoy ftgpio_pin_set_io(struct ftgpio_softc *sc, uint32_t pin_num, bool pin_io)
2661b10e191SStéphane Rochoy {
2671b10e191SStéphane Rochoy 	unsigned group, index;
2681b10e191SStéphane Rochoy 	uint8_t  group_io, ioreg;
2691b10e191SStéphane Rochoy 
2701b10e191SStéphane Rochoy 	index = FTGPIO_PIN_GETINDEX(pin_num);
2711b10e191SStéphane Rochoy 	group = FTGPIO_PIN_GETGROUP(pin_num);
2721b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "set pin %u<GPIO%u%u> io to %s\n",
2731b10e191SStéphane Rochoy 		pin_num, group, index, (pin_io ? "output" : "input"));
2741b10e191SStéphane Rochoy 
2751b10e191SStéphane Rochoy 	ioreg    = ftgpio_group_get_ioreg(sc, REG_OUTPUT_ENABLE, group);
2761b10e191SStéphane Rochoy 	group_io = superio_read(sc->dev, ioreg);
2771b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u io is 0x%x (ioreg=0x%x)\n",
2781b10e191SStéphane Rochoy 		group, group_io, ioreg);
2791b10e191SStéphane Rochoy 	if (pin_io)
2801b10e191SStéphane Rochoy 		group_io |=  (1 << index); /* output */
2811b10e191SStéphane Rochoy 	else
2821b10e191SStéphane Rochoy 		group_io &= ~(1 << index); /* input */
2831b10e191SStéphane Rochoy 	superio_write(sc->dev, ioreg, group_io);
2841b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "set group GPIO%u io to 0x%x (ioreg=0x%x)\n",
2851b10e191SStéphane Rochoy 		group, group_io, ioreg);
2861b10e191SStéphane Rochoy }
2871b10e191SStéphane Rochoy 
2881b10e191SStéphane Rochoy static bool
ftgpio_pin_is_output(struct ftgpio_softc * sc,uint32_t pin_num)2891b10e191SStéphane Rochoy ftgpio_pin_is_output(struct ftgpio_softc *sc, uint32_t pin_num)
2901b10e191SStéphane Rochoy {
2911b10e191SStéphane Rochoy 	unsigned group, index;
2921b10e191SStéphane Rochoy 	bool is_output;
2931b10e191SStéphane Rochoy 
2941b10e191SStéphane Rochoy 	index = FTGPIO_PIN_GETINDEX(pin_num);
2951b10e191SStéphane Rochoy 	group = FTGPIO_PIN_GETGROUP(pin_num);
2961b10e191SStéphane Rochoy 
297*2c1b8eb2SStéphane Rochoy 	is_output = ftgpio_group_get_status(sc, group) & (1 << index);
2981b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> io is %s\n",
2991b10e191SStéphane Rochoy 		pin_num, group, index, (is_output ? "output" : "input"));
3001b10e191SStéphane Rochoy 	return (is_output);
3011b10e191SStéphane Rochoy }
3021b10e191SStéphane Rochoy 
3031b10e191SStéphane Rochoy static int
ftgpio_pin_setflags(struct ftgpio_softc * sc,uint32_t pin_num,uint32_t pin_flags)3041b10e191SStéphane Rochoy ftgpio_pin_setflags(struct ftgpio_softc *sc, uint32_t pin_num, uint32_t pin_flags)
3051b10e191SStéphane Rochoy {
3061b10e191SStéphane Rochoy 	/* check flags consistency */
3071b10e191SStéphane Rochoy 	if ((pin_flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
3081b10e191SStéphane Rochoy 		(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
3091b10e191SStéphane Rochoy 		return (EINVAL);
3101b10e191SStéphane Rochoy 
3111b10e191SStéphane Rochoy 	if ((pin_flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
3121b10e191SStéphane Rochoy 		(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL))
3131b10e191SStéphane Rochoy 		return (EINVAL);
3141b10e191SStéphane Rochoy 
3151b10e191SStéphane Rochoy 	if (pin_flags & GPIO_PIN_OPENDRAIN)
3161b10e191SStéphane Rochoy 		ftgpio_pin_set_drive(sc, pin_num, 0 /* open drain */);
3171b10e191SStéphane Rochoy 	else if (pin_flags & GPIO_PIN_PUSHPULL)
3181b10e191SStéphane Rochoy 		ftgpio_pin_set_drive(sc, pin_num, 1 /* push pull */);
3191b10e191SStéphane Rochoy 
3201b10e191SStéphane Rochoy 	if (pin_flags & GPIO_PIN_INPUT)
3211b10e191SStéphane Rochoy 		ftgpio_pin_set_io(sc, pin_num, 0 /* input */);
3221b10e191SStéphane Rochoy 	else if (pin_flags & GPIO_PIN_OUTPUT)
3231b10e191SStéphane Rochoy 		ftgpio_pin_set_io(sc, pin_num, 1 /* output */);
3241b10e191SStéphane Rochoy 
3251b10e191SStéphane Rochoy 	sc->pins[pin_num].gp_flags = pin_flags;
3261b10e191SStéphane Rochoy 
3271b10e191SStéphane Rochoy 	return (0);
3281b10e191SStéphane Rochoy }
3291b10e191SStéphane Rochoy 
3301b10e191SStéphane Rochoy static int
ftgpio_probe(device_t dev)3311b10e191SStéphane Rochoy ftgpio_probe(device_t dev)
3321b10e191SStéphane Rochoy {
3331b10e191SStéphane Rochoy 	uint16_t devid;
3341b10e191SStéphane Rochoy 	int      i;
3351b10e191SStéphane Rochoy 
3361b10e191SStéphane Rochoy 	if (superio_vendor(dev) != SUPERIO_VENDOR_FINTEK)
3371b10e191SStéphane Rochoy 		return (ENXIO);
3381b10e191SStéphane Rochoy 	if (superio_get_type(dev) != SUPERIO_DEV_GPIO)
3391b10e191SStéphane Rochoy 		return (ENXIO);
3401b10e191SStéphane Rochoy 
3411b10e191SStéphane Rochoy 	/*
3421b10e191SStéphane Rochoy 	 * There are several GPIO devices, we attach only to one of them
3431b10e191SStéphane Rochoy 	 * and use the rest without attaching.
3441b10e191SStéphane Rochoy 	 */
3451b10e191SStéphane Rochoy 	if (superio_get_ldn(dev) != FTGPIO_LDN_GPIO)
3461b10e191SStéphane Rochoy 		return (ENXIO);
3471b10e191SStéphane Rochoy 
3481b10e191SStéphane Rochoy 	devid = superio_devid(dev);
3491b10e191SStéphane Rochoy 	for (i = 0; i < nitems(ftgpio_devices); i++) {
3501b10e191SStéphane Rochoy 		if (devid == ftgpio_devices[i].devid) {
3511b10e191SStéphane Rochoy 			device_set_desc(dev, ftgpio_devices[i].descr);
3521b10e191SStéphane Rochoy 			return (BUS_PROBE_DEFAULT);
3531b10e191SStéphane Rochoy 		}
3541b10e191SStéphane Rochoy 	}
3551b10e191SStéphane Rochoy 	return (ENXIO);
3561b10e191SStéphane Rochoy }
3571b10e191SStéphane Rochoy 
3581b10e191SStéphane Rochoy static int
ftgpio_attach(device_t dev)3591b10e191SStéphane Rochoy ftgpio_attach(device_t dev)
3601b10e191SStéphane Rochoy {
3611b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
3621b10e191SStéphane Rochoy 	int                  i;
3631b10e191SStéphane Rochoy 
3641b10e191SStéphane Rochoy 	sc		= device_get_softc(dev);
3651b10e191SStéphane Rochoy 	sc->dev = dev;
3661b10e191SStéphane Rochoy 
3671b10e191SStéphane Rochoy 	GPIO_LOCK_INIT(sc);
3681b10e191SStéphane Rochoy 	GPIO_LOCK(sc);
3691b10e191SStéphane Rochoy 
3701b10e191SStéphane Rochoy 	for (i = 0; i <= FTGPIO_MAX_PIN; i++) {
3711b10e191SStéphane Rochoy 		struct gpio_pin *pin;
3721b10e191SStéphane Rochoy 
3731b10e191SStéphane Rochoy 		pin           = &sc->pins[i];
3741b10e191SStéphane Rochoy 		pin->gp_pin   = i;
3751b10e191SStéphane Rochoy 		pin->gp_caps  = FTGPIO_GPIO_CAPS;
3761b10e191SStéphane Rochoy 		pin->gp_flags = 0;
3771b10e191SStéphane Rochoy 
3781b10e191SStéphane Rochoy 		if (ftgpio_pin_is_output(sc, i))
3791b10e191SStéphane Rochoy 			pin->gp_flags |= GPIO_PIN_OUTPUT;
3801b10e191SStéphane Rochoy 		else
3811b10e191SStéphane Rochoy 			pin->gp_flags |= GPIO_PIN_INPUT;
3821b10e191SStéphane Rochoy 
3831b10e191SStéphane Rochoy 		if (ftgpio_pin_is_pushpull(sc, i))
3841b10e191SStéphane Rochoy 			pin->gp_flags |= GPIO_PIN_PUSHPULL;
3851b10e191SStéphane Rochoy 		else
3861b10e191SStéphane Rochoy 			pin->gp_flags |= GPIO_PIN_OPENDRAIN;
3871b10e191SStéphane Rochoy 
3881b10e191SStéphane Rochoy 		snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
3891b10e191SStéphane Rochoy 			FTGPIO_PIN_GETGROUP(i), FTGPIO_PIN_GETINDEX(i));
3901b10e191SStéphane Rochoy 	}
3911b10e191SStéphane Rochoy 
3921b10e191SStéphane Rochoy 	/* Enable all groups */
3931b10e191SStéphane Rochoy 	superio_write(sc->dev, GPIO1_ENABLE, 0xFF);
3941b10e191SStéphane Rochoy 	superio_write(sc->dev, GPIO2_ENABLE, 0xFF);
3951b10e191SStéphane Rochoy 	superio_write(sc->dev, GPIO3_ENABLE, 0xFF);
3961b10e191SStéphane Rochoy 	superio_write(sc->dev, GPIO4_ENABLE, 0xFF);
3971b10e191SStéphane Rochoy 	superio_write(sc->dev, FULL_UR5_UR6, 0x0A);
3981b10e191SStéphane Rochoy 	FTGPIO_VERBOSE_PRINTF(sc->dev, "groups GPIO1..GPIO6 enabled\n");
3991b10e191SStéphane Rochoy 
4001b10e191SStéphane Rochoy 	GPIO_UNLOCK(sc);
4011b10e191SStéphane Rochoy 	sc->busdev = gpiobus_attach_bus(dev);
4021b10e191SStéphane Rochoy 	if (sc->busdev == NULL) {
4031b10e191SStéphane Rochoy 		GPIO_LOCK_DESTROY(sc);
4041b10e191SStéphane Rochoy 		return (ENXIO);
4051b10e191SStéphane Rochoy 	}
4061b10e191SStéphane Rochoy 
4071b10e191SStéphane Rochoy 	return (0);
4081b10e191SStéphane Rochoy }
4091b10e191SStéphane Rochoy 
4101b10e191SStéphane Rochoy static int
ftgpio_detach(device_t dev)4111b10e191SStéphane Rochoy ftgpio_detach(device_t dev)
4121b10e191SStéphane Rochoy {
4131b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
4141b10e191SStéphane Rochoy 
4151b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
4161b10e191SStéphane Rochoy 	gpiobus_detach_bus(dev);
4171b10e191SStéphane Rochoy 	GPIO_ASSERT_UNLOCKED(sc);
4181b10e191SStéphane Rochoy 	GPIO_LOCK_DESTROY(sc);
4191b10e191SStéphane Rochoy 
4201b10e191SStéphane Rochoy 	return (0);
4211b10e191SStéphane Rochoy }
4221b10e191SStéphane Rochoy 
4231b10e191SStéphane Rochoy static device_t
ftgpio_gpio_get_bus(device_t dev)4241b10e191SStéphane Rochoy ftgpio_gpio_get_bus(device_t dev)
4251b10e191SStéphane Rochoy {
4261b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
4271b10e191SStéphane Rochoy 
4281b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
4291b10e191SStéphane Rochoy 
4301b10e191SStéphane Rochoy 	return (sc->busdev);
4311b10e191SStéphane Rochoy }
4321b10e191SStéphane Rochoy 
4331b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_max(device_t dev,int * npins)4341b10e191SStéphane Rochoy ftgpio_gpio_pin_max(device_t dev, int *npins)
4351b10e191SStéphane Rochoy {
4361b10e191SStéphane Rochoy 	*npins = FTGPIO_MAX_PIN;
4371b10e191SStéphane Rochoy 	return (0);
4381b10e191SStéphane Rochoy }
4391b10e191SStéphane Rochoy 
4401b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_set(device_t dev,uint32_t pin_num,uint32_t pin_value)4411b10e191SStéphane Rochoy ftgpio_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
4421b10e191SStéphane Rochoy {
4431b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
4441b10e191SStéphane Rochoy 
4451b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num))
4461b10e191SStéphane Rochoy 		return (EINVAL);
4471b10e191SStéphane Rochoy 
4481b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
4491b10e191SStéphane Rochoy 	GPIO_LOCK(sc);
4501b10e191SStéphane Rochoy 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
4511b10e191SStéphane Rochoy 		GPIO_UNLOCK(sc);
4521b10e191SStéphane Rochoy 		return (EINVAL);
4531b10e191SStéphane Rochoy 	}
4541b10e191SStéphane Rochoy 	ftgpio_pin_write(sc, pin_num, pin_value);
4551b10e191SStéphane Rochoy 	GPIO_UNLOCK(sc);
4561b10e191SStéphane Rochoy 
4571b10e191SStéphane Rochoy 	return (0);
4581b10e191SStéphane Rochoy }
4591b10e191SStéphane Rochoy 
4601b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_get(device_t dev,uint32_t pin_num,uint32_t * pin_value)4611b10e191SStéphane Rochoy ftgpio_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
4621b10e191SStéphane Rochoy {
4631b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
4641b10e191SStéphane Rochoy 
4651b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num))
4661b10e191SStéphane Rochoy 		return (EINVAL);
4671b10e191SStéphane Rochoy 
4681b10e191SStéphane Rochoy 	if (pin_value == NULL)
4691b10e191SStéphane Rochoy 		return (EINVAL);
4701b10e191SStéphane Rochoy 
4711b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
4721b10e191SStéphane Rochoy 	GPIO_LOCK(sc);
4731b10e191SStéphane Rochoy 	*pin_value = ftgpio_pin_read(sc, pin_num);
4741b10e191SStéphane Rochoy 	GPIO_UNLOCK(sc);
4751b10e191SStéphane Rochoy 
4761b10e191SStéphane Rochoy 	return (0);
4771b10e191SStéphane Rochoy }
4781b10e191SStéphane Rochoy 
4791b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_toggle(device_t dev,uint32_t pin_num)4801b10e191SStéphane Rochoy ftgpio_gpio_pin_toggle(device_t dev, uint32_t pin_num)
4811b10e191SStéphane Rochoy {
4821b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
4831b10e191SStéphane Rochoy 	bool              pin_value;
4841b10e191SStéphane Rochoy 
4851b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num))
4861b10e191SStéphane Rochoy 		return (EINVAL);
4871b10e191SStéphane Rochoy 
4881b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
4891b10e191SStéphane Rochoy 	GPIO_LOCK(sc);
4901b10e191SStéphane Rochoy 	pin_value = ftgpio_pin_read(sc, pin_num);
4911b10e191SStéphane Rochoy 	ftgpio_pin_write(sc, pin_num, !pin_value);
4921b10e191SStéphane Rochoy 	GPIO_UNLOCK(sc);
4931b10e191SStéphane Rochoy 
4941b10e191SStéphane Rochoy 	return (0);
4951b10e191SStéphane Rochoy }
4961b10e191SStéphane Rochoy 
4971b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_getname(device_t dev,uint32_t pin_num,char * pin_name)4981b10e191SStéphane Rochoy ftgpio_gpio_pin_getname(device_t dev, uint32_t pin_num, char *pin_name)
4991b10e191SStéphane Rochoy {
5001b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
5011b10e191SStéphane Rochoy 
5021b10e191SStéphane Rochoy 	if (pin_name == NULL)
5031b10e191SStéphane Rochoy 		return (EINVAL);
5041b10e191SStéphane Rochoy 
5051b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num))
5061b10e191SStéphane Rochoy 		return (EINVAL);
5071b10e191SStéphane Rochoy 
5081b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
5091b10e191SStéphane Rochoy 	strlcpy(pin_name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
5101b10e191SStéphane Rochoy 
5111b10e191SStéphane Rochoy 	return (0);
5121b10e191SStéphane Rochoy }
5131b10e191SStéphane Rochoy 
5141b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_getcaps(device_t dev,uint32_t pin_num,uint32_t * pin_caps)5151b10e191SStéphane Rochoy ftgpio_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *pin_caps)
5161b10e191SStéphane Rochoy {
5171b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
5181b10e191SStéphane Rochoy 
5191b10e191SStéphane Rochoy 	if (pin_caps == NULL)
5201b10e191SStéphane Rochoy 		return (EINVAL);
5211b10e191SStéphane Rochoy 
5221b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num))
5231b10e191SStéphane Rochoy 		return (EINVAL);
5241b10e191SStéphane Rochoy 
5251b10e191SStéphane Rochoy 	sc        = device_get_softc(dev);
5261b10e191SStéphane Rochoy 	*pin_caps = sc->pins[pin_num].gp_caps;
5271b10e191SStéphane Rochoy 
5281b10e191SStéphane Rochoy 	return (0);
5291b10e191SStéphane Rochoy }
5301b10e191SStéphane Rochoy 
5311b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_getflags(device_t dev,uint32_t pin_num,uint32_t * pin_flags)5321b10e191SStéphane Rochoy ftgpio_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *pin_flags)
5331b10e191SStéphane Rochoy {
5341b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
5351b10e191SStéphane Rochoy 
5361b10e191SStéphane Rochoy 	if (pin_flags == NULL)
5371b10e191SStéphane Rochoy 		return (EINVAL);
5381b10e191SStéphane Rochoy 
5391b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num))
5401b10e191SStéphane Rochoy 		return (EINVAL);
5411b10e191SStéphane Rochoy 
5421b10e191SStéphane Rochoy 	sc         = device_get_softc(dev);
5431b10e191SStéphane Rochoy 	*pin_flags = sc->pins[pin_num].gp_flags;
5441b10e191SStéphane Rochoy 
5451b10e191SStéphane Rochoy 	return (0);
5461b10e191SStéphane Rochoy }
5471b10e191SStéphane Rochoy 
5481b10e191SStéphane Rochoy static int
ftgpio_gpio_pin_setflags(device_t dev,uint32_t pin_num,uint32_t pin_flags)5491b10e191SStéphane Rochoy ftgpio_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t pin_flags)
5501b10e191SStéphane Rochoy {
5511b10e191SStéphane Rochoy 	struct ftgpio_softc *sc;
5521b10e191SStéphane Rochoy 	int               ret;
5531b10e191SStéphane Rochoy 
5541b10e191SStéphane Rochoy 	if (!FTGPIO_IS_VALID_PIN(pin_num)) {
5551b10e191SStéphane Rochoy 		FTGPIO_VERBOSE_PRINTF(dev, "invalid pin number: %u\n", pin_num);
5561b10e191SStéphane Rochoy 		return (EINVAL);
5571b10e191SStéphane Rochoy 	}
5581b10e191SStéphane Rochoy 
5591b10e191SStéphane Rochoy 	sc = device_get_softc(dev);
5601b10e191SStéphane Rochoy 
5611b10e191SStéphane Rochoy 	/* Check for unwanted flags. */
5621b10e191SStéphane Rochoy 	if ((pin_flags & sc->pins[pin_num].gp_caps) != pin_flags) {
5631b10e191SStéphane Rochoy 		FTGPIO_VERBOSE_PRINTF(dev, "invalid pin flags 0x%x, vs caps 0x%x\n",
5641b10e191SStéphane Rochoy 			pin_flags, sc->pins[pin_num].gp_caps);
5651b10e191SStéphane Rochoy 		return (EINVAL);
5661b10e191SStéphane Rochoy 	}
5671b10e191SStéphane Rochoy 
5681b10e191SStéphane Rochoy 	GPIO_LOCK(sc);
5691b10e191SStéphane Rochoy 	ret = ftgpio_pin_setflags(sc, pin_num, pin_flags);
5701b10e191SStéphane Rochoy 	GPIO_UNLOCK(sc);
5711b10e191SStéphane Rochoy 
5721b10e191SStéphane Rochoy 	return (ret);
5731b10e191SStéphane Rochoy }
5741b10e191SStéphane Rochoy 
5751b10e191SStéphane Rochoy static device_method_t ftgpio_methods[] = {
5761b10e191SStéphane Rochoy 	/* Device interface */
5771b10e191SStéphane Rochoy 	DEVMETHOD(device_probe,     ftgpio_probe),
5781b10e191SStéphane Rochoy 	DEVMETHOD(device_attach,    ftgpio_attach),
5791b10e191SStéphane Rochoy 	DEVMETHOD(device_detach,    ftgpio_detach),
5801b10e191SStéphane Rochoy 
5811b10e191SStéphane Rochoy 	/* GPIO */
5821b10e191SStéphane Rochoy 	DEVMETHOD(gpio_get_bus,         ftgpio_gpio_get_bus),
5831b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_max,         ftgpio_gpio_pin_max),
5841b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_set,         ftgpio_gpio_pin_set),
5851b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_get,         ftgpio_gpio_pin_get),
5861b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_toggle,      ftgpio_gpio_pin_toggle),
5871b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_getname,     ftgpio_gpio_pin_getname),
5881b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_getcaps,     ftgpio_gpio_pin_getcaps),
5891b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_getflags,    ftgpio_gpio_pin_getflags),
5901b10e191SStéphane Rochoy 	DEVMETHOD(gpio_pin_setflags,    ftgpio_gpio_pin_setflags),
5911b10e191SStéphane Rochoy 
5921b10e191SStéphane Rochoy 	DEVMETHOD_END
5931b10e191SStéphane Rochoy };
5941b10e191SStéphane Rochoy 
5951b10e191SStéphane Rochoy static driver_t ftgpio_driver = {
5961b10e191SStéphane Rochoy 	"gpio",
5971b10e191SStéphane Rochoy 	ftgpio_methods,
5981b10e191SStéphane Rochoy 	sizeof(struct ftgpio_softc)
5991b10e191SStéphane Rochoy };
6001b10e191SStéphane Rochoy 
6011b10e191SStéphane Rochoy DRIVER_MODULE(ftgpio, superio, ftgpio_driver, NULL,  NULL);
6021b10e191SStéphane Rochoy MODULE_DEPEND(ftgpio, gpiobus, 1, 1, 1);
6031b10e191SStéphane Rochoy MODULE_DEPEND(ftgpio, superio, 1, 1, 1);
6041b10e191SStéphane Rochoy MODULE_VERSION(ftgpio, 1);
605