1 /* $OpenBSD: voyager.c,v 1.4 2010/09/20 06:33:48 matthew 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 nh = (struct intrhand *)malloc(sizeof *nh, M_DEVBUF, M_NOWAIT | M_ZERO); 248 if (nh == NULL) 249 return NULL; 250 251 nh->ih_fun = fun; 252 nh->ih_arg = arg; 253 nh->ih_level = level; 254 nh->ih_irq = irq + BONITO_NINTS; 255 evcount_attach(&nh->ih_count, name, &nh->ih_irq); 256 257 if (sc->sc_intr[irq] == NULL) 258 sc->sc_intr[irq] = nh; 259 else { 260 /* insert at tail */ 261 for (prevh = sc->sc_intr[irq]; prevh->ih_next != NULL; 262 prevh = prevh->ih_next) ; 263 prevh->ih_next = nh; 264 } 265 266 /* enable interrupt source */ 267 imr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR); 268 imr |= 1 << irq; 269 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR, imr); 270 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR); 271 272 return nh; 273 } 274 275 const char * 276 voyager_intr_string(void *vih) 277 { 278 struct intrhand *ih = (struct intrhand *)vih; 279 static char intrstr[1 + 32]; 280 281 snprintf(intrstr, sizeof intrstr, "voyager irq %d", 282 ih->ih_irq - BONITO_NINTS); 283 return intrstr; 284 } 285 286 /* 287 * GPIO support code 288 */ 289 290 int voyager_gpio_pin_read(void *, int); 291 void voyager_gpio_pin_write(void *, int, int); 292 void voyager_gpio_pin_ctl(void *, int, int); 293 294 static const struct gpio_chipset_tag voyager_gpio_tag = { 295 .gp_pin_read = voyager_gpio_pin_read, 296 .gp_pin_write = voyager_gpio_pin_write, 297 .gp_pin_ctl = voyager_gpio_pin_ctl 298 }; 299 300 int 301 voyager_gpio_pin_read(void *cookie, int pin) 302 { 303 struct voyager_softc *sc = (struct voyager_softc *)cookie; 304 bus_addr_t reg; 305 int32_t data, mask; 306 307 if (pin >= 32) { 308 pin -= 32; 309 reg = VOYAGER_GPIO_DATA_HIGH; 310 } else { 311 reg = VOYAGER_GPIO_DATA_LOW; 312 } 313 mask = 1 << pin; 314 315 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 316 return data & mask ? GPIO_PIN_HIGH : GPIO_PIN_LOW; 317 } 318 319 void 320 voyager_gpio_pin_write(void *cookie, int pin, int val) 321 { 322 struct voyager_softc *sc = (struct voyager_softc *)cookie; 323 bus_addr_t reg; 324 int32_t data, mask; 325 326 if (pin >= 32) { 327 pin -= 32; 328 reg = VOYAGER_GPIO_DATA_HIGH; 329 } else { 330 reg = VOYAGER_GPIO_DATA_LOW; 331 } 332 mask = 1 << pin; 333 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 334 if (val) 335 data |= mask; 336 else 337 data &= ~mask; 338 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data); 339 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 340 } 341 342 void 343 voyager_gpio_pin_ctl(void *cookie, int pin, int flags) 344 { 345 struct voyager_softc *sc = (struct voyager_softc *)cookie; 346 bus_addr_t reg; 347 int32_t data, mask; 348 349 if (pin >= 32) { 350 pin -= 32; 351 reg = VOYAGER_GPIO_DIR_HIGH; 352 } else { 353 reg = VOYAGER_GPIO_DIR_LOW; 354 } 355 mask = 1 << pin; 356 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 357 if (ISSET(flags, GPIO_PIN_OUTPUT)) 358 data |= mask; 359 else 360 data &= ~mask; 361 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data); 362 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg); 363 } 364 365 void 366 voyager_attach_gpio(struct voyager_softc *sc) 367 { 368 struct gpiobus_attach_args gba; 369 int pin; 370 uint32_t control, value; 371 372 bcopy(&voyager_gpio_tag, &sc->sc_gpio_tag, sizeof voyager_gpio_tag); 373 sc->sc_gpio_tag.gp_cookie = sc; 374 375 control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 376 VOYAGER_GPIOL_CONTROL); 377 value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 378 VOYAGER_GPIO_DATA_LOW); 379 for (pin = 0; pin < 32; pin++) { 380 sc->sc_gpio_pins[pin].pin_num = pin; 381 if ((control & 1) == 0) { 382 sc->sc_gpio_pins[pin].pin_caps = 383 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; 384 sc->sc_gpio_pins[pin].pin_state = value & 1; 385 } else { 386 /* disable control of taken over pins */ 387 sc->sc_gpio_pins[pin].pin_caps = 0; 388 sc->sc_gpio_pins[pin].pin_state = 0; 389 } 390 } 391 392 control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 393 VOYAGER_GPIOH_CONTROL); 394 value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, 395 VOYAGER_GPIO_DATA_HIGH); 396 for (pin = 32 + 0; pin < 32 + 32; pin++) { 397 sc->sc_gpio_pins[pin].pin_num = pin; 398 if ((control & 1) == 0) { 399 sc->sc_gpio_pins[pin].pin_caps = 400 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; 401 sc->sc_gpio_pins[pin].pin_state = value & 1; 402 } else { 403 /* disable control of taken over pins */ 404 sc->sc_gpio_pins[pin].pin_caps = 0; 405 sc->sc_gpio_pins[pin].pin_state = 0; 406 } 407 } 408 409 gba.gba_name = "gpio"; 410 gba.gba_gc = &sc->sc_gpio_tag; 411 gba.gba_pins = sc->sc_gpio_pins; 412 gba.gba_npins = nitems(sc->sc_gpio_pins); 413 414 config_found(&sc->sc_dev, &gba, voyager_print); 415 } 416