amdsbwd: update to support SB8xx southbridges
Many thanks to Tino <tinotom@gmail.com> for drawing my attention to this, for doing a lot of testing and providing great feedback. Many thanks to AMD for continuing to release public specifications for their chipsets. PR: kern/157568 Tested by: Tino <tinotom@gmail.com> MFC after: 1 week
This commit is contained in:
parent
5e319c480c
commit
d1817e7db7
@ -25,12 +25,12 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd November 30, 2009
|
.Dd June 7, 2011
|
||||||
.Dt AMDSBWD 4
|
.Dt AMDSBWD 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm amdsbwd
|
.Nm amdsbwd
|
||||||
.Nd device driver for the AMD SB600/SB700/SB710/SB750 watchdog timer
|
.Nd device driver for the AMD SB600/SB7xx/SB8xx watchdog timers
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
To compile this driver into the kernel,
|
To compile this driver into the kernel,
|
||||||
place the following line in your
|
place the following line in your
|
||||||
@ -51,7 +51,7 @@ The
|
|||||||
driver provides
|
driver provides
|
||||||
.Xr watchdog 4
|
.Xr watchdog 4
|
||||||
support for the watchdog timers present on
|
support for the watchdog timers present on
|
||||||
AMD SB600 and SB7xx south bridge chips.
|
AMD SB600, SB7xx and SB8xx southbridges.
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr watchdog 4 ,
|
.Xr watchdog 4 ,
|
||||||
.Xr watchdog 8 ,
|
.Xr watchdog 8 ,
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a driver for watchdog timer present in AMD SB600/SB7xx
|
* This is a driver for watchdog timer present in AMD SB600/SB7xx/SB8xx
|
||||||
* south bridges and other watchdog timers advertised via WDRT ACPI table.
|
* southbridges.
|
||||||
* Please see the following specifications for the descriptions of the
|
* Please see the following specifications for the descriptions of the
|
||||||
* registers and flags:
|
* registers and flags:
|
||||||
* - AMD SB600 Register Reference Guide, Public Version, Rev. 3.03 (SB600 RRG)
|
* - AMD SB600 Register Reference Guide, Public Version, Rev. 3.03 (SB600 RRG)
|
||||||
@ -35,11 +35,13 @@
|
|||||||
* http://developer.amd.com/assets/43009_sb7xx_rrg_pub_1.00.pdf
|
* http://developer.amd.com/assets/43009_sb7xx_rrg_pub_1.00.pdf
|
||||||
* - AMD SB700/710/750 Register Programming Requirements (RPR)
|
* - AMD SB700/710/750 Register Programming Requirements (RPR)
|
||||||
* http://developer.amd.com/assets/42413_sb7xx_rpr_pub_1.00.pdf
|
* http://developer.amd.com/assets/42413_sb7xx_rpr_pub_1.00.pdf
|
||||||
|
* - AMD SB800-Series Southbridges Register Reference Guide (RRG)
|
||||||
|
* http://support.amd.com/us/Embedded_TechDocs/45482.pdf
|
||||||
* Please see the following for Watchdog Resource Table specification:
|
* Please see the following for Watchdog Resource Table specification:
|
||||||
* - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT)
|
* - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT)
|
||||||
* http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx
|
* http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx
|
||||||
* AMD SB600/SB7xx watchdog hardware seems to conform to the above,
|
* AMD SB600/SB7xx/SB8xx watchdog hardware seems to conform to the above
|
||||||
* but my system doesn't provide the table.
|
* specifications, but the table hasn't been spotted in the wild yet.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
@ -59,15 +61,15 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <dev/pci/pcivar.h>
|
#include <dev/pci/pcivar.h>
|
||||||
#include <isa/isavar.h>
|
#include <isa/isavar.h>
|
||||||
|
|
||||||
/* RRG 2.3.3.1.1, page 161. */
|
/* SB7xx RRG 2.3.3.1.1. */
|
||||||
#define AMDSB_PMIO_INDEX 0xcd6
|
#define AMDSB_PMIO_INDEX 0xcd6
|
||||||
#define AMDSB_PMIO_DATA (PMIO_INDEX + 1)
|
#define AMDSB_PMIO_DATA (PMIO_INDEX + 1)
|
||||||
#define AMDSB_PMIO_WIDTH 2
|
#define AMDSB_PMIO_WIDTH 2
|
||||||
/* RRG 2.3.3.2, page 181. */
|
/* SB7xx RRG 2.3.3.2. */
|
||||||
#define AMDSB_PM_RESET_STATUS0 0x44
|
#define AMDSB_PM_RESET_STATUS0 0x44
|
||||||
#define AMDSB_PM_RESET_STATUS1 0x45
|
#define AMDSB_PM_RESET_STATUS1 0x45
|
||||||
#define AMDSB_WD_RST_STS 0x02
|
#define AMDSB_WD_RST_STS 0x02
|
||||||
/* RRG 2.3.3.2, page 188; RPR 2.36, page 30. */
|
/* SB7xx RRG 2.3.3.2, RPR 2.36. */
|
||||||
#define AMDSB_PM_WDT_CTRL 0x69
|
#define AMDSB_PM_WDT_CTRL 0x69
|
||||||
#define AMDSB_WDT_DISABLE 0x01
|
#define AMDSB_WDT_DISABLE 0x01
|
||||||
#define AMDSB_WDT_RES_MASK (0x02 | 0x04)
|
#define AMDSB_WDT_RES_MASK (0x02 | 0x04)
|
||||||
@ -77,7 +79,18 @@ __FBSDID("$FreeBSD$");
|
|||||||
#define AMDSB_WDT_RES_1S 0x06
|
#define AMDSB_WDT_RES_1S 0x06
|
||||||
#define AMDSB_PM_WDT_BASE_LSB 0x6c
|
#define AMDSB_PM_WDT_BASE_LSB 0x6c
|
||||||
#define AMDSB_PM_WDT_BASE_MSB 0x6f
|
#define AMDSB_PM_WDT_BASE_MSB 0x6f
|
||||||
/* RRG 2.3.4, page 223, WDRT. */
|
/* SB8xx RRG 2.3.3. */
|
||||||
|
#define AMDSB8_PM_WDT_EN 0x48
|
||||||
|
#define AMDSB8_WDT_DEC_EN 0x01
|
||||||
|
#define AMDSB8_WDT_DISABLE 0x02
|
||||||
|
#define AMDSB8_PM_WDT_CTRL 0x4c
|
||||||
|
#define AMDSB8_WDT_32KHZ 0x00
|
||||||
|
#define AMDSB8_WDT_1HZ 0x03
|
||||||
|
#define AMDSB8_WDT_RES_MASK 0x03
|
||||||
|
#define AMDSB8_PM_RESET_STATUS0 0xC0
|
||||||
|
#define AMDSB8_PM_RESET_STATUS1 0xC1
|
||||||
|
#define AMDSB8_WD_RST_STS 0x20
|
||||||
|
/* SB7xx RRG 2.3.4, WDRT. */
|
||||||
#define AMDSB_WD_CTRL 0x00
|
#define AMDSB_WD_CTRL 0x00
|
||||||
#define AMDSB_WD_RUN 0x01
|
#define AMDSB_WD_RUN 0x01
|
||||||
#define AMDSB_WD_FIRED 0x02
|
#define AMDSB_WD_FIRED 0x02
|
||||||
@ -90,8 +103,9 @@ __FBSDID("$FreeBSD$");
|
|||||||
#define AMDSB_WDIO_REG_WIDTH 4
|
#define AMDSB_WDIO_REG_WIDTH 4
|
||||||
/* WDRT */
|
/* WDRT */
|
||||||
#define MAXCOUNT_MIN_VALUE 511
|
#define MAXCOUNT_MIN_VALUE 511
|
||||||
/* RRG 2.3.1.1, page 122; SB600 RRG 2.3.1.1, page 97. */
|
/* SB7xx RRG 2.3.1.1, SB600 RRG 2.3.1.1, SB8xx RRG 2.3.1. */
|
||||||
#define AMDSB7xx_SMBUS_DEVID 0x43851002
|
#define AMDSB_SMBUS_DEVID 0x43851002
|
||||||
|
#define AMDSB8_SMBUS_REVID 0x40
|
||||||
|
|
||||||
#define amdsbwd_verbose_printf(dev, ...) \
|
#define amdsbwd_verbose_printf(dev, ...) \
|
||||||
do { \
|
do { \
|
||||||
@ -265,7 +279,7 @@ amdsbwd_identify(driver_t *driver, device_t parent)
|
|||||||
smb_dev = pci_find_bsf(0, 20, 0);
|
smb_dev = pci_find_bsf(0, 20, 0);
|
||||||
if (smb_dev == NULL)
|
if (smb_dev == NULL)
|
||||||
return;
|
return;
|
||||||
if (pci_get_devid(smb_dev) != AMDSB7xx_SMBUS_DEVID)
|
if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd", -1);
|
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd", -1);
|
||||||
@ -273,15 +287,102 @@ amdsbwd_identify(driver_t *driver, device_t parent)
|
|||||||
device_printf(parent, "add amdsbwd child failed\n");
|
device_printf(parent, "add amdsbwd child failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
amdsbwd_probe_sb7xx(device_t dev, struct resource *pmres, uint32_t *addr)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Report cause of previous reset for user's convenience. */
|
||||||
|
val = pmio_read(pmres, AMDSB_PM_RESET_STATUS0);
|
||||||
|
if (val != 0)
|
||||||
|
amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val);
|
||||||
|
val = pmio_read(pmres, AMDSB_PM_RESET_STATUS1);
|
||||||
|
if (val != 0)
|
||||||
|
amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val);
|
||||||
|
if ((val & AMDSB_WD_RST_STS) != 0)
|
||||||
|
device_printf(dev, "Previous Reset was caused by Watchdog\n");
|
||||||
|
|
||||||
|
/* Find base address of memory mapped WDT registers. */
|
||||||
|
for (*addr = 0, i = 0; i < 4; i++) {
|
||||||
|
*addr <<= 8;
|
||||||
|
*addr |= pmio_read(pmres, AMDSB_PM_WDT_BASE_MSB - i);
|
||||||
|
}
|
||||||
|
/* Set watchdog timer tick to 1s. */
|
||||||
|
val = pmio_read(pmres, AMDSB_PM_WDT_CTRL);
|
||||||
|
val &= ~AMDSB_WDT_RES_MASK;
|
||||||
|
val |= AMDSB_WDT_RES_10MS;
|
||||||
|
pmio_write(pmres, AMDSB_PM_WDT_CTRL, val);
|
||||||
|
|
||||||
|
/* Enable watchdog device (in stopped state). */
|
||||||
|
val = pmio_read(pmres, AMDSB_PM_WDT_CTRL);
|
||||||
|
val &= ~AMDSB_WDT_DISABLE;
|
||||||
|
pmio_write(pmres, AMDSB_PM_WDT_CTRL, val);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX TODO: Ensure that watchdog decode is enabled
|
||||||
|
* (register 0x41, bit 3).
|
||||||
|
*/
|
||||||
|
device_set_desc(dev, "AMD SB600/SB7xx Watchdog Timer");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
amdsbwd_probe_sb8xx(device_t dev, struct resource *pmres, uint32_t *addr)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Report cause of previous reset for user's convenience. */
|
||||||
|
val = pmio_read(pmres, AMDSB8_PM_RESET_STATUS0);
|
||||||
|
if (val != 0)
|
||||||
|
amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val);
|
||||||
|
val = pmio_read(pmres, AMDSB8_PM_RESET_STATUS1);
|
||||||
|
if (val != 0)
|
||||||
|
amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val);
|
||||||
|
if ((val & AMDSB8_WD_RST_STS) != 0)
|
||||||
|
device_printf(dev, "Previous Reset was caused by Watchdog\n");
|
||||||
|
|
||||||
|
/* Find base address of memory mapped WDT registers. */
|
||||||
|
for (*addr = 0, i = 0; i < 4; i++) {
|
||||||
|
*addr <<= 8;
|
||||||
|
*addr |= pmio_read(pmres, AMDSB8_PM_WDT_EN + 3 - i);
|
||||||
|
}
|
||||||
|
*addr &= ~0x07u;
|
||||||
|
|
||||||
|
/* Set watchdog timer tick to 1s. */
|
||||||
|
val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL);
|
||||||
|
val &= ~AMDSB8_WDT_RES_MASK;
|
||||||
|
val |= AMDSB8_WDT_1HZ;
|
||||||
|
pmio_write(pmres, AMDSB8_PM_WDT_CTRL, val);
|
||||||
|
#ifdef AMDSBWD_DEBUG
|
||||||
|
val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL);
|
||||||
|
amdsbwd_verbose_printf(dev, "AMDSB8_PM_WDT_CTRL value = %#02x\n", val);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable watchdog device (in stopped state)
|
||||||
|
* and decoding of its address.
|
||||||
|
*/
|
||||||
|
val = pmio_read(pmres, AMDSB8_PM_WDT_EN);
|
||||||
|
val &= ~AMDSB8_WDT_DISABLE;
|
||||||
|
val |= AMDSB8_WDT_DEC_EN;
|
||||||
|
pmio_write(pmres, AMDSB8_PM_WDT_EN, val);
|
||||||
|
#ifdef AMDSBWD_DEBUG
|
||||||
|
val = pmio_read(pmres, AMDSB8_PM_WDT_EN);
|
||||||
|
device_printf(dev, "AMDSB8_PM_WDT_EN value = %#02x\n", val);
|
||||||
|
#endif
|
||||||
|
device_set_desc(dev, "AMD SB8xx Watchdog Timer");
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
amdsbwd_probe(device_t dev)
|
amdsbwd_probe(device_t dev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
device_t smb_dev;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
uint32_t val;
|
|
||||||
int rid;
|
int rid;
|
||||||
int rc;
|
int rc;
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Do not claim some ISA PnP device by accident. */
|
/* Do not claim some ISA PnP device by accident. */
|
||||||
if (isa_get_logicalid(dev) != 0)
|
if (isa_get_logicalid(dev) != 0)
|
||||||
@ -301,21 +402,16 @@ amdsbwd_probe(device_t dev)
|
|||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Report cause of previous reset for user's convenience. */
|
smb_dev = pci_find_bsf(0, 20, 0);
|
||||||
val = pmio_read(res, AMDSB_PM_RESET_STATUS0);
|
KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n"));
|
||||||
if (val != 0)
|
if (pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID)
|
||||||
amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val);
|
amdsbwd_probe_sb7xx(dev, res, &addr);
|
||||||
val = pmio_read(res, AMDSB_PM_RESET_STATUS1);
|
else
|
||||||
if (val != 0)
|
amdsbwd_probe_sb8xx(dev, res, &addr);
|
||||||
amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val);
|
|
||||||
if ((val & AMDSB_WD_RST_STS) != 0)
|
bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
|
||||||
device_printf(dev, "Previous Reset was caused by Watchdog\n");
|
bus_delete_resource(dev, SYS_RES_IOPORT, rid);
|
||||||
|
|
||||||
/* Find base address of memory mapped WDT registers. */
|
|
||||||
for (addr = 0, i = 0; i < 4; i++) {
|
|
||||||
addr <<= 8;
|
|
||||||
addr |= pmio_read(res, AMDSB_PM_WDT_BASE_MSB - i);
|
|
||||||
}
|
|
||||||
amdsbwd_verbose_printf(dev, "memory base address = %#010x\n", addr);
|
amdsbwd_verbose_printf(dev, "memory base address = %#010x\n", addr);
|
||||||
rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL,
|
rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL,
|
||||||
AMDSB_WDIO_REG_WIDTH);
|
AMDSB_WDIO_REG_WIDTH);
|
||||||
@ -330,36 +426,25 @@ amdsbwd_probe(device_t dev)
|
|||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set watchdog timer tick to 10ms. */
|
|
||||||
val = pmio_read(res, AMDSB_PM_WDT_CTRL);
|
|
||||||
val &= ~AMDSB_WDT_RES_MASK;
|
|
||||||
val |= AMDSB_WDT_RES_10MS;
|
|
||||||
pmio_write(res, AMDSB_PM_WDT_CTRL, val);
|
|
||||||
|
|
||||||
/* Enable watchdog device (in stopped state). */
|
|
||||||
val = pmio_read(res, AMDSB_PM_WDT_CTRL);
|
|
||||||
val &= ~AMDSB_WDT_DISABLE;
|
|
||||||
pmio_write(res, AMDSB_PM_WDT_CTRL, val);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX TODO: Ensure that watchdog decode is enabled
|
|
||||||
* (register 0x41, bit 3).
|
|
||||||
*/
|
|
||||||
bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
|
|
||||||
bus_delete_resource(dev, SYS_RES_IOPORT, rid);
|
|
||||||
|
|
||||||
device_set_desc(dev, "AMD SB600/SB7xx Watchdog Timer");
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc)
|
amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc)
|
||||||
{
|
{
|
||||||
|
device_t smb_dev;
|
||||||
|
|
||||||
sc->max_ticks = UINT16_MAX;
|
sc->max_ticks = UINT16_MAX;
|
||||||
sc->ms_per_tick = 10;
|
|
||||||
sc->rid_ctrl = 0;
|
sc->rid_ctrl = 0;
|
||||||
sc->rid_count = 1;
|
sc->rid_count = 1;
|
||||||
|
|
||||||
|
smb_dev = pci_find_bsf(0, 20, 0);
|
||||||
|
KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n"));
|
||||||
|
if (pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID)
|
||||||
|
sc->ms_per_tick = 10;
|
||||||
|
else
|
||||||
|
sc->ms_per_tick = 1000;
|
||||||
|
|
||||||
sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
||||||
&sc->rid_ctrl, RF_ACTIVE);
|
&sc->rid_ctrl, RF_ACTIVE);
|
||||||
if (sc->res_ctrl == NULL) {
|
if (sc->res_ctrl == NULL) {
|
||||||
@ -388,6 +473,11 @@ amdsbwd_attach(device_t dev)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
#ifdef AMDSBWD_DEBUG
|
||||||
|
device_printf(dev, "wd ctrl = %#04x\n", wdctrl_read(sc));
|
||||||
|
device_printf(dev, "wd count = %#04x\n", wdcount_read(sc));
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Setup initial state of Watchdog Control. */
|
/* Setup initial state of Watchdog Control. */
|
||||||
wdctrl_write(sc, AMDSB_WD_FIRED);
|
wdctrl_write(sc, AMDSB_WD_FIRED);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user