Home | History | Annotate | Line # | Download | only in ic
      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