Home | History | Annotate | Line # | Download | only in ic
      1 /* $NetBSD: dwc_wdt.c,v 1.1 2023/04/16 16:51:38 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018, 2023 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: dwc_wdt.c,v 1.1 2023/04/16 16:51:38 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/device.h>
     35 #include <sys/intr.h>
     36 #include <sys/systm.h>
     37 #include <sys/mutex.h>
     38 #include <sys/wdog.h>
     39 
     40 #include <dev/sysmon/sysmonvar.h>
     41 #include <dev/ic/dwc_wdt_var.h>
     42 
     43 #define	WDT_CR				0x00
     44 #define	 WDT_CR_RST_PULSE_LENGTH	__BITS(4,2)
     45 #define	 WDT_CR_RESP_MODE		__BIT(1)
     46 #define	 WDT_CR_WDT_EN			__BIT(0)
     47 
     48 #define	WDT_TORR			0x04
     49 #define	 WDT_TORR_TIMEOUT_PERIOD	__BITS(3,0)
     50 
     51 #define	WDT_CCVR			0x08
     52 
     53 #define	WDT_CRR				0x0c
     54 #define	 WDT_CRR_CNT_RESTART		__BITS(7,0)
     55 #define	  WDT_CRR_CNT_RESTART_MAGIC	0x76
     56 
     57 #define	WDT_STAT			0x10
     58 #define	 WDT_STAT_WDT_STATUS		__BIT(0)
     59 
     60 #define	WDT_EOI				0x14
     61 #define	 WDT_EOI_WDT_INT_CLR		__BIT(0)
     62 
     63 static const uint32_t wdt_torr[] = {
     64 	0x0000ffff,
     65 	0x0001ffff,
     66 	0x0003ffff,
     67 	0x0007ffff,
     68 	0x000fffff,
     69 	0x001fffff,
     70 	0x003fffff,
     71 	0x007fffff,
     72 	0x00ffffff,
     73 	0x01ffffff,
     74 	0x03ffffff,
     75 	0x07ffffff,
     76 	0x0fffffff,
     77 	0x1fffffff,
     78 	0x3fffffff,
     79 	0x7fffffff,
     80 };
     81 
     82 #define	DWCWDT_PERIOD_DEFAULT		15
     83 
     84 #define RD4(sc, reg) \
     85 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
     86 #define WR4(sc, reg, val) \
     87 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
     88 
     89 static int
     90 dwcwdt_map_period(struct dwcwdt_softc *sc, u_int period,
     91     u_int *aperiod)
     92 {
     93 	int i;
     94 
     95 	if (period == 0)
     96 		return -1;
     97 
     98 	for (i = 0; i < __arraycount(wdt_torr); i++) {
     99 		const u_int ms = (u_int)((((uint64_t)wdt_torr[i] + 1) * 1000) / sc->sc_clkrate);
    100 		if (ms >= period * 1000) {
    101 			*aperiod = ms / 1000;
    102 			return i;
    103 		}
    104 	}
    105 
    106 	return -1;
    107 }
    108 
    109 static int
    110 dwcwdt_tickle(struct sysmon_wdog *smw)
    111 {
    112 	struct dwcwdt_softc * const sc = smw->smw_cookie;
    113 	const uint32_t crr =
    114 	    __SHIFTIN(WDT_CRR_CNT_RESTART_MAGIC, WDT_CRR_CNT_RESTART);
    115 
    116 	WR4(sc, WDT_CRR, crr);
    117 
    118 	return 0;
    119 }
    120 
    121 static int
    122 dwcwdt_setmode(struct sysmon_wdog *smw)
    123 {
    124 	struct dwcwdt_softc * const sc = smw->smw_cookie;
    125 	uint32_t cr, torr;
    126 	int intv;
    127 
    128 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
    129 		/* Watchdog can only be disarmed by a reset */
    130 		return EIO;
    131 	}
    132 
    133 	if (smw->smw_period == WDOG_PERIOD_DEFAULT)
    134 		smw->smw_period = DWCWDT_PERIOD_DEFAULT;
    135 
    136 	intv = dwcwdt_map_period(sc, smw->smw_period,
    137 	    &sc->sc_smw.smw_period);
    138 	if (intv == -1)
    139 		return EINVAL;
    140 
    141 	torr = __SHIFTIN(intv, WDT_TORR_TIMEOUT_PERIOD);
    142 	WR4(sc, WDT_TORR, torr);
    143 	dwcwdt_tickle(smw);
    144 	cr = RD4(sc, WDT_CR);
    145 	cr &= ~WDT_CR_RESP_MODE;
    146 	cr |= WDT_CR_WDT_EN;
    147 	WR4(sc, WDT_CR, cr);
    148 
    149 	return 0;
    150 }
    151 
    152 void
    153 dwcwdt_init(struct dwcwdt_softc *sc)
    154 {
    155 	if (sc->sc_clkrate == 0) {
    156 		aprint_error_dev(sc->sc_dev, "clock rate not specified\n");
    157 		return;
    158 	}
    159 
    160 	sc->sc_smw.smw_name = device_xname(sc->sc_dev);
    161 	sc->sc_smw.smw_cookie = sc;
    162 	sc->sc_smw.smw_period = DWCWDT_PERIOD_DEFAULT;
    163 	sc->sc_smw.smw_setmode = dwcwdt_setmode;
    164 	sc->sc_smw.smw_tickle = dwcwdt_tickle;
    165 
    166 	aprint_normal_dev(sc->sc_dev,
    167 	    "default watchdog period is %u seconds\n",
    168 	    sc->sc_smw.smw_period);
    169 
    170 	if (sysmon_wdog_register(&sc->sc_smw) != 0) {
    171 		aprint_error_dev(sc->sc_dev, "couldn't register with sysmon\n");
    172 	}
    173 }
    174