Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 31 Mar 2016 04:57:39 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r297443 - in head/sys: dev/nctgpio modules modules/nctgpio
Message-ID:  <201603310457.u2V4vddK041341@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Thu Mar 31 04:57:38 2016
New Revision: 297443
URL: https://svnweb.freebsd.org/changeset/base/297443

Log:
  Add support for the Nuvoton NCT5104D.
  
  Make it compile only for i386/amd64 for now as it's been tested there.
  It's quite possible it'll show up elsewhere and we can enable it
  for other architectures later.
  
  Tested:
  
  * PC Engines APU1C4
  
  Submitted by:	Daniel Wyatt <daniel@dewyatt.com>
  Reviewed by:	adrian, loos
  Differential Revision:	https://reviews.freebsd.org/D5389

Added:
  head/sys/dev/nctgpio/
  head/sys/dev/nctgpio/nctgpio.c   (contents, props changed)
  head/sys/modules/nctgpio/
  head/sys/modules/nctgpio/Makefile   (contents, props changed)
Modified:
  head/sys/modules/Makefile

Added: head/sys/dev/nctgpio/nctgpio.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/nctgpio/nctgpio.c	Thu Mar 31 04:57:38 2016	(r297443)
@@ -0,0 +1,802 @@
+/*-
+ * Copyright (c) 2016 Daniel Wyatt <Daniel.Wyatt@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * Nuvoton GPIO driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/lock.h>
+
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/gpio.h>
+
+#include <isa/isavar.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include "gpio_if.h"
+
+/*
+ * Global configuration registers (CR).
+ */
+#define NCT_CR_LDN			0x07	/* Logical Device Number */
+#define NCT_CR_CHIP_ID			0x20 	/* Chip ID */
+#define NCT_CR_CHIP_ID_H		0x20 	/* Chip ID (high byte) */
+#define NCT_CR_CHIP_ID_L		0x21 	/* Chip ID (low byte) */
+#define NCT_CR_OPT_1			0x26	/* Global Options (1) */
+
+/* Logical Device Numbers. */
+#define NCT_LDN_GPIO			0x07
+#define NCT_LDN_GPIO_CFG		0x08
+#define NCT_LDN_GPIO_MODE		0x0f
+
+/* Logical Device 7 */
+#define NCT_LD7_GPIO_ENABLE		0x30
+#define NCT_LD7_GPIO0_IOR		0xe0
+#define NCT_LD7_GPIO0_DAT		0xe1
+#define NCT_LD7_GPIO0_INV		0xe2
+#define NCT_LD7_GPIO0_DST		0xe3
+#define NCT_LD7_GPIO1_IOR		0xe4
+#define NCT_LD7_GPIO1_DAT		0xe5
+#define NCT_LD7_GPIO1_INV		0xe6
+#define NCT_LD7_GPIO1_DST		0xe7
+
+/* Logical Device F */
+#define NCT_LDF_GPIO0_OUTCFG		0xe0
+#define NCT_LDF_GPIO1_OUTCFG		0xe1
+
+#define NCT_EXTFUNC_ENTER		0x87
+#define NCT_EXTFUNC_EXIT		0xaa
+
+#define NCT_MAX_PIN			15
+#define NCT_IS_VALID_PIN(_p)	((_p) >= 0 && (_p) <= NCT_MAX_PIN)
+
+#define NCT_PIN_BIT(_p)         (1 << ((_p) % 8))
+
+#define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
+	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
+
+struct nct_softc {
+	device_t			dev;
+	device_t			busdev;
+	struct mtx			mtx;
+	struct resource			*portres;
+	int				rid;
+	struct gpio_pin			pins[NCT_MAX_PIN];
+};
+
+#define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
+		device_get_nameunit(dev), NULL, MTX_DEF)
+#define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
+#define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
+#define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
+#define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
+
+#define NCT_BARRIER_WRITE(_sc)	\
+	bus_barrier((_sc)->portres, 0, 2, BUS_SPACE_BARRIER_WRITE)
+
+#define NCT_BARRIER_READ_WRITE(_sc)	\
+	bus_barrier((_sc)->portres, 0, 2, \
+		BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)
+
+static void	ext_cfg_enter(struct nct_softc *);
+static void	ext_cfg_exit(struct nct_softc *);
+
+/*
+ * Potential Extended Function Enable Register addresses.
+ * Same address as EFIR.
+ */
+uint8_t probe_addrs[] = {0x2e, 0x4e};
+
+struct nuvoton_vendor_device_id {
+	uint16_t		chip_id;
+	const char *		descr;
+} nct_devs[] = {
+	{
+		.chip_id	= 0x1061,
+		.descr		= "Nuvoton NCT5104D",
+	},
+	{
+		.chip_id	= 0xc452,
+		.descr		= "Nuvoton NCT5104D (PC-Engines APU)",
+	},
+};
+
+static void
+write_cfg_reg_1(struct nct_softc *sc, uint8_t reg, uint8_t value)
+{
+	GPIO_ASSERT_LOCKED(sc);
+	bus_write_1(sc->portres, 0, reg);
+	NCT_BARRIER_WRITE(sc);
+	bus_write_1(sc->portres, 1, value);
+	NCT_BARRIER_WRITE(sc);
+}
+
+static uint8_t
+read_cfg_reg_1(struct nct_softc *sc, uint8_t reg)
+{
+	uint8_t value;
+
+	GPIO_ASSERT_LOCKED(sc);
+	bus_write_1(sc->portres, 0, reg);
+	NCT_BARRIER_READ_WRITE(sc);
+	value = bus_read_1(sc->portres, 1);
+	NCT_BARRIER_READ_WRITE(sc);
+	
+	return (value);
+}
+
+static uint16_t
+read_cfg_reg_2(struct nct_softc *sc, uint8_t reg)
+{
+	uint16_t value;
+
+	value = read_cfg_reg_1(sc, reg) << 8;
+	value |= read_cfg_reg_1(sc, reg + 1);
+
+	return (value);
+}
+
+/*
+ * Enable extended function mode.
+ *
+ */
+static void
+ext_cfg_enter(struct nct_softc *sc)
+{
+	GPIO_ASSERT_LOCKED(sc);
+	bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
+	NCT_BARRIER_WRITE(sc);
+	bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
+	NCT_BARRIER_WRITE(sc);
+}
+
+/*
+ * Disable extended function mode.
+ *
+ */
+static void
+ext_cfg_exit(struct nct_softc *sc)
+{
+	GPIO_ASSERT_LOCKED(sc);
+	bus_write_1(sc->portres, 0, NCT_EXTFUNC_EXIT);
+	NCT_BARRIER_WRITE(sc);
+}
+
+/*
+ * Select a Logical Device.
+ */
+static void
+select_ldn(struct nct_softc *sc, uint8_t ldn)
+{
+	write_cfg_reg_1(sc, NCT_CR_LDN, ldn);
+}
+
+/*
+ * Get the GPIO Input/Output register address
+ * for a pin.
+ */
+static uint8_t
+nct_ior_addr(uint32_t pin_num)
+{
+	uint8_t addr;
+
+	addr = NCT_LD7_GPIO0_IOR;
+	if (pin_num > 7)
+		addr = NCT_LD7_GPIO1_IOR;
+
+	return (addr);
+}
+
+/*
+ * Get the GPIO Data register address for a pin.
+ */
+static uint8_t
+nct_dat_addr(uint32_t pin_num)
+{
+	uint8_t addr;
+
+	addr = NCT_LD7_GPIO0_DAT;
+	if (pin_num > 7)
+		addr = NCT_LD7_GPIO1_DAT;
+
+	return (addr);
+}
+
+/*
+ * Get the GPIO Inversion register address
+ * for a pin.
+ */
+static uint8_t
+nct_inv_addr(uint32_t pin_num)
+{
+	uint8_t addr;
+
+	addr = NCT_LD7_GPIO0_INV;
+	if (pin_num > 7)
+		addr = NCT_LD7_GPIO1_INV;
+
+	return (addr);
+}
+
+/*
+ * Get the GPIO Output Configuration/Mode
+ * register address for a pin.
+ */
+static uint8_t
+nct_outcfg_addr(uint32_t pin_num)
+{
+	uint8_t addr;
+
+	addr = NCT_LDF_GPIO0_OUTCFG;
+	if (pin_num > 7)
+		addr = NCT_LDF_GPIO1_OUTCFG;
+
+	return (addr);
+}
+
+/*
+ * Set a pin to output mode.
+ */
+static void
+nct_set_pin_is_output(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t ior;
+
+	reg = nct_ior_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	ior = read_cfg_reg_1(sc, reg);
+	ior &= ~(NCT_PIN_BIT(pin_num));
+	write_cfg_reg_1(sc, reg, ior);
+}
+
+/*
+ * Set a pin to input mode.
+ */
+static void
+nct_set_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t ior;
+
+	reg = nct_ior_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	ior = read_cfg_reg_1(sc, reg);
+	ior |= NCT_PIN_BIT(pin_num);
+	write_cfg_reg_1(sc, reg, ior);
+}
+
+/*
+ * Check whether a pin is configured as an input.
+ */
+static bool
+nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t ior;
+
+	reg = nct_ior_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	ior = read_cfg_reg_1(sc, reg);
+
+	return (ior & NCT_PIN_BIT(pin_num));
+}
+
+/*
+ * Write a value to an output pin.
+ */
+static void
+nct_write_pin(struct nct_softc *sc, uint32_t pin_num, uint8_t data)
+{
+	uint8_t reg;
+	uint8_t value;
+
+	reg = nct_dat_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	value = read_cfg_reg_1(sc, reg);
+	if (data)
+		value |= NCT_PIN_BIT(pin_num);
+	else
+		value &= ~(NCT_PIN_BIT(pin_num));
+
+	write_cfg_reg_1(sc, reg, value);
+}
+
+static bool
+nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+
+	reg = nct_dat_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+
+	return (read_cfg_reg_1(sc, reg) & NCT_PIN_BIT(pin_num));
+}
+
+static void
+nct_set_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t inv;
+
+	reg = nct_inv_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	inv = read_cfg_reg_1(sc, reg);
+	inv |= (NCT_PIN_BIT(pin_num));
+	write_cfg_reg_1(sc, reg, inv);
+}
+
+static void
+nct_set_pin_not_inverted(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t inv;
+
+	reg = nct_inv_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	inv = read_cfg_reg_1(sc, reg);
+	inv &= ~(NCT_PIN_BIT(pin_num));
+	write_cfg_reg_1(sc, reg, inv);
+}
+
+static bool
+nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t inv;
+
+	reg = nct_inv_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO);
+	inv = read_cfg_reg_1(sc, reg);
+
+	return (inv & NCT_PIN_BIT(pin_num));
+}
+
+static void
+nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t outcfg;
+
+	reg = nct_outcfg_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO_MODE);
+	outcfg = read_cfg_reg_1(sc, reg);
+	outcfg |= (NCT_PIN_BIT(pin_num));
+	write_cfg_reg_1(sc, reg, outcfg);
+}
+
+static void
+nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t outcfg;
+
+	reg = nct_outcfg_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO_MODE);
+	outcfg = read_cfg_reg_1(sc, reg);
+	outcfg &= ~(NCT_PIN_BIT(pin_num));
+	write_cfg_reg_1(sc, reg, outcfg);
+}
+
+static bool
+nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
+{
+	uint8_t reg;
+	uint8_t outcfg;
+
+	reg = nct_outcfg_addr(pin_num);
+	select_ldn(sc, NCT_LDN_GPIO_MODE);
+	outcfg = read_cfg_reg_1(sc, reg);
+
+	return (outcfg & NCT_PIN_BIT(pin_num));
+}
+
+static void
+nct_identify(driver_t *driver, device_t parent)
+{
+	if (device_find_child(parent, driver->name, 0) != NULL)
+		return;
+
+	BUS_ADD_CHILD(parent, 0, driver->name, 0);
+}
+
+static int
+nct_probe(device_t dev)
+{
+	int i, j;
+	int rc;
+	struct nct_softc *sc;
+	uint16_t chipid;
+
+	/* Make sure we do not claim some ISA PNP device. */
+	if (isa_get_logicalid(dev) != 0)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+
+	for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) {
+		sc->rid = 0;
+		sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
+			probe_addrs[i], probe_addrs[i] + 1, 2, RF_ACTIVE);
+		if (sc->portres == NULL)
+			continue;
+
+		GPIO_LOCK_INIT(sc);
+
+		GPIO_ASSERT_UNLOCKED(sc);
+		GPIO_LOCK(sc);
+		ext_cfg_enter(sc);
+		chipid = read_cfg_reg_2(sc, NCT_CR_CHIP_ID);
+		ext_cfg_exit(sc);
+		GPIO_UNLOCK(sc);
+
+		GPIO_LOCK_DESTROY(sc);
+
+		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
+		bus_delete_resource(dev, SYS_RES_IOPORT, sc->rid);
+
+		for (j = 0; j < sizeof(nct_devs) / sizeof(*nct_devs); j++) {
+			if (chipid == nct_devs[j].chip_id) {
+				rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, probe_addrs[i], 2);
+				if (rc != 0) {
+					device_printf(dev, "bus_set_resource failed for address 0x%02X\n", probe_addrs[i]);
+					continue;
+				}
+				device_set_desc(dev, nct_devs[j].descr);
+				return (BUS_PROBE_DEFAULT);
+			}
+		}
+	}
+	return (ENXIO);
+}
+
+static int
+nct_attach(device_t dev)
+{
+	struct nct_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	sc->rid = 0;
+	sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
+		0ul, ~0ul, 2, RF_ACTIVE);
+	if (sc->portres == NULL) {
+		device_printf(dev, "cannot allocate ioport\n");
+		return (ENXIO);
+	}
+
+	GPIO_LOCK_INIT(sc);
+
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	ext_cfg_enter(sc);
+	select_ldn(sc, NCT_LDN_GPIO);
+	/* Enable gpio0 and gpio1. */
+	write_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE,
+		read_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE) | 0x03);
+
+	for (i = 0; i <= NCT_MAX_PIN; i++) {
+		struct gpio_pin *pin;
+
+		pin = &sc->pins[i];
+		pin->gp_pin = i;
+		pin->gp_caps = NCT_GPIO_CAPS;
+		pin->gp_flags = 0;
+
+		snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02u", i);
+		pin->gp_name[GPIOMAXNAME - 1] = '\0';
+
+		if (nct_pin_is_input(sc, i))
+			pin->gp_flags |= GPIO_PIN_INPUT;
+		else
+			pin->gp_flags |= GPIO_PIN_OUTPUT;
+
+		if (nct_pin_is_opendrain(sc, i))
+			pin->gp_flags |= GPIO_PIN_OPENDRAIN;
+		else
+			pin->gp_flags |= GPIO_PIN_PUSHPULL;
+
+		if (nct_pin_is_inverted(sc, i))
+			pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
+	}
+	GPIO_UNLOCK(sc);
+
+	sc->busdev = gpiobus_attach_bus(dev);
+	if (sc->busdev == NULL) {
+		GPIO_ASSERT_UNLOCKED(sc);
+		GPIO_LOCK(sc);
+		ext_cfg_exit(sc);
+		GPIO_UNLOCK(sc);
+		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
+		GPIO_LOCK_DESTROY(sc);
+
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
+static int
+nct_detach(device_t dev)
+{
+	struct nct_softc *sc;
+
+	sc = device_get_softc(dev);
+	gpiobus_detach_bus(dev);
+
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	ext_cfg_exit(sc);
+	GPIO_UNLOCK(sc);
+
+	/* Cleanup resources. */
+	bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
+
+	GPIO_LOCK_DESTROY(sc);
+
+	return (0);
+}
+
+static device_t
+nct_gpio_get_bus(device_t dev)
+{
+	struct nct_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	return (sc->busdev);
+}
+
+static int
+nct_gpio_pin_max(device_t dev, int *npins)
+{
+	*npins = NCT_MAX_PIN;
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
+{
+	struct nct_softc *sc;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	nct_write_pin(sc, pin_num, pin_value);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
+{
+	struct nct_softc *sc;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	*pin_value = nct_read_pin(sc, pin_num);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
+{
+	struct nct_softc *sc;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	if (nct_read_pin(sc, pin_num))
+		nct_write_pin(sc, pin_num, 0);
+	else
+		nct_write_pin(sc, pin_num, 1);
+
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
+{
+	struct nct_softc *sc;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	*caps = sc->pins[pin_num].gp_caps;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
+{
+	struct nct_softc *sc;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	*flags = sc->pins[pin_num].gp_flags;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
+{
+	struct nct_softc *sc;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
+{
+	struct nct_softc *sc;
+	struct gpio_pin *pin;
+
+	if (!NCT_IS_VALID_PIN(pin_num))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+	pin = &sc->pins[pin_num];
+	if ((flags & pin->gp_caps) != flags)
+		return (EINVAL);
+
+	GPIO_ASSERT_UNLOCKED(sc);
+	GPIO_LOCK(sc);
+	if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
+		if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
+			(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
+				GPIO_UNLOCK(sc);
+				return (EINVAL);
+		}
+
+		if (flags & GPIO_PIN_INPUT)
+			nct_set_pin_is_input(sc, pin_num);
+		else
+			nct_set_pin_is_output(sc, pin_num);
+	}
+
+	if (flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
+		if (flags & GPIO_PIN_INPUT) {
+			GPIO_UNLOCK(sc);
+			return (EINVAL);
+		}
+
+		if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
+			(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
+				GPIO_UNLOCK(sc);
+				return (EINVAL);
+		}
+
+		if (flags & GPIO_PIN_OPENDRAIN)
+			nct_set_pin_opendrain(sc, pin_num);
+		else
+			nct_set_pin_pushpull(sc, pin_num);
+	}
+
+	if (flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
+		if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) !=
+			(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
+				GPIO_UNLOCK(sc);
+				return (EINVAL);
+		}
+
+		if (flags & GPIO_PIN_INVIN)
+			nct_set_pin_is_inverted(sc, pin_num);
+		else
+			nct_set_pin_not_inverted(sc, pin_num);
+	}
+
+	pin->gp_flags = flags;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static device_method_t nct_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	nct_identify),
+	DEVMETHOD(device_probe,		nct_probe),
+	DEVMETHOD(device_attach,	nct_attach),
+	DEVMETHOD(device_detach,	nct_detach),
+
+	/* GPIO */
+	DEVMETHOD(gpio_get_bus,			nct_gpio_get_bus),
+	DEVMETHOD(gpio_pin_max,			nct_gpio_pin_max),
+	DEVMETHOD(gpio_pin_get,			nct_gpio_pin_get),
+	DEVMETHOD(gpio_pin_set,			nct_gpio_pin_set),
+	DEVMETHOD(gpio_pin_toggle,		nct_gpio_pin_toggle),
+	DEVMETHOD(gpio_pin_getname,		nct_gpio_pin_getname),
+	DEVMETHOD(gpio_pin_getcaps,		nct_gpio_pin_getcaps),
+	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
+	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
+
+	DEVMETHOD_END
+};
+
+static driver_t nct_isa_driver = {
+	"gpio",
+	nct_methods,
+	sizeof(struct nct_softc)
+};
+
+static devclass_t nct_devclass;
+
+DRIVER_MODULE(nctgpio, isa, nct_isa_driver, nct_devclass, NULL, NULL);
+MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);

Modified: head/sys/modules/Makefile
==============================================================================
--- head/sys/modules/Makefile	Thu Mar 31 03:04:26 2016	(r297442)
+++ head/sys/modules/Makefile	Thu Mar 31 04:57:38 2016	(r297443)
@@ -250,6 +250,7 @@ SUBDIR=	\
 	${_nandfs} \
 	${_nandsim} \
 	${_ncr} \
+	${_nctgpio} \
 	${_ncv} \
 	${_ndis} \
 	netfpga10g \
@@ -558,6 +559,7 @@ _ixv=		ixv
 _linprocfs=	linprocfs
 _linsysfs=	linsysfs
 _linux=		linux
+_nctgpio=	nctgpio
 _ndis=		ndis
 _pccard=	pccard
 .if ${MK_OFED} != "no" || defined(ALL_MODULES)

Added: head/sys/modules/nctgpio/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/modules/nctgpio/Makefile	Thu Mar 31 04:57:38 2016	(r297443)
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH:	${.CURDIR}/../../dev/nctgpio
+KMOD=	nctgpio
+SRCS=	nctgpio.c
+SRCS+=	device_if.h bus_if.h isa_if.h gpio_if.h opt_platform.h
+
+.include <bsd.kmod.mk>



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201603310457.u2V4vddK041341>