1 /* $NetBSD: pcf8584.c,v 1.26 2026/09/16 06:51:01 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 (at) 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/intr.h> 24 #include <sys/kernel.h> 25 #include <sys/mutex.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 void pcfiic_init(struct pcfiic_softc *); 35 int pcfiic_i2c_acquire_bus(void *, int); 36 void pcfiic_i2c_release_bus(void *, int); 37 int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, 38 size_t, void *, size_t, int); 39 40 int pcfiic_xmit(struct pcfiic_softc *, const u_int8_t *, size_t, 41 const u_int8_t *, size_t); 42 int pcfiic_recv(struct pcfiic_softc *, u_int8_t *, size_t); 43 44 u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); 45 void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); 46 void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t); 47 int pcfiic_wait_BBN(struct pcfiic_softc *); 48 int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *); 49 50 void 51 pcfiic_init(struct pcfiic_softc *sc) 52 { 53 /* init S1 */ 54 pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN); 55 /* own address */ 56 pcfiic_write(sc, PCF8584_S0, sc->sc_addr); 57 58 /* select clock reg */ 59 pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN | PCF8584_CTRL_ES1); 60 pcfiic_write(sc, PCF8584_S0, sc->sc_clock); 61 62 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_IDLE); 63 64 delay(200000); /* Multi-Master mode, wait for longest i2c message */ 65 } 66 67 void 68 pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock) 69 { 70 sc->sc_clock = clock; 71 sc->sc_addr = addr; 72 73 pcfiic_init(sc); 74 75 if (sc->sc_has_mux) 76 pcfiic_choose_bus(sc, 0); 77 78 iic_tag_init(&sc->sc_i2c); 79 sc->sc_i2c.ic_cookie = sc; 80 sc->sc_i2c.ic_exec = pcfiic_i2c_exec; 81 82 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); 83 cv_init(&sc->sc_cv, "pcfiic"); 84 85 iicbus_attach(sc->sc_dev, &sc->sc_i2c); 86 } 87 88 int 89 pcfiic_intr(void *arg) 90 { 91 struct pcfiic_softc *sc = arg; 92 u_int8_t s1, r; 93 94 mutex_enter(&sc->sc_mutex); 95 96 if (sc->sc_stage == PCFIIC_STAGE_IDLE) { 97 /* printf("%s: intr and idle\n", device_xname(sc->sc_dev)); */ 98 mutex_exit(&sc->sc_mutex); 99 return (0); 100 } 101 102 s1 = pcfiic_read(sc, PCF8584_S1); 103 if ((s1 & PCF8584_STATUS_PIN) != 0) { 104 /* printf("%s: intr +PIN (0x%02x)\n", 105 device_xname(sc->sc_dev), s1); */ 106 mutex_exit(&sc->sc_mutex); 107 return (0); 108 } 109 110 switch (sc->sc_stage) { 111 case PCFIIC_STAGE_WRITE: 112 if (s1 & PCF8584_STATUS_LRB) { 113 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 114 sc->sc_err = 1; 115 sc->sc_stage = PCFIIC_STAGE_STOP; 116 break; 117 } 118 119 /* Start the write from cmd */ 120 if (sc->sc_i < sc->sc_cmdlen) { 121 pcfiic_write(sc, PCF8584_S0, sc->sc_cmdbuf[sc->sc_i]); 122 sc->sc_i++; 123 break; 124 } 125 126 /* If a read OP, switch with repeat start after writing cmd */ 127 if (sc->sc_op == PCFIIC_OP_WR_RD) { 128 sc->sc_i = 0; 129 sc->sc_stage = PCFIIC_STAGE_READ; 130 pcfiic_write(sc, PCF8584_S1, 131 PCF8584_CMD_REPSTART | PCF8584_CTRL_ENI); 132 pcfiic_write(sc, PCF8584_S0, 133 (sc->sc_target << 1) | 0x01); 134 break; 135 } 136 137 /* If a write OP, continue the write, now from buf */ 138 if (sc->sc_i < sc->sc_cmdlen + sc->sc_len) { 139 pcfiic_write(sc, PCF8584_S0, 140 sc->sc_buf[sc->sc_i - sc->sc_cmdlen]); 141 sc->sc_i++; 142 } else { 143 /* All data written, send stop */ 144 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 145 sc->sc_stage = PCFIIC_STAGE_STOP; 146 } 147 break; 148 149 case PCFIIC_STAGE_READ: 150 if ((sc->sc_i != sc->sc_len) && (s1 & PCF8584_STATUS_LRB)) { 151 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 152 pcfiic_read(sc, PCF8584_S0); 153 sc->sc_err = 1; 154 sc->sc_stage = PCFIIC_STAGE_STOP; 155 break; 156 } 157 158 /* Read to buf, send nak on last - 1 */ 159 if (sc->sc_i == sc->sc_len - 1) { 160 pcfiic_write(sc, PCF8584_S1, 161 PCF8584_CMD_NAK | PCF8584_CTRL_ENI); 162 } else if (sc->sc_i == sc->sc_len) { 163 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 164 sc->sc_stage = PCFIIC_STAGE_STOP; 165 } 166 167 /* We need a dummy read to start data shifting into S0 */ 168 r = pcfiic_read(sc, PCF8584_S0); 169 if (sc->sc_i > 0) 170 sc->sc_buf[sc->sc_i - 1] = r; 171 172 sc->sc_i++; 173 break; 174 } 175 176 if (sc->sc_stage == PCFIIC_STAGE_STOP) { 177 cv_signal(&sc->sc_cv); 178 } 179 180 mutex_exit(&sc->sc_mutex); 181 182 return (1); 183 } 184 185 int 186 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr, 187 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 188 { 189 struct pcfiic_softc *sc = arg; 190 int ret = 0; 191 u_int8_t intr; 192 unsigned deadline, rem; 193 194 #if 0 195 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n", 196 device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags); 197 #endif 198 199 if (sc->sc_poll) 200 flags |= I2C_F_POLL; 201 202 if (sc->sc_has_mux) 203 pcfiic_choose_bus(sc, addr >> 7); 204 addr &= 0x7f; 205 206 if (I2C_OP_WRITE_P(op)) 207 sc->sc_op = PCFIIC_OP_WRITE; 208 else 209 if(cmdlen > 0) 210 sc->sc_op = PCFIIC_OP_WR_RD; 211 else 212 sc->sc_op = PCFIIC_OP_READ; 213 214 if (flags & I2C_F_POLL) { 215 intr = 0; 216 } else { 217 /* Interrupt-driven setup */ 218 intr = PCF8584_CTRL_ENI; 219 sc->sc_target = addr; 220 sc->sc_cmdbuf = cmdbuf; 221 sc->sc_cmdlen = cmdlen; 222 sc->sc_buf = buf; 223 sc->sc_len = len; 224 sc->sc_i = 0; 225 sc->sc_err = 0; 226 mutex_enter(&sc->sc_mutex); 227 sc->sc_stage = PCFIIC_STAGE_IDLE; 228 mutex_exit(&sc->sc_mutex); 229 } 230 231 if (pcfiic_wait_BBN(sc) != 0) { 232 printf("%s: transmit failed (BBN)\n", device_xname(sc->sc_dev)); 233 return (1); 234 } 235 236 /* Start the transaction */ 237 if (sc->sc_op == PCFIIC_OP_READ) { 238 mutex_enter(&sc->sc_mutex); 239 sc->sc_stage = PCFIIC_STAGE_READ; 240 mutex_exit(&sc->sc_mutex); 241 pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); 242 } else { 243 mutex_enter(&sc->sc_mutex); 244 sc->sc_stage = PCFIIC_STAGE_WRITE; 245 mutex_exit(&sc->sc_mutex); 246 pcfiic_write(sc, PCF8584_S0, (addr << 1)); 247 } 248 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START | intr); 249 250 if (flags & I2C_F_POLL) { 251 /* 252 * If we are writing, write address, cmdbuf, buf. 253 * If we are reading, either: 254 * write addr, cmdbuf, repeat-start, then read addr to buf, 255 * or: 256 * read addr to buf. 257 */ 258 if (sc->sc_op == PCFIIC_OP_WRITE) { 259 ret = pcfiic_xmit(sc, cmdbuf, cmdlen, buf, len); 260 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 261 } else { 262 if (sc->sc_op == PCFIIC_OP_WR_RD) { 263 if (pcfiic_xmit(sc, cmdbuf, cmdlen, 264 NULL, 0) != 0) { 265 pcfiic_write(sc, PCF8584_S1, 266 PCF8584_CMD_STOP); 267 return (1); 268 } 269 pcfiic_write(sc, PCF8584_S1, 270 PCF8584_CMD_REPSTART); 271 pcfiic_write(sc, PCF8584_S0, 272 (addr << 1) | 0x01); 273 } 274 /* PCFIIC_OP_READ */ 275 ret = pcfiic_recv(sc, buf, len); 276 } 277 return (ret); 278 } else { /* interrupt-driven */ 279 /* Wait for the transfer to complete (stop). */ 280 deadline = getticks() + 281 /*timeout*/ hz / 10 + 10 * (cmdlen + len); 282 mutex_enter(&sc->sc_mutex); 283 while (sc->sc_stage != PCFIIC_STAGE_STOP) { 284 rem = deadline - getticks(); 285 if (!rem || rem >= INT_MAX) { 286 printf("%s: intr timeout\n", 287 device_xname(sc->sc_dev)); 288 sc->sc_stage = PCFIIC_STAGE_IDLE; 289 mutex_exit(&sc->sc_mutex); 290 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 291 return (1); 292 } 293 cv_timedwait(&sc->sc_cv, &sc->sc_mutex, rem); 294 } 295 mutex_exit(&sc->sc_mutex); 296 return (sc->sc_err); 297 } 298 } 299 300 /* Polling write */ 301 int 302 pcfiic_xmit(struct pcfiic_softc *sc, const u_int8_t *cmdbuf, size_t cmdlen, 303 const u_int8_t *buf, size_t len) 304 { 305 int i; 306 volatile u_int8_t r; 307 308 for (i = 0; i <= cmdlen + len; i++) { 309 if (pcfiic_wait_pin(sc, &r) != 0) { 310 printf("%s: transmit failed at %d (PIN)\n", 311 device_xname(sc->sc_dev), i); 312 return (1); 313 } 314 315 if (r & PCF8584_STATUS_LRB) { 316 return (1); 317 } 318 319 /* Write from cmd then buf */ 320 if (i < cmdlen) 321 pcfiic_write(sc, PCF8584_S0, cmdbuf[i]); 322 else if (i < cmdlen + len) 323 pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]); 324 } 325 return (0); 326 } 327 328 /* Polling read */ 329 int 330 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t *buf, size_t len) 331 { 332 int i = 0, err = 0; 333 volatile u_int8_t r; 334 335 for (i = 0; i <= len; i++) { 336 if (pcfiic_wait_pin(sc, &r) != 0) { 337 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 338 printf("%s: receive failed at %d (PIN)\n", 339 device_xname(sc->sc_dev), i); 340 return (1); 341 } 342 343 if ((i != len) && (r & PCF8584_STATUS_LRB)) { 344 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 345 pcfiic_read(sc, PCF8584_S0); 346 return (1); 347 } 348 349 /* Read to buf, send nak on last - 1 */ 350 if (i == len - 1) { 351 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK); 352 } else if (i == len) { 353 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 354 } 355 356 /* We need a dummy read to start data shifting into S0 */ 357 r = pcfiic_read(sc, PCF8584_S0); 358 if (i > 0) 359 buf[i - 1] = r; 360 } 361 return (err); 362 } 363 364 static inline void 365 pcfiic_delay(struct pcfiic_softc *sc) 366 { 367 if (sc->sc_delay) { 368 delay(sc->sc_delay); 369 } 370 } 371 372 u_int8_t 373 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r) 374 { 375 u_int8_t val; 376 377 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1, 378 BUS_SPACE_BARRIER_READ); 379 val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]); 380 pcfiic_delay(sc); 381 return val; 382 } 383 384 void 385 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v) 386 { 387 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v); 388 pcfiic_delay(sc); 389 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[PCF8584_S1], 1, 390 BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); 391 (void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, 392 sc->sc_regmap[PCF8584_S1]); 393 pcfiic_delay(sc); 394 } 395 396 void 397 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus) 398 { 399 bus_space_write_1(sc->sc_iot, sc->sc_mux_ioh, 0, bus); 400 bus_space_barrier(sc->sc_iot, sc->sc_mux_ioh, 0, 1, 401 BUS_SPACE_BARRIER_WRITE); 402 } 403 404 int 405 pcfiic_wait_BBN(struct pcfiic_softc *sc) 406 { 407 int i; 408 409 for (i = 0; i < 1000; i++) { 410 if (pcfiic_read(sc, PCF8584_S1) & PCF8584_STATUS_BBN) 411 return (0); 412 delay(1000); 413 } 414 return (1); 415 } 416 417 int 418 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r) 419 { 420 int i; 421 422 for (i = 0; i < 1000; i++) { 423 *r = pcfiic_read(sc, PCF8584_S1); 424 if ((*r & PCF8584_STATUS_PIN) == 0) 425 return (0); 426 delay(1000); 427 } 428 return (1); 429 } 430