Module Name: src Committed By: knakahara Date: Wed Dec 13 00:27:53 UTC 2017
Modified Files: src/sys/dev/isa: wbsio.c wbsioreg.h Log Message: Add Watchdog timer implementation to wbsio(4). Implemeted by s-yamaguchi@IIJ, reviewed by msaitoh@n.o. I just commit by proxy. To generate a diff of this commit: cvs rdiff -u -r1.17 -r1.18 src/sys/dev/isa/wbsio.c cvs rdiff -u -r1.6 -r1.7 src/sys/dev/isa/wbsioreg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/isa/wbsio.c diff -u src/sys/dev/isa/wbsio.c:1.17 src/sys/dev/isa/wbsio.c:1.18 --- src/sys/dev/isa/wbsio.c:1.17 Wed Dec 13 00:27:01 2017 +++ src/sys/dev/isa/wbsio.c Wed Dec 13 00:27:53 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: wbsio.c,v 1.17 2017/12/13 00:27:01 knakahara Exp $ */ +/* $NetBSD: wbsio.c,v 1.18 2017/12/13 00:27:53 knakahara Exp $ */ /* $OpenBSD: wbsio.c,v 1.10 2015/03/14 03:38:47 jsg Exp $ */ /* * Copyright (c) 2008 Mark Kettenis <kette...@openbsd.org> @@ -34,6 +34,7 @@ #include <dev/isa/isareg.h> #include <dev/isa/isavar.h> #include <dev/isa/wbsioreg.h> +#include <dev/sysmon/sysmonvar.h> /* Don't use gpio for now in the module */ #ifndef _MODULE @@ -52,6 +53,7 @@ struct wbsio_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; + kmutex_t sc_conf_lock; struct isa_attach_args sc_ia; struct isa_io sc_io; @@ -63,6 +65,9 @@ struct wbsio_softc { struct gpio_pin sc_gpio_pins[WBSIO_GPIO_NPINS]; bool sc_gpio_rt; #endif + + struct sysmon_wdog sc_smw; + bool sc_smw_valid; }; static const struct wbsio_product { @@ -101,6 +106,7 @@ static int wbsio_rescan(device_t, const static void wbsio_childdet(device_t, device_t); static int wbsio_print(void *, const char *); static int wbsio_search(device_t, cfdata_t, const int *, void *); +static bool wbsio_suspend(device_t, const pmf_qual_t *); #if NGPIO > 0 static int wbsio_gpio_search(device_t, cfdata_t, const int *, void *); static int wbsio_gpio_rt_init(struct wbsio_softc *); @@ -112,22 +118,35 @@ static void wbsio_gpio_rt_pin_ctl(void * static void wbsio_gpio_enable_nct6779d(device_t); static void wbsio_gpio_pinconfig_nct6779d(device_t); #endif +static void wbsio_wdog_attach(device_t); +static int wbsio_wdog_detach(device_t); +static int wbsio_wdog_setmode(struct sysmon_wdog *); +static int wbsio_wdog_tickle(struct sysmon_wdog *); +static void wbsio_wdog_setcounter(struct wbsio_softc *, uint8_t); +static void wbsio_wdog_clear_timeout(struct wbsio_softc *); CFATTACH_DECL2_NEW(wbsio, sizeof(struct wbsio_softc), wbsio_match, wbsio_attach, wbsio_detach, NULL, wbsio_rescan, wbsio_childdet); static __inline void -wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) +wbsio_conf_enable(kmutex_t *lock, bus_space_tag_t iot, bus_space_handle_t ioh) { + if (lock) + mutex_enter(lock); + bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); } static __inline void -wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) +wbsio_conf_disable(kmutex_t *lock, bus_space_tag_t iot, bus_space_handle_t ioh) { + bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); + + if (lock) + mutex_exit(lock); } static __inline uint8_t @@ -188,11 +207,11 @@ wbsio_match(device_t parent, cfdata_t ma iot = ia->ia_iot; if (bus_space_map(iot, ia->ia_io[0].ir_addr, WBSIO_IOSIZE, 0, &ioh)) return 0; - wbsio_conf_enable(iot, ioh); + wbsio_conf_enable(NULL, iot, ioh); id = wbsio_conf_read(iot, ioh, WBSIO_ID); rev = wbsio_conf_read(iot, ioh, WBSIO_REV); aprint_debug("wbsio_probe: id 0x%02x, rev 0x%02x\n", id, rev); - wbsio_conf_disable(iot, ioh); + wbsio_conf_disable(NULL, iot, ioh); bus_space_unmap(iot, ioh, WBSIO_IOSIZE); if ((product = wbsio_lookup(id, rev)) == NULL) @@ -228,8 +247,10 @@ wbsio_attach(device_t parent, device_t s return; } + mutex_init(&sc->sc_conf_lock, MUTEX_DEFAULT, IPL_NONE); + /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); /* Read device ID */ id = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); @@ -237,7 +258,7 @@ wbsio_attach(device_t parent, device_t s rev = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); if ((product = wbsio_lookup(id, rev)) == NULL) { aprint_error_dev(self, "Unknown device. Failed to attach\n"); @@ -259,8 +280,11 @@ wbsio_attach(device_t parent, device_t s } else aprint_normal("0x%02x\n", rev); - if (!pmf_device_register(self, NULL, NULL)) + if (!pmf_device_register(self, wbsio_suspend, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); + + wbsio_wdog_attach(self); + wbsio_rescan(self, "wbsio", NULL); #if NGPIO > 0 @@ -275,6 +299,9 @@ wbsio_detach(device_t self, int flags) struct wbsio_softc *sc = device_private(self); int rc; + if ((rc = wbsio_wdog_detach(self)) != 0) + return rc; + if ((rc = config_detach_children(self, flags)) != 0) return rc; bus_space_unmap(sc->sc_iot, sc->sc_ioh, WBSIO_IOSIZE); @@ -290,6 +317,8 @@ wbsio_detach(device_t self, int flags) mutex_destroy(&sc->sc_gpio_lock); } #endif + + mutex_destroy(&sc->sc_conf_lock); return 0; } @@ -328,7 +357,7 @@ wbsio_search(device_t parent, cfdata_t c uint8_t reg0, reg1, rev; /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); /* Select HM logical device */ wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); @@ -343,7 +372,7 @@ wbsio_search(device_t parent, cfdata_t c reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); iobase = (reg1 << 8) | (reg0 & ~0x7); @@ -351,12 +380,12 @@ wbsio_search(device_t parent, cfdata_t c return -1; /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); /* Read device ID and revision */ devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); rev = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); if ((product = wbsio_lookup(devid, rev)) == NULL) { aprint_error_dev(parent, "%s: Unknown device.\n", __func__); @@ -394,6 +423,20 @@ wbsio_print(void *aux, const char *pnp) return (UNCONF); } +static bool +wbsio_suspend(device_t self, const pmf_qual_t *qual) +{ + struct wbsio_softc *sc = device_private(self); + + if (sc->sc_smw_valid) { + if ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) + != WDOG_MODE_DISARMED) + return false; + } + + return true; +} + #if NGPIO > 0 static int wbsio_gpio_search(device_t parent, cfdata_t cf, const int *slocs, void *aux) @@ -406,12 +449,12 @@ wbsio_gpio_search(device_t parent, cfdat int i; /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); /* Read device ID and revision */ devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); rev = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); if ((product = wbsio_lookup(devid, rev)) == NULL) { aprint_error_dev(parent, "%s: Unknown device.\n", __func__); @@ -479,7 +522,7 @@ wbsio_gpio_rt_init(struct wbsio_softc *s uint8_t reg0, reg1; /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); /* Get GPIO Register Table address */ wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO0); @@ -488,7 +531,7 @@ wbsio_gpio_rt_init(struct wbsio_softc *s iobase = (reg1 << 8) | (reg0 & ~0x7); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); if (bus_space_map(sc->sc_iot, iobase, WBSIO_GPIO_IOSIZE, 0, &sc->sc_gpio_ioh)) { @@ -606,7 +649,7 @@ wbsio_gpio_enable_nct6779d(device_t pare uint8_t reg, conf; /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO0); reg = WBSIO_GPIO_CONF; @@ -630,7 +673,7 @@ wbsio_gpio_enable_nct6779d(device_t pare wbsio_conf_write(sc->sc_iot, sc->sc_ioh, reg, conf); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); } static void @@ -641,7 +684,7 @@ wbsio_gpio_pinconfig_nct6779d(device_t p uint8_t mfs4, mfs5, mfs6, gopt2, hm_conf; /* Enter configuration mode */ - wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); /* Strapping Function Result */ sfr = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_SFR); @@ -740,10 +783,157 @@ wbsio_gpio_pinconfig_nct6779d(device_t p wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_HM_CONF, hm_conf); /* Escape from configuration mode */ - wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); +} + +#endif /* NGPIO > 0 */ + +static void +wbsio_wdog_attach(device_t self) +{ + struct wbsio_softc *sc = device_private(self); + const struct wbsio_product *product; + uint8_t gpio, mode; + uint16_t devid; + uint8_t rev; + + sc->sc_smw_valid = false; + + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); + devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); + rev = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); + + if ((product = wbsio_lookup(devid, rev)) == NULL) { + return; + } + + switch (product->id) { + case WBSIO_ID_NCT6779D: + break; + default: + /* WDT is not supoorted */ + return; + } + + wbsio_wdog_setcounter(sc, WBSIO_WDT_CNTR_STOP); + + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO0); + + gpio = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_GPIO_CONF); + mode = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_WDT_MODE); + + gpio |= WBSIO_GPIO0_WDT1; + + mode &= ~WBSIO_WDT_MODE_FASTER; + mode &= ~WBSIO_WDT_MODE_MINUTES; + mode &= ~WBSIO_WDT_MODE_KBCRST; + mode &= ~WBSIO_WDT_MODE_LEVEL; + + /* initialize WDT mode */ + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_WDT_MODE, mode); + /* Activate WDT1 function */ + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_GPIO_CONF, gpio); + + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); + + sc->sc_smw.smw_name = device_xname(self); + sc->sc_smw.smw_cookie = sc; + sc->sc_smw.smw_setmode = wbsio_wdog_setmode; + sc->sc_smw.smw_tickle = wbsio_wdog_tickle; + sc->sc_smw.smw_period = WBSIO_WDT_CNTR_MAX; + + if (sysmon_wdog_register(&sc->sc_smw)) + aprint_error_dev(self, "couldn't register with sysmon\n"); + else + sc->sc_smw_valid = true; +} + +static int +wbsio_wdog_detach(device_t self) +{ + struct wbsio_softc *sc = device_private(self); + int error; + + error = 0; + + if (sc->sc_smw_valid) { + if ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) + != WDOG_MODE_DISARMED) + return EBUSY; + + error = sysmon_wdog_unregister(&sc->sc_smw); + } + + if (!error) + sc->sc_smw_valid = false; + + return error; +} + +static int +wbsio_wdog_setmode(struct sysmon_wdog *smw) +{ + + switch(smw->smw_mode & WDOG_MODE_MASK) { + case WDOG_MODE_DISARMED: + wbsio_wdog_setcounter(smw->smw_cookie, WBSIO_WDT_CNTR_STOP); + wbsio_wdog_clear_timeout(smw->smw_cookie); + break; + default: + if (smw->smw_period > WBSIO_WDT_CNTR_MAX + || smw->smw_period == 0) + return EINVAL; + + wbsio_wdog_setcounter(smw->smw_cookie, smw->smw_period); + } + + return 0; +} + +static void +wbsio_wdog_setcounter(struct wbsio_softc *sc, uint8_t period) +{ + + KASSERT(!mutex_owned(&sc->sc_conf_lock)); + + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); + + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO0); + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_WDT_CNTR, period); + + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO0); + + + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); +} + +static void +wbsio_wdog_clear_timeout(struct wbsio_softc *sc) +{ + uint8_t st; + + KASSERT(!mutex_owned(&sc->sc_conf_lock)); + + wbsio_conf_enable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); + + st = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_WDT_STAT); + st &= ~WBSIO_WDT_STAT_TIMEOUT; + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_WDT_STAT, st); + + wbsio_conf_disable(&sc->sc_conf_lock, sc->sc_iot, sc->sc_ioh); +} + +static int +wbsio_wdog_tickle(struct sysmon_wdog *smw) +{ + + wbsio_wdog_setcounter(smw->smw_cookie, smw->smw_period); + + return 0; } -#endif MODULE(MODULE_CLASS_DRIVER, wbsio, NULL); Index: src/sys/dev/isa/wbsioreg.h diff -u src/sys/dev/isa/wbsioreg.h:1.6 src/sys/dev/isa/wbsioreg.h:1.7 --- src/sys/dev/isa/wbsioreg.h:1.6 Wed Dec 13 00:26:06 2017 +++ src/sys/dev/isa/wbsioreg.h Wed Dec 13 00:27:53 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: wbsioreg.h,v 1.6 2017/12/13 00:26:06 knakahara Exp $ */ +/* $NetBSD: wbsioreg.h,v 1.7 2017/12/13 00:27:53 knakahara Exp $ */ /* $OpenBSD: wbsioreg.h,v 1.4 2015/01/02 23:02:54 chris Exp $ */ /* @@ -96,8 +96,15 @@ #define WBSIO_GPIO_ADDR_MSB 0x60 /* Address [15:8] */ #define WBSIO_GPIO_ADDR_LSB 0x61 /* Address [7:0] */ #define WBSIO_GPIO_CONF 0x30 /* GPIO0, WDT1 config */ +#define WBSIO_WDT_MODE 0xF5 /* WDT1 Control Mode */ +#define WBSIO_WDT_CNTR 0xF6 /* WDT1 Counter */ +#define WBSIO_WDT_STAT 0xF7 /* WDT1 Control & Status */ +#define WBSIO_GPIO4_MFS 0xEE /* GPIO4 Multi-Function Select */ +#define WBSIO_GPIO0_WDT1 __BIT(0) #define WBSIO_GPIO0_ENABLE __BIT(1) + + /* Reserved */ #define WBSIO_GPIO_BASEADDR __BIT(3) /* Base address mode */ #define WBSIO_GPIO1_ENABLE __BIT(1) #define WBSIO_GPIO2_ENABLE __BIT(2) @@ -116,8 +123,20 @@ #define WBSIO_GPIO_DAT 0x02 /* Data */ #define WBSIO_GPIO_INV 0x03 /* Inversion */ #define WBSIO_GPIO_DST 0x04 /* Event Status */ -#define WBSIO_GPIO_WDTMOD 0x05 -#define WBSIO_GPIO_WDTTIM 0x06 + /* WDT1 Control Mode */ + /* WDT1 control */ +#define WBSIO_WDT_MODE_LEVEL __BIT(0) + /* enable/disable KBRST */ +#define WBSIO_WDT_MODE_KBCRST __BIT(2) +#define WBSIO_WDT_MODE_MINUTES __BIT(3) +#define WBSIO_WDT_MODE_FASTER __BIT(4) + +#define WBSIO_WDT_CNTR_STOP 0 +#define WBSIO_WDT_CNTR_MAX 255 + +#define WBSIO_WDT_STAT_TIMEOUT __BIT(4) +#define WBSIO_WDT_STAT_EVENT __BIT(5) + /* NCT6779D */ #define WBSIO_NCT6779D_MFS2_GP00 __BIT(0) @@ -159,3 +178,4 @@ #define WBSIO_NCT6779D_MFS5_GP74 __BIT(5) #define WBSIO_NCT6779D_MFS5_GP75 __BIT(6) #define WBSIO_NCT6779D_MFS5_GP76 __BIT(7) +#define WBSIO_NCT6779D_GPIO4_WDTO __BIT(4)