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 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 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 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 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 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 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 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 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 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 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 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