mirror of
https://git.freebsd.org/src.git
synced 2026-01-16 23:02:24 +00:00
ichwd: introduce i6300esbwd watch dog driver
The intel 6300ESB watchdog is a special ICH-based watchdog device with a different interface. QEMU implements this watchdog for x86 systems. This change enables watchdog mode (rather than free-running mode) and introduces 1 sysctl: - hw.i6300esbwd.0.locked: locks the watchdog register after the event is triggered, preventing it from being disabled until a hard reset. This feature has been tested on a Vultr AMD guest machine and local qemu machine. PR: 259673 Approved by: markj (mentor), lwhsu (mentor) MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D52049
This commit is contained in:
parent
2765d8d5bc
commit
2b74ff5fce
6 changed files with 296 additions and 3 deletions
|
|
@ -146,6 +146,7 @@ dev/hyperv/vmbus/vmbus_et.c optional hyperv
|
|||
dev/hyperv/vmbus/vmbus_if.m optional hyperv
|
||||
dev/hyperv/vmbus/vmbus_res.c optional hyperv
|
||||
dev/hyperv/vmbus/vmbus_xact.c optional hyperv
|
||||
dev/ichwd/i6300esbwd.c optional ichwd
|
||||
dev/ichwd/ichwd.c optional ichwd
|
||||
dev/imcsmb/imcsmb.c optional imcsmb
|
||||
dev/imcsmb/imcsmb_pci.c optional imcsmb pci
|
||||
|
|
|
|||
245
sys/dev/ichwd/i6300esbwd.c
Normal file
245
sys/dev/ichwd/i6300esbwd.c
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (c) 2025 The FreeBSD Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Reference: Intel 6300ESB Controller Hub Datasheet Section 16
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/resource.h>
|
||||
#include <sys/watchdog.h>
|
||||
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include <dev/ichwd/ichwd.h>
|
||||
#include <dev/ichwd/i6300esbwd.h>
|
||||
|
||||
#include <x86/pci_cfgreg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pci_private.h>
|
||||
|
||||
struct i6300esbwd_softc {
|
||||
device_t dev;
|
||||
int res_id;
|
||||
struct resource *res;
|
||||
eventhandler_tag ev_tag;
|
||||
bool locked;
|
||||
};
|
||||
|
||||
static const struct i6300esbwd_pci_id {
|
||||
uint16_t id;
|
||||
const char *name;
|
||||
} i6300esbwd_pci_devices[] = {
|
||||
{ DEVICEID_6300ESB_2, "6300ESB Watchdog Timer" },
|
||||
};
|
||||
|
||||
static uint16_t
|
||||
i6300esbwd_cfg_read(struct i6300esbwd_softc *sc)
|
||||
{
|
||||
return (pci_read_config(sc->dev, WDT_CONFIG_REG, 2));
|
||||
}
|
||||
|
||||
static void
|
||||
i6300esbwd_cfg_write(struct i6300esbwd_softc *sc, uint16_t val)
|
||||
{
|
||||
pci_write_config(sc->dev, WDT_CONFIG_REG, val, 2);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
i6300esbwd_lock_read(struct i6300esbwd_softc *sc)
|
||||
{
|
||||
return (pci_read_config(sc->dev, WDT_LOCK_REG, 1));
|
||||
}
|
||||
|
||||
static void
|
||||
i6300esbwd_lock_write(struct i6300esbwd_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
|
||||
i6300esbwd_unlock_res(struct i6300esbwd_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 int
|
||||
i6300esbwd_sysctl_locked(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct i6300esbwd_softc *sc = (struct i6300esbwd_softc *)arg1;
|
||||
int error;
|
||||
int result;
|
||||
|
||||
result = sc->locked;
|
||||
error = sysctl_handle_int(oidp, &result, 0, req);
|
||||
|
||||
if (error || !req->newptr)
|
||||
return (error);
|
||||
|
||||
if (result == 1 && !sc->locked) {
|
||||
i6300esbwd_lock_write(sc, i6300esbwd_lock_read(sc) | WDT_LOCK);
|
||||
sc->locked = true;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
i6300esbwd_event(void *arg, unsigned int cmd, int *error)
|
||||
{
|
||||
struct i6300esbwd_softc *sc = arg;
|
||||
uint32_t timeout;
|
||||
uint16_t regval;
|
||||
|
||||
cmd &= WD_INTERVAL;
|
||||
if (cmd != 0 &&
|
||||
(cmd < WD_TO_1MS || (cmd - WD_TO_1MS) >= WDT_PRELOAD_BIT)) {
|
||||
*error = EINVAL;
|
||||
return;
|
||||
}
|
||||
timeout = 1 << (cmd - WD_TO_1MS);
|
||||
|
||||
/* reset the timer to prevent timeout a timeout is about to occur */
|
||||
i6300esbwd_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;
|
||||
else
|
||||
i6300esbwd_lock_write(sc,
|
||||
i6300esbwd_lock_read(sc) & ~WDT_ENABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
i6300esbwd_unlock_res(sc);
|
||||
bus_write_4(sc->res, WDT_PRELOAD_1_REG, timeout);
|
||||
|
||||
i6300esbwd_unlock_res(sc);
|
||||
bus_write_4(sc->res, WDT_PRELOAD_2_REG, timeout);
|
||||
|
||||
i6300esbwd_unlock_res(sc);
|
||||
bus_write_2(sc->res, WDT_RELOAD_REG, WDT_RELOAD);
|
||||
|
||||
if (!sc->locked) {
|
||||
i6300esbwd_lock_write(sc, WDT_ENABLE);
|
||||
regval = i6300esbwd_lock_read(sc);
|
||||
sc->locked = regval & WDT_LOCK;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
i6300esbwd_probe(device_t dev)
|
||||
{
|
||||
const struct i6300esbwd_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 = i6300esbwd_pci_devices;
|
||||
pci_id < i6300esbwd_pci_devices + nitems(i6300esbwd_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
|
||||
i6300esbwd_attach(device_t dev)
|
||||
{
|
||||
struct i6300esbwd_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);
|
||||
}
|
||||
|
||||
i6300esbwd_cfg_write(sc, WDT_INT_TYPE_DISABLED_VAL);
|
||||
regval = i6300esbwd_lock_read(sc);
|
||||
if (regval & WDT_LOCK)
|
||||
sc->locked = true;
|
||||
else {
|
||||
sc->locked = false;
|
||||
i6300esbwd_lock_write(sc, WDT_TOUT_CNF_WT_MODE);
|
||||
}
|
||||
|
||||
i6300esbwd_unlock_res(sc);
|
||||
bus_write_2(sc->res, WDT_RELOAD_REG, WDT_RELOAD | WDT_TIMEOUT);
|
||||
|
||||
sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, i6300esbwd_event, sc,
|
||||
0);
|
||||
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "locked",
|
||||
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
|
||||
i6300esbwd_sysctl_locked, "I",
|
||||
"Lock the timer so that we cannot disable it");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
i6300esbwd_detach(device_t dev)
|
||||
{
|
||||
struct i6300esbwd_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 i6300esbwd_methods[] = {
|
||||
DEVMETHOD(device_probe, i6300esbwd_probe),
|
||||
DEVMETHOD(device_attach, i6300esbwd_attach),
|
||||
DEVMETHOD(device_detach, i6300esbwd_detach),
|
||||
DEVMETHOD(device_shutdown, i6300esbwd_detach),
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t i6300esbwd_driver = {
|
||||
"i6300esbwd",
|
||||
i6300esbwd_methods,
|
||||
sizeof(struct i6300esbwd_softc),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(i6300esbwd, pci, i6300esbwd_driver, NULL, NULL);
|
||||
46
sys/dev/ichwd/i6300esbwd.h
Normal file
46
sys/dev/ichwd/i6300esbwd.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2025 The FreeBSD Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#ifndef _I6300ESBWD_H_
|
||||
#define _I6300ESBWD_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 /* _I6300ESBWD_H_ */
|
||||
|
|
@ -90,7 +90,7 @@ static struct ichwd_device ichwd_devices[] = {
|
|||
{ 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 },
|
||||
|
|
|
|||
|
|
@ -151,7 +151,8 @@ struct ichwd_softc {
|
|||
#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 0x25ab
|
||||
#define DEVICEID_82801FBR 0x2640
|
||||
#define DEVICEID_ICH6M 0x2641
|
||||
#define DEVICEID_ICH6W 0x2642
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.PATH: ${SRCTOP}/sys/dev/ichwd
|
||||
|
||||
KMOD= ichwd
|
||||
SRCS= ichwd.c device_if.h bus_if.h pci_if.h isa_if.h
|
||||
SRCS= i6300esbwd.c ichwd.c device_if.h bus_if.h pci_if.h isa_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue