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