1*71fbb921Smsaitoh /* $NetBSD: geodewdg.c,v 1.12 2016/07/11 11:31:49 msaitoh Exp $ */
209b51ec9Skardel
309b51ec9Skardel /*-
409b51ec9Skardel * Copyright (c) 2005 David Young. All rights reserved.
509b51ec9Skardel *
609b51ec9Skardel * This code was written by David Young.
709b51ec9Skardel *
809b51ec9Skardel * Redistribution and use in source and binary forms, with or without
909b51ec9Skardel * modification, are permitted provided that the following conditions
1009b51ec9Skardel * are met:
1109b51ec9Skardel * 1. Redistributions of source code must retain the above copyright
1209b51ec9Skardel * notice, this list of conditions and the following disclaimer.
1309b51ec9Skardel * 2. Redistributions in binary form must reproduce the above copyright
1409b51ec9Skardel * notice, this list of conditions and the following disclaimer in the
1509b51ec9Skardel * documentation and/or other materials provided with the distribution.
1609b51ec9Skardel *
1709b51ec9Skardel * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
1809b51ec9Skardel * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
1909b51ec9Skardel * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
2009b51ec9Skardel * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
2109b51ec9Skardel * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2209b51ec9Skardel * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2309b51ec9Skardel * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2409b51ec9Skardel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2509b51ec9Skardel * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2609b51ec9Skardel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2709b51ec9Skardel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
2809b51ec9Skardel * OF SUCH DAMAGE.
2909b51ec9Skardel */
3009b51ec9Skardel /*-
3109b51ec9Skardel * Copyright (c) 2002 The NetBSD Foundation, Inc.
3209b51ec9Skardel * All rights reserved.
3309b51ec9Skardel *
3409b51ec9Skardel * This code is derived from software contributed to The NetBSD Foundation
3509b51ec9Skardel * by Jason R. Thorpe.
3609b51ec9Skardel *
3709b51ec9Skardel * Redistribution and use in source and binary forms, with or without
3809b51ec9Skardel * modification, are permitted provided that the following conditions
3909b51ec9Skardel * are met:
4009b51ec9Skardel * 1. Redistributions of source code must retain the above copyright
4109b51ec9Skardel * notice, this list of conditions and the following disclaimer.
4209b51ec9Skardel * 2. Redistributions in binary form must reproduce the above copyright
4309b51ec9Skardel * notice, this list of conditions and the following disclaimer in the
4409b51ec9Skardel * documentation and/or other materials provided with the distribution.
4509b51ec9Skardel *
4609b51ec9Skardel * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
4709b51ec9Skardel * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4809b51ec9Skardel * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4909b51ec9Skardel * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5009b51ec9Skardel * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5109b51ec9Skardel * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5209b51ec9Skardel * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5309b51ec9Skardel * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5409b51ec9Skardel * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5509b51ec9Skardel * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5609b51ec9Skardel * POSSIBILITY OF SUCH DAMAGE.
5709b51ec9Skardel */
5809b51ec9Skardel
5909b51ec9Skardel /*
6009b51ec9Skardel * Device driver for the watchdog timer built into the
6109b51ec9Skardel * AMD Geode SC1100 processor.
6209b51ec9Skardel */
6309b51ec9Skardel
6409b51ec9Skardel #include <sys/cdefs.h>
6509b51ec9Skardel
66*71fbb921Smsaitoh __KERNEL_RCSID(0, "$NetBSD: geodewdg.c,v 1.12 2016/07/11 11:31:49 msaitoh Exp $");
6709b51ec9Skardel
6809b51ec9Skardel #include <sys/param.h>
6909b51ec9Skardel #include <sys/systm.h>
7009b51ec9Skardel #include <sys/device.h>
7109b51ec9Skardel #include <sys/wdog.h>
7209b51ec9Skardel #include <uvm/uvm_extern.h>
73f5b064eeSdyoung #include <sys/bus.h>
7409b51ec9Skardel #include <dev/pci/pcivar.h>
7509b51ec9Skardel #include <dev/pci/pcidevs.h>
7609b51ec9Skardel #include <arch/i386/pci/geodevar.h>
7709b51ec9Skardel #include <arch/i386/pci/geodereg.h>
7809b51ec9Skardel #include <dev/sysmon/sysmonvar.h>
7909b51ec9Skardel
8009b51ec9Skardel #ifdef GEODE_DEBUG
8109b51ec9Skardel #define GEODE_DPRINTF(__x) printf __x
8209b51ec9Skardel #else /* GEODE_DEBUG */
8309b51ec9Skardel #define GEODE_DPRINTF(__x) /* nothing */
8409b51ec9Skardel #endif
8509b51ec9Skardel
8609b51ec9Skardel struct geode_wdog_softc {
8709b51ec9Skardel struct geode_gcb_softc *sc_gcb_dev;
8809b51ec9Skardel
8909b51ec9Skardel uint16_t sc_countdown;
9009b51ec9Skardel uint8_t sc_prescale;
9109b51ec9Skardel struct sysmon_wdog sc_smw;
9209b51ec9Skardel };
9309b51ec9Skardel
9409b51ec9Skardel static int attached = 0;
9509b51ec9Skardel
9609b51ec9Skardel static void
geode_wdog_disable(struct geode_wdog_softc * sc)9709b51ec9Skardel geode_wdog_disable(struct geode_wdog_softc *sc)
9809b51ec9Skardel {
9909b51ec9Skardel uint16_t wdcnfg;
10009b51ec9Skardel
10109b51ec9Skardel /* cancel any pending countdown */
10209b51ec9Skardel sc->sc_countdown = 0;
103d4755581Sdyoung bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
104d4755581Sdyoung SC1100_GCB_WDTO, 0);
10509b51ec9Skardel /* power-down clock */
106d4755581Sdyoung wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot,
107d4755581Sdyoung sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG);
10809b51ec9Skardel
10909b51ec9Skardel GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg));
11009b51ec9Skardel
11109b51ec9Skardel wdcnfg |= SC1100_WDCNFG_WD32KPD;
11209b51ec9Skardel wdcnfg &= ~(SC1100_WDCNFG_WDTYPE2_MASK | SC1100_WDCNFG_WDTYPE1_MASK);
11309b51ec9Skardel /* This no-op is for the reader's benefit. */
11409b51ec9Skardel wdcnfg |= SC1100_WDCNFG_WDTYPE1_NOACTION |
11509b51ec9Skardel SC1100_WDCNFG_WDTYPE2_NOACTION;
116d4755581Sdyoung bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
117d4755581Sdyoung SC1100_GCB_WDCNFG, wdcnfg);
11809b51ec9Skardel
11909b51ec9Skardel GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg));
12009b51ec9Skardel }
12109b51ec9Skardel
12209b51ec9Skardel static void
geode_wdog_enable(struct geode_wdog_softc * sc)12309b51ec9Skardel geode_wdog_enable(struct geode_wdog_softc *sc)
12409b51ec9Skardel {
12509b51ec9Skardel uint16_t wdcnfg;
12609b51ec9Skardel
12709b51ec9Skardel /* power-up clock and set prescale */
128d4755581Sdyoung wdcnfg = bus_space_read_2(sc->sc_gcb_dev->sc_iot,
129d4755581Sdyoung sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDCNFG);
13009b51ec9Skardel
13109b51ec9Skardel GEODE_DPRINTF(("%s: wdcnfg %#04" PRIx16 " -> ", __func__, wdcnfg));
13209b51ec9Skardel
13309b51ec9Skardel wdcnfg &= ~(SC1100_WDCNFG_WD32KPD | SC1100_WDCNFG_WDPRES_MASK |
13409b51ec9Skardel SC1100_WDCNFG_WDTYPE1_MASK | SC1100_WDCNFG_WDTYPE2_MASK);
1358cd106d3Sdyoung wdcnfg |= __SHIFTIN(sc->sc_prescale, SC1100_WDCNFG_WDPRES_MASK);
13609b51ec9Skardel wdcnfg |= SC1100_WDCNFG_WDTYPE1_RESET | SC1100_WDCNFG_WDTYPE2_NOACTION;
13709b51ec9Skardel
138d4755581Sdyoung bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
139d4755581Sdyoung SC1100_GCB_WDCNFG, wdcnfg);
14009b51ec9Skardel
14109b51ec9Skardel GEODE_DPRINTF(("%#04" PRIx16 "\n", wdcnfg));
14209b51ec9Skardel }
14309b51ec9Skardel
14409b51ec9Skardel static void
geode_wdog_reset(struct geode_wdog_softc * sc)14509b51ec9Skardel geode_wdog_reset(struct geode_wdog_softc *sc)
14609b51ec9Skardel {
14709b51ec9Skardel /* set countdown */
148d4755581Sdyoung bus_space_write_2(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
149d4755581Sdyoung SC1100_GCB_WDTO, sc->sc_countdown);
15009b51ec9Skardel }
15109b51ec9Skardel
15209b51ec9Skardel static int
geode_wdog_tickle(struct sysmon_wdog * smw)15309b51ec9Skardel geode_wdog_tickle(struct sysmon_wdog *smw)
15409b51ec9Skardel {
15509b51ec9Skardel int s;
15609b51ec9Skardel struct geode_wdog_softc *sc = smw->smw_cookie;
15709b51ec9Skardel
15809b51ec9Skardel s = splhigh();
15909b51ec9Skardel geode_wdog_reset(sc);
16009b51ec9Skardel splx(s);
16109b51ec9Skardel return 0;
16209b51ec9Skardel }
16309b51ec9Skardel
16409b51ec9Skardel static int
geode_wdog_setmode(struct sysmon_wdog * smw)16509b51ec9Skardel geode_wdog_setmode(struct sysmon_wdog *smw)
16609b51ec9Skardel {
16709b51ec9Skardel struct geode_wdog_softc *sc = smw->smw_cookie;
16809b51ec9Skardel uint32_t ticks;
16909b51ec9Skardel int prescale, s;
17009b51ec9Skardel
17109b51ec9Skardel if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
17209b51ec9Skardel s = splhigh();
17309b51ec9Skardel geode_wdog_disable(sc);
17409b51ec9Skardel splx(s);
17509b51ec9Skardel return 0;
17609b51ec9Skardel }
17709b51ec9Skardel if (smw->smw_period == WDOG_PERIOD_DEFAULT)
17809b51ec9Skardel smw->smw_period = 32;
17909b51ec9Skardel else if (smw->smw_period > SC1100_WDIVL_MAX) /* too big? */
18009b51ec9Skardel return EINVAL;
18109b51ec9Skardel
18209b51ec9Skardel GEODE_DPRINTF(("%s: period %u\n", __func__, smw->smw_period));
18309b51ec9Skardel
18409b51ec9Skardel ticks = smw->smw_period * SC1100_WDCLK_HZ;
18509b51ec9Skardel
18609b51ec9Skardel GEODE_DPRINTF(("%s: ticks0 %" PRIu32 "\n", __func__, ticks));
18709b51ec9Skardel
18809b51ec9Skardel for (prescale = 0; ticks > UINT16_MAX; prescale++)
18909b51ec9Skardel ticks /= 2;
19009b51ec9Skardel
19109b51ec9Skardel GEODE_DPRINTF(("%s: ticks %" PRIu32 "\n", __func__, ticks));
19209b51ec9Skardel GEODE_DPRINTF(("%s: prescale %d\n", __func__, prescale));
19309b51ec9Skardel
19409b51ec9Skardel KASSERT(prescale <= SC1100_WDCNFG_WDPRES_MAX);
19509b51ec9Skardel KASSERT(ticks <= UINT16_MAX);
19609b51ec9Skardel
19709b51ec9Skardel s = splhigh();
19809b51ec9Skardel
19909b51ec9Skardel sc->sc_prescale = (uint8_t)prescale;
20009b51ec9Skardel sc->sc_countdown = (uint16_t)ticks;
20109b51ec9Skardel
20209b51ec9Skardel geode_wdog_enable(sc);
20309b51ec9Skardel
20409b51ec9Skardel geode_wdog_reset(sc);
20509b51ec9Skardel
20609b51ec9Skardel splx(s);
20709b51ec9Skardel return 0;
20809b51ec9Skardel }
20909b51ec9Skardel
21009b51ec9Skardel static int
geode_wdog_match(device_t parent,cfdata_t match,void * aux)211ed38b748Sxtraeme geode_wdog_match(device_t parent, cfdata_t match, void *aux)
21209b51ec9Skardel {
21309b51ec9Skardel return !attached;
21409b51ec9Skardel }
21509b51ec9Skardel
21609b51ec9Skardel static void
geode_wdog_attach(device_t parent,device_t self,void * aux)217d4755581Sdyoung geode_wdog_attach(device_t parent, device_t self, void *aux)
21809b51ec9Skardel {
219d4755581Sdyoung struct geode_wdog_softc *sc = device_private(self);
22009b51ec9Skardel uint8_t wdsts;
22109b51ec9Skardel
22209b51ec9Skardel aprint_naive(": Watchdog Timer\n");
22309b51ec9Skardel aprint_normal(": AMD Geode SC1100 Watchdog Timer\n");
22409b51ec9Skardel
22509b51ec9Skardel
22609b51ec9Skardel /*
22709b51ec9Skardel * Hook up the watchdog timer.
22809b51ec9Skardel */
229d4755581Sdyoung sc->sc_gcb_dev = device_private(parent);
230d4755581Sdyoung sc->sc_smw.smw_name = device_xname(self);
23109b51ec9Skardel sc->sc_smw.smw_cookie = sc;
23209b51ec9Skardel sc->sc_smw.smw_setmode = geode_wdog_setmode;
23309b51ec9Skardel sc->sc_smw.smw_tickle = geode_wdog_tickle;
23409b51ec9Skardel sc->sc_smw.smw_period = 32;
23509b51ec9Skardel
23609b51ec9Skardel /*
23709b51ec9Skardel * Determine cause of the last reset, and issue a warning if it
23809b51ec9Skardel * was due to watchdog expiry.
23909b51ec9Skardel */
240*71fbb921Smsaitoh wdsts = bus_space_read_1(sc->sc_gcb_dev->sc_iot,
241*71fbb921Smsaitoh sc->sc_gcb_dev->sc_ioh, SC1100_GCB_WDSTS);
24209b51ec9Skardel
243d4755581Sdyoung GEODE_DPRINTF(("%s: status %#02" PRIx8 "\n", device_xname(self),
24409b51ec9Skardel wdsts));
24509b51ec9Skardel
24609b51ec9Skardel if (wdsts & SC1100_WDSTS_WDRST)
24709b51ec9Skardel aprint_error(
24809b51ec9Skardel "%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n",
249d4755581Sdyoung device_xname(self));
25009b51ec9Skardel
25109b51ec9Skardel /* reset WDOVF by writing 1 to it */
252d4755581Sdyoung bus_space_write_1(sc->sc_gcb_dev->sc_iot, sc->sc_gcb_dev->sc_ioh,
253d4755581Sdyoung SC1100_GCB_WDSTS, wdsts & SC1100_WDSTS_WDOVF);
25409b51ec9Skardel
25509b51ec9Skardel if (sysmon_wdog_register(&sc->sc_smw) != 0)
25609b51ec9Skardel aprint_error("%s: unable to register watchdog with sysmon\n",
257d4755581Sdyoung device_xname(self));
25809b51ec9Skardel
25909b51ec9Skardel /* cancel any pending countdown */
26009b51ec9Skardel geode_wdog_disable(sc);
26109b51ec9Skardel
26209b51ec9Skardel attached = 1;
26309b51ec9Skardel }
26409b51ec9Skardel
2658d9f58eaSdyoung static int
geode_wdog_detach(device_t self,int flags)2668d9f58eaSdyoung geode_wdog_detach(device_t self, int flags)
2678d9f58eaSdyoung {
2688d9f58eaSdyoung int rc;
2698d9f58eaSdyoung struct geode_wdog_softc *sc = device_private(self);
2708d9f58eaSdyoung
2718d9f58eaSdyoung if ((rc = sysmon_wdog_unregister(&sc->sc_smw)) != 0) {
2728d9f58eaSdyoung if (rc == ERESTART)
2738d9f58eaSdyoung rc = EINTR;
2748d9f58eaSdyoung return rc;
2758d9f58eaSdyoung }
2768d9f58eaSdyoung
2778d9f58eaSdyoung /* cancel any pending countdown */
2788d9f58eaSdyoung geode_wdog_disable(sc);
2798d9f58eaSdyoung
2808d9f58eaSdyoung attached = 0;
2818d9f58eaSdyoung
2828d9f58eaSdyoung return 0;
2838d9f58eaSdyoung }
2848d9f58eaSdyoung
285ed38b748Sxtraeme CFATTACH_DECL_NEW(geodewdog, sizeof(struct geode_wdog_softc),
2868d9f58eaSdyoung geode_wdog_match, geode_wdog_attach, geode_wdog_detach, NULL);
287