vr4181giu.c revision 1.7
1/* $NetBSD: vr4181giu.c,v 1.7 2021/04/24 23:36:38 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 1999-2001 5 * Shin Takemura and PocketBSD Project. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the PocketBSD project 18 * and its contributors. 19 * 4. Neither the name of the project nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: vr4181giu.c,v 1.7 2021/04/24 23:36:38 thorpej Exp $"); 39 40#include <sys/param.h> 41#include <sys/device.h> 42#include <sys/kmem.h> 43#include <sys/queue.h> 44#include <sys/systm.h> 45 46#include <machine/bus.h> 47 48#include <hpcmips/vr/vripif.h> 49#include <hpcmips/vr/vr4181giureg.h> 50 51#define MAX_GIU4181INTR 16 52 53struct vr4181giu_intr_entry { 54 int ih_port; 55 int (*ih_fun)(void *); 56 void *ih_arg; 57 TAILQ_ENTRY(vr4181giu_intr_entry) ih_link; 58}; 59 60struct vr4181giu_softc { 61 bus_space_tag_t sc_iot; 62 bus_space_handle_t sc_ioh; 63 vrip_chipset_tag_t sc_vc; 64 void *sc_ih; 65 u_int32_t sc_intr_mode[MAX_GIU4181INTR]; 66 TAILQ_HEAD(, vr4181giu_intr_entry) 67 sc_intr_head[MAX_GIU4181INTR]; 68 struct hpcio_chip sc_iochip; 69 struct hpcio_attach_args sc_haa; 70}; 71 72static int vr4181giu_match(device_t, cfdata_t, void *); 73static void vr4181giu_attach(device_t, device_t, void *); 74 75static void vr4181giu_callback(device_t); 76static int vr4181giu_print(void *, const char *); 77static int vr4181giu_port_read(hpcio_chip_t, int); 78static void vr4181giu_port_write(hpcio_chip_t, int, int); 79static void vr4181giu_update(hpcio_chip_t); 80static void vr4181giu_dump(hpcio_chip_t); 81static hpcio_chip_t vr4181giu_getchip(void *, int); 82static void *vr4181giu_intr_establish(hpcio_chip_t, int, int, 83 int (*)(void *),void *); 84static void vr4181giu_intr_disestablish(hpcio_chip_t, void *); 85static void vr4181giu_intr_clear(hpcio_chip_t, void *); 86static void vr4181giu_register_iochip(hpcio_chip_t, hpcio_chip_t); 87static int vr4181giu_intr(void *); 88 89static struct hpcio_chip vr4181giu_iochip = { 90 .hc_portread = vr4181giu_port_read, 91 .hc_portwrite = vr4181giu_port_write, 92 .hc_intr_establish = vr4181giu_intr_establish, 93 .hc_intr_disestablish = vr4181giu_intr_disestablish, 94 .hc_intr_clear = vr4181giu_intr_clear, 95 .hc_register_iochip = vr4181giu_register_iochip, 96 .hc_update = vr4181giu_update, 97 .hc_dump = vr4181giu_dump, 98}; 99 100CFATTACH_DECL_NEW(vr4181giu, sizeof(struct vr4181giu_softc), 101 vr4181giu_match, vr4181giu_attach, NULL, NULL); 102 103static int 104vr4181giu_match(device_t parent, cfdata_t match, void *aux) 105{ 106 return (2); /* 1st attach group of vrip */ 107} 108 109static void 110vr4181giu_attach(device_t parent, device_t self, void *aux) 111{ 112 struct vr4181giu_softc *sc = device_private(self); 113 struct vrip_attach_args *va = aux; 114 int i; 115 116 sc->sc_iot = va->va_iot; 117 sc->sc_vc = va->va_vc; 118 119 if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size, 120 0 /* no cache */, &sc->sc_ioh)) { 121 printf(": can't map i/o space\n"); 122 return; 123 } 124 125 for (i = 0; i < MAX_GIU4181INTR; i++) 126 TAILQ_INIT(&sc->sc_intr_head[i]); 127 128 if (!(sc->sc_ih 129 = vrip_intr_establish(va->va_vc, va->va_unit, 0, 130 IPL_BIO, vr4181giu_intr, sc))) { 131 printf("%s: can't establish interrupt\n", device_xname(self)); 132 return; 133 } 134 135 /* 136 * fill hpcio_chip structure 137 */ 138 sc->sc_iochip = vr4181giu_iochip; /* structure copy */ 139 sc->sc_iochip.hc_chipid = VRIP_IOCHIP_VR4181GIU; 140 sc->sc_iochip.hc_name = device_xname(self); 141 sc->sc_iochip.hc_sc = sc; 142 /* Register functions to upper interface */ 143 vrip_register_gpio(va->va_vc, &sc->sc_iochip); 144 145 printf("\n"); 146 147 /* 148 * hpcio I/F 149 */ 150 sc->sc_haa.haa_busname = HPCIO_BUSNAME; 151 sc->sc_haa.haa_sc = sc; 152 sc->sc_haa.haa_getchip = vr4181giu_getchip; 153 sc->sc_haa.haa_iot = sc->sc_iot; 154 while (config_found(self, &sc->sc_haa, vr4181giu_print, CFARG_EOL)) ; 155 156 /* 157 * GIU-ISA bridge 158 */ 159#if 1 /* XXX Sometimes mounting root device failed. Why? XXX*/ 160 config_defer(self, vr4181giu_callback); 161#else 162 vr4181giu_callback(self); 163#endif 164} 165 166static void 167vr4181giu_callback(device_t self) 168{ 169 struct vr4181giu_softc *sc = (void *) self; 170 171 sc->sc_haa.haa_busname = "vrisab"; 172 config_found(self, &sc->sc_haa, vr4181giu_print, CFARG_EOL); 173} 174 175static int 176vr4181giu_print(void *aux, const char *pnp) 177{ 178 if (pnp) 179 return (QUIET); 180 return (UNCONF); 181} 182 183static int 184vr4181giu_port_read(hpcio_chip_t hc, int port) 185{ 186 struct vr4181giu_softc *sc = hc->hc_sc; 187 u_int16_t r; 188 189 if (port < 0 || 32 <= port) 190 panic("vr4181giu_port_read: invalid gpio port"); 191 192 if (port < 16) { 193 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 194 VR4181GIU_PIOD_L_REG_W) 195 & 1 << port; 196 } else { 197 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 198 VR4181GIU_PIOD_H_REG_W) 199 & 1 << (port - 16); 200 } 201 return r ? 1 : 0; 202} 203 204static void 205vr4181giu_port_write(hpcio_chip_t hc, int port, int onoff) 206{ 207 struct vr4181giu_softc *sc = hc->hc_sc; 208 u_int16_t r; 209 210 if (port < 16) { 211 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 212 VR4181GIU_PIOD_L_REG_W); 213 if (onoff) { 214 r |= 1 << port; 215 } else { 216 r &= ~(1 << port); 217 } 218 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 219 VR4181GIU_PIOD_L_REG_W, r); 220 } else { 221 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 222 VR4181GIU_PIOD_H_REG_W); 223 if (onoff) { 224 r |= 1 << (port - 16); 225 } else { 226 r &= ~(1 << (port - 16)); 227 } 228 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 229 VR4181GIU_PIOD_H_REG_W, r); 230 } 231} 232 233/* 234 * XXXXXXXXXXXXXXXXXXXXXXXX 235 */ 236static void 237vr4181giu_update(hpcio_chip_t hc) 238{ 239} 240 241static void 242vr4181giu_dump(hpcio_chip_t hc) 243{ 244} 245 246static hpcio_chip_t 247vr4181giu_getchip(void* scx, int chipid) 248{ 249 struct vr4181giu_softc *sc = scx; 250 251 return (&sc->sc_iochip); 252} 253 254static void * 255vr4181giu_intr_establish( 256 hpcio_chip_t hc, 257 int port, /* GPIO pin # */ 258 int mode, /* GIU trigger setting */ 259 int (*ih_fun)(void *), 260 void *ih_arg) 261{ 262 struct vr4181giu_softc *sc = hc->hc_sc; 263 struct vr4181giu_intr_entry *ih; 264 int s; 265 u_int32_t mask; 266 u_int32_t raw_intr_type; 267 int regmod; 268 int reghl; 269 int bitoff; 270 u_int16_t r; 271 272 /* 273 * trigger mode translation 274 * 275 * VR4181 only support for four type of interrupt trigger 276 * listed below: 277 * 278 * 1. high level 279 * 2. low level 280 * 3. rising edge 281 * 4. falling edge 282 * 283 * argument mode is a bitmap as following: 284 * 285 * 001 detection trigger (1:edge/0:level ) 286 * 010 signal hold/through (1:hold/0:through) 287 * 100 detection level (1:high/0:low ) 288 * 289 * possible mode value is 000B to 111B. 290 * 291 * 000 HPCIO_INTR_LEVEL_LOW_THROUGH 292 * 001 HPCIO_INTR_EDGE_THROUGH 293 * 010 HPCIO_INTR_LEVEL_LOW_HOLD 294 * 011 HPCIO_INTR_EDGE_HOLD 295 * 100 HPCIO_INTR_LEVEL_HIGH_THROUGH 296 * 101 falling edge and through? 297 * 110 HPCIO_INTR_LEVEL_HIGH_HOLD 298 * 111 falling edge and hold? 299 */ 300 301 static u_int32_t intr_mode_trans[8] = { 302 VR4181GIU_INTTYP_LOW_LEVEL, /* 000 */ 303 VR4181GIU_INTTYP_RISING_EDGE, /* 001 */ 304 VR4181GIU_INTTYP_LOW_LEVEL, /* 010 */ 305 VR4181GIU_INTTYP_RISING_EDGE, /* 011 */ 306 VR4181GIU_INTTYP_HIGH_LEVEL, /* 100 */ 307 VR4181GIU_INTTYP_FALLING_EDGE, /* 101 */ 308 VR4181GIU_INTTYP_HIGH_LEVEL, /* 110 */ 309 VR4181GIU_INTTYP_FALLING_EDGE, /* 111 */ 310 }; 311 312 raw_intr_type = intr_mode_trans[mode]; 313 if (raw_intr_type == VR4181GIU_INTTYP_INVALID) 314 panic("vr4181giu_intr_establish: invalid interrupt mode."); 315 316 if (port < 0 || MAX_GIU4181INTR <= port) 317 panic("vr4181giu_intr_establish: invalid interrupt line."); 318 if (!TAILQ_EMPTY(&sc->sc_intr_head[port]) 319 && raw_intr_type != sc->sc_intr_mode[port]) 320 panic("vr4181giu_intr_establish: " 321 "cannot use one line with two modes at a time."); 322 else 323 sc->sc_intr_mode[port] = raw_intr_type; 324 mask = (1 << port); 325 326 s = splhigh(); 327 328 ih = kmem_alloc(sizeof *ih, KM_SLEEP); 329 ih->ih_port = port; 330 ih->ih_fun = ih_fun; 331 ih->ih_arg = ih_arg; 332 TAILQ_INSERT_TAIL(&sc->sc_intr_head[port], ih, ih_link); 333 334 /* 335 * setup GIU registers 336 */ 337 338 /* disable interrupt at first */ 339 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W); 340 r &= ~mask; 341 bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W, r); 342 343 /* mode */ 344 regmod = port >> 3; 345 bitoff = (port & 0x7) << 1; 346 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 347 VR4181GIU_MODE0_REG_W + regmod); 348 r &= ~(0x3 << bitoff); 349 r |= (VR4181GIU_MODE_IN | VR4181GIU_MODE_GPIO) << bitoff; 350 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 351 VR4181GIU_MODE0_REG_W + regmod, r); 352 /* interrupt type */ 353 reghl = port < 8 ? 2 : 0; /* high byte: 0x0, lowbyte: 0x2 */ 354 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 355 VR4181GIU_INTTYP_REG + reghl); 356 r &= ~(0x3 << bitoff); 357 r |= raw_intr_type << bitoff; 358 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 359 VR4181GIU_INTTYP_REG + reghl, r); 360 361 /* clear status */ 362 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 363 VR4181GIU_INTSTAT_REG_W, mask); 364 365 /* unmask */ 366 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTMASK_REG_W); 367 r &= ~mask; 368 bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTMASK_REG_W, r); 369 370 /* enable */ 371 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W); 372 r |= mask; 373 bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W, r); 374 375 splx(s); 376 377 return ih; 378} 379 380static void 381vr4181giu_intr_disestablish(hpcio_chip_t hc, void *arg) 382{ 383} 384 385static void 386vr4181giu_intr_clear(hpcio_chip_t hc, void *arg) 387{ 388 struct vr4181giu_softc *sc = hc->hc_sc; 389 struct vr4181giu_intr_entry *ih = arg; 390 391 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 392 VR4181GIU_INTSTAT_REG_W, 1 << ih->ih_port); 393} 394 395static void 396vr4181giu_register_iochip(hpcio_chip_t hc, hpcio_chip_t iochip) 397{ 398 struct vr4181giu_softc *sc = hc->hc_sc; 399 400 vrip_register_gpio(sc->sc_vc, iochip); 401} 402 403/* 404 * interrupt handler 405 */ 406static int 407vr4181giu_intr(void *arg) 408{ 409 struct vr4181giu_softc *sc = arg; 410 int i; 411 u_int16_t r; 412 413 r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTSTAT_REG_W); 414 bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTSTAT_REG_W, r); 415 416 for (i = 0; i < MAX_GIU4181INTR; i++) { 417 if (r & (1 << i)) { 418 struct vr4181giu_intr_entry *ih; 419 TAILQ_FOREACH(ih, &sc->sc_intr_head[i], ih_link) { 420 ih->ih_fun(ih->ih_arg); 421 } 422 } 423 } 424 425 return 0; 426} 427