octeon_wdog.c revision 215989
1215989Sgonzo/*- 2215989Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3215989Sgonzo * All rights reserved. 4215989Sgonzo * 5215989Sgonzo * Redistribution and use in source and binary forms, with or without 6215989Sgonzo * modification, are permitted provided that the following conditions 7215989Sgonzo * are met: 8215989Sgonzo * 1. Redistributions of source code must retain the above copyright 9215989Sgonzo * notice unmodified, this list of conditions, and the following 10215989Sgonzo * disclaimer. 11215989Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12215989Sgonzo * notice, this list of conditions and the following disclaimer in the 13215989Sgonzo * documentation and/or other materials provided with the distribution. 14215989Sgonzo * 15215989Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16215989Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17215989Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18215989Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19215989Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20215989Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21215989Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22215989Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23215989Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24215989Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25215989Sgonzo * SUCH DAMAGE. 26215989Sgonzo */ 27215989Sgonzo 28215989Sgonzo/* 29215989Sgonzo * Watchdog driver for Cavium Octeon 30215989Sgonzo */ 31215989Sgonzo 32215989Sgonzo#include <sys/cdefs.h> 33215989Sgonzo__FBSDID("$FreeBSD: head/sys/mips/cavium/octeon_wdog.c 215989 2010-11-28 08:11:05Z gonzo $"); 34215989Sgonzo 35215989Sgonzo#include <sys/param.h> 36215989Sgonzo#include <sys/systm.h> 37215989Sgonzo#include <sys/watchdog.h> 38215989Sgonzo#include <sys/bus.h> 39215989Sgonzo#include <sys/kernel.h> 40215989Sgonzo#include <sys/module.h> 41215989Sgonzo#include <sys/sysctl.h> 42215989Sgonzo#include <sys/rman.h> 43215989Sgonzo#include <sys/smp.h> 44215989Sgonzo 45215989Sgonzo#include <contrib/octeon-sdk/cvmx.h> 46215989Sgonzo#include <contrib/octeon-sdk/cvmx-interrupt.h> 47215989Sgonzo 48215989Sgonzo#define DEFAULT_TIMER_VAL 65535 49215989Sgonzo 50215989Sgonzostruct octeon_wdog_softc { 51215989Sgonzo device_t dev; 52215989Sgonzo /* XXX: replace with repscive CVMX_ constant */ 53215989Sgonzo struct resource *irq_res[16]; 54215989Sgonzo void *intr_hdl[16]; 55215989Sgonzo int armed; 56215989Sgonzo int debug; 57215989Sgonzo}; 58215989Sgonzo 59215989Sgonzoextern void octeon_wdog_nmi_handler(void); 60215989Sgonzovoid octeon_wdog_nmi(void); 61215989Sgonzo 62215989Sgonzostatic void octeon_watchdog_arm_core(int core, unsigned long timer_val); 63215989Sgonzostatic void octeon_watchdog_disarm_core(int core); 64215989Sgonzostatic int octeon_wdog_attach(device_t dev); 65215989Sgonzostatic void octeon_wdog_identify(driver_t *drv, device_t parent); 66215989Sgonzostatic int octeon_wdog_intr(void *);; 67215989Sgonzostatic int octeon_wdog_probe(device_t dev); 68215989Sgonzostatic void octeon_wdog_setup(struct octeon_wdog_softc *sc, int cpu); 69215989Sgonzostatic void octeon_wdog_sysctl(device_t dev); 70215989Sgonzostatic void octeon_wdog_watchdog_fn(void *private, u_int cmd, int *error); 71215989Sgonzo 72215989Sgonzovoid 73215989Sgonzoocteon_wdog_nmi() 74215989Sgonzo{ 75215989Sgonzo 76215989Sgonzo /* XXX: Add something useful here */ 77215989Sgonzo printf("NMI detected\n"); 78215989Sgonzo 79215989Sgonzo /* 80215989Sgonzo * This is the end 81215989Sgonzo * Beautiful friend 82215989Sgonzo * 83215989Sgonzo * Just wait for Soft Reset to come and take us 84215989Sgonzo */ 85215989Sgonzo for (;;) 86215989Sgonzo ; 87215989Sgonzo} 88215989Sgonzo 89215989Sgonzostatic void 90215989Sgonzoocteon_watchdog_arm_core(int core, unsigned long timer_val) 91215989Sgonzo{ 92215989Sgonzo cvmx_ciu_wdogx_t ciu_wdog; 93215989Sgonzo 94215989Sgonzo /* Poke it! */ 95215989Sgonzo cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); 96215989Sgonzo 97215989Sgonzo ciu_wdog.u64 = 0; 98215989Sgonzo ciu_wdog.s.len = timer_val; 99215989Sgonzo ciu_wdog.s.mode = 3; 100215989Sgonzo cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); 101215989Sgonzo} 102215989Sgonzo 103215989Sgonzostatic void 104215989Sgonzoocteon_watchdog_disarm_core(int core) 105215989Sgonzo{ 106215989Sgonzo 107215989Sgonzo cvmx_write_csr(CVMX_CIU_WDOGX(core), 0); 108215989Sgonzo} 109215989Sgonzo 110215989Sgonzo 111215989Sgonzo 112215989Sgonzostatic void 113215989Sgonzoocteon_wdog_watchdog_fn(void *private, u_int cmd, int *error) 114215989Sgonzo{ 115215989Sgonzo struct octeon_wdog_softc *sc = private; 116215989Sgonzo uint64_t timer_val = 0; 117215989Sgonzo 118215989Sgonzo cmd &= WD_INTERVAL; 119215989Sgonzo if (sc->debug) 120215989Sgonzo device_printf(sc->dev, "octeon_wdog_watchdog_fn: cmd: %x\n", cmd); 121215989Sgonzo if (cmd > 0) { 122215989Sgonzo if (sc->debug) 123215989Sgonzo device_printf(sc->dev, "octeon_wdog_watchdog_fn: programming timer: %jx\n", (uintmax_t) timer_val); 124215989Sgonzo /* 125215989Sgonzo * XXX: This should be done for every core and with value 126215989Sgonzo * calculated based on CPU frquency 127215989Sgonzo */ 128215989Sgonzo octeon_watchdog_arm_core(cvmx_get_core_num(), DEFAULT_TIMER_VAL); 129215989Sgonzo sc->armed = 1; 130215989Sgonzo *error = 0; 131215989Sgonzo } else { 132215989Sgonzo if (sc->debug) 133215989Sgonzo device_printf(sc->dev, "octeon_wdog_watchdog_fn: disarming\n"); 134215989Sgonzo if (sc->armed) { 135215989Sgonzo sc->armed = 0; 136215989Sgonzo /* XXX: This should be done for every core */ 137215989Sgonzo octeon_watchdog_disarm_core(cvmx_get_core_num()); 138215989Sgonzo } 139215989Sgonzo } 140215989Sgonzo} 141215989Sgonzo 142215989Sgonzostatic void 143215989Sgonzoocteon_wdog_sysctl(device_t dev) 144215989Sgonzo{ 145215989Sgonzo struct octeon_wdog_softc *sc = device_get_softc(dev); 146215989Sgonzo 147215989Sgonzo struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); 148215989Sgonzo struct sysctl_oid *tree = device_get_sysctl_tree(sc->dev); 149215989Sgonzo 150215989Sgonzo SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 151215989Sgonzo "debug", CTLFLAG_RW, &sc->debug, 0, 152215989Sgonzo "enable watchdog debugging"); 153215989Sgonzo SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 154215989Sgonzo "armed", CTLFLAG_RD, &sc->armed, 0, 155215989Sgonzo "whether the watchdog is armed"); 156215989Sgonzo} 157215989Sgonzo 158215989Sgonzostatic void 159215989Sgonzoocteon_wdog_setup(struct octeon_wdog_softc *sc, int cpu) 160215989Sgonzo{ 161215989Sgonzo int core, rid, err; 162215989Sgonzo 163215989Sgonzo /* XXX: map cpu id to core here ? */ 164215989Sgonzo core = cvmx_get_core_num(); 165215989Sgonzo 166215989Sgonzo /* Interrupt part */ 167215989Sgonzo rid = 0; 168215989Sgonzo sc->irq_res[core] = 169215989Sgonzo bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 170215989Sgonzo CVMX_IRQ_WDOG0+core, 171215989Sgonzo CVMX_IRQ_WDOG0+core, 1, RF_ACTIVE); 172215989Sgonzo if (!(sc->irq_res[core])) 173215989Sgonzo goto error; 174215989Sgonzo 175215989Sgonzo err = bus_setup_intr(sc->dev, sc->irq_res[core], INTR_TYPE_MISC, 176215989Sgonzo octeon_wdog_intr, NULL, sc, &sc->intr_hdl[core]); 177215989Sgonzo if (err) 178215989Sgonzo goto error; 179215989Sgonzo 180215989Sgonzo /* XXX: pin interrupt handler to the respective core */ 181215989Sgonzo 182215989Sgonzo /* Disarm by default */ 183215989Sgonzo octeon_watchdog_disarm_core(core); 184215989Sgonzo 185215989Sgonzo return; 186215989Sgonzo 187215989Sgonzoerror: 188215989Sgonzo panic("failed to setup watchdog interrupt for core %d", core); 189215989Sgonzo} 190215989Sgonzo 191215989Sgonzo 192215989Sgonzostatic int 193215989Sgonzoocteon_wdog_intr(void *sc) 194215989Sgonzo{ 195215989Sgonzo 196215989Sgonzo /* Poke it! */ 197215989Sgonzo cvmx_write_csr(CVMX_CIU_PP_POKEX(cvmx_get_core_num()), 1); 198215989Sgonzo 199215989Sgonzo return (FILTER_HANDLED); 200215989Sgonzo} 201215989Sgonzo 202215989Sgonzostatic int 203215989Sgonzoocteon_wdog_probe(device_t dev) 204215989Sgonzo{ 205215989Sgonzo 206215989Sgonzo device_set_desc(dev, "Cavium Octeon watchdog timer"); 207215989Sgonzo return (0); 208215989Sgonzo} 209215989Sgonzo 210215989Sgonzostatic int 211215989Sgonzoocteon_wdog_attach(device_t dev) 212215989Sgonzo{ 213215989Sgonzo struct octeon_wdog_softc *sc = device_get_softc(dev); 214215989Sgonzo int i; 215215989Sgonzo uint64_t *nmi_handler = (uint64_t*)octeon_wdog_nmi_handler; 216215989Sgonzo 217215989Sgonzo /* Initialise */ 218215989Sgonzo sc->armed = 0; 219215989Sgonzo sc->debug = 0; 220215989Sgonzo 221215989Sgonzo sc->dev = dev; 222215989Sgonzo EVENTHANDLER_REGISTER(watchdog_list, octeon_wdog_watchdog_fn, sc, 0); 223215989Sgonzo octeon_wdog_sysctl(dev); 224215989Sgonzo 225215989Sgonzo for (i = 0; i < 16; i++) { 226215989Sgonzo cvmx_write_csr(CVMX_MIO_BOOT_LOC_ADR, i * 8); 227215989Sgonzo cvmx_write_csr(CVMX_MIO_BOOT_LOC_DAT, nmi_handler[i]); 228215989Sgonzo } 229215989Sgonzo 230215989Sgonzo cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0x81fc0000); 231215989Sgonzo 232215989Sgonzo /* XXX: This should be done for every core */ 233215989Sgonzo octeon_wdog_setup(sc, cvmx_get_core_num()); 234215989Sgonzo return (0); 235215989Sgonzo} 236215989Sgonzo 237215989Sgonzostatic void 238215989Sgonzoocteon_wdog_identify(driver_t *drv, device_t parent) 239215989Sgonzo{ 240215989Sgonzo 241215989Sgonzo BUS_ADD_CHILD(parent, 0, "octeon_wdog", 0); 242215989Sgonzo} 243215989Sgonzo 244215989Sgonzostatic device_method_t octeon_wdog_methods[] = { 245215989Sgonzo DEVMETHOD(device_identify, octeon_wdog_identify), 246215989Sgonzo 247215989Sgonzo DEVMETHOD(device_probe, octeon_wdog_probe), 248215989Sgonzo DEVMETHOD(device_attach, octeon_wdog_attach), 249215989Sgonzo {0, 0}, 250215989Sgonzo}; 251215989Sgonzo 252215989Sgonzostatic driver_t octeon_wdog_driver = { 253215989Sgonzo "octeon_wdog", 254215989Sgonzo octeon_wdog_methods, 255215989Sgonzo sizeof(struct octeon_wdog_softc), 256215989Sgonzo}; 257215989Sgonzostatic devclass_t octeon_wdog_devclass; 258215989Sgonzo 259215989SgonzoDRIVER_MODULE(octeon_wdog, ciu, octeon_wdog_driver, octeon_wdog_devclass, 0, 0); 260