xref: /netbsd-src/sys/dev/ic/dwc_wdt.c (revision 10f5d8578aac5933e73eaf5d1b3867d798c1f026)
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@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
dwcwdt_map_period(struct dwcwdt_softc * sc,u_int period,u_int * aperiod)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
dwcwdt_tickle(struct sysmon_wdog * smw)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
dwcwdt_setmode(struct sysmon_wdog * smw)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
dwcwdt_init(struct dwcwdt_softc * sc)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