diff --git a/sys/dev/ichwd/i6300esb.h b/sys/dev/ichwd/i6300esb.h new file mode 100644 --- /dev/null +++ b/sys/dev/ichwd/i6300esb.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 ShengYi Hung + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _I6300WD_H_ +#define _I6300WD_H_ + +#define WDT_CONFIG_REG 0x60 +#define WDT_LOCK_REG 0x68 + +#define WDT_PRELOAD_1_REG 0x00 +#define WDT_PRELOAD_2_REG 0x04 +#define WDT_INTR_REG 0x08 +#define WDT_RELOAD_REG 0x0C + +/* For config register */ +#define WDT_OUTPUT_EN (0x1 << 5) +#define WDT_PRE_SEL (0x1 << 2) +#define WDT_INT_TYPE_BITS (0x3) +#define WDT_INT_TYPE_IRQ_VAL (0x0) +#define WDT_INT_TYPE_RES_VAL (0x1) +#define WDT_INT_TYPE_SMI_VAL (0x2) +#define WDT_INT_TYPE_DISABLED_VAL (0x3) + +/* For lock register */ +#define WDT_TOUT_CNF_WT_MODE (0x0 << 2) +#define WDT_TOUT_CNF_FR_MODE (0x1 << 2) +#define WDT_ENABLE (0x02) +#define WDT_LOCK (0x01) + +/* For preload 1/2 registers */ +#define WDT_PRELOAD_BIT 20 +#define WDT_PRELOAD_BITS ((0x1 << WDT_PRELOAD_BIT) - 1) + +/* For interrupt register */ +#define WDT_INTR_ACT (0x01 << 0) + +/* For reload register */ +#define WDT_TIMEOUT (0x01 << 9) +#define WDT_RELOAD (0x01 << 8) +#define WDT_UNLOCK_SEQ_1_VAL 0x80 +#define WDT_UNLOCK_SEQ_2_VAL 0x86 + +#endif /* _I6300WD_H_ */ diff --git a/sys/dev/ichwd/i6300esb.c b/sys/dev/ichwd/i6300esb.c new file mode 100644 --- /dev/null +++ b/sys/dev/ichwd/i6300esb.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025 ShengYi Hung + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * Reference: Intel 6300ESB Controller Hub Datasheet Section 16 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +static int i6300esb_locked; +static int i6300esb_scale; +static int i6300esb_reboot = 1; +static SYSCTL_NODE(_hw, OID_AUTO, i6300esb, CTLFLAG_RW, 0, "i6300esb"); +SYSCTL_INT(_hw_i6300esb, OID_AUTO, locked, CTLFLAG_RWTUN, &i6300esb_locked, 1, + "Lock enable status forever"); +SYSCTL_INT(_hw_i6300esb, OID_AUTO, scale, CTLFLAG_RDTUN, &i6300esb_scale, 1, + "Scale the i6300esb from 1khz to 1mhz"); +SYSCTL_INT(_hw_i6300esb, OID_AUTO, reboot, CTLFLAG_RDTUN, &i6300esb_reboot, 1, + "Reboot when timeout"); + +struct i6300esb_softc { + device_t dev; + int res_id; + struct resource *res; + eventhandler_tag ev_tag; + bool locked; +}; + +static const struct i6300esb_pci_id { + uint16_t id; + const char *name; +} i6300esb_pci_devices[] = { + { DEVICEID_6300ESB_5, "6300ESB Watchdog Timer" }, +}; + +static uint16_t +i6300esb_cfg_read(struct i6300esb_softc *sc) +{ + return (pci_read_config(sc->dev, WDT_CONFIG_REG, 2)); +} + +static void +i6300esb_cfg_write(struct i6300esb_softc *sc, uint16_t val) +{ + pci_write_config(sc->dev, WDT_CONFIG_REG, val, 2); +} + +static uint8_t +i6300esb_lock_read(struct i6300esb_softc *sc) +{ + return (pci_read_config(sc->dev, WDT_LOCK_REG, 1)); +} + +static void +i6300esb_lock_write(struct i6300esb_softc *sc, uint8_t val) +{ + pci_write_config(sc->dev, WDT_LOCK_REG, val, 1); +} + +/* + * According to Intel 6300ESB I/O Controller Hub Datasheet 16.5.2, + * the resource should be unlocked before modifing any registers. + * The way to unlock is by write 0x80, 0x86 to the reload register. + */ +static void +i6300esb_unlock_res(struct i6300esb_softc *sc) +{ + bus_write_2(sc->res, WDT_RELOAD_REG, WDT_UNLOCK_SEQ_1_VAL); + bus_write_2(sc->res, WDT_RELOAD_REG, WDT_UNLOCK_SEQ_2_VAL); +} + +static void +i6300esb_event(void *arg, unsigned int cmd, int *error) +{ + struct i6300esb_softc *sc = arg; + uint32_t timeout; + uint16_t regval; + int ticks; + + if (i6300esb_scale) + ticks = WD_TO_1US; + else + ticks = WD_TO_1MS; + + cmd &= WD_INTERVAL; + if (cmd != 0 && (cmd < ticks || (cmd - ticks) >= WDT_PRELOAD_BIT)) { + *error = EINVAL; + return; + } + timeout = 1 << (cmd - ticks); + + /* reset the timer to prevent timeout a timeout is about to occur */ + i6300esb_unlock_res(sc); + bus_write_2(sc->res, WDT_RELOAD_REG, WDT_RELOAD); + + if (!cmd) { + /* + * when the lock is enabled, we are unable to overwrite LOCK + * register + */ + if (sc->locked) { + *error = EPERM; + } + i6300esb_lock_write(sc, i6300esb_lock_read(sc) & ~(WDT_ENABLE)); + return; + } + + i6300esb_unlock_res(sc); + bus_write_4(sc->res, WDT_PRELOAD_1_REG, timeout); + + i6300esb_unlock_res(sc); + bus_write_4(sc->res, WDT_PRELOAD_2_REG, timeout); + + i6300esb_unlock_res(sc); + bus_write_2(sc->res, WDT_RELOAD_REG, WDT_RELOAD); + + if (!sc->locked) { + i6300esb_lock_write(sc, + WDT_ENABLE | (i6300esb_locked ? WDT_LOCK : 0)); + regval = i6300esb_lock_read(sc); + sc->locked = regval & WDT_LOCK; + } +} + +static int +i6300esb_probe(device_t dev) +{ + const struct i6300esb_pci_id *pci_id; + uint16_t pci_dev_id; + int err = ENXIO; + + if (pci_get_vendor(dev) != VENDORID_INTEL) + goto end; + + pci_dev_id = pci_get_device(dev); + for (pci_id = i6300esb_pci_devices; + pci_id < i6300esb_pci_devices + nitems(i6300esb_pci_devices); + ++pci_id) { + if (pci_id->id == pci_dev_id) { + device_set_desc(dev, pci_id->name); + err = BUS_PROBE_DEFAULT; + break; + } + } + +end: + return (err); +} + +static int +i6300esb_attach(device_t dev) +{ + struct i6300esb_softc *sc = device_get_softc(dev); + uint16_t regval; + + sc->dev = dev; + sc->res_id = PCIR_BAR(0); + sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->res_id, + RF_ACTIVE); + if (sc->res == NULL) { + device_printf(dev, "unable to map memory region\n"); + return (ENXIO); + } + + i6300esb_cfg_write(sc, + WDT_INT_TYPE_DISABLED_VAL | (i6300esb_scale ? WDT_PRE_SEL : 0) | + (!i6300esb_reboot ? WDT_OUTPUT_EN : 0)); + regval = i6300esb_lock_read(sc); + if (regval & WDT_LOCK) + sc->locked = true; + i6300esb_lock_write(sc, WDT_TOUT_CNF_WT_MODE); + + i6300esb_unlock_res(sc); + bus_write_2(sc->res, WDT_RELOAD_REG, WDT_RELOAD | WDT_TIMEOUT); + + sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, i6300esb_event, sc, + 0); + + return (0); +} + +static int +i6300esb_detach(device_t dev) +{ + struct i6300esb_softc *sc = device_get_softc(dev); + + if (sc->ev_tag) + EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); + + if (sc->res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->res_id, sc->res); + + return (0); +} + +static device_method_t i6300esb_methods[] = { + DEVMETHOD(device_probe, i6300esb_probe), + DEVMETHOD(device_attach, i6300esb_attach), + DEVMETHOD(device_detach, i6300esb_detach), + DEVMETHOD(device_shutdown, i6300esb_detach), + DEVMETHOD_END +}; + +static driver_t i6300esb_driver = { + "i6300esb", + i6300esb_methods, + sizeof(struct i6300esb_softc), +}; + +DRIVER_MODULE(i6300esb, pci, i6300esb_driver, NULL, NULL); diff --git a/sys/dev/ichwd/ichwd.h b/sys/dev/ichwd/ichwd.h --- a/sys/dev/ichwd/ichwd.h +++ b/sys/dev/ichwd/ichwd.h @@ -151,7 +151,12 @@ #define DEVICEID_82801E 0x2450 #define DEVICEID_82801EB 0x24dc #define DEVICEID_82801EBR 0x24d0 -#define DEVICEID_6300ESB 0x25a1 +#define DEVICEID_6300ESB_1 0x25a1 +#define DEVICEID_6300ESB_2 0x25a2 +#define DEVICEID_6300ESB_3 0x25a4 +#define DEVICEID_6300ESB_4 0x25a6 +#define DEVICEID_6300ESB_5 0x25ab +#define DEVICEID_6300ESB_6 0x25ac #define DEVICEID_82801FBR 0x2640 #define DEVICEID_ICH6M 0x2641 #define DEVICEID_ICH6W 0x2642 diff --git a/sys/dev/ichwd/ichwd.c b/sys/dev/ichwd/ichwd.c --- a/sys/dev/ichwd/ichwd.c +++ b/sys/dev/ichwd/ichwd.c @@ -90,7 +90,7 @@ { DEVICEID_82801E, "Intel 82801E watchdog timer", 5, 1 }, { DEVICEID_82801EB, "Intel 82801EB watchdog timer", 5, 1 }, { DEVICEID_82801EBR, "Intel 82801EB/ER watchdog timer", 5, 1 }, - { DEVICEID_6300ESB, "Intel 6300ESB watchdog timer", 5, 1 }, + { DEVICEID_6300ESB_1, "Intel 6300ESB watchdog timer", 5, 1 }, { DEVICEID_82801FBR, "Intel 82801FB/FR watchdog timer", 6, 2 }, { DEVICEID_ICH6M, "Intel ICH6M watchdog timer", 6, 2 }, { DEVICEID_ICH6W, "Intel ICH6W watchdog timer", 6, 2 }, diff --git a/sys/modules/Makefile b/sys/modules/Makefile --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -145,6 +145,7 @@ ${_hwt} \ ${_hyperv} \ i2c \ + ${_i6300esb} \ ${_iavf} \ ${_ibcore} \ ${_ichwd} \ @@ -798,6 +799,7 @@ _hptrr= hptrr .endif _hyperv= hyperv +_i6300esb= i6300esb _ichwd= ichwd _ida= ida _intelspi= intelspi diff --git a/sys/modules/i6300esb/Makefile b/sys/modules/i6300esb/Makefile new file mode 100644 --- /dev/null +++ b/sys/modules/i6300esb/Makefile @@ -0,0 +1,6 @@ +.PATH: ${SRCTOP}/sys/dev/ichwd + +KMOD= i6300esb +SRCS= i6300esb.c device_if.h bus_if.h pci_if.h + +.include diff --git a/sys/sys/watchdog.h b/sys/sys/watchdog.h --- a/sys/sys/watchdog.h +++ b/sys/sys/watchdog.h @@ -83,6 +83,7 @@ /* Handy macros for humans not used to power of two nanoseconds */ #define WD_TO_NEVER 0 +#define WD_TO_1US 10 #define WD_TO_1MS 20 #define WD_TO_125MS 27 #define WD_TO_250MS 28