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$
|
||||
.\"
|
||||
.Dd November 30, 2009
|
||||
.Dd June 7, 2011
|
||||
.Dt AMDSBWD 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.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
|
||||
To compile this driver into the kernel,
|
||||
place the following line in your
|
||||
@ -51,7 +51,7 @@ The
|
||||
driver provides
|
||||
.Xr watchdog 4
|
||||
support for the watchdog timers present on
|
||||
AMD SB600 and SB7xx south bridge chips.
|
||||
AMD SB600, SB7xx and SB8xx southbridges.
|
||||
.Sh SEE ALSO
|
||||
.Xr watchdog 4 ,
|
||||
.Xr watchdog 8 ,
|
||||
|
@ -25,8 +25,8 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a driver for watchdog timer present in AMD SB600/SB7xx
|
||||
* south bridges and other watchdog timers advertised via WDRT ACPI table.
|
||||
* This is a driver for watchdog timer present in AMD SB600/SB7xx/SB8xx
|
||||
* southbridges.
|
||||
* Please see the following specifications for the descriptions of the
|
||||
* registers and flags:
|
||||
* - 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
|
||||
* - AMD SB700/710/750 Register Programming Requirements (RPR)
|
||||
* 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:
|
||||
* - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT)
|
||||
* http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx
|
||||
* AMD SB600/SB7xx watchdog hardware seems to conform to the above,
|
||||
* but my system doesn't provide the table.
|
||||
* AMD SB600/SB7xx/SB8xx watchdog hardware seems to conform to the above
|
||||
* specifications, but the table hasn't been spotted in the wild yet.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
@ -59,15 +61,15 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/pci/pcivar.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_DATA (PMIO_INDEX + 1)
|
||||
#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_STATUS1 0x45
|
||||
#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_WDT_DISABLE 0x01
|
||||
#define AMDSB_WDT_RES_MASK (0x02 | 0x04)
|
||||
@ -77,7 +79,18 @@ __FBSDID("$FreeBSD$");
|
||||
#define AMDSB_WDT_RES_1S 0x06
|
||||
#define AMDSB_PM_WDT_BASE_LSB 0x6c
|
||||
#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_RUN 0x01
|
||||
#define AMDSB_WD_FIRED 0x02
|
||||
@ -90,8 +103,9 @@ __FBSDID("$FreeBSD$");
|
||||
#define AMDSB_WDIO_REG_WIDTH 4
|
||||
/* WDRT */
|
||||
#define MAXCOUNT_MIN_VALUE 511
|
||||
/* RRG 2.3.1.1, page 122; SB600 RRG 2.3.1.1, page 97. */
|
||||
#define AMDSB7xx_SMBUS_DEVID 0x43851002
|
||||
/* SB7xx RRG 2.3.1.1, SB600 RRG 2.3.1.1, SB8xx RRG 2.3.1. */
|
||||
#define AMDSB_SMBUS_DEVID 0x43851002
|
||||
#define AMDSB8_SMBUS_REVID 0x40
|
||||
|
||||
#define amdsbwd_verbose_printf(dev, ...) \
|
||||
do { \
|
||||
@ -265,7 +279,7 @@ amdsbwd_identify(driver_t *driver, device_t parent)
|
||||
smb_dev = pci_find_bsf(0, 20, 0);
|
||||
if (smb_dev == NULL)
|
||||
return;
|
||||
if (pci_get_devid(smb_dev) != AMDSB7xx_SMBUS_DEVID)
|
||||
if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID)
|
||||
return;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
amdsbwd_probe(device_t dev)
|
||||
{
|
||||
struct resource *res;
|
||||
device_t smb_dev;
|
||||
uint32_t addr;
|
||||
uint32_t val;
|
||||
int rid;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
/* Do not claim some ISA PnP device by accident. */
|
||||
if (isa_get_logicalid(dev) != 0)
|
||||
@ -301,21 +402,16 @@ amdsbwd_probe(device_t dev)
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Report cause of previous reset for user's convenience. */
|
||||
val = pmio_read(res, AMDSB_PM_RESET_STATUS0);
|
||||
if (val != 0)
|
||||
amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val);
|
||||
val = pmio_read(res, 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");
|
||||
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)
|
||||
amdsbwd_probe_sb7xx(dev, res, &addr);
|
||||
else
|
||||
amdsbwd_probe_sb8xx(dev, res, &addr);
|
||||
|
||||
bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
|
||||
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);
|
||||
rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL,
|
||||
AMDSB_WDIO_REG_WIDTH);
|
||||
@ -330,36 +426,25 @@ amdsbwd_probe(device_t dev)
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc)
|
||||
{
|
||||
device_t smb_dev;
|
||||
|
||||
sc->max_ticks = UINT16_MAX;
|
||||
sc->ms_per_tick = 10;
|
||||
sc->rid_ctrl = 0;
|
||||
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->rid_ctrl, RF_ACTIVE);
|
||||
if (sc->res_ctrl == NULL) {
|
||||
@ -388,6 +473,11 @@ amdsbwd_attach(device_t dev)
|
||||
if (rc != 0)
|
||||
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. */
|
||||
wdctrl_write(sc, AMDSB_WD_FIRED);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user