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