Home | History | Annotate | Line # | Download | only in isa
nct.c revision 1.1
      1  1.1  martin /*	$NetBSD: nct.c,v 1.1 2019/10/25 17:39:57 martin Exp $	*/
      2  1.1  martin 
      3  1.1  martin /*-
      4  1.1  martin  * Copyright (c) 2019 The NetBSD Foundation, Inc.
      5  1.1  martin  * All rights reserved.
      6  1.1  martin  *
      7  1.1  martin  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1  martin  * by Andrew Doran.
      9  1.1  martin  *
     10  1.1  martin  * Redistribution and use in source and binary forms, with or without
     11  1.1  martin  * modification, are permitted provided that the following conditions
     12  1.1  martin  * are met:
     13  1.1  martin  * 1. Redistributions of source code must retain the above copyright
     14  1.1  martin  *    notice, this list of conditions and the following disclaimer.
     15  1.1  martin  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1  martin  *    notice, this list of conditions and the following disclaimer in the
     17  1.1  martin  *    documentation and/or other materials provided with the distribution.
     18  1.1  martin  *
     19  1.1  martin  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  1.1  martin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  1.1  martin  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  1.1  martin  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  1.1  martin  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  1.1  martin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  1.1  martin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  1.1  martin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  1.1  martin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  1.1  martin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  1.1  martin  * POSSIBILITY OF SUCH DAMAGE.
     30  1.1  martin  */
     31  1.1  martin 
     32  1.1  martin /*
     33  1.1  martin  * Nuvoton NCT5104D
     34  1.1  martin  *
     35  1.1  martin  * - GPIO: full support.
     36  1.1  martin  * - Watchdog: no support.  Watchdog uses GPIO pins.
     37  1.1  martin  * - UARTS: handled by com driver.  3rd & 4th UARTs use GPIO pins.
     38  1.1  martin  *
     39  1.1  martin  * If asked to probe with a wildcard address, we'll only do so if known to
     40  1.1  martin  * be running on a PC Engines APU board.  Probe is invasive.
     41  1.1  martin  *
     42  1.1  martin  * Register access on Super I/O chips typically involves one or two levels
     43  1.1  martin  * of indirection, so we try hard to avoid needless register access.
     44  1.1  martin  */
     45  1.1  martin 
     46  1.1  martin #include <sys/cdefs.h>
     47  1.1  martin __KERNEL_RCSID(0, "$NetBSD: nct.c,v 1.1 2019/10/25 17:39:57 martin Exp $");
     48  1.1  martin 
     49  1.1  martin #include <sys/param.h>
     50  1.1  martin #include <sys/systm.h>
     51  1.1  martin #include <sys/types.h>
     52  1.1  martin #include <sys/device.h>
     53  1.1  martin #include <sys/module.h>
     54  1.1  martin #include <sys/bus.h>
     55  1.1  martin #include <sys/gpio.h>
     56  1.1  martin 
     57  1.1  martin #include <machine/autoconf.h>
     58  1.1  martin 
     59  1.1  martin #include <dev/isa/isavar.h>
     60  1.1  martin 
     61  1.1  martin #include <dev/gpio/gpiovar.h>
     62  1.1  martin 
     63  1.1  martin /*
     64  1.1  martin  * Hardware interface definition (enough for GPIO only).
     65  1.1  martin  */
     66  1.1  martin 
     67  1.1  martin /* I/O basics */
     68  1.1  martin #define	NCT_IOBASE_A		0x2e
     69  1.1  martin #define	NCT_IOBASE_B		0x4e
     70  1.1  martin #define	NCT_IOSIZE		2
     71  1.1  martin #define	NCT_CHIP_ID_1		0x1061
     72  1.1  martin #define	NCT_CHIP_ID_2		0xc452	/* PC Engines APU1 */
     73  1.1  martin #define	NCT_NUM_PINS		17
     74  1.1  martin 
     75  1.1  martin /* Enable/disable keys */
     76  1.1  martin #define NCT_KEY_UNLOCK		0x87
     77  1.1  martin #define NCT_KEY_LOCK		0xaa
     78  1.1  martin 
     79  1.1  martin /* I/O ports */
     80  1.1  martin #define	NCT_PORT_SELECT		0
     81  1.1  martin #define	NCT_PORT_DATA		1
     82  1.1  martin 
     83  1.1  martin /* Global registers */
     84  1.1  martin #define	GD_DEVSEL		0x0007	/* logical device select */
     85  1.1  martin #define	GD_MULTIFUN		0x001c	/* multi function selection */
     86  1.1  martin #define		GD_MULTIFUN_GPIO1	0x04	/* clr: gpio1 available */
     87  1.1  martin #define		GD_MULTIFUN_GPIO0	0x08	/* clr: gpio0 available */
     88  1.1  martin #define		GD_MULTIFUN_GPIO67	0x10	/* set: gpio67 available */
     89  1.1  martin #define	GD_GLOBOPT		0x0027	/* global option */
     90  1.1  martin #define 	GD_GLOBOPT_GPIO67	0x04	/* clr: gpio67 available */
     91  1.1  martin #define	GD_ID_HIGH		0x0020	/* ID high byte */
     92  1.1  martin #define	GD_ID_LOW		0x0021	/* ID low byte */
     93  1.1  martin 
     94  1.1  martin /* Logical device 7 */
     95  1.1  martin #define	LD7_ENABLE		0x0730	/* GPIO function enable */
     96  1.1  martin #define		LD7_ENABLE_GPIO0	0x01
     97  1.1  martin #define		LD7_ENABLE_GPIO1	0x02
     98  1.1  martin #define		LD7_ENABLE_GPIO67	0x40
     99  1.1  martin #define	LD7_GPIO0_DIRECTION	0x07e0	/* clr for output, set for input */
    100  1.1  martin #define	LD7_GPIO0_DATA		0x07e1	/* current status */
    101  1.1  martin #define	LD7_GPIO0_INVERSION	0x07e2	/* set to invert i/o */
    102  1.1  martin #define	LD7_GPIO0_STATUS	0x07e3	/* edge detect, reading clears */
    103  1.1  martin #define	LD7_GPIO1_DIRECTION	0x07e4	/* clr for output, set for input */
    104  1.1  martin #define	LD7_GPIO1_DATA		0x07e5	/* current status */
    105  1.1  martin #define	LD7_GPIO1_INVERSION	0x07e6	/* set to invert i/o */
    106  1.1  martin #define	LD7_GPIO1_STATUS	0x07e7	/* edge detect, reading clears */
    107  1.1  martin #define	LD7_GPIO67_DIRECTION	0x07f8	/* clr for output, set for input */
    108  1.1  martin #define	LD7_GPIO67_DATA		0x07f9	/* current status */
    109  1.1  martin #define	LD7_GPIO67_INVERSION	0x07fa	/* set to invert i/o */
    110  1.1  martin #define	LD7_GPIO67_STATUS	0x07fb	/* edge detect, reading clears */
    111  1.1  martin 
    112  1.1  martin /* Logical device 8 */
    113  1.1  martin #define	LD8_DEVCFG		0x0830	/* WDT/GPIO device config */
    114  1.1  martin #define	LD8_GPIO0_MULTIFUNC	0x08e0	/* clr: gpio, set: pin unusable */
    115  1.1  martin #define	LD8_GPIO1_MULTIFUNC	0x08e1	/* clr: gpio, set: pin unusable */
    116  1.1  martin #define	LD8_GPIO67_MULTIFUNC	0x08e7	/* clr: gpio, set: pin unusable */
    117  1.1  martin 
    118  1.1  martin /* Logical device 10 */
    119  1.1  martin #define	LDA_UARTC_ENABLE	0x0a30	/* bit 0: UARTC active */
    120  1.1  martin 
    121  1.1  martin /* Logical device 11 */
    122  1.1  martin #define	LDB_UARTD_ENABLE	0x0b30	/* bit 0: UARTD active */
    123  1.1  martin 
    124  1.1  martin /* Logical device 15 */
    125  1.1  martin #define	LDF_GPIO0_OUTMODE	0x0fe0	/* clr: push/pull, set: open drain */
    126  1.1  martin #define	LDF_GPIO1_OUTMODE	0x0fe1	/* clr: push/pull, set: open drain */
    127  1.1  martin #define	LDF_GPIO67_OUTMODE	0x0fe6	/* clr: push/pull, set: open drain */
    128  1.1  martin 
    129  1.1  martin /*
    130  1.1  martin  * Internal GPIO bank description, including register addresses and cached
    131  1.1  martin  * register content.
    132  1.1  martin  */
    133  1.1  martin struct nct_bank {
    134  1.1  martin 	/* Pin descriptions */
    135  1.1  martin 	u_int8_t	nb_firstpin;
    136  1.1  martin 	u_int8_t	nb_numpins;
    137  1.1  martin 	u_int8_t	nb_enabled;
    138  1.1  martin 
    139  1.1  martin 	/* Cached values */
    140  1.1  martin 	u_int8_t	nb_val_dir;
    141  1.1  martin 	u_int8_t	nb_val_inv;
    142  1.1  martin 	u_int8_t	nb_val_mode;
    143  1.1  martin 
    144  1.1  martin 	/* Register addresses */
    145  1.1  martin 	u_int16_t	nb_reg_dir;
    146  1.1  martin 	u_int16_t	nb_reg_data;
    147  1.1  martin 	u_int16_t	nb_reg_inv;
    148  1.1  martin 	u_int16_t	nb_reg_stat;
    149  1.1  martin 	u_int16_t	nb_reg_mode;
    150  1.1  martin };
    151  1.1  martin 
    152  1.1  martin /*
    153  1.1  martin  * Driver instance.
    154  1.1  martin  */
    155  1.1  martin struct nct_softc {
    156  1.1  martin 	device_t		sc_dev;			/* MI device */
    157  1.1  martin 	bus_space_tag_t		sc_iot;			/* I/O tag */
    158  1.1  martin 	bus_space_handle_t	sc_ioh;			/* I/O handle */
    159  1.1  martin 	struct gpio_chipset_tag	 sc_gc;			/* GPIO tag */
    160  1.1  martin 	gpio_pin_t		sc_pins[NCT_NUM_PINS];	/* GPIO pin descr. */
    161  1.1  martin 
    162  1.1  martin 	/* Access to the remaining members is covered by sc_lock. */
    163  1.1  martin 	kmutex_t		sc_lock;		/* Serialization */
    164  1.1  martin 	int			sc_curdev;		/* Cur. logical dev */
    165  1.1  martin 	int			sc_curreg;		/* Cur. register */
    166  1.1  martin 	struct nct_bank		sc_bank[3];		/* Bank descriptions */
    167  1.1  martin };
    168  1.1  martin 
    169  1.1  martin static void	nct_attach(device_t, device_t, void *);
    170  1.1  martin static int	nct_detach(device_t, int);
    171  1.1  martin static void	nct_gpio_ctl(void *, int, int);
    172  1.1  martin static int	nct_gpio_read(void *, int);
    173  1.1  martin static void	nct_gpio_write(void *, int, int);
    174  1.1  martin static int	nct_match(device_t, cfdata_t , void *);
    175  1.1  martin static u_int8_t	nct_rd(struct nct_softc *, int);
    176  1.1  martin static struct	nct_bank *nct_sel(struct nct_softc *, int, u_int8_t *);
    177  1.1  martin static void	nct_wr(struct nct_softc *, int, u_int8_t);
    178  1.1  martin 
    179  1.1  martin static inline void
    180  1.1  martin nct_outb(struct nct_softc *sc, int reg, u_int8_t data)
    181  1.1  martin {
    182  1.1  martin 
    183  1.1  martin 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, data);
    184  1.1  martin }
    185  1.1  martin 
    186  1.1  martin static inline u_int8_t
    187  1.1  martin nct_inb(struct nct_softc *sc, int reg)
    188  1.1  martin {
    189  1.1  martin 
    190  1.1  martin 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg);
    191  1.1  martin }
    192  1.1  martin 
    193  1.1  martin CFATTACH_DECL_NEW(nct,
    194  1.1  martin 		  sizeof(struct nct_softc),
    195  1.1  martin 		  nct_match,
    196  1.1  martin 		  nct_attach,
    197  1.1  martin 		  nct_detach,
    198  1.1  martin 		  NULL);
    199  1.1  martin 
    200  1.1  martin MODULE(MODULE_CLASS_DRIVER, nct, "gpio");
    201  1.1  martin 
    202  1.1  martin /*
    203  1.1  martin  * Module linkage.
    204  1.1  martin  */
    205  1.1  martin #ifdef _MODULE
    206  1.1  martin #include "ioconf.c"
    207  1.1  martin #endif
    208  1.1  martin 
    209  1.1  martin static int
    210  1.1  martin nct_modcmd(modcmd_t cmd, void *priv)
    211  1.1  martin {
    212  1.1  martin 	int error = 0;
    213  1.1  martin 
    214  1.1  martin 	switch (cmd) {
    215  1.1  martin 	case MODULE_CMD_INIT:
    216  1.1  martin #ifdef _MODULE
    217  1.1  martin 		error = config_init_component(cfdriver_ioconf_nct,
    218  1.1  martin 		    cfattach_ioconf_nct, cfdata_ioconf_nct);
    219  1.1  martin #endif
    220  1.1  martin 		return error;
    221  1.1  martin 	case MODULE_CMD_FINI:
    222  1.1  martin #ifdef _MODULE
    223  1.1  martin 		error = config_fini_component(cfdriver_ioconf_nct,
    224  1.1  martin 		    cfattach_ioconf_nct, cfdata_ioconf_nct);
    225  1.1  martin #endif
    226  1.1  martin 		return error;
    227  1.1  martin 	default:
    228  1.1  martin 		return ENOTTY;
    229  1.1  martin 	}
    230  1.1  martin }
    231  1.1  martin 
    232  1.1  martin /*
    233  1.1  martin  * Probe for device.
    234  1.1  martin  */
    235  1.1  martin static int
    236  1.1  martin nct_match(device_t parent, cfdata_t match, void *aux)
    237  1.1  martin {
    238  1.1  martin 	int ioaddrs[2] = { 0x2e, 0x4e };
    239  1.1  martin 	struct isa_attach_args *ia = aux;
    240  1.1  martin 	bus_space_handle_t ioh;
    241  1.1  martin 	int nioaddr, i;
    242  1.1  martin 	u_int8_t low, high;
    243  1.1  martin 	u_int16_t id;
    244  1.1  martin 
    245  1.1  martin 	/*
    246  1.1  martin 	 * Allow override of I/O base address.  If no I/O base address is
    247  1.1  martin 	 * provided, proceed to probe if running on a PC Engines APU.
    248  1.1  martin 	 */
    249  1.1  martin 	if (ia->ia_nio > 0 && ia->ia_io[0].ir_addr != ISA_UNKNOWN_PORT) {
    250  1.1  martin 		ioaddrs[0] = ia->ia_io[0].ir_addr;
    251  1.1  martin 		nioaddr = 1;
    252  1.1  martin 	} else if ((strcmp(pmf_get_platform("system-vendor"), "PC Engines") |
    253  1.1  martin 	            strcmp(pmf_get_platform("system-product"), "APU")) == 0) {
    254  1.1  martin 		nioaddr = __arraycount(ioaddrs);
    255  1.1  martin 	} else {
    256  1.1  martin 		nioaddr = 0;
    257  1.1  martin 	}
    258  1.1  martin 
    259  1.1  martin 	/*
    260  1.1  martin 	 * Probe at the selected addresses, if any.
    261  1.1  martin 	 */
    262  1.1  martin 	for (i = 0; i < nioaddr; i++) {
    263  1.1  martin 		if (bus_space_map(ia->ia_iot, ioaddrs[i], NCT_IOSIZE, 0,
    264  1.1  martin 		    &ioh) != 0) {
    265  1.1  martin 		    continue;
    266  1.1  martin 		}
    267  1.1  martin                 /* Unlock chip */
    268  1.1  martin 		bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT,
    269  1.1  martin 		    NCT_KEY_UNLOCK);
    270  1.1  martin 		bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT,
    271  1.1  martin 		    NCT_KEY_UNLOCK);
    272  1.1  martin 		/* Read ID */
    273  1.1  martin 		bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, GD_ID_LOW);
    274  1.1  martin 		low = bus_space_read_1(ia->ia_iot, ioh, NCT_PORT_DATA);
    275  1.1  martin 		bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, GD_ID_HIGH);
    276  1.1  martin 		high = bus_space_read_1(ia->ia_iot, ioh, NCT_PORT_DATA);
    277  1.1  martin 		id = (u_int16_t)low | ((u_int16_t)high << 8);
    278  1.1  martin 		bus_space_unmap(ia->ia_iot, ioh, NCT_IOSIZE);
    279  1.1  martin 		if (id == NCT_CHIP_ID_1 || id == NCT_CHIP_ID_2) {
    280  1.1  martin 			ia->ia_nirq = 0;
    281  1.1  martin 			ia->ia_ndrq = 0;
    282  1.1  martin 			ia->ia_niomem = 0;
    283  1.1  martin 			ia->ia_nio = 1;
    284  1.1  martin 			ia->ia_io[0].ir_size = NCT_IOSIZE;
    285  1.1  martin 			ia->ia_io[0].ir_addr = ioaddrs[i];
    286  1.1  martin 			return 1;
    287  1.1  martin 		}
    288  1.1  martin 	}
    289  1.1  martin 	return 0;
    290  1.1  martin }
    291  1.1  martin 
    292  1.1  martin /*
    293  1.1  martin  * Attach device instance.
    294  1.1  martin  */
    295  1.1  martin static void
    296  1.1  martin nct_attach(device_t parent, device_t self, void *aux)
    297  1.1  martin {
    298  1.1  martin 	struct nct_softc *sc = device_private(self);
    299  1.1  martin 	struct isa_attach_args *ia = aux;
    300  1.1  martin 	struct gpiobus_attach_args gba;
    301  1.1  martin 	struct nct_bank *nb;
    302  1.1  martin 	u_int8_t multifun, enable;
    303  1.1  martin 	bool apu;
    304  1.1  martin 	int i, j;
    305  1.1  martin 
    306  1.1  martin 	/*
    307  1.1  martin 	 * Set up register space and basics of our state.
    308  1.1  martin 	 */
    309  1.1  martin 	if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr,
    310  1.1  martin 	    ia->ia_io[0].ir_size, 0, &sc->sc_ioh) != 0) {
    311  1.1  martin 		aprint_normal(": can't map i/o space\n");
    312  1.1  martin 		return;
    313  1.1  martin 	}
    314  1.1  martin 	aprint_normal(": Nuvoton NCT5104D GPIO\n");
    315  1.1  martin 	sc->sc_dev = self;
    316  1.1  martin 	sc->sc_iot = ia->ia_iot;
    317  1.1  martin 	sc->sc_curdev = -1;
    318  1.1  martin 	sc->sc_curreg = -1;
    319  1.1  martin 	apu = ((strcmp(pmf_get_platform("system-vendor"), "PC Engines") |
    320  1.1  martin 	    strcmp(pmf_get_platform("system-product"), "APU")) == 0);
    321  1.1  martin 
    322  1.1  martin         /*
    323  1.1  martin          * All pin access is funneled through a common, indirect register
    324  1.1  martin          * interface.  The gpio framework doesn't serialize calls to our
    325  1.1  martin          * access methods, so do it internally.  This is likely such a
    326  1.1  martin          * common requirement that it should be factored out as is done for
    327  1.1  martin          * audio devices, allowing the driver to specify the appropriate
    328  1.1  martin          * locks.  Anyhow, acquire the lock immediately to pacify locking
    329  1.1  martin          * assertions.
    330  1.1  martin          */
    331  1.1  martin         mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
    332  1.1  martin 	mutex_spin_enter(&sc->sc_lock);
    333  1.1  martin 
    334  1.1  martin         /*
    335  1.1  martin          * Disable watchdog timer and GPIO alternate I/O mapping.
    336  1.1  martin          */
    337  1.1  martin         nct_wr(sc, LD8_DEVCFG, 0);
    338  1.1  martin 
    339  1.1  martin 	/*
    340  1.1  martin 	 * Fill out descriptions of GPIO0, GPIO1 and GPIO67.
    341  1.1  martin 	 * Determine which banks and pins are enabled.
    342  1.1  martin 	 */
    343  1.1  martin 	multifun = nct_rd(sc, GD_MULTIFUN);
    344  1.1  martin 	enable = nct_rd(sc, LD7_ENABLE);
    345  1.1  martin 
    346  1.1  martin 	nb = &sc->sc_bank[0];
    347  1.1  martin 	nb->nb_firstpin = 0;
    348  1.1  martin 	nb->nb_numpins = 8;
    349  1.1  martin 	nb->nb_reg_dir = LD7_GPIO0_DIRECTION;
    350  1.1  martin 	nb->nb_reg_data = LD7_GPIO0_DATA;
    351  1.1  martin 	nb->nb_reg_inv = LD7_GPIO0_INVERSION;
    352  1.1  martin 	nb->nb_reg_stat = LD7_GPIO0_STATUS;
    353  1.1  martin 	nb->nb_reg_mode = LDF_GPIO0_OUTMODE;
    354  1.1  martin 	if ((multifun & GD_MULTIFUN_GPIO0) == 0 &&
    355  1.1  martin 	    ((nct_rd(sc, LDA_UARTC_ENABLE) & 1) == 0 || apu)) {
    356  1.1  martin 		nct_wr(sc, LD8_GPIO0_MULTIFUNC, 0);
    357  1.1  martin 		nb->nb_enabled = 0xff;
    358  1.1  martin 		enable |= LD7_ENABLE_GPIO0;
    359  1.1  martin 	} else {
    360  1.1  martin 		sc->sc_bank[0].nb_enabled = 0;
    361  1.1  martin 	}
    362  1.1  martin 
    363  1.1  martin 	nb = &sc->sc_bank[1];
    364  1.1  martin 	nb->nb_firstpin = 8;
    365  1.1  martin 	nb->nb_numpins = 8;
    366  1.1  martin 	nb->nb_reg_dir = LD7_GPIO1_DIRECTION;
    367  1.1  martin 	nb->nb_reg_data = LD7_GPIO1_DATA;
    368  1.1  martin 	nb->nb_reg_inv = LD7_GPIO1_INVERSION;
    369  1.1  martin 	nb->nb_reg_stat = LD7_GPIO1_STATUS;
    370  1.1  martin 	nb->nb_reg_mode = LDF_GPIO1_OUTMODE;
    371  1.1  martin 	if ((multifun & GD_MULTIFUN_GPIO1) == 0 &&
    372  1.1  martin 	    (nct_rd(sc, LDB_UARTD_ENABLE) & 1) == 0) {
    373  1.1  martin 		nct_wr(sc, LD8_GPIO1_MULTIFUNC, 0);
    374  1.1  martin 		nb->nb_enabled = 0xff;
    375  1.1  martin 		enable |= LD7_ENABLE_GPIO1;
    376  1.1  martin 	} else {
    377  1.1  martin 		sc->sc_bank[1].nb_enabled = 0;
    378  1.1  martin 	}
    379  1.1  martin 
    380  1.1  martin 	nb = &sc->sc_bank[2];
    381  1.1  martin 	nb->nb_firstpin = 16;
    382  1.1  martin 	nb->nb_numpins = 1;
    383  1.1  martin 	nb->nb_reg_dir = LD7_GPIO67_DIRECTION;
    384  1.1  martin 	nb->nb_reg_data = LD7_GPIO67_DATA;
    385  1.1  martin 	nb->nb_reg_stat = LD7_GPIO67_STATUS;
    386  1.1  martin 	nb->nb_reg_mode = LDF_GPIO67_OUTMODE;
    387  1.1  martin 	if ((multifun & GD_MULTIFUN_GPIO67) != 0 &&
    388  1.1  martin 	    (nct_rd(sc, GD_GLOBOPT) & GD_GLOBOPT_GPIO67) == 0) {
    389  1.1  martin 		nct_wr(sc, LD8_GPIO67_MULTIFUNC, 0);
    390  1.1  martin 		nb->nb_enabled = 0x01;
    391  1.1  martin 		enable |= LD7_ENABLE_GPIO67;
    392  1.1  martin 	} else {
    393  1.1  martin 		sc->sc_bank[2].nb_enabled = 0;
    394  1.1  martin 	}
    395  1.1  martin 
    396  1.1  martin 	/*
    397  1.1  martin 	 * Display enabled pins and enable GPIO devices accordingly.
    398  1.1  martin 	 */
    399  1.1  martin 	nct_wr(sc, LD7_ENABLE, enable);
    400  1.1  martin 	mutex_spin_exit(&sc->sc_lock);
    401  1.1  martin 
    402  1.1  martin 	aprint_normal_dev(self,
    403  1.1  martin 	    "enabled pins: GPIO0(%02x) GPIO1(%02x) GPIO67(%01x)\n",
    404  1.1  martin 	    (unsigned)sc->sc_bank[0].nb_enabled,
    405  1.1  martin 	    (unsigned)sc->sc_bank[1].nb_enabled,
    406  1.1  martin 	    (unsigned)sc->sc_bank[2].nb_enabled);
    407  1.1  martin 
    408  1.1  martin 	/*
    409  1.1  martin 	 * Fill pin descriptions and initialize registers.
    410  1.1  martin 	 */
    411  1.1  martin 	memset(sc->sc_pins, 0, sizeof(sc->sc_pins));
    412  1.1  martin 	for (i = 0; i < __arraycount(sc->sc_bank); i++) {
    413  1.1  martin 		nb = &sc->sc_bank[i];
    414  1.1  martin 		mutex_spin_enter(&sc->sc_lock);
    415  1.1  martin 		nb->nb_val_dir = nct_rd(sc, nb->nb_reg_dir);
    416  1.1  martin 		nb->nb_val_inv = nct_rd(sc, nb->nb_reg_inv);
    417  1.1  martin 		nb->nb_val_mode = nct_rd(sc, nb->nb_reg_mode);
    418  1.1  martin 		mutex_spin_exit(&sc->sc_lock);
    419  1.1  martin 		for (j = 0; j < nb->nb_numpins; j++) {
    420  1.1  martin 			gpio_pin_t *pin = &sc->sc_pins[nb->nb_firstpin + j];
    421  1.1  martin 			pin->pin_num = nb->nb_firstpin + j;
    422  1.1  martin 			/* Skip pin if not configured as GPIO. */
    423  1.1  martin 			if ((nb->nb_enabled & (1 << j)) == 0) {
    424  1.1  martin 				continue;
    425  1.1  martin 			}
    426  1.1  martin 			pin->pin_caps =
    427  1.1  martin 			    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
    428  1.1  martin 			    GPIO_PIN_OPENDRAIN |
    429  1.1  martin 			    GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
    430  1.1  martin 			    GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
    431  1.1  martin 			pin->pin_flags =
    432  1.1  martin 			    GPIO_PIN_INPUT | GPIO_PIN_OPENDRAIN;
    433  1.1  martin 			nct_gpio_ctl(sc, pin->pin_num, pin->pin_flags);
    434  1.1  martin 			pin->pin_state = nct_gpio_read(sc, pin->pin_num);
    435  1.1  martin 		}
    436  1.1  martin 	}
    437  1.1  martin 
    438  1.1  martin 	/*
    439  1.1  martin 	 * Attach to gpio framework, and attach all pins unconditionally.
    440  1.1  martin 	 * If the pins are disabled, we'll ignore any access later.
    441  1.1  martin 	 */
    442  1.1  martin 	sc->sc_gc.gp_cookie = sc;
    443  1.1  martin 	sc->sc_gc.gp_pin_read = nct_gpio_read;
    444  1.1  martin 	sc->sc_gc.gp_pin_write = nct_gpio_write;
    445  1.1  martin 	sc->sc_gc.gp_pin_ctl = nct_gpio_ctl;
    446  1.1  martin 
    447  1.1  martin 	gba.gba_gc = &sc->sc_gc;
    448  1.1  martin 	gba.gba_pins = sc->sc_pins;
    449  1.1  martin 	gba.gba_npins = NCT_NUM_PINS;
    450  1.1  martin 
    451  1.1  martin 	(void)config_found(sc->sc_dev, &gba, gpiobus_print);
    452  1.1  martin }
    453  1.1  martin 
    454  1.1  martin /*
    455  1.1  martin  * Detach device instance.
    456  1.1  martin  */
    457  1.1  martin static int
    458  1.1  martin nct_detach(device_t self, int flags)
    459  1.1  martin {
    460  1.1  martin 	struct nct_softc *sc = device_private(self);
    461  1.1  martin 
    462  1.1  martin 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, NCT_IOSIZE);
    463  1.1  martin 	mutex_destroy(&sc->sc_lock);
    464  1.1  martin 	return 0;
    465  1.1  martin }
    466  1.1  martin 
    467  1.1  martin /*
    468  1.1  martin  * Read byte from specified register.
    469  1.1  martin  */
    470  1.1  martin static u_int8_t
    471  1.1  martin nct_rd(struct nct_softc *sc, int reg)
    472  1.1  martin {
    473  1.1  martin 	int dev;
    474  1.1  martin 
    475  1.1  martin 	KASSERT(mutex_owned(&sc->sc_lock));
    476  1.1  martin 
    477  1.1  martin 	dev = reg >> 8;
    478  1.1  martin 	reg &= 0xff;
    479  1.1  martin 
    480  1.1  martin 	if (dev != sc->sc_curdev && dev != 0x00) {
    481  1.1  martin 		sc->sc_curdev = dev;
    482  1.1  martin 		sc->sc_curreg = reg;
    483  1.1  martin 		nct_outb(sc, NCT_PORT_SELECT, GD_DEVSEL);
    484  1.1  martin 		nct_outb(sc, NCT_PORT_DATA, dev);
    485  1.1  martin 		nct_outb(sc, NCT_PORT_SELECT, reg);
    486  1.1  martin 		return nct_inb(sc, NCT_PORT_DATA);
    487  1.1  martin 	} else if (reg != sc->sc_curreg) {
    488  1.1  martin 		sc->sc_curreg = reg;
    489  1.1  martin 		nct_outb(sc, NCT_PORT_SELECT, reg);
    490  1.1  martin 		return nct_inb(sc, NCT_PORT_DATA);
    491  1.1  martin 	} else {
    492  1.1  martin 		return nct_inb(sc, NCT_PORT_DATA);
    493  1.1  martin 	}
    494  1.1  martin }
    495  1.1  martin 
    496  1.1  martin /*
    497  1.1  martin  * Write byte to specified register.
    498  1.1  martin  */
    499  1.1  martin static void
    500  1.1  martin nct_wr(struct nct_softc *sc, int reg, u_int8_t data)
    501  1.1  martin {
    502  1.1  martin 	int dev;
    503  1.1  martin 
    504  1.1  martin 	KASSERT(mutex_owned(&sc->sc_lock));
    505  1.1  martin 
    506  1.1  martin 	dev = reg >> 8;
    507  1.1  martin 	reg &= 0xff;
    508  1.1  martin 
    509  1.1  martin 	if (dev != sc->sc_curdev && dev != 0x00) {
    510  1.1  martin 		sc->sc_curdev = dev;
    511  1.1  martin 		sc->sc_curreg = reg;
    512  1.1  martin 		nct_outb(sc, NCT_PORT_SELECT, GD_DEVSEL);
    513  1.1  martin 		nct_outb(sc, NCT_PORT_DATA, dev);
    514  1.1  martin 		nct_outb(sc, NCT_PORT_SELECT, reg);
    515  1.1  martin 		nct_outb(sc, NCT_PORT_DATA, data);
    516  1.1  martin 	} else if (reg != sc->sc_curreg) {
    517  1.1  martin 		sc->sc_curreg = reg;
    518  1.1  martin 		nct_outb(sc, NCT_PORT_SELECT, reg);
    519  1.1  martin 		nct_outb(sc, NCT_PORT_DATA, data);
    520  1.1  martin 	} else {
    521  1.1  martin 		nct_outb(sc, NCT_PORT_DATA, data);
    522  1.1  martin 	}
    523  1.1  martin }
    524  1.1  martin 
    525  1.1  martin /*
    526  1.1  martin  * Given pin number, return bank and pin mask.  This alters no state and so
    527  1.1  martin  * can safely be called without the mutex held.
    528  1.1  martin  */
    529  1.1  martin static struct nct_bank *
    530  1.1  martin nct_sel(struct nct_softc *sc, int pin, u_int8_t *mask)
    531  1.1  martin {
    532  1.1  martin 	struct nct_bank *nb;
    533  1.1  martin 
    534  1.1  martin 	KASSERT(pin >= 0 && pin < NCT_NUM_PINS);
    535  1.1  martin 	nb = &sc->sc_bank[pin >> 3];
    536  1.1  martin 	KASSERT(pin >= nb->nb_firstpin);
    537  1.1  martin 	KASSERT((pin & 7) < nb->nb_numpins);
    538  1.1  martin 	*mask = (u_int8_t)(1 << (pin & 7)) & nb->nb_enabled;
    539  1.1  martin 	return nb;
    540  1.1  martin }
    541  1.1  martin 
    542  1.1  martin /*
    543  1.1  martin  * GPIO hook: read pin.
    544  1.1  martin  */
    545  1.1  martin static int
    546  1.1  martin nct_gpio_read(void *arg, int pin)
    547  1.1  martin {
    548  1.1  martin 	struct nct_softc *sc = arg;
    549  1.1  martin 	struct nct_bank *nb;
    550  1.1  martin 	u_int8_t data, mask;
    551  1.1  martin 	int rv = GPIO_PIN_LOW;
    552  1.1  martin 
    553  1.1  martin 	nb = nct_sel(sc, pin, &mask);
    554  1.1  martin 	if (__predict_true(mask != 0)) {
    555  1.1  martin 		mutex_spin_enter(&sc->sc_lock);
    556  1.1  martin 		data = nct_rd(sc, nb->nb_reg_data);
    557  1.1  martin 		if ((data & mask) != 0) {
    558  1.1  martin 			rv = GPIO_PIN_HIGH;
    559  1.1  martin 		}
    560  1.1  martin 		mutex_spin_exit(&sc->sc_lock);
    561  1.1  martin 	}
    562  1.1  martin 	return rv;
    563  1.1  martin }
    564  1.1  martin 
    565  1.1  martin /*
    566  1.1  martin  * GPIO hook: write pin.
    567  1.1  martin  */
    568  1.1  martin static void
    569  1.1  martin nct_gpio_write(void *arg, int pin, int val)
    570  1.1  martin {
    571  1.1  martin 	struct nct_softc *sc = arg;
    572  1.1  martin 	struct nct_bank *nb;
    573  1.1  martin 	u_int8_t data, mask;
    574  1.1  martin 
    575  1.1  martin 	nb = nct_sel(sc, pin, &mask);
    576  1.1  martin 	if (__predict_true(mask != 0)) {
    577  1.1  martin 		mutex_spin_enter(&sc->sc_lock);
    578  1.1  martin 		data = nct_rd(sc, nb->nb_reg_data);
    579  1.1  martin 		if (val == GPIO_PIN_LOW) {
    580  1.1  martin 			data &= ~mask;
    581  1.1  martin 		} else if (val == GPIO_PIN_HIGH) {
    582  1.1  martin 			data |= mask;
    583  1.1  martin 		}
    584  1.1  martin 		nct_wr(sc, nb->nb_reg_data, data);
    585  1.1  martin 		mutex_spin_exit(&sc->sc_lock);
    586  1.1  martin 	}
    587  1.1  martin }
    588  1.1  martin 
    589  1.1  martin /*
    590  1.1  martin  * GPIO hook: change pin parameters.
    591  1.1  martin  */
    592  1.1  martin static void
    593  1.1  martin nct_gpio_ctl(void *arg, int pin, int flg)
    594  1.1  martin {
    595  1.1  martin 	struct nct_softc *sc = arg;
    596  1.1  martin 	struct nct_bank *nb;
    597  1.1  martin 	u_int8_t data, mask;
    598  1.1  martin 
    599  1.1  martin 	nb = nct_sel(sc, pin, &mask);
    600  1.1  martin 	if (__predict_false(mask == 0)) {
    601  1.1  martin 		return;
    602  1.1  martin 	}
    603  1.1  martin 
    604  1.1  martin 	/*
    605  1.1  martin 	 * Set input direction early to avoid perturbation.
    606  1.1  martin 	 */
    607  1.1  martin 	mutex_spin_enter(&sc->sc_lock);
    608  1.1  martin 	data = nb->nb_val_dir;
    609  1.1  martin 	if ((flg & (GPIO_PIN_INPUT | GPIO_PIN_TRISTATE)) != 0) {
    610  1.1  martin 		data |= mask;
    611  1.1  martin 	}
    612  1.1  martin 	if (data != nb->nb_val_dir) {
    613  1.1  martin 		nct_wr(sc, nb->nb_reg_dir, data);
    614  1.1  martin 		nb->nb_val_dir = data;
    615  1.1  martin 	}
    616  1.1  martin 
    617  1.1  martin 	/*
    618  1.1  martin 	 * Set inversion.
    619  1.1  martin 	 */
    620  1.1  martin 	data = nb->nb_val_inv;
    621  1.1  martin 	if ((flg & (GPIO_PIN_OUTPUT | GPIO_PIN_INVOUT)) ==
    622  1.1  martin 	             (GPIO_PIN_OUTPUT | GPIO_PIN_INVOUT) ||
    623  1.1  martin             (flg & (GPIO_PIN_INPUT | GPIO_PIN_INVIN)) ==
    624  1.1  martin   	             (GPIO_PIN_INPUT | GPIO_PIN_INVIN)) {
    625  1.1  martin 		data |= mask;
    626  1.1  martin 	} else {
    627  1.1  martin 		data &= ~mask;
    628  1.1  martin 	}
    629  1.1  martin 	if (data != nb->nb_val_inv) {
    630  1.1  martin 		nct_wr(sc, nb->nb_reg_inv, data);
    631  1.1  martin 		nb->nb_val_inv = data;
    632  1.1  martin 	}
    633  1.1  martin 
    634  1.1  martin 	/*
    635  1.1  martin 	 * Set drain mode.
    636  1.1  martin 	 */
    637  1.1  martin 	data = nb->nb_val_mode;
    638  1.1  martin 	if ((flg & GPIO_PIN_PUSHPULL) != 0) {
    639  1.1  martin 		data |= mask;
    640  1.1  martin 	} else /* GPIO_PIN_OPENDRAIN */ {
    641  1.1  martin 		data &= ~mask;
    642  1.1  martin 	}
    643  1.1  martin 	if (data != nb->nb_val_mode) {
    644  1.1  martin 		nct_wr(sc, nb->nb_reg_mode, data);
    645  1.1  martin 		nb->nb_val_mode = data;
    646  1.1  martin 	}
    647  1.1  martin 
    648  1.1  martin 	/*
    649  1.1  martin 	 * Set output direction late to avoid perturbation.
    650  1.1  martin 	 */
    651  1.1  martin 	data = nb->nb_val_dir;
    652  1.1  martin 	if ((flg & (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE)) == GPIO_PIN_OUTPUT) {
    653  1.1  martin 		data &= ~mask;
    654  1.1  martin 	}
    655  1.1  martin 	if (data != nb->nb_val_dir) {
    656  1.1  martin 		nct_wr(sc, nb->nb_reg_dir, data);
    657  1.1  martin 		nb->nb_val_dir = data;
    658  1.1  martin 	}
    659  1.1  martin 	mutex_spin_exit(&sc->sc_lock);
    660  1.1  martin }
    661