xref: /netbsd-src/sys/arch/x86/pci/rdcpcib.c (revision c2ac6d470a5024e58aa2482dcb6ff645aeb883bb)
1 /*	$NetBSD: rdcpcib.c,v 1.3 2020/04/07 12:42:11 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Manuel Bouyer.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 /*
28  * driver for the RDC vortex86/PMX-1000 SoC PCI-ISA bridge, which also drives
29  * the watchdog timer
30  */
31 
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: rdcpcib.c,v 1.3 2020/04/07 12:42:11 christos Exp $");
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/sysctl.h>
41 #include <sys/timetc.h>
42 #include <sys/gpio.h>
43 #include <sys/bus.h>
44 
45 #include <dev/pci/pcivar.h>
46 #include <dev/pci/pcireg.h>
47 #include <dev/pci/pcidevs.h>
48 
49 #include <dev/gpio/gpiovar.h>
50 #include <dev/sysmon/sysmonvar.h>
51 
52 #include "pcibvar.h"
53 
54 /*
55  * special registers: iospace-registers for indirect access to timer and GPIO
56  */
57 #define RDC_IND_BASE 0x22
58 #define RDC_IND_SIZE 0x2
59 #define RDC_IND_ADDR 0
60 #define RDC_IND_DATA 1
61 
62 struct rdcpcib_softc {
63 	struct pcib_softc	rdc_pcib;
64 
65 	/* indirect registers mapping */
66 	bus_space_tag_t		rdc_iot;
67 	bus_space_handle_t	rdc_ioh;
68 
69 	/* Watchdog suppoprt */
70 	struct sysmon_wdog	rdc_smw;
71 };
72 
73 static int rdcpcibmatch(device_t, cfdata_t, void *);
74 static void rdcpcibattach(device_t, device_t, void *);
75 static int rdcpcibdetach(device_t, int);
76 
77 static uint8_t rdc_ind_read(struct rdcpcib_softc *, uint8_t);
78 static void rdc_ind_write(struct rdcpcib_softc *, uint8_t, uint8_t);
79 
80 static void rdc_wdtimer_configure(device_t);
81 static int rdc_wdtimer_unconfigure(device_t, int);
82 static int rdc_wdtimer_setmode(struct sysmon_wdog *);
83 static int rdc_wdtimer_tickle(struct sysmon_wdog *);
84 static void rdc_wdtimer_stop(struct rdcpcib_softc *);
85 static void rdc_wdtimer_start(struct rdcpcib_softc *);
86 
87 CFATTACH_DECL2_NEW(rdcpcib, sizeof(struct rdcpcib_softc),
88     rdcpcibmatch, rdcpcibattach, rdcpcibdetach, NULL,
89     pcibrescan, pcibchilddet);
90 
91 
92 static const struct rdcpcib_device {
93 	pcireg_t vendor, product;
94 } rdcpcib_devices[] = {
95 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6011_PCIB},
96 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6013_PCIB},
97 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6031_PCIB},
98 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6035_PCIB},
99 	{ PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6036_PCIB},
100 };
101 
102 static int
rdcpcibmatch(device_t parent,cfdata_t match,void * aux)103 rdcpcibmatch(device_t parent, cfdata_t match, void *aux)
104 {
105 	struct pci_attach_args *pa = aux;
106 
107 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
108 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA)
109 		return 0;
110 
111 	for (size_t i = 0; i < __arraycount(rdcpcib_devices); i++) {
112 		if (PCI_VENDOR(pa->pa_id) == rdcpcib_devices[i].vendor &&
113 		    PCI_PRODUCT(pa->pa_id) == rdcpcib_devices[i].product)
114 			return 10;
115 	}
116 
117 	return 0;
118 }
119 
120 static void
rdcpcibattach(device_t parent,device_t self,void * aux)121 rdcpcibattach(device_t parent, device_t self, void *aux)
122 {
123 	struct rdcpcib_softc *sc = device_private(self);
124 
125 	/* generic PCI/ISA bridge */
126 	pcibattach(parent, self, aux);
127 
128 	/* map indirect registers */
129 	sc->rdc_iot = x86_bus_space_io;
130 	if (bus_space_map(sc->rdc_iot, RDC_IND_BASE, RDC_IND_SIZE, 0,
131 	    &sc->rdc_ioh) != 0) {
132 		aprint_error_dev(self, "couldn't map indirect registers\n");
133 		return;
134 	}
135 
136 	/* Set up the watchdog. */
137 	rdc_wdtimer_configure(self);
138 
139 	/* Install power handler XXX */
140 	if (!pmf_device_register(self, NULL, NULL))
141 		aprint_error_dev(self, "couldn't establish power handler\n");
142 }
143 
144 static int
rdcpcibdetach(device_t self,int flags)145 rdcpcibdetach(device_t self, int flags)
146 {
147 	struct rdcpcib_softc *sc = device_private(self);
148 	int rc;
149 
150 	pmf_device_deregister(self);
151 
152 	if ((rc = rdc_wdtimer_unconfigure(self, flags)) != 0)
153 		return rc;
154 
155 	bus_space_unmap(sc->rdc_iot, sc->rdc_ioh, RDC_IND_SIZE);
156 	return pcibdetach(self, flags);
157 }
158 
159 /* indirect registers read/write */
160 static uint8_t
rdc_ind_read(struct rdcpcib_softc * sc,uint8_t addr)161 rdc_ind_read(struct rdcpcib_softc *sc, uint8_t addr)
162 {
163 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr);
164 	return bus_space_read_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA);
165 }
166 
167 static void
rdc_ind_write(struct rdcpcib_softc * sc,uint8_t addr,uint8_t data)168 rdc_ind_write(struct rdcpcib_softc *sc, uint8_t addr, uint8_t data)
169 {
170 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr);
171 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA, data);
172 }
173 
174 /*
175  * watchdog timer registers
176  */
177 
178 /* control */
179 #define RDC_WDT0_CTRL	0x37
180 #define RDC_WDT0_CTRL_EN	0x40
181 
182 /* signal select */
183 #define RDC_WDT0_SSEL	0x38
184 #define RDC_WDT0_SSEL_MSK	0xf0
185 #define RDC_WDT0_SSEL_NMI	0xc0
186 #define RDC_WDT0_SSEL_RST	0xd0
187 
188 /* counter */
189 #define RDC_WDT0_CNTL	0x39
190 #define RDC_WDT0_CNTH	0x3A
191 #define RDC_WDT0_CNTU	0x3B
192 #define RDC_WDT0_FREQ		32768 /* Hz */
193 #define RDC_WDT0_PERIOD_MAX	(1 << 24)
194 
195 /* clear counter */
196 #define RDC_WDT0_CTRL1	0x3c
197 #define RDC_WDT0_CTRL1_RELOAD	0x40
198 #define RDC_WDT0_CRTL1_FIRE	0x80
199 
200 
201 /*
202  * Initialize the watchdog timer.
203  */
204 static void
rdc_wdtimer_configure(device_t self)205 rdc_wdtimer_configure(device_t self)
206 {
207 	struct rdcpcib_softc *sc = device_private(self);
208 	uint8_t reg;
209 
210 	/* Explicitly stop the timer. */
211 	rdc_wdtimer_stop(sc);
212 
213 	/*
214 	 * Register the driver with the sysmon watchdog framework.
215 	 */
216 	sc->rdc_smw.smw_name = device_xname(self);
217 	sc->rdc_smw.smw_cookie = sc;
218 	sc->rdc_smw.smw_setmode = rdc_wdtimer_setmode;
219 	sc->rdc_smw.smw_tickle = rdc_wdtimer_tickle;
220 	sc->rdc_smw.smw_period = RDC_WDT0_PERIOD_MAX / RDC_WDT0_FREQ;
221 
222 	if (sysmon_wdog_register(&sc->rdc_smw)) {
223 		aprint_error_dev(self, "unable to register wdt"
224 		       "as a sysmon watchdog device.\n");
225 		return;
226 	}
227 
228 	aprint_verbose_dev(self, "watchdog timer configured.\n");
229 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL1);
230 	if (reg & RDC_WDT0_CRTL1_FIRE) {
231 		aprint_error_dev(self, "watchdog fired bit set, clearing\n");
232 		rdc_ind_write(sc, RDC_WDT0_CTRL1, reg & ~RDC_WDT0_CRTL1_FIRE);
233 	}
234 }
235 
236 static int
rdc_wdtimer_unconfigure(device_t self,int flags)237 rdc_wdtimer_unconfigure(device_t self, int flags)
238 {
239 	struct rdcpcib_softc *sc = device_private(self);
240 	int rc;
241 
242 	if ((rc = sysmon_wdog_unregister(&sc->rdc_smw)) != 0) {
243 		if (rc == ERESTART)
244 			rc = EINTR;
245 		return rc;
246 	}
247 
248 	/* Explicitly stop the timer. */
249 	rdc_wdtimer_stop(sc);
250 
251 	return 0;
252 }
253 
254 
255 /*
256  * Sysmon watchdog callbacks.
257  */
258 static int
rdc_wdtimer_setmode(struct sysmon_wdog * smw)259 rdc_wdtimer_setmode(struct sysmon_wdog *smw)
260 {
261 	struct rdcpcib_softc *sc = smw->smw_cookie;
262 	unsigned int period;
263 
264 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
265 		/* Stop the timer. */
266 		rdc_wdtimer_stop(sc);
267 	} else {
268 		period = smw->smw_period * RDC_WDT0_FREQ;
269 		if (period < 1 ||
270 		    period > RDC_WDT0_PERIOD_MAX)
271 			return EINVAL;
272 		period = period - 1;
273 
274 		/* Stop the timer, */
275 		rdc_wdtimer_stop(sc);
276 
277 		/* set the timeout, */
278 		rdc_ind_write(sc, RDC_WDT0_CNTL, (period >>  0) & 0xff);
279 		rdc_ind_write(sc, RDC_WDT0_CNTH, (period >>  8) & 0xff);
280 		rdc_ind_write(sc, RDC_WDT0_CNTU, (period >> 16) & 0xff);
281 
282 		/* and start the timer again */
283 		rdc_wdtimer_start(sc);
284 	}
285 	return 0;
286 }
287 
288 static int
rdc_wdtimer_tickle(struct sysmon_wdog * smw)289 rdc_wdtimer_tickle(struct sysmon_wdog *smw)
290 {
291 	struct rdcpcib_softc *sc = smw->smw_cookie;
292 	uint8_t reg;
293 
294 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL1);
295 	rdc_ind_write(sc, RDC_WDT0_CTRL1, reg | RDC_WDT0_CTRL1_RELOAD);
296 	return 0;
297 }
298 
299 static void
rdc_wdtimer_stop(struct rdcpcib_softc * sc)300 rdc_wdtimer_stop(struct rdcpcib_softc *sc)
301 {
302 	uint8_t reg;
303 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL);
304 	rdc_ind_write(sc, RDC_WDT0_CTRL, reg & ~RDC_WDT0_CTRL_EN);
305 }
306 
307 static void
rdc_wdtimer_start(struct rdcpcib_softc * sc)308 rdc_wdtimer_start(struct rdcpcib_softc *sc)
309 {
310 	uint8_t reg;
311 	rdc_ind_write(sc, RDC_WDT0_SSEL, RDC_WDT0_SSEL_RST);
312 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL);
313 	rdc_ind_write(sc, RDC_WDT0_CTRL, reg | RDC_WDT0_CTRL_EN);
314 }
315