1 /* $NetBSD: pcf8584.c,v 1.15 2016/01/11 18:24:56 jdc Exp $ */ 2 /* $OpenBSD: pcf8584.c,v 1.9 2007/10/20 18:46:21 kettenis Exp $ */ 3 4 /* 5 * Copyright (c) 2006 David Gwynne <dlg@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 #include <sys/kernel.h> 25 #include <sys/rwlock.h> 26 #include <sys/proc.h> 27 #include <sys/bus.h> 28 29 #include <dev/i2c/i2cvar.h> 30 31 #include <dev/ic/pcf8584var.h> 32 #include <dev/ic/pcf8584reg.h> 33 34 /* Internal registers */ 35 #define PCF8584_S0 0x00 36 #define PCF8584_S1 0x01 37 #define PCF8584_S2 0x02 38 #define PCF8584_S3 0x03 39 40 void pcfiic_init(struct pcfiic_softc *); 41 int pcfiic_i2c_acquire_bus(void *, int); 42 void pcfiic_i2c_release_bus(void *, int); 43 int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, 44 size_t, void *, size_t, int); 45 46 int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *, 47 size_t, const u_int8_t *, size_t); 48 int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *, 49 size_t); 50 51 u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); 52 void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); 53 void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t); 54 int pcfiic_wait_BBN(struct pcfiic_softc *); 55 int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *); 56 57 void 58 pcfiic_init(struct pcfiic_softc *sc) 59 { 60 /* init S1 */ 61 pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN); 62 /* own address */ 63 pcfiic_write(sc, PCF8584_S0, sc->sc_addr); 64 65 /* select clock reg */ 66 pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN | PCF8584_CTRL_ES1); 67 pcfiic_write(sc, PCF8584_S0, sc->sc_clock); 68 69 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_IDLE); 70 71 delay(200000); /* Multi-Master mode, wait for longest i2c message */ 72 } 73 74 void 75 pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock, 76 int swapregs) 77 { 78 struct i2cbus_attach_args iba; 79 80 if (swapregs) { 81 sc->sc_regmap[PCF8584_S1] = PCF8584_S0; 82 sc->sc_regmap[PCF8584_S0] = PCF8584_S1; 83 } else { 84 sc->sc_regmap[PCF8584_S0] = PCF8584_S0; 85 sc->sc_regmap[PCF8584_S1] = PCF8584_S1; 86 } 87 sc->sc_clock = clock; 88 sc->sc_addr = addr; 89 90 pcfiic_init(sc); 91 92 printf("\n"); 93 94 if (sc->sc_master) 95 pcfiic_choose_bus(sc, 0); 96 97 rw_init(&sc->sc_lock); 98 sc->sc_i2c.ic_cookie = sc; 99 sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus; 100 sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus; 101 sc->sc_i2c.ic_exec = pcfiic_i2c_exec; 102 103 bzero(&iba, sizeof(iba)); 104 iba.iba_tag = &sc->sc_i2c; 105 config_found(sc->sc_dev, &iba, iicbus_print); 106 } 107 108 int 109 pcfiic_intr(void *arg) 110 { 111 return (0); 112 } 113 114 int 115 pcfiic_i2c_acquire_bus(void *arg, int flags) 116 { 117 struct pcfiic_softc *sc = arg; 118 119 rw_enter(&sc->sc_lock, RW_WRITER); 120 return 0; 121 } 122 123 void 124 pcfiic_i2c_release_bus(void *arg, int flags) 125 { 126 struct pcfiic_softc *sc = arg; 127 128 rw_exit(&sc->sc_lock); 129 } 130 131 int 132 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr, 133 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 134 { 135 struct pcfiic_softc *sc = arg; 136 int ret = 0; 137 138 #if 0 139 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n", 140 device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags); 141 #endif 142 143 if (cold || sc->sc_poll) 144 flags |= I2C_F_POLL; 145 146 if (sc->sc_master) 147 pcfiic_choose_bus(sc, addr >> 7); 148 149 /* 150 * If we are writing, write address, cmdbuf, buf. 151 * If we are reading, write address, cmdbuf, then read address, buf. 152 */ 153 if (I2C_OP_WRITE_P(op)) { 154 ret = pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, buf, len); 155 } else { 156 if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, NULL, 0) != 0) 157 return (1); 158 ret = pcfiic_recv(sc, addr & 0x7f, buf, len); 159 } 160 return (ret); 161 } 162 163 int 164 pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *cmdbuf, 165 size_t cmdlen, const u_int8_t *buf, size_t len) 166 { 167 int i, err = 0; 168 volatile u_int8_t r; 169 170 if (pcfiic_wait_BBN(sc) != 0) 171 return (1); 172 173 pcfiic_write(sc, PCF8584_S0, addr << 1); 174 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START); 175 176 for (i = 0; i <= cmdlen + len; i++) { 177 if (pcfiic_wait_pin(sc, &r) != 0) { 178 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 179 return (1); 180 } 181 182 if (r & PCF8584_STATUS_LRB) { 183 err = 1; 184 break; 185 } 186 187 if (i < cmdlen) 188 pcfiic_write(sc, PCF8584_S0, cmdbuf[i]); 189 else if (i < cmdlen + len) 190 pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]); 191 } 192 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 193 return (err); 194 } 195 196 int 197 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len) 198 { 199 int i = 0, err = 0; 200 volatile u_int8_t r; 201 202 if (pcfiic_wait_BBN(sc) != 0) 203 return (1); 204 205 pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); 206 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START); 207 208 for (i = 0; i <= len; i++) { 209 if (pcfiic_wait_pin(sc, &r) != 0) { 210 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 211 return (1); 212 } 213 214 if ((i != len) && (r & PCF8584_STATUS_LRB)) { 215 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 216 return (1); 217 } 218 219 if (i == len - 1) { 220 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK); 221 } else if (i == len) { 222 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 223 } 224 225 r = pcfiic_read(sc, PCF8584_S0); 226 if (i > 0) 227 buf[i - 1] = r; 228 } 229 return (err); 230 } 231 232 u_int8_t 233 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r) 234 { 235 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1, 236 BUS_SPACE_BARRIER_READ); 237 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r])); 238 } 239 240 void 241 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v) 242 { 243 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v); 244 (void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF8584_S1); 245 } 246 247 void 248 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus) 249 { 250 bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus); 251 bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1, 252 BUS_SPACE_BARRIER_WRITE); 253 } 254 255 int 256 pcfiic_wait_BBN(struct pcfiic_softc *sc) 257 { 258 int i; 259 260 for (i = 0; i < 1000; i++) { 261 if (pcfiic_read(sc, PCF8584_S1) & PCF8584_STATUS_BBN) 262 return (0); 263 delay(1000); 264 } 265 return (1); 266 } 267 268 int 269 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r) 270 { 271 int i; 272 273 for (i = 0; i < 1000; i++) { 274 *r = pcfiic_read(sc, PCF8584_S1); 275 if ((*r & PCF8584_STATUS_PIN) == 0) 276 return (0); 277 delay(1000); 278 } 279 return (1); 280 } 281