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