1 /* $OpenBSD: voyager.c,v 1.5 2017/05/17 11:52:25 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Silicon Motion SM501/SM502 (VoyagerGX) master driver. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <sys/gpio.h> 27 28 #include <machine/bus.h> 29 #include <machine/cpu.h> 30 #include <machine/intr.h> 31 32 #include <dev/gpio/gpiovar.h> 33 34 #include <dev/pci/pcireg.h> 35 #include <dev/pci/pcivar.h> 36 #include <dev/pci/pcidevs.h> 37 38 #include <loongson/dev/bonito_irq.h> /* for BONITO_NINTS */ 39 #include <loongson/dev/voyagerreg.h> 40 #include <loongson/dev/voyagervar.h> 41 42 struct voyager_softc { 43 struct device sc_dev; 44 45 bus_space_tag_t sc_fbt; 46 bus_space_handle_t sc_fbh; 47 bus_size_t sc_fbsize; 48 49 bus_space_tag_t sc_mmiot; 50 bus_space_handle_t sc_mmioh; 51 bus_size_t sc_mmiosize; 52 53 struct gpio_chipset_tag sc_gpio_tag; 54 gpio_pin_t sc_gpio_pins[32 + 32]; 55 56 void *sc_ih; 57 struct intrhand *sc_intr[32]; 58 }; 59 60 int voyager_match(struct device *, void *, void *); 61 void voyager_attach(struct device *, struct device *, void *); 62 63 const struct cfattach voyager_ca = { 64 sizeof(struct voyager_softc), voyager_match, voyager_attach 65 }; 66 67 struct cfdriver voyager_cd = { 68 NULL, "voyager", DV_DULL 69 }; 70 71 void voyager_attach_gpio(struct voyager_softc *); 72 int voyager_intr(void *); 73 int voyager_print(void *, const char *); 74 int voyager_search(struct device *, void *, void *); 75 76 const struct pci_matchid voyager_devices[] = { 77 /* 78 * 502 shares the same device ID as 501, but uses a different 79 * revision number. 80 */ 81 { PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM501 } 82 }; 83 84 int 85 voyager_match(struct device *parent, void *vcf, void *aux) 86 { 87 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 88 89 return pci_matchbyid(pa, voyager_devices, nitems(voyager_devices)); 90 } 91 92 void 93 voyager_attach(struct device *parent, struct device *self, void *aux) 94 { 95 struct voyager_softc *sc = (struct voyager_softc *)self; 96 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 97 pci_intr_handle_t ih; 98 const char *intrstr; 99 100 printf(": "); 101 102 /* 103 * Map registers. 104 */ 105 106 if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 107 BUS_SPACE_MAP_LINEAR, &sc->sc_fbt, &sc->sc_fbh, 108 NULL, &sc->sc_fbsize, 0) != 0) { 109 printf("can't map frame buffer\n"); 110 return; 111 } 112 113 if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x04, PCI_MAPREG_TYPE_MEM, 114 BUS_SPACE_MAP_LINEAR, &sc->sc_mmiot, &sc->sc_mmioh, 115 NULL, &sc->sc_mmiosize, 0) != 0) { 116 printf("can't map mmio\n"); 117 goto fail1; 118 } 119 120 /* 121 * Setup interrut handling. 122 */ 123 124 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_RAW_ICR, 125 0xffffffff); 126 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR, 0); 127 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR); 128 129 if (pci_intr_map(pa, &ih) != 0) { 130 printf("can't map interrupt\n"); 131 goto fail2; 132 } 133 intrstr = pci_intr_string(pa->pa_pc, ih); 134 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY, voyager_intr, 135 sc, self->dv_xname); 136 if (sc->sc_ih == NULL) { 137 printf("can't establish interrupt"); 138 if (intrstr != NULL) 139 printf(" at %s", intrstr); 140 printf("\n"); 141 goto fail2; 142 } 143 144 printf("%s\n", intrstr); 145 146 /* 147 * Attach GPIO devices. 148 */ 149 voyager_attach_gpio(sc); 150 151 /* 152 * Attach child devices. 153 */ 154 config_search(voyager_search, self, pa); 155 156 return; 157 fail2: 158 bus_space_unmap(sc->sc_mmiot, sc->sc_mmioh, sc->sc_mmiosize); 159 fail1: 160 bus_space_unmap(sc->sc_fbt, sc->sc_fbh, sc->sc_fbsize); 161 } 162 163 int 164 voyager_print(void *args, const char *parentname) 165 { 166 struct voyager_attach_args *vaa = (struct voyager_attach_args *)args; 167 168 if (parentname != NULL) 169 printf("%s at %s", vaa->vaa_name, parentname); 170 171 return UNCONF; 172 } 173 174 int 175 voyager_search(struct device *parent, void *vcf, void *args) 176 { 177 struct voyager_softc *sc = (struct voyager_softc *)parent; 178 struct cfdata *cf = (struct cfdata *)vcf; 179 struct pci_attach_args *pa = (struct pci_attach_args *)args; 180 struct voyager_attach_args vaa; 181 182 /* 183 * Caller should have attached gpio already. If it didn't, bail 184 * out here. 185 */ 186 if (strcmp(cf->cf_driver->cd_name, "gpio") == 0) 187 return 0; 188 189 vaa.vaa_name = cf->cf_driver->cd_name; 190 vaa.vaa_pa = pa; 191 vaa.vaa_fbt = sc->sc_fbt; 192 vaa.vaa_fbh = sc->sc_fbh; 193 vaa.vaa_mmiot = sc->sc_mmiot; 194 vaa.vaa_mmioh = sc->sc_mmioh; 195 196 if (cf->cf_attach->ca_match(parent, cf, &vaa) == 0) 197 return 0; 198 199 config_attach(parent, cf, &vaa, voyager_print); 200 return 1; 201 } 202 203 /* 204 * Interrupt disatcher 205 */ 206 207 int 208 voyager_intr(void *vsc) 209 { 210 struct voyager_softc *sc = (struct voyager_softc *)vsc; 211 uint32_t isr, imr, mask, bitno; 212 struct intrhand *ih; 213 214 isr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_ISR); 215 imr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR); 216 217 isr &= imr; 218 if (isr == 0) 219 return 0; 220 221 for (bitno = 0, mask = 1 << 0; isr != 0; bitno++, mask <<= 1) { 222 if ((isr & mask) == 0) 223 continue; 224 isr ^= mask; 225 for (ih = sc->sc_intr[bitno]; ih != NULL; ih = ih->ih_next) { 226 if ((*ih->ih_fun)(ih->ih_arg) != 0) 227 ih->ih_count.ec_count++; 228 } 229 } 230 231 return 1; 232 } 233 234 void * 235 voyager_intr_establish(void *cookie, int irq, int level, int (*fun)(void *), 236 void *arg, const char *name) 237 { 238 struct voyager_softc *sc = (struct voyager_softc *)cookie; 239 struct intrhand *prevh, *nh; 240 uint32_t imr; 241 242 #ifdef DIAGNOSTIC 243 if (irq < 0 || irq >= nitems(sc->sc_intr)) 244 return NULL; 245 #endif 246 247 level &= ~IPL_MPSAFE; 248 249 nh = (struct intrhand *)malloc(sizeof *nh, M_DEVBUF, M_NOWAIT | M_ZERO); 250 if (nh == NULL) 251 return NULL; 252 253 nh->ih_fun = fun; 254 nh->ih_arg = arg; 255 nh->ih_level = level; 256 nh->ih_irq = irq + BONITO_NINTS; 257 evcount_attach(&nh->ih_count, name, &nh->ih_irq); 258 259 if (sc->sc_intr[irq] == NULL) 260 sc->sc_intr[irq] = nh; 261 else { 262 /* insert at tail */ 263 for (prevh = sc->sc_intr[irq]; prevh->ih_next != NULL; 264 prevh = prevh->ih_next) ; 265 prevh->ih_next = nh; 266 } 267 268 /* enable interrupt source */ 269 imr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR); 270 imr |= 1 << irq; 271 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR, imr); 272 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR); 273 274 return nh; 275 } 276 277 const char * 278 voyager_intr_string(void *vih) 279 { 280 struct intrhand *ih = (struct intrhand *)vih; 281 static char intrstr[1 + 32]; 282 283 snprintf(intrstr, sizeof intrstr, "voyager irq %d", 284 ih->ih_irq - BONITO_NINTS); 285 return intrstr; 286 } 287 288 /* 289 * GPIO support code 290 */ 291 292 int voyager_gpio_pin_read(void *, int); 293 void voyager_gpio_pin_write(void *, int, int); 294 void voyager_gpio_pin_ctl(void *, int, int); 295 296 static const struct gpio_chipset_tag voyager_gpio_tag = { 297 .gp_pin_read = voyager_gpio_pin_read, 298 .gp_pin_write = voyager_gpio_pin_write, 299 .gp_pin_ctl = voyager_gpio_pin_ctl 300 }; 301 302 int 303 voyager_gpio_pin_read(void *cookie, int pin) 304 { 305 struct voyager_softc *sc = (struct voyager_softc *)cookie; 306 bus_addr_t reg; 307 int32_t data, mask; 308 309 if (pin >= 32) { 310 pin -= 32; 311 reg = VOYAGER_GPIO_DATA_HIGH; 312 } else { 313 reg = VOYAGER_GPIO_DATA_LOW; 314 } 315 mask = 1 << pin; 316 317 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 318 return data & mask ? GPIO_PIN_HIGH : GPIO_PIN_LOW; 319 } 320 321 void 322 voyager_gpio_pin_write(void *cookie, int pin, int val) 323 { 324 struct voyager_softc *sc = (struct voyager_softc *)cookie; 325 bus_addr_t reg; 326 int32_t data, mask; 327 328 if (pin >= 32) { 329 pin -= 32; 330 reg = VOYAGER_GPIO_DATA_HIGH; 331 } else { 332 reg = VOYAGER_GPIO_DATA_LOW; 333 } 334 mask = 1 << pin; 335 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 336 if (val) 337 data |= mask; 338 else 339 data &= ~mask; 340 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data); 341 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 342 } 343 344 void 345 voyager_gpio_pin_ctl(void *cookie, int pin, int flags) 346 { 347 struct voyager_softc *sc = (struct voyager_softc *)cookie; 348 bus_addr_t reg; 349 int32_t data, mask; 350 351 if (pin >= 32) { 352 pin -= 32; 353 reg = VOYAGER_GPIO_DIR_HIGH; 354 } else { 355 reg = VOYAGER_GPIO_DIR_LOW; 356 } 357 mask = 1 << pin; 358 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 359 if (ISSET(flags, GPIO_PIN_OUTPUT)) 360 data |= mask; 361 else 362 data &= ~mask; 363 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data); 364 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 365 } 366 367 void 368 voyager_attach_gpio(struct voyager_softc *sc) 369 { 370 struct gpiobus_attach_args gba; 371 int pin; 372 uint32_t control, value; 373 374 bcopy(&voyager_gpio_tag, &sc->sc_gpio_tag, sizeof voyager_gpio_tag); 375 sc->sc_gpio_tag.gp_cookie = sc; 376 377 control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 378 VOYAGER_GPIOL_CONTROL); 379 value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 380 VOYAGER_GPIO_DATA_LOW); 381 for (pin = 0; pin < 32; pin++) { 382 sc->sc_gpio_pins[pin].pin_num = pin; 383 if ((control & 1) == 0) { 384 sc->sc_gpio_pins[pin].pin_caps = 385 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; 386 sc->sc_gpio_pins[pin].pin_state = value & 1; 387 } else { 388 /* disable control of taken over pins */ 389 sc->sc_gpio_pins[pin].pin_caps = 0; 390 sc->sc_gpio_pins[pin].pin_state = 0; 391 } 392 } 393 394 control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 395 VOYAGER_GPIOH_CONTROL); 396 value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 397 VOYAGER_GPIO_DATA_HIGH); 398 for (pin = 32 + 0; pin < 32 + 32; pin++) { 399 sc->sc_gpio_pins[pin].pin_num = pin; 400 if ((control & 1) == 0) { 401 sc->sc_gpio_pins[pin].pin_caps = 402 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; 403 sc->sc_gpio_pins[pin].pin_state = value & 1; 404 } else { 405 /* disable control of taken over pins */ 406 sc->sc_gpio_pins[pin].pin_caps = 0; 407 sc->sc_gpio_pins[pin].pin_state = 0; 408 } 409 } 410 411 gba.gba_name = "gpio"; 412 gba.gba_gc = &sc->sc_gpio_tag; 413 gba.gba_pins = sc->sc_gpio_pins; 414 gba.gba_npins = nitems(sc->sc_gpio_pins); 415 416 config_found(&sc->sc_dev, &gba, voyager_print); 417 } 418