Annotation of sys/dev/isa/gscsio.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: gscsio.c,v 1.9 2007/06/05 08:37:20 jsg Exp $ */
! 2: /*
! 3: * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17:
! 18: /*
! 19: * National Semiconductor Geode SC1100 Super I/O.
! 20: * Only ACCESS.bus logical device is supported.
! 21: */
! 22:
! 23: #include <sys/param.h>
! 24: #include <sys/systm.h>
! 25: #include <sys/device.h>
! 26: #include <sys/kernel.h>
! 27: #include <sys/rwlock.h>
! 28: #include <sys/proc.h>
! 29:
! 30: #include <machine/bus.h>
! 31:
! 32: #include <dev/i2c/i2cvar.h>
! 33:
! 34: #include <dev/isa/isareg.h>
! 35: #include <dev/isa/isavar.h>
! 36:
! 37: #include <dev/isa/gscsioreg.h>
! 38:
! 39: struct gscsio_softc {
! 40: struct device sc_dev;
! 41:
! 42: bus_space_tag_t sc_iot;
! 43: bus_space_handle_t sc_ioh;
! 44:
! 45: int sc_ld_en[GSCSIO_LDNUM];
! 46: bus_space_handle_t sc_ld_ioh0[GSCSIO_LDNUM];
! 47: bus_space_handle_t sc_ld_ioh1[GSCSIO_LDNUM];
! 48:
! 49: /* ACCESS.bus */
! 50: struct gscsio_acb {
! 51: void *sc;
! 52: bus_space_handle_t ioh;
! 53: struct rwlock buslock;
! 54: } sc_acb[2];
! 55: struct i2c_controller sc_acb1_tag;
! 56: struct i2c_controller sc_acb2_tag;
! 57: };
! 58:
! 59: /* Supported logical devices description */
! 60: static const struct {
! 61: const char *ld_name;
! 62: int ld_num;
! 63: int ld_iosize0;
! 64: int ld_iosize1;
! 65: } gscsio_ld[] = {
! 66: { "ACB1", GSCSIO_LDN_ACB1, 6, 0 },
! 67: { "ACB2", GSCSIO_LDN_ACB2, 6, 0 },
! 68: };
! 69:
! 70: int gscsio_probe(struct device *, void *, void *);
! 71: void gscsio_attach(struct device *, struct device *, void *);
! 72:
! 73: void gscsio_acb_init(struct gscsio_acb *, i2c_tag_t);
! 74: int gscsio_acb_wait(struct gscsio_acb *, int, int);
! 75: void gscsio_acb_reset(struct gscsio_acb *acb);
! 76:
! 77: int gscsio_acb_acquire_bus(void *, int);
! 78: void gscsio_acb_release_bus(void *, int);
! 79: int gscsio_acb_send_start(void *, int);
! 80: int gscsio_acb_send_stop(void *, int);
! 81: int gscsio_acb_initiate_xfer(void *, uint16_t, int);
! 82: int gscsio_acb_read_byte(void *, uint8_t *, int);
! 83: int gscsio_acb_write_byte(void *, uint8_t, int);
! 84:
! 85: struct cfattach gscsio_ca = {
! 86: sizeof(struct gscsio_softc),
! 87: gscsio_probe,
! 88: gscsio_attach
! 89: };
! 90:
! 91: struct cfdriver gscsio_cd = {
! 92: NULL, "gscsio", DV_DULL
! 93: };
! 94:
! 95: #define ACB_READ(reg) \
! 96: bus_space_read_1(sc->sc_iot, acb->ioh, (reg))
! 97: #define ACB_WRITE(reg, val) \
! 98: bus_space_write_1(sc->sc_iot, acb->ioh, (reg), (val))
! 99:
! 100: static __inline u_int8_t
! 101: idxread(bus_space_tag_t iot, bus_space_handle_t ioh, int idx)
! 102: {
! 103: bus_space_write_1(iot, ioh, GSCSIO_IDX, idx);
! 104:
! 105: return (bus_space_read_1(iot, ioh, GSCSIO_DAT));
! 106: }
! 107:
! 108: static __inline void
! 109: idxwrite(bus_space_tag_t iot, bus_space_handle_t ioh, int idx, u_int8_t data)
! 110: {
! 111: bus_space_write_1(iot, ioh, GSCSIO_IDX, idx);
! 112: bus_space_write_1(iot, ioh, GSCSIO_DAT, data);
! 113: }
! 114:
! 115: int
! 116: gscsio_probe(struct device *parent, void *match, void *aux)
! 117: {
! 118: struct isa_attach_args *ia = aux;
! 119: bus_space_tag_t iot;
! 120: bus_space_handle_t ioh;
! 121: int iobase;
! 122: int rv = 0;
! 123:
! 124: iot = ia->ia_iot;
! 125: iobase = ia->ipa_io[0].base;
! 126: if (bus_space_map(iot, iobase, GSCSIO_IOSIZE, 0, &ioh))
! 127: return (0);
! 128: if (idxread(iot, ioh, GSCSIO_ID) == GSCSIO_ID_SC1100)
! 129: rv = 1;
! 130: bus_space_unmap(iot, ioh, GSCSIO_IOSIZE);
! 131:
! 132: if (rv) {
! 133: ia->ipa_nio = 1;
! 134: ia->ipa_io[0].length = GSCSIO_IOSIZE;
! 135: ia->ipa_nmem = 0;
! 136: ia->ipa_nirq = 0;
! 137: ia->ipa_ndrq = 0;
! 138: }
! 139:
! 140: return (rv);
! 141: }
! 142:
! 143: void
! 144: gscsio_attach(struct device *parent, struct device *self, void *aux)
! 145: {
! 146: struct gscsio_softc *sc = (void *)self;
! 147: struct isa_attach_args *ia = aux;
! 148: int i;
! 149: int iobase;
! 150:
! 151: sc->sc_iot = ia->ia_iot;
! 152: if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, GSCSIO_IOSIZE,
! 153: 0, &sc->sc_ioh)) {
! 154: printf(": can't map I/O space\n");
! 155: return;
! 156: }
! 157: printf(": SC1100 SIO rev %d:",
! 158: idxread(sc->sc_iot, sc->sc_ioh, GSCSIO_REV));
! 159:
! 160: /* Configure all supported logical devices */
! 161: for (i = 0; i < sizeof (gscsio_ld) / sizeof(gscsio_ld[0]); i++) {
! 162: sc->sc_ld_en[gscsio_ld[i].ld_num] = 0;
! 163:
! 164: /* Select the device and check if it's activated */
! 165: idxwrite(sc->sc_iot, sc->sc_ioh, GSCSIO_LDN,
! 166: gscsio_ld[i].ld_num);
! 167: if ((idxread(sc->sc_iot, sc->sc_ioh, GSCSIO_ACT) &
! 168: GSCSIO_ACT_EN) == 0)
! 169: continue;
! 170:
! 171: /* Map I/O space 0 if necessary */
! 172: if (gscsio_ld[i].ld_iosize0 != 0) {
! 173: iobase = idxread(sc->sc_iot, sc->sc_ioh,
! 174: GSCSIO_IO0_MSB);
! 175: iobase <<= 8;
! 176: iobase |= idxread(sc->sc_iot, sc->sc_ioh,
! 177: GSCSIO_IO0_LSB);
! 178: if (bus_space_map(sc->sc_iot, iobase,
! 179: gscsio_ld[i].ld_iosize0, 0,
! 180: &sc->sc_ld_ioh0[gscsio_ld[i].ld_num]))
! 181: continue;
! 182: }
! 183:
! 184: /* Map I/O space 1 if necessary */
! 185: if (gscsio_ld[i].ld_iosize1 != 0) {
! 186: iobase = idxread(sc->sc_iot, sc->sc_ioh,
! 187: GSCSIO_IO1_MSB);
! 188: iobase <<= 8;
! 189: iobase |= idxread(sc->sc_iot, sc->sc_ioh,
! 190: GSCSIO_IO1_LSB);
! 191: if (bus_space_map(sc->sc_iot, iobase,
! 192: gscsio_ld[i].ld_iosize1, 0,
! 193: &sc->sc_ld_ioh0[gscsio_ld[i].ld_num])) {
! 194: bus_space_unmap(sc->sc_iot,
! 195: sc->sc_ld_ioh0[gscsio_ld[i].ld_num],
! 196: gscsio_ld[i].ld_iosize0);
! 197: continue;
! 198: }
! 199: }
! 200:
! 201: sc->sc_ld_en[gscsio_ld[i].ld_num] = 1;
! 202: printf(" %s", gscsio_ld[i].ld_name);
! 203: }
! 204: printf("\n");
! 205:
! 206: /* Initialize ACCESS.bus 1 */
! 207: if (sc->sc_ld_en[GSCSIO_LDN_ACB1]) {
! 208: sc->sc_acb[0].sc = sc;
! 209: sc->sc_acb[0].ioh = sc->sc_ld_ioh0[GSCSIO_LDN_ACB1];
! 210: rw_init(&sc->sc_acb[0].buslock, "iiclk");
! 211: gscsio_acb_init(&sc->sc_acb[0], &sc->sc_acb1_tag);
! 212: }
! 213:
! 214: /* Initialize ACCESS.bus 2 */
! 215: if (sc->sc_ld_en[GSCSIO_LDN_ACB2]) {
! 216: sc->sc_acb[1].sc = sc;
! 217: sc->sc_acb[1].ioh = sc->sc_ld_ioh0[GSCSIO_LDN_ACB2];
! 218: rw_init(&sc->sc_acb[1].buslock, "iiclk");
! 219: gscsio_acb_init(&sc->sc_acb[1], &sc->sc_acb2_tag);
! 220: }
! 221: }
! 222:
! 223: void
! 224: gscsio_acb_init(struct gscsio_acb *acb, i2c_tag_t tag)
! 225: {
! 226: struct gscsio_softc *sc = acb->sc;
! 227: struct i2cbus_attach_args iba;
! 228:
! 229: /* Enable ACB and configure clock frequency */
! 230: ACB_WRITE(GSCSIO_ACB_CTL2, GSCSIO_ACB_CTL2_EN |
! 231: (GSCSIO_ACB_FREQ << GSCSIO_ACB_CTL2_FREQ_SHIFT));
! 232:
! 233: /* Select polling mode */
! 234: ACB_WRITE(GSCSIO_ACB_CTL1, ACB_READ(GSCSIO_ACB_CTL1) &
! 235: ~GSCSIO_ACB_CTL1_INTEN);
! 236:
! 237: /* Disable slave address */
! 238: ACB_WRITE(GSCSIO_ACB_ADDR, ACB_READ(GSCSIO_ACB_ADDR) &
! 239: ~GSCSIO_ACB_ADDR_SAEN);
! 240:
! 241: /* Attach I2C framework */
! 242: tag->ic_cookie = acb;
! 243: tag->ic_acquire_bus = gscsio_acb_acquire_bus;
! 244: tag->ic_release_bus = gscsio_acb_release_bus;
! 245: tag->ic_send_start = gscsio_acb_send_start;
! 246: tag->ic_send_stop = gscsio_acb_send_stop;
! 247: tag->ic_initiate_xfer = gscsio_acb_initiate_xfer;
! 248: tag->ic_read_byte = gscsio_acb_read_byte;
! 249: tag->ic_write_byte = gscsio_acb_write_byte;
! 250:
! 251: bzero(&iba, sizeof(iba));
! 252: iba.iba_name = "iic";
! 253: iba.iba_tag = tag;
! 254: config_found(&sc->sc_dev, &iba, iicbus_print);
! 255: }
! 256:
! 257: int
! 258: gscsio_acb_wait(struct gscsio_acb *acb, int bits, int flags)
! 259: {
! 260: struct gscsio_softc *sc = acb->sc;
! 261: u_int8_t st;
! 262: int i;
! 263:
! 264: for (i = 0; i < 100; i++) {
! 265: st = ACB_READ(GSCSIO_ACB_ST);
! 266: if (st & GSCSIO_ACB_ST_BER) {
! 267: printf("%s: bus error, flags=0x%x\n",
! 268: sc->sc_dev.dv_xname, flags);
! 269: gscsio_acb_reset(acb);
! 270: return (EIO);
! 271: }
! 272: if (st & GSCSIO_ACB_ST_NEGACK) {
! 273: #if 0
! 274: printf("%s: negative ack, flags=0x%x\n",
! 275: sc->sc_dev.dv_xname, flags);
! 276: #endif
! 277: gscsio_acb_reset(acb);
! 278: return (EIO);
! 279: }
! 280: if ((st & bits) == bits)
! 281: break;
! 282: delay(10);
! 283: }
! 284: if ((st & bits) != bits) {
! 285: printf("%s: timeout, flags=0x%x\n",
! 286: sc->sc_dev.dv_xname, flags);
! 287: gscsio_acb_reset(acb);
! 288: return (ETIMEDOUT);
! 289: }
! 290:
! 291: return (0);
! 292: }
! 293:
! 294: void
! 295: gscsio_acb_reset(struct gscsio_acb *acb)
! 296: {
! 297: struct gscsio_softc *sc = acb->sc;
! 298: u_int8_t st, ctl;
! 299:
! 300: /* Clear MASTER, NEGACK and BER */
! 301: st = ACB_READ(GSCSIO_ACB_ST);
! 302: st |= GSCSIO_ACB_ST_MASTER | GSCSIO_ACB_ST_NEGACK | GSCSIO_ACB_ST_BER;
! 303: ACB_WRITE(GSCSIO_ACB_ST, st);
! 304:
! 305: /* Disable and re-enable ACB */
! 306: ACB_WRITE(GSCSIO_ACB_CTL2, 0);
! 307: ACB_WRITE(GSCSIO_ACB_CTL2, GSCSIO_ACB_CTL2_EN |
! 308: (GSCSIO_ACB_FREQ << GSCSIO_ACB_CTL2_FREQ_SHIFT));
! 309:
! 310: /* Send stop */
! 311: ctl = ACB_READ(GSCSIO_ACB_CTL1);
! 312: ctl |= GSCSIO_ACB_CTL1_STOP;
! 313: ACB_WRITE(GSCSIO_ACB_CTL1, ctl);
! 314: }
! 315:
! 316: int
! 317: gscsio_acb_acquire_bus(void *cookie, int flags)
! 318: {
! 319: struct gscsio_acb *acb = cookie;
! 320:
! 321: if (cold || flags & I2C_F_POLL)
! 322: return (0);
! 323:
! 324: return (rw_enter(&acb->buslock, RW_WRITE | RW_INTR));
! 325: }
! 326:
! 327: void
! 328: gscsio_acb_release_bus(void *cookie, int flags)
! 329: {
! 330: struct gscsio_acb *acb = cookie;
! 331:
! 332: if (cold || flags & I2C_F_POLL)
! 333: return;
! 334:
! 335: rw_exit(&acb->buslock);
! 336: }
! 337:
! 338: int
! 339: gscsio_acb_send_start(void *cookie, int flags)
! 340: {
! 341: struct gscsio_acb *acb = cookie;
! 342: struct gscsio_softc *sc = acb->sc;
! 343: u_int8_t ctl;
! 344:
! 345: ctl = ACB_READ(GSCSIO_ACB_CTL1);
! 346: ctl |= GSCSIO_ACB_CTL1_START;
! 347: ACB_WRITE(GSCSIO_ACB_CTL1, ctl);
! 348:
! 349: return (0);
! 350: }
! 351:
! 352: int
! 353: gscsio_acb_send_stop(void *cookie, int flags)
! 354: {
! 355: struct gscsio_acb *acb = cookie;
! 356: struct gscsio_softc *sc = acb->sc;
! 357: u_int8_t ctl;
! 358:
! 359: ctl = ACB_READ(GSCSIO_ACB_CTL1);
! 360: ctl |= GSCSIO_ACB_CTL1_STOP;
! 361: ACB_WRITE(GSCSIO_ACB_CTL1, ctl);
! 362:
! 363: return (0);
! 364: }
! 365:
! 366: int
! 367: gscsio_acb_initiate_xfer(void *cookie, uint16_t addr, int flags)
! 368: {
! 369: struct gscsio_acb *acb = cookie;
! 370: struct gscsio_softc *sc = acb->sc;
! 371: u_int8_t ctl;
! 372: int dir;
! 373: int error;
! 374:
! 375: /* Issue start condition */
! 376: ctl = ACB_READ(GSCSIO_ACB_CTL1);
! 377: ctl |= GSCSIO_ACB_CTL1_START;
! 378: ACB_WRITE(GSCSIO_ACB_CTL1, ctl);
! 379:
! 380: /* Wait for bus mastership */
! 381: if ((error = gscsio_acb_wait(acb,
! 382: GSCSIO_ACB_ST_MASTER | GSCSIO_ACB_ST_SDAST, flags)))
! 383: return (error);
! 384:
! 385: /* Send address byte */
! 386: dir = (flags & I2C_F_READ ? 1 : 0);
! 387: ACB_WRITE(GSCSIO_ACB_SDA, (addr << 1) | dir);
! 388:
! 389: return (0);
! 390: }
! 391:
! 392: int
! 393: gscsio_acb_read_byte(void *cookie, uint8_t *bytep, int flags)
! 394: {
! 395: struct gscsio_acb *acb = cookie;
! 396: struct gscsio_softc *sc = acb->sc;
! 397: u_int8_t ctl;
! 398: int error;
! 399:
! 400: /* Wait for the bus to be ready */
! 401: if ((error = gscsio_acb_wait(acb, GSCSIO_ACB_ST_SDAST, flags)))
! 402: return (error);
! 403:
! 404: /* Acknowledge the last byte */
! 405: if (flags & I2C_F_LAST) {
! 406: ctl = ACB_READ(GSCSIO_ACB_CTL1);
! 407: ctl |= GSCSIO_ACB_CTL1_ACK;
! 408: ACB_WRITE(GSCSIO_ACB_CTL1, ctl);
! 409: }
! 410:
! 411: /* Read data byte */
! 412: *bytep = ACB_READ(GSCSIO_ACB_SDA);
! 413:
! 414: return (0);
! 415: }
! 416:
! 417: int
! 418: gscsio_acb_write_byte(void *cookie, uint8_t byte, int flags)
! 419: {
! 420: struct gscsio_acb *acb = cookie;
! 421: struct gscsio_softc *sc = acb->sc;
! 422: u_int8_t ctl;
! 423: int error;
! 424:
! 425: /* Wait for the bus to be ready */
! 426: if ((error = gscsio_acb_wait(acb, GSCSIO_ACB_ST_SDAST, flags)))
! 427: return (error);
! 428:
! 429: /* Send stop after the last byte */
! 430: if (flags & I2C_F_STOP) {
! 431: ctl = ACB_READ(GSCSIO_ACB_CTL1);
! 432: ctl |= GSCSIO_ACB_CTL1_STOP;
! 433: ACB_WRITE(GSCSIO_ACB_CTL1, ctl);
! 434: }
! 435:
! 436: /* Write data byte */
! 437: ACB_WRITE(GSCSIO_ACB_SDA, byte);
! 438:
! 439: return (0);
! 440: }
CVSweb