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