1 /* $NetBSD: rdcpcib.c,v 1.4 2026/03/31 19:47:47 andvar 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.4 2026/03/31 19:47:47 andvar 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 bool wdt0_mapped; 72 }; 73 74 static int rdcpcibmatch(device_t, cfdata_t, void *); 75 static void rdcpcibattach(device_t, device_t, void *); 76 static int rdcpcibdetach(device_t, int); 77 78 static uint8_t rdc_ind_read(struct rdcpcib_softc *, uint8_t); 79 static void rdc_ind_write(struct rdcpcib_softc *, uint8_t, uint8_t); 80 81 static void rdc_wdtimer_configure(device_t); 82 static int rdc_wdtimer_unconfigure(device_t, int); 83 static int rdc_wdtimer_setmode(struct sysmon_wdog *); 84 static int rdc_wdtimer_tickle(struct sysmon_wdog *); 85 static void rdc_wdtimer_stop(struct rdcpcib_softc *); 86 static void rdc_wdtimer_start(struct rdcpcib_softc *); 87 88 CFATTACH_DECL2_NEW(rdcpcib, sizeof(struct rdcpcib_softc), 89 rdcpcibmatch, rdcpcibattach, rdcpcibdetach, NULL, 90 pcibrescan, pcibchilddet); 91 92 93 static const struct rdcpcib_device { 94 pcireg_t vendor, product; 95 } rdcpcib_devices[] = { 96 { PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6011_PCIB}, 97 { PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6013_PCIB}, 98 { PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6031_PCIB}, 99 { PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6035_PCIB}, 100 { PCI_VENDOR_RDC, PCI_PRODUCT_RDC_R6036_PCIB}, 101 }; 102 103 static int 104 rdcpcibmatch(device_t parent, cfdata_t match, void *aux) 105 { 106 struct pci_attach_args *pa = aux; 107 108 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE || 109 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA) 110 return 0; 111 112 for (size_t i = 0; i < __arraycount(rdcpcib_devices); i++) { 113 if (PCI_VENDOR(pa->pa_id) == rdcpcib_devices[i].vendor && 114 PCI_PRODUCT(pa->pa_id) == rdcpcib_devices[i].product) 115 return 10; 116 } 117 118 return 0; 119 } 120 121 static void 122 rdcpcibattach(device_t parent, device_t self, void *aux) 123 { 124 struct rdcpcib_softc *sc = device_private(self); 125 struct pci_attach_args *pa = aux; 126 127 /* generic PCI/ISA bridge */ 128 pcibattach(parent, self, aux); 129 130 sc->wdt0_mapped = false; 131 /* SB function 0/1 have separate configuration register sets */ 132 if (pa->pa_function == 0) 133 /* Set up the watchdog. */ 134 rdc_wdtimer_configure(self); 135 136 /* Install power handler XXX */ 137 if (!pmf_device_register(self, NULL, NULL)) 138 aprint_error_dev(self, "couldn't establish power handler\n"); 139 } 140 141 static int 142 rdcpcibdetach(device_t self, int flags) 143 { 144 struct rdcpcib_softc *sc = device_private(self); 145 int rc; 146 147 pmf_device_deregister(self); 148 149 if (sc->wdt0_mapped) 150 if ((rc = rdc_wdtimer_unconfigure(self, flags)) != 0) 151 return rc; 152 153 return pcibdetach(self, flags); 154 } 155 156 /* indirect registers read/write */ 157 static uint8_t 158 rdc_ind_read(struct rdcpcib_softc *sc, uint8_t addr) 159 { 160 bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr); 161 return bus_space_read_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA); 162 } 163 164 static void 165 rdc_ind_write(struct rdcpcib_softc *sc, uint8_t addr, uint8_t data) 166 { 167 bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr); 168 bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA, data); 169 } 170 171 /* 172 * watchdog timer registers 173 */ 174 175 /* control */ 176 #define RDC_WDT0_CTRL 0x37 177 #define RDC_WDT0_CTRL_EN 0x40 178 179 /* signal select */ 180 #define RDC_WDT0_SSEL 0x38 181 #define RDC_WDT0_SSEL_MSK 0xf0 182 #define RDC_WDT0_SSEL_NMI 0xc0 183 #define RDC_WDT0_SSEL_RST 0xd0 184 185 /* counter */ 186 #define RDC_WDT0_CNTL 0x39 187 #define RDC_WDT0_CNTH 0x3A 188 #define RDC_WDT0_CNTU 0x3B 189 #define RDC_WDT0_FREQ 32768 /* Hz */ 190 #define RDC_WDT0_PERIOD_MAX (1 << 24) 191 192 /* clear counter */ 193 #define RDC_WDT0_CTRL1 0x3c 194 #define RDC_WDT0_CTRL1_RELOAD 0x40 195 #define RDC_WDT0_CRTL1_FIRE 0x80 196 197 198 /* 199 * Initialize the watchdog timer. 200 */ 201 static void 202 rdc_wdtimer_configure(device_t self) 203 { 204 struct rdcpcib_softc *sc = device_private(self); 205 uint8_t reg; 206 207 /* map indirect access registers */ 208 sc->rdc_iot = x86_bus_space_io; 209 if (bus_space_map(sc->rdc_iot, RDC_IND_BASE, RDC_IND_SIZE, 0, 210 &sc->rdc_ioh) != 0) { 211 aprint_error_dev(self, 212 "couldn't map indirect access registers\n"); 213 return; 214 } 215 216 /* Explicitly stop the timer. */ 217 rdc_wdtimer_stop(sc); 218 219 /* 220 * Register the driver with the sysmon watchdog framework. 221 */ 222 sc->rdc_smw.smw_name = device_xname(self); 223 sc->rdc_smw.smw_cookie = sc; 224 sc->rdc_smw.smw_setmode = rdc_wdtimer_setmode; 225 sc->rdc_smw.smw_tickle = rdc_wdtimer_tickle; 226 sc->rdc_smw.smw_period = RDC_WDT0_PERIOD_MAX / RDC_WDT0_FREQ; 227 228 if (sysmon_wdog_register(&sc->rdc_smw)) { 229 aprint_error_dev(self, "unable to register wdt" 230 "as a sysmon watchdog device.\n"); 231 bus_space_unmap(sc->rdc_iot, sc->rdc_ioh, RDC_IND_SIZE); 232 return; 233 } 234 sc->wdt0_mapped = true; 235 236 aprint_verbose_dev(self, "watchdog timer configured.\n"); 237 reg = rdc_ind_read(sc, RDC_WDT0_CTRL1); 238 if (reg & RDC_WDT0_CRTL1_FIRE) { 239 aprint_error_dev(self, "watchdog fired bit set, clearing\n"); 240 rdc_ind_write(sc, RDC_WDT0_CTRL1, reg & ~RDC_WDT0_CRTL1_FIRE); 241 } 242 } 243 244 static int 245 rdc_wdtimer_unconfigure(device_t self, int flags) 246 { 247 struct rdcpcib_softc *sc = device_private(self); 248 int rc; 249 250 if ((rc = sysmon_wdog_unregister(&sc->rdc_smw)) != 0) { 251 if (rc == ERESTART) 252 rc = EINTR; 253 return rc; 254 } 255 256 /* Explicitly stop the timer. */ 257 rdc_wdtimer_stop(sc); 258 bus_space_unmap(sc->rdc_iot, sc->rdc_ioh, RDC_IND_SIZE); 259 sc->wdt0_mapped = false; 260 261 return 0; 262 } 263 264 265 /* 266 * Sysmon watchdog callbacks. 267 */ 268 static int 269 rdc_wdtimer_setmode(struct sysmon_wdog *smw) 270 { 271 struct rdcpcib_softc *sc = smw->smw_cookie; 272 unsigned int period; 273 274 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 275 /* Stop the timer. */ 276 rdc_wdtimer_stop(sc); 277 } else { 278 period = smw->smw_period * RDC_WDT0_FREQ; 279 if (period < 1 || 280 period > RDC_WDT0_PERIOD_MAX) 281 return EINVAL; 282 period = period - 1; 283 284 /* Stop the timer, */ 285 rdc_wdtimer_stop(sc); 286 287 /* set the timeout, */ 288 rdc_ind_write(sc, RDC_WDT0_CNTL, (period >> 0) & 0xff); 289 rdc_ind_write(sc, RDC_WDT0_CNTH, (period >> 8) & 0xff); 290 rdc_ind_write(sc, RDC_WDT0_CNTU, (period >> 16) & 0xff); 291 292 /* and start the timer again */ 293 rdc_wdtimer_start(sc); 294 } 295 return 0; 296 } 297 298 static int 299 rdc_wdtimer_tickle(struct sysmon_wdog *smw) 300 { 301 struct rdcpcib_softc *sc = smw->smw_cookie; 302 uint8_t reg; 303 304 reg = rdc_ind_read(sc, RDC_WDT0_CTRL1); 305 rdc_ind_write(sc, RDC_WDT0_CTRL1, reg | RDC_WDT0_CTRL1_RELOAD); 306 return 0; 307 } 308 309 static void 310 rdc_wdtimer_stop(struct rdcpcib_softc *sc) 311 { 312 uint8_t reg; 313 reg = rdc_ind_read(sc, RDC_WDT0_CTRL); 314 rdc_ind_write(sc, RDC_WDT0_CTRL, reg & ~RDC_WDT0_CTRL_EN); 315 } 316 317 static void 318 rdc_wdtimer_start(struct rdcpcib_softc *sc) 319 { 320 uint8_t reg; 321 rdc_ind_write(sc, RDC_WDT0_SSEL, RDC_WDT0_SSEL_RST); 322 reg = rdc_ind_read(sc, RDC_WDT0_CTRL); 323 rdc_ind_write(sc, RDC_WDT0_CTRL, reg | RDC_WDT0_CTRL_EN); 324 } 325