1199969Savg/*- 2199969Savg * Copyright (c) 2009 Andriy Gapon <avg@FreeBSD.org> 3199969Savg * All rights reserved. 4199969Savg * 5199969Savg * Redistribution and use in source and binary forms, with or without 6199969Savg * modification, are permitted provided that the following conditions 7199969Savg * are met: 8199969Savg * 1. Redistributions of source code must retain the above copyright 9199969Savg * notice, this list of conditions and the following disclaimer. 10199969Savg * 2. Redistributions in binary form must reproduce the above copyright 11199969Savg * notice, this list of conditions and the following disclaimer in the 12199969Savg * documentation and/or other materials provided with the distribution. 13199969Savg * 14199969Savg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15199969Savg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16199969Savg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17199969Savg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18199969Savg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19199969Savg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20199969Savg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21199969Savg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22199969Savg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23199969Savg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24199969Savg * SUCH DAMAGE. 25199969Savg */ 26199969Savg 27199969Savg/* 28222805Savg * This is a driver for watchdog timer present in AMD SB600/SB7xx/SB8xx 29222805Savg * southbridges. 30199969Savg * Please see the following specifications for the descriptions of the 31199969Savg * registers and flags: 32199969Savg * - AMD SB600 Register Reference Guide, Public Version, Rev. 3.03 (SB600 RRG) 33199969Savg * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/46155_sb600_rrg_pub_3.03.pdf 34199969Savg * - AMD SB700/710/750 Register Reference Guide (RRG) 35199969Savg * http://developer.amd.com/assets/43009_sb7xx_rrg_pub_1.00.pdf 36199969Savg * - AMD SB700/710/750 Register Programming Requirements (RPR) 37199969Savg * http://developer.amd.com/assets/42413_sb7xx_rpr_pub_1.00.pdf 38222805Savg * - AMD SB800-Series Southbridges Register Reference Guide (RRG) 39222805Savg * http://support.amd.com/us/Embedded_TechDocs/45482.pdf 40199969Savg * Please see the following for Watchdog Resource Table specification: 41199969Savg * - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT) 42199969Savg * http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx 43222805Savg * AMD SB600/SB7xx/SB8xx watchdog hardware seems to conform to the above 44222805Savg * specifications, but the table hasn't been spotted in the wild yet. 45199969Savg */ 46199969Savg 47199969Savg#include <sys/cdefs.h> 48199969Savg__FBSDID("$FreeBSD$"); 49199969Savg 50199969Savg#include <sys/param.h> 51199969Savg#include <sys/kernel.h> 52199969Savg#include <sys/module.h> 53199969Savg#include <sys/systm.h> 54199969Savg#include <sys/sysctl.h> 55199969Savg#include <sys/bus.h> 56199969Savg#include <machine/bus.h> 57199969Savg#include <sys/rman.h> 58199969Savg#include <machine/resource.h> 59199969Savg#include <sys/watchdog.h> 60199969Savg 61199969Savg#include <dev/pci/pcivar.h> 62199969Savg#include <isa/isavar.h> 63199969Savg 64222805Savg/* SB7xx RRG 2.3.3.1.1. */ 65199969Savg#define AMDSB_PMIO_INDEX 0xcd6 66199969Savg#define AMDSB_PMIO_DATA (PMIO_INDEX + 1) 67199969Savg#define AMDSB_PMIO_WIDTH 2 68222805Savg/* SB7xx RRG 2.3.3.2. */ 69199969Savg#define AMDSB_PM_RESET_STATUS0 0x44 70199969Savg#define AMDSB_PM_RESET_STATUS1 0x45 71199969Savg#define AMDSB_WD_RST_STS 0x02 72222805Savg/* SB7xx RRG 2.3.3.2, RPR 2.36. */ 73199969Savg#define AMDSB_PM_WDT_CTRL 0x69 74199969Savg#define AMDSB_WDT_DISABLE 0x01 75199969Savg#define AMDSB_WDT_RES_MASK (0x02 | 0x04) 76199969Savg#define AMDSB_WDT_RES_32US 0x00 77199969Savg#define AMDSB_WDT_RES_10MS 0x02 78199969Savg#define AMDSB_WDT_RES_100MS 0x04 79199969Savg#define AMDSB_WDT_RES_1S 0x06 80199969Savg#define AMDSB_PM_WDT_BASE_LSB 0x6c 81199969Savg#define AMDSB_PM_WDT_BASE_MSB 0x6f 82222805Savg/* SB8xx RRG 2.3.3. */ 83222805Savg#define AMDSB8_PM_WDT_EN 0x48 84222805Savg#define AMDSB8_WDT_DEC_EN 0x01 85222805Savg#define AMDSB8_WDT_DISABLE 0x02 86222805Savg#define AMDSB8_PM_WDT_CTRL 0x4c 87222805Savg#define AMDSB8_WDT_32KHZ 0x00 88222805Savg#define AMDSB8_WDT_1HZ 0x03 89222805Savg#define AMDSB8_WDT_RES_MASK 0x03 90222805Savg#define AMDSB8_PM_RESET_STATUS0 0xC0 91222805Savg#define AMDSB8_PM_RESET_STATUS1 0xC1 92222805Savg#define AMDSB8_WD_RST_STS 0x20 93222805Savg/* SB7xx RRG 2.3.4, WDRT. */ 94199969Savg#define AMDSB_WD_CTRL 0x00 95199969Savg#define AMDSB_WD_RUN 0x01 96199969Savg#define AMDSB_WD_FIRED 0x02 97199969Savg#define AMDSB_WD_SHUTDOWN 0x04 98199969Savg#define AMDSB_WD_DISABLE 0x08 99199969Savg#define AMDSB_WD_RESERVED 0x70 100199969Savg#define AMDSB_WD_RELOAD 0x80 101199969Savg#define AMDSB_WD_COUNT 0x04 102199969Savg#define AMDSB_WD_COUNT_MASK 0xffff 103199969Savg#define AMDSB_WDIO_REG_WIDTH 4 104199969Savg/* WDRT */ 105199969Savg#define MAXCOUNT_MIN_VALUE 511 106222805Savg/* SB7xx RRG 2.3.1.1, SB600 RRG 2.3.1.1, SB8xx RRG 2.3.1. */ 107222805Savg#define AMDSB_SMBUS_DEVID 0x43851002 108222805Savg#define AMDSB8_SMBUS_REVID 0x40 109199969Savg 110199969Savg#define amdsbwd_verbose_printf(dev, ...) \ 111199969Savg do { \ 112199969Savg if (bootverbose) \ 113199969Savg device_printf(dev, __VA_ARGS__);\ 114199969Savg } while (0) 115199969Savg 116199969Savgstruct amdsbwd_softc { 117199969Savg device_t dev; 118199969Savg eventhandler_tag ev_tag; 119199969Savg struct resource *res_ctrl; 120199969Savg struct resource *res_count; 121199969Savg int rid_ctrl; 122199969Savg int rid_count; 123199969Savg int ms_per_tick; 124199969Savg int max_ticks; 125199969Savg int active; 126199969Savg unsigned int timeout; 127199969Savg}; 128199969Savg 129199969Savgstatic void amdsbwd_identify(driver_t *driver, device_t parent); 130199969Savgstatic int amdsbwd_probe(device_t dev); 131199969Savgstatic int amdsbwd_attach(device_t dev); 132199969Savgstatic int amdsbwd_detach(device_t dev); 133199969Savg 134199969Savgstatic device_method_t amdsbwd_methods[] = { 135199969Savg DEVMETHOD(device_identify, amdsbwd_identify), 136199969Savg DEVMETHOD(device_probe, amdsbwd_probe), 137199969Savg DEVMETHOD(device_attach, amdsbwd_attach), 138199969Savg DEVMETHOD(device_detach, amdsbwd_detach), 139199969Savg#if 0 140199969Savg DEVMETHOD(device_shutdown, amdsbwd_detach), 141199969Savg#endif 142199969Savg {0, 0} 143199969Savg}; 144199969Savg 145199969Savgstatic devclass_t amdsbwd_devclass; 146199969Savgstatic driver_t amdsbwd_driver = { 147199969Savg "amdsbwd", 148199969Savg amdsbwd_methods, 149199969Savg sizeof(struct amdsbwd_softc) 150199969Savg}; 151199969Savg 152199969SavgDRIVER_MODULE(amdsbwd, isa, amdsbwd_driver, amdsbwd_devclass, NULL, NULL); 153199969Savg 154199969Savg 155199969Savgstatic uint8_t 156199969Savgpmio_read(struct resource *res, uint8_t reg) 157199969Savg{ 158199969Savg bus_write_1(res, 0, reg); /* Index */ 159199969Savg return (bus_read_1(res, 1)); /* Data */ 160199969Savg} 161199969Savg 162199969Savgstatic void 163199969Savgpmio_write(struct resource *res, uint8_t reg, uint8_t val) 164199969Savg{ 165199969Savg bus_write_1(res, 0, reg); /* Index */ 166199969Savg bus_write_1(res, 1, val); /* Data */ 167199969Savg} 168199969Savg 169199969Savgstatic uint32_t 170199969Savgwdctrl_read(struct amdsbwd_softc *sc) 171199969Savg{ 172199969Savg return (bus_read_4(sc->res_ctrl, 0)); 173199969Savg} 174199969Savg 175199969Savgstatic void 176199969Savgwdctrl_write(struct amdsbwd_softc *sc, uint32_t val) 177199969Savg{ 178199969Savg bus_write_4(sc->res_ctrl, 0, val); 179199969Savg} 180199969Savg 181199969Savgstatic __unused uint32_t 182199969Savgwdcount_read(struct amdsbwd_softc *sc) 183199969Savg{ 184199969Savg return (bus_read_4(sc->res_count, 0)); 185199969Savg} 186199969Savg 187199969Savgstatic void 188199969Savgwdcount_write(struct amdsbwd_softc *sc, uint32_t val) 189199969Savg{ 190199969Savg bus_write_4(sc->res_count, 0, val); 191199969Savg} 192199969Savg 193199969Savgstatic void 194199969Savgamdsbwd_tmr_enable(struct amdsbwd_softc *sc) 195199969Savg{ 196199969Savg uint32_t val; 197199969Savg 198199969Savg val = wdctrl_read(sc); 199199969Savg val |= AMDSB_WD_RUN; 200199969Savg wdctrl_write(sc, val); 201199969Savg sc->active = 1; 202199969Savg amdsbwd_verbose_printf(sc->dev, "timer enabled\n"); 203199969Savg} 204199969Savg 205199969Savgstatic void 206199969Savgamdsbwd_tmr_disable(struct amdsbwd_softc *sc) 207199969Savg{ 208199969Savg uint32_t val; 209199969Savg 210199969Savg val = wdctrl_read(sc); 211199969Savg val &= ~AMDSB_WD_RUN; 212199969Savg wdctrl_write(sc, val); 213199969Savg sc->active = 0; 214199969Savg amdsbwd_verbose_printf(sc->dev, "timer disabled\n"); 215199969Savg} 216199969Savg 217199969Savgstatic void 218199969Savgamdsbwd_tmr_reload(struct amdsbwd_softc *sc) 219199969Savg{ 220199969Savg uint32_t val; 221199969Savg 222199969Savg val = wdctrl_read(sc); 223199969Savg val |= AMDSB_WD_RELOAD; 224199969Savg wdctrl_write(sc, val); 225199969Savg} 226199969Savg 227199969Savgstatic void 228199969Savgamdsbwd_tmr_set(struct amdsbwd_softc *sc, uint16_t timeout) 229199969Savg{ 230199969Savg 231199969Savg timeout &= AMDSB_WD_COUNT_MASK; 232199969Savg wdcount_write(sc, timeout); 233199969Savg sc->timeout = timeout; 234199969Savg amdsbwd_verbose_printf(sc->dev, "timeout set to %u ticks\n", timeout); 235199969Savg} 236199969Savg 237199969Savgstatic void 238199969Savgamdsbwd_event(void *arg, unsigned int cmd, int *error) 239199969Savg{ 240199969Savg struct amdsbwd_softc *sc = arg; 241199969Savg unsigned int timeout; 242199969Savg 243199969Savg /* convert from power-of-two-ns to WDT ticks */ 244199969Savg cmd &= WD_INTERVAL; 245199969Savg if (cmd < WD_TO_1SEC) 246199969Savg cmd = 0; 247199969Savg if (cmd) { 248208670Savg timeout = ((uint64_t)1 << (cmd - WD_TO_1MS)) / sc->ms_per_tick; 249208670Savg if (timeout > sc->max_ticks) 250208670Savg timeout = sc->max_ticks; 251199969Savg if (timeout != sc->timeout) { 252199969Savg amdsbwd_tmr_set(sc, timeout); 253199969Savg if (!sc->active) 254199969Savg amdsbwd_tmr_enable(sc); 255199969Savg } 256199969Savg amdsbwd_tmr_reload(sc); 257199969Savg *error = 0; 258199969Savg } else { 259199969Savg if (sc->active) 260199969Savg amdsbwd_tmr_disable(sc); 261199969Savg } 262199969Savg} 263199969Savg 264199969Savgstatic void 265199969Savgamdsbwd_identify(driver_t *driver, device_t parent) 266199969Savg{ 267199969Savg device_t child; 268199969Savg device_t smb_dev; 269199969Savg 270199969Savg if (resource_disabled("amdsbwd", 0)) 271199969Savg return; 272199969Savg if (device_find_child(parent, "amdsbwd", -1) != NULL) 273199969Savg return; 274199969Savg 275199969Savg /* 276199969Savg * Try to identify SB600/SB7xx by PCI Device ID of SMBus device 277199969Savg * that should be present at bus 0, device 20, function 0. 278199969Savg */ 279199969Savg smb_dev = pci_find_bsf(0, 20, 0); 280199969Savg if (smb_dev == NULL) 281199969Savg return; 282222805Savg if (pci_get_devid(smb_dev) != AMDSB_SMBUS_DEVID) 283199969Savg return; 284199969Savg 285199969Savg child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd", -1); 286199969Savg if (child == NULL) 287199969Savg device_printf(parent, "add amdsbwd child failed\n"); 288199969Savg} 289199969Savg 290222805Savg 291222805Savgstatic void 292222805Savgamdsbwd_probe_sb7xx(device_t dev, struct resource *pmres, uint32_t *addr) 293222805Savg{ 294222805Savg uint32_t val; 295222805Savg int i; 296222805Savg 297222805Savg /* Report cause of previous reset for user's convenience. */ 298222805Savg val = pmio_read(pmres, AMDSB_PM_RESET_STATUS0); 299222805Savg if (val != 0) 300222805Savg amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val); 301222805Savg val = pmio_read(pmres, AMDSB_PM_RESET_STATUS1); 302222805Savg if (val != 0) 303222805Savg amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val); 304222805Savg if ((val & AMDSB_WD_RST_STS) != 0) 305222805Savg device_printf(dev, "Previous Reset was caused by Watchdog\n"); 306222805Savg 307222805Savg /* Find base address of memory mapped WDT registers. */ 308222805Savg for (*addr = 0, i = 0; i < 4; i++) { 309222805Savg *addr <<= 8; 310222805Savg *addr |= pmio_read(pmres, AMDSB_PM_WDT_BASE_MSB - i); 311222805Savg } 312222805Savg /* Set watchdog timer tick to 1s. */ 313222805Savg val = pmio_read(pmres, AMDSB_PM_WDT_CTRL); 314222805Savg val &= ~AMDSB_WDT_RES_MASK; 315222805Savg val |= AMDSB_WDT_RES_10MS; 316222805Savg pmio_write(pmres, AMDSB_PM_WDT_CTRL, val); 317222805Savg 318222805Savg /* Enable watchdog device (in stopped state). */ 319222805Savg val = pmio_read(pmres, AMDSB_PM_WDT_CTRL); 320222805Savg val &= ~AMDSB_WDT_DISABLE; 321222805Savg pmio_write(pmres, AMDSB_PM_WDT_CTRL, val); 322222805Savg 323222805Savg /* 324222805Savg * XXX TODO: Ensure that watchdog decode is enabled 325222805Savg * (register 0x41, bit 3). 326222805Savg */ 327222805Savg device_set_desc(dev, "AMD SB600/SB7xx Watchdog Timer"); 328222805Savg} 329222805Savg 330222805Savgstatic void 331222805Savgamdsbwd_probe_sb8xx(device_t dev, struct resource *pmres, uint32_t *addr) 332222805Savg{ 333222805Savg uint32_t val; 334222805Savg int i; 335222805Savg 336222805Savg /* Report cause of previous reset for user's convenience. */ 337222805Savg val = pmio_read(pmres, AMDSB8_PM_RESET_STATUS0); 338222805Savg if (val != 0) 339222805Savg amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val); 340222805Savg val = pmio_read(pmres, AMDSB8_PM_RESET_STATUS1); 341222805Savg if (val != 0) 342222805Savg amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val); 343222805Savg if ((val & AMDSB8_WD_RST_STS) != 0) 344222805Savg device_printf(dev, "Previous Reset was caused by Watchdog\n"); 345222805Savg 346222805Savg /* Find base address of memory mapped WDT registers. */ 347222805Savg for (*addr = 0, i = 0; i < 4; i++) { 348222805Savg *addr <<= 8; 349222805Savg *addr |= pmio_read(pmres, AMDSB8_PM_WDT_EN + 3 - i); 350222805Savg } 351222805Savg *addr &= ~0x07u; 352222805Savg 353222805Savg /* Set watchdog timer tick to 1s. */ 354222805Savg val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL); 355222805Savg val &= ~AMDSB8_WDT_RES_MASK; 356222805Savg val |= AMDSB8_WDT_1HZ; 357222805Savg pmio_write(pmres, AMDSB8_PM_WDT_CTRL, val); 358222805Savg#ifdef AMDSBWD_DEBUG 359222805Savg val = pmio_read(pmres, AMDSB8_PM_WDT_CTRL); 360222805Savg amdsbwd_verbose_printf(dev, "AMDSB8_PM_WDT_CTRL value = %#02x\n", val); 361222805Savg#endif 362222805Savg 363222805Savg /* 364222805Savg * Enable watchdog device (in stopped state) 365222805Savg * and decoding of its address. 366222805Savg */ 367222805Savg val = pmio_read(pmres, AMDSB8_PM_WDT_EN); 368222805Savg val &= ~AMDSB8_WDT_DISABLE; 369222805Savg val |= AMDSB8_WDT_DEC_EN; 370222805Savg pmio_write(pmres, AMDSB8_PM_WDT_EN, val); 371222805Savg#ifdef AMDSBWD_DEBUG 372222805Savg val = pmio_read(pmres, AMDSB8_PM_WDT_EN); 373222805Savg device_printf(dev, "AMDSB8_PM_WDT_EN value = %#02x\n", val); 374222805Savg#endif 375222805Savg device_set_desc(dev, "AMD SB8xx Watchdog Timer"); 376222805Savg} 377222805Savg 378199969Savgstatic int 379199969Savgamdsbwd_probe(device_t dev) 380199969Savg{ 381199969Savg struct resource *res; 382222805Savg device_t smb_dev; 383199969Savg uint32_t addr; 384199969Savg int rid; 385199969Savg int rc; 386199969Savg 387199969Savg /* Do not claim some ISA PnP device by accident. */ 388199969Savg if (isa_get_logicalid(dev) != 0) 389199969Savg return (ENXIO); 390199969Savg 391199969Savg rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, AMDSB_PMIO_INDEX, 392199969Savg AMDSB_PMIO_WIDTH); 393199969Savg if (rc != 0) { 394199969Savg device_printf(dev, "bus_set_resource for IO failed\n"); 395199969Savg return (ENXIO); 396199969Savg } 397199969Savg rid = 0; 398199969Savg res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul, 399199969Savg AMDSB_PMIO_WIDTH, RF_ACTIVE | RF_SHAREABLE); 400199969Savg if (res == NULL) { 401199969Savg device_printf(dev, "bus_alloc_resource for IO failed\n"); 402199969Savg return (ENXIO); 403199969Savg } 404199969Savg 405222805Savg smb_dev = pci_find_bsf(0, 20, 0); 406222805Savg KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n")); 407222805Savg if (pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID) 408222805Savg amdsbwd_probe_sb7xx(dev, res, &addr); 409222805Savg else 410222805Savg amdsbwd_probe_sb8xx(dev, res, &addr); 411199969Savg 412222805Savg bus_release_resource(dev, SYS_RES_IOPORT, rid, res); 413222805Savg bus_delete_resource(dev, SYS_RES_IOPORT, rid); 414222805Savg 415199969Savg amdsbwd_verbose_printf(dev, "memory base address = %#010x\n", addr); 416199969Savg rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL, 417199969Savg AMDSB_WDIO_REG_WIDTH); 418199969Savg if (rc != 0) { 419199969Savg device_printf(dev, "bus_set_resource for control failed\n"); 420199969Savg return (ENXIO); 421199969Savg } 422199969Savg rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, addr + AMDSB_WD_COUNT, 423199969Savg AMDSB_WDIO_REG_WIDTH); 424199969Savg if (rc != 0) { 425199969Savg device_printf(dev, "bus_set_resource for count failed\n"); 426199969Savg return (ENXIO); 427199969Savg } 428199969Savg 429199969Savg return (0); 430199969Savg} 431199969Savg 432199969Savgstatic int 433199969Savgamdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc) 434199969Savg{ 435222805Savg device_t smb_dev; 436222805Savg 437199969Savg sc->max_ticks = UINT16_MAX; 438199969Savg sc->rid_ctrl = 0; 439199969Savg sc->rid_count = 1; 440199969Savg 441222805Savg smb_dev = pci_find_bsf(0, 20, 0); 442222805Savg KASSERT(smb_dev != NULL, ("can't find SMBus PCI device\n")); 443222805Savg if (pci_get_revid(smb_dev) < AMDSB8_SMBUS_REVID) 444222805Savg sc->ms_per_tick = 10; 445222805Savg else 446222805Savg sc->ms_per_tick = 1000; 447222805Savg 448199969Savg sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 449199969Savg &sc->rid_ctrl, RF_ACTIVE); 450199969Savg if (sc->res_ctrl == NULL) { 451199969Savg device_printf(dev, "bus_alloc_resource for ctrl failed\n"); 452199969Savg return (ENXIO); 453199969Savg } 454199969Savg sc->res_count = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 455199969Savg &sc->rid_count, RF_ACTIVE); 456199969Savg if (sc->res_count == NULL) { 457199969Savg device_printf(dev, "bus_alloc_resource for count failed\n"); 458199969Savg return (ENXIO); 459199969Savg } 460199969Savg return (0); 461199969Savg} 462199969Savg 463199969Savgstatic int 464199969Savgamdsbwd_attach(device_t dev) 465199969Savg{ 466199969Savg struct amdsbwd_softc *sc; 467199969Savg int rc; 468199969Savg 469199969Savg sc = device_get_softc(dev); 470199969Savg sc->dev = dev; 471199969Savg 472199969Savg rc = amdsbwd_attach_sb(dev, sc); 473199969Savg if (rc != 0) 474199969Savg goto fail; 475199969Savg 476222805Savg#ifdef AMDSBWD_DEBUG 477222805Savg device_printf(dev, "wd ctrl = %#04x\n", wdctrl_read(sc)); 478222805Savg device_printf(dev, "wd count = %#04x\n", wdcount_read(sc)); 479222805Savg#endif 480222805Savg 481199969Savg /* Setup initial state of Watchdog Control. */ 482199969Savg wdctrl_write(sc, AMDSB_WD_FIRED); 483199969Savg 484199969Savg if (wdctrl_read(sc) & AMDSB_WD_DISABLE) { 485199969Savg device_printf(dev, "watchdog hardware is disabled\n"); 486199969Savg goto fail; 487199969Savg } 488199969Savg 489199969Savg sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, amdsbwd_event, sc, 490199969Savg EVENTHANDLER_PRI_ANY); 491199969Savg 492199969Savg return (0); 493199969Savg 494199969Savgfail: 495199969Savg amdsbwd_detach(dev); 496199969Savg return (ENXIO); 497199969Savg} 498199969Savg 499199969Savgstatic int 500199969Savgamdsbwd_detach(device_t dev) 501199969Savg{ 502199969Savg struct amdsbwd_softc *sc; 503199969Savg 504199969Savg sc = device_get_softc(dev); 505199969Savg if (sc->ev_tag != NULL) 506199969Savg EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); 507199969Savg 508199969Savg if (sc->active) 509199969Savg amdsbwd_tmr_disable(sc); 510199969Savg 511199969Savg if (sc->res_ctrl != NULL) 512199969Savg bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_ctrl, 513199969Savg sc->res_ctrl); 514199969Savg 515199969Savg if (sc->res_count != NULL) 516199969Savg bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_count, 517199969Savg sc->res_count); 518199969Savg 519199969Savg return (0); 520199969Savg} 521199969Savg 522