From nobody Sat Jul 1 17:20:46 2023 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Qtf8t4xYYz4l8TS; Sat, 1 Jul 2023 17:20:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Qtf8t4RxGz3pQN; Sat, 1 Jul 2023 17:20:46 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1688232046; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=3sAM+SpRv5bmiWRU0s+Gv1+UKC0L9LIcSZTT345gWDU=; b=wuHAgU8Z96c5dl4r76mnZNV/wroF4ljBMpsgXcs/hM5PzPWhV9PiV/jKWhlMk/tsy2geeB AAsFoIccQm74q5yKr3Y3VJYGUGRrwyl6vFJ5L8NQN/ydpOAO1/03Dn37Ojq0twEoncANcv sY5nDsNeb3H1Kffz7XjQ3DSrQEL/9eXy1T6wrQDnG4WeddA+T+YcGLw2ami2QhVtMupeMT fgFw25ymq94Tz9duHLFQ2UX6i15C7uD+3wXjIY6Vwg08IWYtVaUDftGChIeHkFEuh00i8Q vAG59Tt8J22kxq+/wLNn/4Y0IaxfPOp2CIpJv6qxbeDvGAsxFUUsjQmCSrwXEg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1688232046; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=3sAM+SpRv5bmiWRU0s+Gv1+UKC0L9LIcSZTT345gWDU=; b=W3sIj1KVPmLmtl1NKIZh4AO4b3bl+dAzrMPXOZ56AabHN4bBwtv9LFlmrU9gu/U/LH4byD BCsM47gfg8PwdyTesZaOqUotndeFQPFlc2UO//z3t+VldXESLCs/oUzOGHE+NRsUA/D+h9 5TA7piSugX9e2agP/+IiRnvF5EpH1vJbITRXSONUylsrx3b4GgVMHxl0NlXE4Hrg7k9B2s eqcU2cZyQVh2OVQdg+77ZFvV2o4rBAfrP18vt6DyilOMCzOHdd2Nz1oeuzLrDikL5xl9XO QWgGqXQqFrwaNeCoAfZD4O473CwJhwPaiCG0+/GKLVyp3hQWVBdCYYrx6VvbSA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1688232046; a=rsa-sha256; cv=none; b=WOFcDPvnfzVaUMSQWxgAH5daVDTuD9PXMzT//PkM0em7ObLPfml6pTZfS+SQvW21xhh+jJ +kLp8g/fbTe0i452buZY7mSTcyHcA2799hf2SE8BwuQklLMl+FrfhNzyVRloIdLxKUHNny p2jelDuGrLEV0mZjbFc9OM6IyGUL0EiASBJs0KR7DUBJcBujlzlVi2X5ZP1WUCpkkIVlPx CGouJeuNlrrVmXl9M5G5BOWUFJ2sSz/lCVc5M9dwntlaGzC8EL8TxoAun9QPBpQNLHXtIw tB0SAnlxy2XGBXWTbm0K7OEns4pq4ENbTfNiCoe8TF7k078mSwu9nPToFCFajQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Qtf8t3K1rzYwd; Sat, 1 Jul 2023 17:20:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 361HKkoK066351; Sat, 1 Jul 2023 17:20:46 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 361HKkA7066350; Sat, 1 Jul 2023 17:20:46 GMT (envelope-from git) Date: Sat, 1 Jul 2023 17:20:46 GMT Message-Id: <202307011720.361HKkA7066350@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Warner Losh Subject: git: 7f8d2ed03bc6 - main - superio+nctgpio: Prepare to support some new (weird) chips List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: imp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 7f8d2ed03bc670393d7a8322b0681f46ead745e7 Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=7f8d2ed03bc670393d7a8322b0681f46ead745e7 commit 7f8d2ed03bc670393d7a8322b0681f46ead745e7 Author: Stéphane Rochoy AuthorDate: 2023-07-01 17:19:44 +0000 Commit: Warner Losh CommitDate: 2023-07-01 17:19:51 +0000 superio+nctgpio: Prepare to support some new (weird) chips - Increase verbosity to direct i/o code path (iores != 0) - Fix pin inversion configuration - Allow forcing the use of indirect access channel via hint.gpio.0.flags=2 - Document the PREFER_INDIRECT_CHANNEL tunable in nctgpio(4) Reviewed by: imp Pull Request: https://github.com/freebsd/freebsd-src/pull/719 --- sys/dev/nctgpio/nctgpio.c | 705 ++++++++++++++++++++++++++++++---------------- 1 file changed, 465 insertions(+), 240 deletions(-) diff --git a/sys/dev/nctgpio/nctgpio.c b/sys/dev/nctgpio/nctgpio.c index 56f37da45bc3..607a5f3e56cf 100644 --- a/sys/dev/nctgpio/nctgpio.c +++ b/sys/dev/nctgpio/nctgpio.c @@ -29,7 +29,6 @@ /* * Nuvoton GPIO driver. - * */ #include @@ -51,39 +50,36 @@ #include "gpio_if.h" -/* Logical Device Numbers. */ -#define NCT_LDN_GPIO 0x07 -#define NCT_LDN_GPIO_MODE 0x0f - -/* Logical Device 7 */ -#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_PPOD_LDN 0xf /* LDN used to select Push-Pull/Open-Drain */ -/* Direct I/O port access. */ -#define NCT_IO_GSR 0 -#define NCT_IO_IOR 1 -#define NCT_IO_DAT 2 -#define NCT_IO_INV 3 +/* Direct access through GPIO register table */ +#define NCT_IO_GSR 0 /* Group Select */ +#define NCT_IO_IOR 1 /* I/O */ +#define NCT_IO_DAT 2 /* Data */ +#define NCT_IO_INV 3 /* Inversion */ +#define NCT_IO_DST 4 /* Status */ -#define NCT_MAX_PIN 15 -#define NCT_IS_VALID_PIN(_p) ((_p) >= 0 && (_p) <= NCT_MAX_PIN) +#define NCT_MAX_GROUP 9 +#define NCT_MAX_PIN 75 -#define NCT_PIN_BIT(_p) (1 << ((_p) & 7)) +#define NCT_PIN_IS_VALID(_sc, _p) ((_p) < (_sc)->npins) +#define NCT_PIN_GROUP(_sc, _p) ((_sc)->pinmap[(_p)].group) +#define NCT_PIN_GRPNUM(_sc, _p) ((_sc)->pinmap[(_p)].grpnum) +#define NCT_PIN_BIT(_sc, _p) ((_sc)->pinmap[(_p)].bit) +#define NCT_PIN_BITMASK(_p) (1 << ((_p) & 7)) #define NCT_GPIO_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ GPIO_PIN_INVIN | GPIO_PIN_INVOUT) +#define NCT_PREFER_INDIRECT_CHANNEL 2 + +#define NCT_VERBOSE_PRINTF(dev, ...) \ + do { \ + if (__predict_false(bootverbose)) \ + device_printf(dev, __VA_ARGS__); \ + } while (0) + /* * Note that the values are important. * They match actual register offsets. @@ -94,25 +90,43 @@ typedef enum { REG_INV = 2, } reg_t; +struct nct_gpio_group { + uint32_t caps; + uint8_t enable_ldn; + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t data_ldn; + uint8_t iobase; + uint8_t ppod_reg; /* Push-Pull/Open-Drain */ + uint8_t grpnum; + uint8_t pinbits[8]; + uint8_t npins; +}; + struct nct_softc { device_t dev; - device_t dev_f; device_t busdev; struct mtx mtx; struct resource *iores; int iorid; int curgrp; struct { - /* direction, 1: pin is input */ - uint8_t ior[2]; - /* output value */ - uint8_t out[2]; - /* whether out is valid */ - uint8_t out_known[2]; - /* inversion, 1: pin is inverted */ - uint8_t inv[2]; - } cache; - struct gpio_pin pins[NCT_MAX_PIN + 1]; + uint8_t ior[NCT_MAX_GROUP + 1]; /* direction, 1: input 0: output */ + uint8_t out[NCT_MAX_GROUP + 1]; /* output value */ + uint8_t out_known[NCT_MAX_GROUP + 1]; /* whether out is valid */ + uint8_t inv[NCT_MAX_GROUP + 1]; /* inversion, 1: inverted */ + } cache; + struct gpio_pin pins[NCT_MAX_PIN + 1]; + struct nct_device *nctdevp; + int npins; /* Total number of pins */ + + /* Lookup tables */ + struct { + struct nct_gpio_group *group; + uint8_t grpnum; + uint8_t bit; + } pinmap[NCT_MAX_PIN+1]; + struct nct_gpio_group *grpmap[NCT_MAX_GROUP+1]; }; #define GPIO_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ @@ -123,93 +137,204 @@ struct nct_softc { #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) #define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED) -struct nuvoton_vendor_device_id { - uint16_t chip_id; - const char * descr; -} nct_devs[] = { +#define GET_BIT(v, b) (((v) >> (b)) & 1) + +/* + * For most devices there are several GPIO devices, we attach only to one of + * them and use the rest without attaching. + */ +struct nct_device { + uint16_t devid; + int extid; + const char *descr; + int ngroups; + struct nct_gpio_group groups[NCT_MAX_GROUP + 1]; +} nct_devices[] = { { - .chip_id = 0x1061, - .descr = "Nuvoton NCT5104D", + .devid = 0x1061, + .descr = "GPIO on Nuvoton NCT5104D", + .ngroups = 2, + .groups = { + { + .grpnum = 0, + .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .enable_ldn = 0x07, + .enable_reg = 0x30, + .enable_mask = 0x01, + .data_ldn = 0x07, + .ppod_reg = 0xe0, + .caps = NCT_GPIO_CAPS, + .npins = 8, + .iobase = 0xe0, + }, + { + .grpnum = 1, + .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .enable_ldn = 0x07, + .enable_reg = 0x30, + .enable_mask = 0x02, + .data_ldn = 0x07, + .ppod_reg = 0xe1, + .caps = NCT_GPIO_CAPS, + .npins = 8, + .iobase = 0xe4, + }, + }, }, { - .chip_id = 0xc452, - .descr = "Nuvoton NCT5104D (PC-Engines APU)", + .devid = 0xc452, + .descr = "GPIO on Nuvoton NCT5104D (PC-Engines APU)", + .ngroups = 2, + .groups = { + { + .grpnum = 0, + .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .enable_ldn = 0x07, + .enable_reg = 0x30, + .enable_mask = 0x01, + .data_ldn = 0x07, + .ppod_reg = 0xe0, + .caps = NCT_GPIO_CAPS, + .npins = 8, + .iobase = 0xe0, + }, + { + .grpnum = 1, + .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .enable_ldn = 0x07, + .enable_reg = 0x30, + .enable_mask = 0x02, + .data_ldn = 0x07, + .ppod_reg = 0xe1, + .caps = NCT_GPIO_CAPS, + .npins = 8, + .iobase = 0xe4, + }, + }, }, { - .chip_id = 0xc453, - .descr = "Nuvoton NCT5104D (PC-Engines APU3)", + .devid = 0xc453, + .descr = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)", + .ngroups = 2, + .groups = { + { + .grpnum = 0, + .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .enable_ldn = 0x07, + .enable_reg = 0x30, + .enable_mask = 0x01, + .data_ldn = 0x07, + .ppod_reg = 0xe0, + .caps = NCT_GPIO_CAPS, + .npins = 8, + .iobase = 0xe0, + }, + { + .grpnum = 1, + .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, + .enable_ldn = 0x07, + .enable_reg = 0x30, + .enable_mask = 0x02, + .data_ldn = 0x07, + .ppod_reg = 0xe1, + .caps = NCT_GPIO_CAPS, + .npins = 8, + .iobase = 0xe4, + }, + }, }, }; -static void -nct_io_set_group(struct nct_softc *sc, int group) +static const char * +io2str(uint8_t ioport) { + switch (ioport) { + case NCT_IO_GSR: return ("grpsel"); + case NCT_IO_IOR: return ("io"); + case NCT_IO_DAT: return ("data"); + case NCT_IO_INV: return ("inv"); + case NCT_IO_DST: return ("status"); + default: return ("?"); + } +} +static void +nct_io_set_group(struct nct_softc *sc, uint8_t grpnum) +{ GPIO_ASSERT_LOCKED(sc); - if (group != sc->curgrp) { - bus_write_1(sc->iores, NCT_IO_GSR, group); - sc->curgrp = group; - } + + if (grpnum == sc->curgrp) + return; + + NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n", + io2str(NCT_IO_GSR), grpnum, NCT_IO_GSR); + bus_write_1(sc->iores, NCT_IO_GSR, grpnum); + sc->curgrp = grpnum; } static uint8_t -nct_io_read(struct nct_softc *sc, int group, uint8_t reg) +nct_io_read(struct nct_softc *sc, uint8_t grpnum, uint8_t reg) { - nct_io_set_group(sc, group); - return (bus_read_1(sc->iores, reg)); + uint8_t val; + + nct_io_set_group(sc, grpnum); + + val = bus_read_1(sc->iores, reg); + NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d\n", + io2str(reg), val, reg); + return (val); } static void -nct_io_write(struct nct_softc *sc, int group, uint8_t reg, uint8_t val) +nct_io_write(struct nct_softc *sc, uint8_t grpnum, uint8_t reg, uint8_t val) { - nct_io_set_group(sc, group); - return (bus_write_1(sc->iores, reg, val)); + nct_io_set_group(sc, grpnum); + + NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n", + io2str(reg), val, reg); + bus_write_1(sc->iores, reg, val); } static uint8_t -nct_get_ioreg(struct nct_softc *sc, reg_t reg, int group) +nct_get_ioreg(struct nct_softc *sc, reg_t reg, uint8_t grpnum) { - uint8_t ioreg; + uint8_t iobase; if (sc->iores != NULL) - ioreg = NCT_IO_IOR + reg; - else if (group == 0) - ioreg = NCT_LD7_GPIO0_IOR + reg; + iobase = NCT_IO_IOR; else - ioreg = NCT_LD7_GPIO1_IOR + reg; - return (ioreg); + iobase = sc->grpmap[grpnum]->iobase; + return (iobase + reg); } -static uint8_t -nct_read_reg(struct nct_softc *sc, reg_t reg, int group) +static const char * +reg2str(reg_t reg) { - uint8_t ioreg; - uint8_t val; - - ioreg = nct_get_ioreg(sc, reg, group); - if (sc->iores != NULL) - val = nct_io_read(sc, group, ioreg); - else - val = superio_read(sc->dev, ioreg); - - return (val); + switch (reg) { + case REG_IOR: return ("io"); + case REG_DAT: return ("data"); + case REG_INV: return ("inv"); + default: return ("?"); + } } -#define GET_BIT(v, b) (((v) >> (b)) & 1) -static bool -nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num) +static uint8_t +nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum) { - uint8_t bit; - uint8_t group; - uint8_t val; + struct nct_gpio_group *gp; + uint8_t ioreg; + uint8_t val; - KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d", - __func__, pin_num)); + ioreg = nct_get_ioreg(sc, reg, grpnum); - group = pin_num >> 3; - bit = pin_num & 7; - val = nct_read_reg(sc, reg, group); - return (GET_BIT(val, bit)); + if (sc->iores != NULL) + return (nct_io_read(sc, grpnum, ioreg)); + + gp = sc->grpmap[grpnum]; + val = superio_ldn_read(sc->dev, gp->data_ldn, ioreg); + NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x\n", + reg2str(reg), val, grpnum, ioreg); + return (val); } static int @@ -219,25 +344,33 @@ nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache) uint8_t group; uint8_t val; - KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d", + KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d", __func__, pin_num)); - group = pin_num >> 3; - bit = pin_num & 7; - val = cache[group]; + group = NCT_PIN_GRPNUM(sc, pin_num); + bit = NCT_PIN_BIT(sc, pin_num); + val = cache[group]; return (GET_BIT(val, bit)); } static void -nct_write_reg(struct nct_softc *sc, reg_t reg, int group, uint8_t val) +nct_write_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum, uint8_t val) { - uint8_t ioreg; + struct nct_gpio_group *gp; + uint8_t ioreg; - ioreg = nct_get_ioreg(sc, reg, group); - if (sc->iores != NULL) - nct_io_write(sc, group, ioreg, val); - else - superio_write(sc->dev, ioreg, val); + ioreg = nct_get_ioreg(sc, reg, grpnum); + + if (sc->iores != NULL) { + nct_io_write(sc, grpnum, ioreg, val); + return; + } + + gp = sc->grpmap[grpnum]; + superio_ldn_write(sc->dev, gp->data_ldn, ioreg, val); + + NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x\n", + reg2str(reg), val, grpnum, ioreg); } static void @@ -249,14 +382,14 @@ nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val) uint8_t group; uint8_t mask; - KASSERT(NCT_IS_VALID_PIN(pin_num), + KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d", __func__, pin_num)); KASSERT(reg == REG_IOR || reg == REG_INV, ("%s: unsupported register %d", __func__, reg)); - group = pin_num >> 3; - bit = pin_num & 7; - mask = (uint8_t)1 << bit; + group = NCT_PIN_GRPNUM(sc, pin_num); + bit = NCT_PIN_BIT(sc, pin_num); + mask = (uint8_t)1 << bit; bitval = (uint8_t)val << bit; if (reg == REG_IOR) @@ -317,8 +450,9 @@ nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val) uint8_t group; KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin")); - group = pin_num >> 3; - bit = pin_num & 7; + group = NCT_PIN_GRPNUM(sc, pin_num); + bit = NCT_PIN_BIT(sc, pin_num); + if (GET_BIT(sc->cache.out_known[group], bit) && GET_BIT(sc->cache.out[group], bit) == val) { /* The pin is already in requested state. */ @@ -332,6 +466,35 @@ nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val) nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]); } +static bool +nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num) +{ + uint8_t bit; + uint8_t group; + uint8_t val; + bool b; + + KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d", + __func__, pin_num)); + + group = NCT_PIN_GRPNUM(sc, pin_num); + bit = NCT_PIN_BIT(sc, pin_num); + val = nct_read_reg(sc, reg, group); + b = GET_BIT(val, bit); + + if (__predict_false(bootverbose)) { + if (nct_pin_is_input(sc, pin_num)) + NCT_VERBOSE_PRINTF(sc->dev, "read %d from input pin %u\n", + b, pin_num, group, bit); + else + NCT_VERBOSE_PRINTF(sc->dev, + "read %d from output pin %u, cache miss\n", + b, pin_num, group, bit); + } + + return (b); +} + /* * NB: state of an input pin cannot be cached, of course. * For an output we can either take the value from the cache if it's valid @@ -342,15 +505,24 @@ nct_read_pin(struct nct_softc *sc, uint32_t pin_num) { uint8_t bit; uint8_t group; - bool val; + bool val; - if (nct_pin_is_input(sc, pin_num)) + if (nct_pin_is_input(sc, pin_num)) { return (nct_get_pin_reg(sc, REG_DAT, pin_num)); + } - group = pin_num >> 3; - bit = pin_num & 7; - if (GET_BIT(sc->cache.out_known[group], bit)) - return (GET_BIT(sc->cache.out[group], bit)); + group = NCT_PIN_GRPNUM(sc, pin_num); + bit = NCT_PIN_BIT(sc, pin_num); + + if (GET_BIT(sc->cache.out_known[group], bit)) { + val = GET_BIT(sc->cache.out[group], bit); + + NCT_VERBOSE_PRINTF(sc->dev, + "read %d from output pin %u, cache hit\n", + val, pin_num, group, bit); + + return (val); + } val = nct_get_pin_reg(sc, REG_DAT, pin_num); sc->cache.out_known[group] |= 1 << bit; @@ -362,14 +534,11 @@ nct_read_pin(struct nct_softc *sc, uint32_t pin_num) } static uint8_t -nct_outcfg_addr(uint32_t pin_num) +nct_ppod_reg(struct nct_softc *sc, uint32_t pin_num) { - KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d", - __func__, pin_num)); - if ((pin_num >> 3) == 0) - return (NCT_LDF_GPIO0_OUTCFG); - else - return (NCT_LDF_GPIO1_OUTCFG); + uint8_t group = NCT_PIN_GRPNUM(sc, pin_num); + + return (sc->grpmap[group]->ppod_reg); } /* @@ -383,10 +552,10 @@ nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num) uint8_t reg; uint8_t outcfg; - reg = nct_outcfg_addr(pin_num); - outcfg = superio_read(sc->dev_f, reg); - outcfg |= NCT_PIN_BIT(pin_num); - superio_write(sc->dev_f, reg, outcfg); + reg = nct_ppod_reg(sc, pin_num); + outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg); + outcfg |= NCT_PIN_BITMASK(pin_num); + superio_ldn_write(sc->dev, 0xf, reg, outcfg); } static void @@ -395,10 +564,10 @@ nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num) uint8_t reg; uint8_t outcfg; - reg = nct_outcfg_addr(pin_num); - outcfg = superio_read(sc->dev_f, reg); - outcfg &= ~NCT_PIN_BIT(pin_num); - superio_write(sc->dev_f, reg, outcfg); + reg = nct_ppod_reg(sc, pin_num); + outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg); + outcfg &= ~NCT_PIN_BITMASK(pin_num); + superio_ldn_write(sc->dev, 0xf, reg, outcfg); } static bool @@ -407,94 +576,156 @@ nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num) uint8_t reg; uint8_t outcfg; - reg = nct_outcfg_addr(pin_num); - outcfg = superio_read(sc->dev_f, reg); - return (outcfg & NCT_PIN_BIT(pin_num)); + reg = nct_ppod_reg(sc, pin_num); + outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg); + return (outcfg & NCT_PIN_BITMASK(pin_num)); +} + +static struct nct_device * +nct_lookup_device(device_t dev) +{ + struct nct_device *nctdevp; + uint16_t devid; + int i, extid; + + devid = superio_devid(dev); + extid = superio_extid(dev); + for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) { + if (devid == nctdevp->devid && nctdevp->extid == extid) + return (nctdevp); + } + return (NULL); } static int nct_probe(device_t dev) { - int j; - uint16_t chipid; + struct nct_device *nctdevp; + uint8_t ldn; - if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) + ldn = superio_get_ldn(dev); + + if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) { + NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn); return (ENXIO); - if (superio_get_type(dev) != SUPERIO_DEV_GPIO) + } + if (superio_get_type(dev) != SUPERIO_DEV_GPIO) { + NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a GPIO device\n", ldn); return (ENXIO); + } - /* - * There are several GPIO devices, we attach only to one of them - * and use the rest without attaching. - */ - if (superio_get_ldn(dev) != NCT_LDN_GPIO) + nctdevp = nct_lookup_device(dev); + if (nctdevp == NULL) { + NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn); return (ENXIO); - - chipid = superio_devid(dev); - for (j = 0; j < nitems(nct_devs); j++) { - if (chipid == nct_devs[j].chip_id) { - device_set_desc(dev, "Nuvoton GPIO controller"); - return (BUS_PROBE_DEFAULT); - } } - return (ENXIO); + device_set_desc(dev, nctdevp->descr); + return (BUS_PROBE_DEFAULT); } static int nct_attach(device_t dev) { struct nct_softc *sc; - device_t dev_8; - uint16_t iobase; - int err; - int i; - - sc = device_get_softc(dev); - sc->dev = dev; - sc->dev_f = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_GPIO, - NCT_LDN_GPIO_MODE); - if (sc->dev_f == NULL) { - device_printf(dev, "failed to find LDN F\n"); - return (ENXIO); - } - - /* - * As strange as it may seem, I/O port base is configured in the - * Logical Device 8 which is primarily used for WDT, but also plays - * a role in GPIO configuration. - */ - iobase = 0; - dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8); - if (dev_8 != NULL) - iobase = superio_get_iobase(dev_8); - if (iobase != 0 && iobase != 0xffff) { - sc->curgrp = -1; - sc->iorid = 0; - err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid, - iobase, 7); - if (err == 0) { - sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, - &sc->iorid, RF_ACTIVE); - if (sc->iores == NULL) { - device_printf(dev, "can't map i/o space, " - "iobase=0x%04x\n", iobase); + struct nct_gpio_group *gp; + uint32_t pin_num; + uint8_t v; + int flags, i, g; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->nctdevp = nct_lookup_device(dev); + + flags = 0; + (void)resource_int_value(device_get_name(dev), device_get_unit(dev), "flags", &flags); + + if ((flags & NCT_PREFER_INDIRECT_CHANNEL) == 0) { + uint16_t iobase; + device_t dev_8; + + /* + * As strange as it may seem, I/O port base is configured in the + * Logical Device 8 which is primarily used for WDT, but also plays + * a role in GPIO configuration. + */ + iobase = 0; + dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8); + if (dev_8 != NULL) + iobase = superio_get_iobase(dev_8); + if (iobase != 0 && iobase != 0xffff) { + int err; + + NCT_VERBOSE_PRINTF(dev, "iobase %#x\n", iobase); + sc->curgrp = -1; + sc->iorid = 0; + err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid, + iobase, 7); + if (err == 0) { + sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &sc->iorid, RF_ACTIVE); + if (sc->iores == NULL) { + device_printf(dev, "can't map i/o space, " + "iobase=%#x\n", iobase); + } + } else { + device_printf(dev, + "failed to set io port resource at %#x\n", iobase); } - } else { - device_printf(dev, - "failed to set io port resource at 0x%x\n", iobase); } } - - /* Enable gpio0 and gpio1. */ - superio_dev_enable(dev, 0x03); + NCT_VERBOSE_PRINTF(dev, "iores %p %s channel\n", + sc->iores, (sc->iores ? "direct" : "indirect")); + + /* Enable GPIO groups */ + for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) { + NCT_VERBOSE_PRINTF(dev, + "GPIO%d: %d pins, enable with mask 0x%x via ldn 0x%x reg 0x%x\n", + gp->grpnum, gp->npins, gp->enable_mask, gp->enable_ldn, + gp->enable_reg); + v = superio_ldn_read(dev, gp->enable_ldn, gp->enable_reg); + v |= gp->enable_mask; + superio_ldn_write(dev, gp->enable_ldn, gp->enable_reg, v); + } GPIO_LOCK_INIT(sc); GPIO_LOCK(sc); - sc->cache.inv[0] = nct_read_reg(sc, REG_INV, 0); - sc->cache.inv[1] = nct_read_reg(sc, REG_INV, 1); - sc->cache.ior[0] = nct_read_reg(sc, REG_IOR, 0); - sc->cache.ior[1] = nct_read_reg(sc, REG_IOR, 1); + pin_num = 0; + sc->npins = 0; + for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) { + sc->npins += gp->npins; + for (i = 0; i < gp->npins; i++, pin_num++) { + struct gpio_pin *pin; + + sc->pinmap[pin_num].group = gp; + sc->pinmap[pin_num].grpnum = gp->grpnum; + sc->pinmap[pin_num].bit = gp->pinbits[i]; + + sc->grpmap[gp->grpnum] = gp; + + pin = &sc->pins[pin_num]; + pin->gp_pin = pin_num; + pin->gp_caps = gp->caps; + pin->gp_flags = 0; + + snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u", + gp->grpnum, gp->pinbits[i]); + + if (nct_pin_is_input(sc, pin_num)) + pin->gp_flags |= GPIO_PIN_INPUT; + else + pin->gp_flags |= GPIO_PIN_OUTPUT; + + if (nct_pin_is_opendrain(sc, pin_num)) + pin->gp_flags |= GPIO_PIN_OPENDRAIN; + else + pin->gp_flags |= GPIO_PIN_PUSHPULL; + + if (nct_pin_is_inverted(sc, pin_num)) + pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); + } + } + NCT_VERBOSE_PRINTF(dev, "%d pins available\n", sc->npins); /* * Caching input values is meaningless as an input can be changed at any @@ -514,39 +745,18 @@ nct_attach(device_t dev) * | 1 | 1 | 1 | * |-----+-----------+---------| */ - sc->cache.out[0] = nct_read_reg(sc, REG_DAT, 0); - sc->cache.out[1] = nct_read_reg(sc, REG_DAT, 1); - sc->cache.out_known[0] = ~sc->cache.ior[0]; - sc->cache.out_known[1] = ~sc->cache.ior[1]; - - 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%02o", 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); + for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) { + sc->cache.inv[gp->grpnum] = nct_read_reg(sc, REG_INV, gp->grpnum); + sc->cache.ior[gp->grpnum] = nct_read_reg(sc, REG_IOR, gp->grpnum); + sc->cache.out[gp->grpnum] = nct_read_reg(sc, REG_DAT, gp->grpnum); + sc->cache.out_known[gp->grpnum] = ~sc->cache.ior[gp->grpnum]; } + GPIO_UNLOCK(sc); sc->busdev = gpiobus_attach_bus(dev); if (sc->busdev == NULL) { + device_printf(dev, "failed to attach to gpiobus\n"); GPIO_LOCK_DESTROY(sc); return (ENXIO); } @@ -581,10 +791,12 @@ nct_gpio_get_bus(device_t dev) } static int -nct_gpio_pin_max(device_t dev, int *npins) +nct_gpio_pin_max(device_t dev, int *maxpin) { - *npins = NCT_MAX_PIN; + struct nct_softc *sc; + sc = device_get_softc(dev); + *maxpin = sc->npins - 1; return (0); } @@ -593,10 +805,11 @@ 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)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, pin_num)) return (EINVAL); - sc = device_get_softc(dev); GPIO_LOCK(sc); if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) { GPIO_UNLOCK(sc); @@ -613,10 +826,11 @@ 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)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, pin_num)) return (EINVAL); - sc = device_get_softc(dev); GPIO_ASSERT_UNLOCKED(sc); GPIO_LOCK(sc); *pin_value = nct_read_pin(sc, pin_num); @@ -630,10 +844,11 @@ nct_gpio_pin_toggle(device_t dev, uint32_t pin_num) { struct nct_softc *sc; - if (!NCT_IS_VALID_PIN(pin_num)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, pin_num)) return (EINVAL); - sc = device_get_softc(dev); GPIO_ASSERT_UNLOCKED(sc); GPIO_LOCK(sc); if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) { @@ -655,10 +870,11 @@ 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)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, pin_num)) return (EINVAL); - sc = device_get_softc(dev); GPIO_ASSERT_UNLOCKED(sc); GPIO_LOCK(sc); *caps = sc->pins[pin_num].gp_caps; @@ -672,10 +888,11 @@ 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)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, pin_num)) return (EINVAL); - sc = device_get_softc(dev); GPIO_ASSERT_UNLOCKED(sc); GPIO_LOCK(sc); *flags = sc->pins[pin_num].gp_flags; @@ -689,10 +906,11 @@ nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name) { struct nct_softc *sc; - if (!NCT_IS_VALID_PIN(pin_num)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, 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); @@ -707,10 +925,11 @@ 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)) + sc = device_get_softc(dev); + + if (!NCT_PIN_IS_VALID(sc, pin_num)) return (EINVAL); - sc = device_get_softc(dev); pin = &sc->pins[pin_num]; if ((flags & pin->gp_caps) != flags) return (EINVAL); @@ -730,17 +949,24 @@ nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags) GPIO_ASSERT_UNLOCKED(sc); GPIO_LOCK(sc); *** 29 LINES SKIPPED ***