xref: /netbsd-src/sys/arch/x86/pci/rdcpcib.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: rdcpcib.c,v 1.2 2011/07/01 18:22:08 dyoung 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.2 2011/07/01 18:22:08 dyoung 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 static int
92 rdcpcibmatch(device_t parent, cfdata_t match, void *aux)
93 {
94 	struct pci_attach_args *pa = aux;
95 
96 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
97 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA)
98 		return 0;
99 
100 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_RDC &&
101 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_RDC_PCIB)
102 			return 10;
103 
104 	return 0;
105 }
106 
107 static void
108 rdcpcibattach(device_t parent, device_t self, void *aux)
109 {
110 	struct rdcpcib_softc *sc = device_private(self);
111 
112 	/* generic PCI/ISA bridge */
113 	pcibattach(parent, self, aux);
114 
115 	/* map indirect registers */
116 	sc->rdc_iot = x86_bus_space_io;
117 	if (bus_space_map(sc->rdc_iot, RDC_IND_BASE, RDC_IND_SIZE, 0,
118 	    &sc->rdc_ioh) != 0) {
119 		aprint_error_dev(self, "couldn't map indirect registers\n");
120 		return;
121 	}
122 
123 	/* Set up the watchdog. */
124 	rdc_wdtimer_configure(self);
125 
126 	/* Install power handler XXX */
127 	if (!pmf_device_register(self, NULL, NULL))
128 		aprint_error_dev(self, "couldn't establish power handler\n");
129 }
130 
131 static int
132 rdcpcibdetach(device_t self, int flags)
133 {
134 	struct rdcpcib_softc *sc = device_private(self);
135 	int rc;
136 
137 	pmf_device_deregister(self);
138 
139 	if ((rc = rdc_wdtimer_unconfigure(self, flags)) != 0)
140 		return rc;
141 
142 	bus_space_unmap(sc->rdc_iot, sc->rdc_ioh, RDC_IND_SIZE);
143 	return pcibdetach(self, flags);
144 }
145 
146 /* indirect registers read/write */
147 static uint8_t
148 rdc_ind_read(struct rdcpcib_softc *sc, uint8_t addr)
149 {
150 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr);
151 	return bus_space_read_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA);
152 }
153 
154 static void
155 rdc_ind_write(struct rdcpcib_softc *sc, uint8_t addr, uint8_t data)
156 {
157 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr);
158 	bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA, data);
159 }
160 
161 /*
162  * watchdog timer registers
163  */
164 
165 /* control */
166 #define RDC_WDT0_CTRL	0x37
167 #define RDC_WDT0_CTRL_EN	0x40
168 
169 /* signal select */
170 #define RDC_WDT0_SSEL	0x38
171 #define RDC_WDT0_SSEL_MSK	0xf0
172 #define RDC_WDT0_SSEL_NMI	0xc0
173 #define RDC_WDT0_SSEL_RST	0xd0
174 
175 /* counter */
176 #define RDC_WDT0_CNTL	0x39
177 #define RDC_WDT0_CNTH	0x3A
178 #define RDC_WDT0_CNTU	0x3B
179 #define RDC_WDT0_FREQ		32768 /* Hz */
180 #define RDC_WDT0_PERIOD_MAX	(1 << 24)
181 
182 /* clear counter */
183 #define RDC_WDT0_CTRL1	0x3c
184 #define RDC_WDT0_CTRL1_RELOAD	0x40
185 #define RDC_WDT0_CRTL1_FIRE	0x80
186 
187 
188 /*
189  * Initialize the watchdog timer.
190  */
191 static void
192 rdc_wdtimer_configure(device_t self)
193 {
194 	struct rdcpcib_softc *sc = device_private(self);
195 	uint8_t reg;
196 
197 	/* Explicitly stop the timer. */
198 	rdc_wdtimer_stop(sc);
199 
200 	/*
201 	 * Register the driver with the sysmon watchdog framework.
202 	 */
203 	sc->rdc_smw.smw_name = device_xname(self);
204 	sc->rdc_smw.smw_cookie = sc;
205 	sc->rdc_smw.smw_setmode = rdc_wdtimer_setmode;
206 	sc->rdc_smw.smw_tickle = rdc_wdtimer_tickle;
207 	sc->rdc_smw.smw_period = RDC_WDT0_PERIOD_MAX / RDC_WDT0_FREQ;
208 
209 	if (sysmon_wdog_register(&sc->rdc_smw)) {
210 		aprint_error_dev(self, "unable to register wdt"
211 		       "as a sysmon watchdog device.\n");
212 		return;
213 	}
214 
215 	aprint_verbose_dev(self, "watchdog timer configured.\n");
216 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL1);
217 	if (reg & RDC_WDT0_CRTL1_FIRE) {
218 		aprint_error_dev(self, "watchdog fired bit set, clearing\n");
219 		rdc_ind_write(sc, RDC_WDT0_CTRL1, reg & ~RDC_WDT0_CRTL1_FIRE);
220 	}
221 }
222 
223 static int
224 rdc_wdtimer_unconfigure(device_t self, int flags)
225 {
226 	struct rdcpcib_softc *sc = device_private(self);
227 	int rc;
228 
229 	if ((rc = sysmon_wdog_unregister(&sc->rdc_smw)) != 0) {
230 		if (rc == ERESTART)
231 			rc = EINTR;
232 		return rc;
233 	}
234 
235 	/* Explicitly stop the timer. */
236 	rdc_wdtimer_stop(sc);
237 
238 	return 0;
239 }
240 
241 
242 /*
243  * Sysmon watchdog callbacks.
244  */
245 static int
246 rdc_wdtimer_setmode(struct sysmon_wdog *smw)
247 {
248 	struct rdcpcib_softc *sc = smw->smw_cookie;
249 	unsigned int period;
250 
251 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
252 		/* Stop the timer. */
253 		rdc_wdtimer_stop(sc);
254 	} else {
255 		period = smw->smw_period * RDC_WDT0_FREQ;
256 		if (period < 1 ||
257 		    period > RDC_WDT0_PERIOD_MAX)
258 			return EINVAL;
259 		period = period - 1;
260 
261 		/* Stop the timer, */
262 		rdc_wdtimer_stop(sc);
263 
264 		/* set the timeout, */
265 		rdc_ind_write(sc, RDC_WDT0_CNTL, (period >>  0) & 0xff);
266 		rdc_ind_write(sc, RDC_WDT0_CNTH, (period >>  8) & 0xff);
267 		rdc_ind_write(sc, RDC_WDT0_CNTU, (period >> 16) & 0xff);
268 
269 		/* and start the timer again */
270 		rdc_wdtimer_start(sc);
271 	}
272 	return 0;
273 }
274 
275 static int
276 rdc_wdtimer_tickle(struct sysmon_wdog *smw)
277 {
278 	struct rdcpcib_softc *sc = smw->smw_cookie;
279 	uint8_t reg;
280 
281 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL1);
282 	rdc_ind_write(sc, RDC_WDT0_CTRL1, reg | RDC_WDT0_CTRL1_RELOAD);
283 	return 0;
284 }
285 
286 static void
287 rdc_wdtimer_stop(struct rdcpcib_softc *sc)
288 {
289 	uint8_t reg;
290 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL);
291 	rdc_ind_write(sc, RDC_WDT0_CTRL, reg & ~RDC_WDT0_CTRL_EN);
292 }
293 
294 static void
295 rdc_wdtimer_start(struct rdcpcib_softc *sc)
296 {
297 	uint8_t reg;
298 	rdc_ind_write(sc, RDC_WDT0_SSEL, RDC_WDT0_SSEL_RST);
299 	reg = rdc_ind_read(sc, RDC_WDT0_CTRL);
300 	rdc_ind_write(sc, RDC_WDT0_CTRL, reg | RDC_WDT0_CTRL_EN);
301 }
302