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