1 /* $NetBSD: pcf8584.c,v 1.22 2025/09/15 13:23:03 thorpej 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/kernel.h> 24 #include <sys/proc.h> 25 #include <sys/bus.h> 26 27 #include <dev/i2c/i2cvar.h> 28 29 #include <dev/ic/pcf8584var.h> 30 #include <dev/ic/pcf8584reg.h> 31 32 /* Internal registers */ 33 #define PCF8584_S0 0x00 34 #define PCF8584_S1 0x01 35 #define PCF8584_S2 0x02 36 #define PCF8584_S3 0x03 37 38 void pcfiic_init(struct pcfiic_softc *); 39 int pcfiic_i2c_acquire_bus(void *, int); 40 void pcfiic_i2c_release_bus(void *, int); 41 int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, 42 size_t, void *, size_t, int); 43 44 int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *, 45 size_t, const u_int8_t *, size_t); 46 int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *, 47 size_t); 48 49 u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); 50 void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); 51 void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t); 52 int pcfiic_wait_BBN(struct pcfiic_softc *); 53 int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *); 54 55 void 56 pcfiic_init(struct pcfiic_softc *sc) 57 { 58 /* init S1 */ 59 pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN); 60 /* own address */ 61 pcfiic_write(sc, PCF8584_S0, sc->sc_addr); 62 63 /* select clock reg */ 64 pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN | PCF8584_CTRL_ES1); 65 pcfiic_write(sc, PCF8584_S0, sc->sc_clock); 66 67 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_IDLE); 68 69 delay(200000); /* Multi-Master mode, wait for longest i2c message */ 70 } 71 72 void 73 pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock, 74 int swapregs) 75 { 76 if (swapregs) { 77 sc->sc_regmap[PCF8584_S1] = PCF8584_S0; 78 sc->sc_regmap[PCF8584_S0] = PCF8584_S1; 79 } else { 80 sc->sc_regmap[PCF8584_S0] = PCF8584_S0; 81 sc->sc_regmap[PCF8584_S1] = PCF8584_S1; 82 } 83 sc->sc_clock = clock; 84 sc->sc_addr = addr; 85 86 pcfiic_init(sc); 87 88 if (sc->sc_master) 89 pcfiic_choose_bus(sc, 0); 90 91 iic_tag_init(&sc->sc_i2c); 92 sc->sc_i2c.ic_cookie = sc; 93 sc->sc_i2c.ic_exec = pcfiic_i2c_exec; 94 95 iicbus_attach(sc->sc_dev, &sc->sc_i2c); 96 } 97 98 int 99 pcfiic_intr(void *arg) 100 { 101 return (0); 102 } 103 104 int 105 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr, 106 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 107 { 108 struct pcfiic_softc *sc = arg; 109 int ret = 0; 110 111 #if 0 112 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n", 113 device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags); 114 #endif 115 116 if (sc->sc_poll) 117 flags |= I2C_F_POLL; 118 119 if (sc->sc_master) 120 pcfiic_choose_bus(sc, addr >> 7); 121 122 /* 123 * If we are writing, write address, cmdbuf, buf. 124 * If we are reading, write address, cmdbuf, then read address, buf. 125 */ 126 if (I2C_OP_WRITE_P(op)) { 127 ret = pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, buf, len); 128 } else { 129 if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, NULL, 0) != 0) 130 return (1); 131 ret = pcfiic_recv(sc, addr & 0x7f, buf, len); 132 } 133 return (ret); 134 } 135 136 int 137 pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *cmdbuf, 138 size_t cmdlen, const u_int8_t *buf, size_t len) 139 { 140 int i, err = 0; 141 volatile u_int8_t r; 142 143 if (pcfiic_wait_BBN(sc) != 0) 144 return (1); 145 146 pcfiic_write(sc, PCF8584_S0, addr << 1); 147 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START); 148 149 for (i = 0; i <= cmdlen + len; i++) { 150 if (pcfiic_wait_pin(sc, &r) != 0) { 151 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 152 return (1); 153 } 154 155 if (r & PCF8584_STATUS_LRB) { 156 err = 1; 157 break; 158 } 159 160 if (i < cmdlen) 161 pcfiic_write(sc, PCF8584_S0, cmdbuf[i]); 162 else if (i < cmdlen + len) 163 pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]); 164 } 165 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 166 return (err); 167 } 168 169 int 170 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len) 171 { 172 int i = 0, err = 0; 173 volatile u_int8_t r; 174 175 if (pcfiic_wait_BBN(sc) != 0) 176 return (1); 177 178 pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); 179 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START); 180 181 for (i = 0; i <= len; i++) { 182 if (pcfiic_wait_pin(sc, &r) != 0) { 183 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 184 return (1); 185 } 186 187 if ((i != len) && (r & PCF8584_STATUS_LRB)) { 188 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 189 return (1); 190 } 191 192 if (i == len - 1) { 193 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK); 194 } else if (i == len) { 195 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); 196 } 197 198 r = pcfiic_read(sc, PCF8584_S0); 199 if (i > 0) 200 buf[i - 1] = r; 201 } 202 return (err); 203 } 204 205 u_int8_t 206 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r) 207 { 208 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1, 209 BUS_SPACE_BARRIER_READ); 210 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r])); 211 } 212 213 void 214 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v) 215 { 216 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v); 217 (void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF8584_S1); 218 } 219 220 void 221 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus) 222 { 223 bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus); 224 bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1, 225 BUS_SPACE_BARRIER_WRITE); 226 } 227 228 int 229 pcfiic_wait_BBN(struct pcfiic_softc *sc) 230 { 231 int i; 232 233 for (i = 0; i < 1000; i++) { 234 if (pcfiic_read(sc, PCF8584_S1) & PCF8584_STATUS_BBN) 235 return (0); 236 delay(1000); 237 } 238 return (1); 239 } 240 241 int 242 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r) 243 { 244 int i; 245 246 for (i = 0; i < 1000; i++) { 247 *r = pcfiic_read(sc, PCF8584_S1); 248 if ((*r & PCF8584_STATUS_PIN) == 0) 249 return (0); 250 delay(1000); 251 } 252 return (1); 253 } 254