Annotation of sys/dev/ic/pcf8584.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: pcf8584.c,v 1.8 2007/05/21 03:11:11 jsg Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
! 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: #include <sys/param.h>
! 20: #include <sys/systm.h>
! 21: #include <sys/device.h>
! 22: #include <sys/malloc.h>
! 23: #include <sys/kernel.h>
! 24: #include <sys/rwlock.h>
! 25: #include <sys/proc.h>
! 26:
! 27: #include <machine/bus.h>
! 28:
! 29: #include <dev/i2c/i2cvar.h>
! 30:
! 31: #include <dev/ic/pcf8584var.h>
! 32:
! 33: #define PCF_S0 0x00
! 34: #define PCF_S1 0x01
! 35: #define PCF_S2 0x02
! 36: #define PCF_S3 0x03
! 37:
! 38: #define PCF_CTRL_ACK (1<<0)
! 39: #define PCF_CTRL_STO (1<<1)
! 40: #define PCF_CTRL_STA (1<<2)
! 41: #define PCF_CTRL_ENI (1<<3)
! 42: #define PCF_CTRL_ES2 (1<<4)
! 43: #define PCF_CTRL_ES1 (1<<5)
! 44: #define PCF_CTRL_ESO (1<<6)
! 45: #define PCF_CTRL_PIN (1<<7)
! 46:
! 47: #define PCF_CTRL_START (PCF_CTRL_PIN | PCF_CTRL_ESO | \
! 48: PCF_CTRL_STA | PCF_CTRL_ACK)
! 49: #define PCF_CTRL_STOP (PCF_CTRL_PIN | PCF_CTRL_ESO | \
! 50: PCF_CTRL_STO | PCF_CTRL_ACK)
! 51: #define PCF_CTRL_REPSTART (PCF_CTRL_ESO | PCF_CTRL_STA | PCF_CTRL_ACK)
! 52: #define PCF_CTRL_IDLE (PCF_CTRL_PIN | PCF_CTRL_ESO | PCF_CTRL_ACK)
! 53:
! 54: #define PCF_STAT_nBB (1<<0)
! 55: #define PCF_STAT_LAB (1<<1)
! 56: #define PCF_STAT_AAS (1<<2)
! 57: #define PCF_STAT_AD0 (1<<3)
! 58: #define PCF_STAT_LRB (1<<3)
! 59: #define PCF_STAT_BER (1<<4)
! 60: #define PCF_STAT_STS (1<<5)
! 61: #define PCF_STAT_PIN (1<<7)
! 62:
! 63: #define PCF_FREQ_90 0x00 /* 90 kHz */
! 64: #define PCF_FREQ_45 0x01 /* 45 kHz */
! 65: #define PCF_FREQ_11 0x02 /* 11 kHz */
! 66: #define PCF_FREQ_1_5 0x03 /* 1.5 kHz */
! 67:
! 68: struct cfdriver pcfiic_cd = {
! 69: NULL, "pcfiic", DV_DULL
! 70: };
! 71:
! 72: void pcfiic_init(struct pcfiic_softc *);
! 73: int pcfiic_i2c_acquire_bus(void *, int);
! 74: void pcfiic_i2c_release_bus(void *, int);
! 75: int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
! 76: size_t, void *, size_t, int);
! 77:
! 78: int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
! 79: size_t);
! 80: int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
! 81: size_t);
! 82:
! 83: volatile u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t);
! 84: volatile void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
! 85: void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
! 86: int pcfiic_wait_nBB(struct pcfiic_softc *);
! 87: int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
! 88:
! 89: void
! 90: pcfiic_init(struct pcfiic_softc *sc)
! 91: {
! 92: /* init S1 */
! 93: pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN);
! 94: /* own address */
! 95: pcfiic_write(sc, PCF_S0, sc->sc_addr);
! 96:
! 97: /* select clock reg */
! 98: pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1);
! 99: pcfiic_write(sc, PCF_S0, sc->sc_clock);
! 100:
! 101: pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE);
! 102:
! 103: delay(200000); /* Multi-Master mode, wait for longest i2c message */
! 104: }
! 105:
! 106: void
! 107: pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock,
! 108: int swapregs,
! 109: void (*scan_func)(struct device *, struct i2cbus_attach_args *, void *),
! 110: void *scan_arg)
! 111: {
! 112: struct i2cbus_attach_args iba;
! 113:
! 114: if (swapregs) {
! 115: sc->sc_regmap[PCF_S1] = PCF_S0;
! 116: sc->sc_regmap[PCF_S0] = PCF_S1;
! 117: } else {
! 118: sc->sc_regmap[PCF_S0] = PCF_S0;
! 119: sc->sc_regmap[PCF_S1] = PCF_S1;
! 120: }
! 121: sc->sc_clock = clock;
! 122: sc->sc_addr = addr;
! 123:
! 124: pcfiic_init(sc);
! 125:
! 126: printf("\n");
! 127:
! 128: if (sc->sc_master)
! 129: pcfiic_choose_bus(sc, 0);
! 130:
! 131: rw_init(&sc->sc_lock, "iiclk");
! 132: sc->sc_i2c.ic_cookie = sc;
! 133: sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus;
! 134: sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus;
! 135: sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
! 136:
! 137: bzero(&iba, sizeof(iba));
! 138: iba.iba_name = "iic";
! 139: iba.iba_tag = &sc->sc_i2c;
! 140: iba.iba_bus_scan = scan_func;
! 141: iba.iba_bus_scan_arg = scan_arg;
! 142: config_found(&sc->sc_dev, &iba, iicbus_print);
! 143: }
! 144:
! 145: int
! 146: pcfiic_intr(void *arg)
! 147: {
! 148: return (0);
! 149: }
! 150:
! 151: int
! 152: pcfiic_i2c_acquire_bus(void *arg, int flags)
! 153: {
! 154: struct pcfiic_softc *sc = arg;
! 155:
! 156: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 157: return (0);
! 158:
! 159: return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
! 160: }
! 161:
! 162: void
! 163: pcfiic_i2c_release_bus(void *arg, int flags)
! 164: {
! 165: struct pcfiic_softc *sc = arg;
! 166:
! 167: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 168: return;
! 169:
! 170: rw_exit(&sc->sc_lock);
! 171: }
! 172:
! 173: int
! 174: pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
! 175: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
! 176: {
! 177: struct pcfiic_softc *sc = arg;
! 178: int ret = 0;
! 179:
! 180: #if 0
! 181: printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
! 182: sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
! 183: #endif
! 184:
! 185: if (cold || sc->sc_poll)
! 186: flags |= I2C_F_POLL;
! 187:
! 188: if (sc->sc_master)
! 189: pcfiic_choose_bus(sc, addr >> 7);
! 190:
! 191: if (cmdlen > 0)
! 192: if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0)
! 193: return (1);
! 194:
! 195: if (len > 0) {
! 196: if (I2C_OP_WRITE_P(op))
! 197: ret = pcfiic_xmit(sc, addr & 0x7f, buf, len);
! 198: else
! 199: ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
! 200: }
! 201: return (ret);
! 202: }
! 203:
! 204: int
! 205: pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
! 206: size_t len)
! 207: {
! 208: int i, err = 0;
! 209: volatile u_int8_t r;
! 210:
! 211: if (pcfiic_wait_nBB(sc) != 0)
! 212: return (1);
! 213:
! 214: pcfiic_write(sc, PCF_S0, addr << 1);
! 215: pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
! 216:
! 217: for (i = 0; i <= len; i++) {
! 218: if (pcfiic_wait_pin(sc, &r) != 0) {
! 219: pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
! 220: return (1);
! 221: }
! 222:
! 223: if (r & PCF_STAT_LRB) {
! 224: err = 1;
! 225: break;
! 226: }
! 227:
! 228: if (i < len)
! 229: pcfiic_write(sc, PCF_S0, buf[i]);
! 230: }
! 231: pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
! 232: return (err);
! 233: }
! 234:
! 235: int
! 236: pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
! 237: {
! 238: int i = 0, err = 0;
! 239: volatile u_int8_t r;
! 240:
! 241: if (pcfiic_wait_nBB(sc) != 0)
! 242: return (1);
! 243:
! 244: pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01);
! 245: pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
! 246:
! 247: for (i = 0; i <= len; i++) {
! 248: if (pcfiic_wait_pin(sc, &r) != 0) {
! 249: pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
! 250: return (1);
! 251: }
! 252:
! 253: if ((i != len) && (r & PCF_STAT_LRB)) {
! 254: pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
! 255: return (1);
! 256: }
! 257:
! 258: if (i == len - 1) {
! 259: pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO);
! 260: } else if (i == len) {
! 261: pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
! 262: }
! 263:
! 264: r = pcfiic_read(sc, PCF_S0);
! 265: if (i > 0)
! 266: buf[i - 1] = r;
! 267: }
! 268: return (err);
! 269: }
! 270:
! 271: volatile u_int8_t
! 272: pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
! 273: {
! 274: bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
! 275: BUS_SPACE_BARRIER_READ);
! 276: return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
! 277: }
! 278:
! 279: volatile void
! 280: pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
! 281: {
! 282: bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
! 283: bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
! 284: BUS_SPACE_BARRIER_WRITE);
! 285: }
! 286:
! 287: void
! 288: pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
! 289: {
! 290: bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
! 291: bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
! 292: BUS_SPACE_BARRIER_WRITE);
! 293: }
! 294:
! 295: int
! 296: pcfiic_wait_nBB(struct pcfiic_softc *sc)
! 297: {
! 298: int i;
! 299:
! 300: for (i = 0; i < 1000; i++) {
! 301: if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
! 302: return (0);
! 303: delay(1000);
! 304: }
! 305: return (1);
! 306: }
! 307:
! 308: int
! 309: pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
! 310: {
! 311: int i;
! 312:
! 313: for (i = 0; i < 1000; i++) {
! 314: *r = pcfiic_read(sc, PCF_S1);
! 315: if ((*r & PCF_STAT_PIN) == 0)
! 316: return (0);
! 317: delay(1000);
! 318: }
! 319: return (1);
! 320: }
CVSweb