rdcpcib.c revision 1.1.4.2 1 /* $NetBSD: rdcpcib.c,v 1.1.4.2 2011/05/02 22:49:57 jym 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.1.4.2 2011/05/02 22:49:57 jym 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 <machine/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