Home | History | Annotate | Line # | Download | only in ic
      1 /*	$NetBSD: pcf8584.c,v 1.26 2026/09/16 06:51:01 jdc 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/intr.h>
     24 #include <sys/kernel.h>
     25 #include <sys/mutex.h>
     26 #include <sys/proc.h>
     27 #include <sys/bus.h>
     28 
     29 #include <dev/i2c/i2cvar.h>
     30 
     31 #include <dev/ic/pcf8584var.h>
     32 #include <dev/ic/pcf8584reg.h>
     33 
     34 void		pcfiic_init(struct pcfiic_softc *);
     35 int		pcfiic_i2c_acquire_bus(void *, int);
     36 void		pcfiic_i2c_release_bus(void *, int);
     37 int		pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
     38 		    size_t, void *, size_t, int);
     39 
     40 int		pcfiic_xmit(struct pcfiic_softc *, const u_int8_t *, size_t,
     41 		    const u_int8_t *, size_t);
     42 int		pcfiic_recv(struct pcfiic_softc *, u_int8_t *, size_t);
     43 
     44 u_int8_t	pcfiic_read(struct pcfiic_softc *, bus_size_t);
     45 void		pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
     46 void		pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
     47 int		pcfiic_wait_BBN(struct pcfiic_softc *);
     48 int		pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
     49 
     50 void
     51 pcfiic_init(struct pcfiic_softc *sc)
     52 {
     53 	/* init S1 */
     54 	pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN);
     55 	/* own address */
     56 	pcfiic_write(sc, PCF8584_S0, sc->sc_addr);
     57 
     58 	/* select clock reg */
     59 	pcfiic_write(sc, PCF8584_S1, PCF8584_CTRL_PIN | PCF8584_CTRL_ES1);
     60 	pcfiic_write(sc, PCF8584_S0, sc->sc_clock);
     61 
     62 	pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_IDLE);
     63 
     64 	delay(200000);	/* Multi-Master mode, wait for longest i2c message */
     65 }
     66 
     67 void
     68 pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock)
     69 {
     70 	sc->sc_clock = clock;
     71 	sc->sc_addr = addr;
     72 
     73 	pcfiic_init(sc);
     74 
     75 	if (sc->sc_has_mux)
     76 		pcfiic_choose_bus(sc, 0);
     77 
     78 	iic_tag_init(&sc->sc_i2c);
     79 	sc->sc_i2c.ic_cookie = sc;
     80 	sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
     81 
     82 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM);
     83 	cv_init(&sc->sc_cv, "pcfiic");
     84 
     85 	iicbus_attach(sc->sc_dev, &sc->sc_i2c);
     86 }
     87 
     88 int
     89 pcfiic_intr(void *arg)
     90 {
     91 	struct pcfiic_softc	*sc = arg;
     92 	u_int8_t 		s1, r;
     93 
     94 	mutex_enter(&sc->sc_mutex);
     95 
     96 	if (sc->sc_stage == PCFIIC_STAGE_IDLE) {
     97 		/* printf("%s: intr and idle\n", device_xname(sc->sc_dev)); */
     98 		mutex_exit(&sc->sc_mutex);
     99 		return (0);
    100 	}
    101 
    102 	s1 = pcfiic_read(sc, PCF8584_S1);
    103 	if ((s1 & PCF8584_STATUS_PIN) != 0) {
    104 		/* printf("%s: intr +PIN (0x%02x)\n",
    105 		    device_xname(sc->sc_dev), s1); */
    106 		mutex_exit(&sc->sc_mutex);
    107 		return (0);
    108 	}
    109 
    110 	switch (sc->sc_stage) {
    111 	case PCFIIC_STAGE_WRITE:
    112 		if (s1 & PCF8584_STATUS_LRB) {
    113 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    114 			sc->sc_err = 1;
    115 			sc->sc_stage = PCFIIC_STAGE_STOP;
    116 			break;
    117 		}
    118 
    119 		/* Start the write from cmd */
    120 		if (sc->sc_i < sc->sc_cmdlen) {
    121 			pcfiic_write(sc, PCF8584_S0, sc->sc_cmdbuf[sc->sc_i]);
    122 			sc->sc_i++;
    123 			break;
    124 		}
    125 
    126 		/* If a read OP, switch with repeat start after writing cmd */
    127 		if (sc->sc_op == PCFIIC_OP_WR_RD) {
    128 			sc->sc_i = 0;
    129 			sc->sc_stage = PCFIIC_STAGE_READ;
    130 			pcfiic_write(sc, PCF8584_S1,
    131 			    PCF8584_CMD_REPSTART | PCF8584_CTRL_ENI);
    132 			pcfiic_write(sc, PCF8584_S0,
    133 			    (sc->sc_target << 1) | 0x01);
    134 			break;
    135 		}
    136 
    137 		/* If a write OP, continue the write, now from buf */
    138 		if (sc->sc_i < sc->sc_cmdlen + sc->sc_len) {
    139 			pcfiic_write(sc, PCF8584_S0,
    140 			    sc->sc_buf[sc->sc_i - sc->sc_cmdlen]);
    141 			sc->sc_i++;
    142 		} else {
    143 			/* All data written, send stop */
    144 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    145 			sc->sc_stage = PCFIIC_STAGE_STOP;
    146 		}
    147 		break;
    148 
    149 	case PCFIIC_STAGE_READ:
    150 		if ((sc->sc_i != sc->sc_len) && (s1 & PCF8584_STATUS_LRB)) {
    151 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    152 			pcfiic_read(sc, PCF8584_S0);
    153 			sc->sc_err = 1;
    154 			sc->sc_stage = PCFIIC_STAGE_STOP;
    155 			break;
    156 		}
    157 
    158 		/* Read to buf, send nak on last - 1 */
    159 		if (sc->sc_i == sc->sc_len - 1) {
    160 			pcfiic_write(sc, PCF8584_S1,
    161 			    PCF8584_CMD_NAK | PCF8584_CTRL_ENI);
    162 		} else if (sc->sc_i == sc->sc_len) {
    163 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    164 			sc->sc_stage = PCFIIC_STAGE_STOP;
    165 		}
    166 
    167 		/* We need a dummy read to start data shifting into S0 */
    168 		r = pcfiic_read(sc, PCF8584_S0);
    169 		if (sc->sc_i > 0)
    170 			sc->sc_buf[sc->sc_i - 1] = r;
    171 
    172 		sc->sc_i++;
    173 		break;
    174 	}
    175 
    176 	if (sc->sc_stage == PCFIIC_STAGE_STOP) {
    177 		cv_signal(&sc->sc_cv);
    178 	}
    179 
    180 	mutex_exit(&sc->sc_mutex);
    181 
    182 	return (1);
    183 }
    184 
    185 int
    186 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
    187     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
    188 {
    189 	struct pcfiic_softc	*sc = arg;
    190 	int			ret = 0;
    191 	u_int8_t		intr;
    192 	unsigned		deadline, rem;
    193 
    194 #if 0
    195         printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
    196             device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags);
    197 #endif
    198 
    199 	if (sc->sc_poll)
    200 		flags |= I2C_F_POLL;
    201 
    202 	if (sc->sc_has_mux)
    203 		pcfiic_choose_bus(sc, addr >> 7);
    204 	addr &= 0x7f;
    205 
    206 	if (I2C_OP_WRITE_P(op))
    207 		sc->sc_op = PCFIIC_OP_WRITE;
    208 	else
    209 		if(cmdlen > 0)
    210 			sc->sc_op = PCFIIC_OP_WR_RD;
    211 		else
    212 			sc->sc_op = PCFIIC_OP_READ;
    213 
    214 	if (flags & I2C_F_POLL) {
    215 		intr = 0;
    216 	} else {
    217 		/* Interrupt-driven setup */
    218 		intr = PCF8584_CTRL_ENI;
    219 		sc->sc_target = addr;
    220 		sc->sc_cmdbuf = cmdbuf;
    221 		sc->sc_cmdlen = cmdlen;
    222 		sc->sc_buf = buf;
    223 		sc->sc_len = len;
    224 		sc->sc_i = 0;
    225 		sc->sc_err = 0;
    226 		mutex_enter(&sc->sc_mutex);
    227 		sc->sc_stage = PCFIIC_STAGE_IDLE;
    228 		mutex_exit(&sc->sc_mutex);
    229 	}
    230 
    231 	if (pcfiic_wait_BBN(sc) != 0) {
    232 		printf("%s: transmit failed (BBN)\n", device_xname(sc->sc_dev));
    233 		return (1);
    234 	}
    235 
    236 	/* Start the transaction */
    237 	if (sc->sc_op == PCFIIC_OP_READ) {
    238 		mutex_enter(&sc->sc_mutex);
    239 		sc->sc_stage = PCFIIC_STAGE_READ;
    240 		mutex_exit(&sc->sc_mutex);
    241 		pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01);
    242 	} else {
    243 		mutex_enter(&sc->sc_mutex);
    244 		sc->sc_stage = PCFIIC_STAGE_WRITE;
    245 		mutex_exit(&sc->sc_mutex);
    246 		pcfiic_write(sc, PCF8584_S0, (addr << 1));
    247 	}
    248 	pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START | intr);
    249 
    250 	if (flags & I2C_F_POLL) {
    251 		/*
    252 		 * If we are writing, write address, cmdbuf, buf.
    253 		 * If we are reading, either:
    254 		 *   write addr, cmdbuf, repeat-start, then read addr to buf,
    255 		 * or:
    256 		 *   read addr to buf.
    257 		 */
    258 		if (sc->sc_op == PCFIIC_OP_WRITE) {
    259 			ret = pcfiic_xmit(sc, cmdbuf, cmdlen, buf, len);
    260 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    261 		} else {
    262 			if (sc->sc_op == PCFIIC_OP_WR_RD) {
    263 				if (pcfiic_xmit(sc, cmdbuf, cmdlen,
    264 				    NULL, 0) != 0) {
    265 					pcfiic_write(sc, PCF8584_S1,
    266 					    PCF8584_CMD_STOP);
    267 					return (1);
    268 				}
    269 				pcfiic_write(sc, PCF8584_S1,
    270 				    PCF8584_CMD_REPSTART);
    271 				pcfiic_write(sc, PCF8584_S0,
    272 				    (addr << 1) | 0x01);
    273 			}
    274 			/* PCFIIC_OP_READ */
    275 			ret = pcfiic_recv(sc, buf, len);
    276 		}
    277 		return (ret);
    278 	} else {	/* interrupt-driven */
    279 		/* Wait for the transfer to complete (stop). */
    280 		deadline = getticks() +
    281 		    /*timeout*/ hz / 10 + 10 * (cmdlen + len);
    282 		mutex_enter(&sc->sc_mutex);
    283 		while (sc->sc_stage != PCFIIC_STAGE_STOP) {
    284 			rem = deadline - getticks();
    285 			if (!rem || rem >= INT_MAX) {
    286 				printf("%s: intr timeout\n",
    287 				    device_xname(sc->sc_dev));
    288 				sc->sc_stage = PCFIIC_STAGE_IDLE;
    289 				mutex_exit(&sc->sc_mutex);
    290 				pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    291 				return (1);
    292 			}
    293 			cv_timedwait(&sc->sc_cv, &sc->sc_mutex, rem);
    294 		}
    295 		mutex_exit(&sc->sc_mutex);
    296 		return (sc->sc_err);
    297 	}
    298 }
    299 
    300 /* Polling write */
    301 int
    302 pcfiic_xmit(struct pcfiic_softc *sc, const u_int8_t *cmdbuf, size_t cmdlen,
    303     const u_int8_t *buf, size_t len)
    304 {
    305 	int			i;
    306 	volatile u_int8_t	r;
    307 
    308 	for (i = 0; i <= cmdlen + len; i++) {
    309 		if (pcfiic_wait_pin(sc, &r) != 0) {
    310 			printf("%s: transmit failed at %d (PIN)\n",
    311 			    device_xname(sc->sc_dev), i);
    312 			return (1);
    313 		}
    314 
    315 		if (r & PCF8584_STATUS_LRB) {
    316 			return (1);
    317 		}
    318 
    319 		/* Write from cmd then buf */
    320 		if (i < cmdlen)
    321 			pcfiic_write(sc, PCF8584_S0, cmdbuf[i]);
    322 		else if (i < cmdlen + len)
    323 			pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]);
    324 	}
    325 	return (0);
    326 }
    327 
    328 /* Polling read */
    329 int
    330 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t *buf, size_t len)
    331 {
    332 	int			i = 0, err = 0;
    333 	volatile u_int8_t	r;
    334 
    335 	for (i = 0; i <= len; i++) {
    336 		if (pcfiic_wait_pin(sc, &r) != 0) {
    337 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    338 			printf("%s: receive failed at %d (PIN)\n",
    339 			    device_xname(sc->sc_dev), i);
    340 			return (1);
    341 		}
    342 
    343 		if ((i != len) && (r & PCF8584_STATUS_LRB)) {
    344 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    345 			pcfiic_read(sc, PCF8584_S0);
    346 			return (1);
    347 		}
    348 
    349 		/* Read to buf, send nak on last - 1 */
    350 		if (i == len - 1) {
    351 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK);
    352 		} else if (i == len) {
    353 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
    354 		}
    355 
    356 		/* We need a dummy read to start data shifting into S0 */
    357 		r = pcfiic_read(sc, PCF8584_S0);
    358 		if (i > 0)
    359 			buf[i - 1] = r;
    360 	}
    361 	return (err);
    362 }
    363 
    364 static inline void
    365 pcfiic_delay(struct pcfiic_softc *sc)
    366 {
    367 	if (sc->sc_delay) {
    368 		delay(sc->sc_delay);
    369 	}
    370 }
    371 
    372 u_int8_t
    373 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
    374 {
    375 	u_int8_t val;
    376 
    377 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
    378 	    BUS_SPACE_BARRIER_READ);
    379 	val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]);
    380 	pcfiic_delay(sc);
    381 	return val;
    382 }
    383 
    384 void
    385 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
    386 {
    387 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
    388 	pcfiic_delay(sc);
    389 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[PCF8584_S1], 1,
    390 	    BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
    391 	(void)bus_space_read_1(sc->sc_iot, sc->sc_ioh,
    392 	    sc->sc_regmap[PCF8584_S1]);
    393 	pcfiic_delay(sc);
    394 }
    395 
    396 void
    397 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
    398 {
    399 	bus_space_write_1(sc->sc_iot, sc->sc_mux_ioh, 0, bus);
    400 	bus_space_barrier(sc->sc_iot, sc->sc_mux_ioh, 0, 1,
    401 	    BUS_SPACE_BARRIER_WRITE);
    402 }
    403 
    404 int
    405 pcfiic_wait_BBN(struct pcfiic_softc *sc)
    406 {
    407 	int		i;
    408 
    409 	for (i = 0; i < 1000; i++) {
    410 		if (pcfiic_read(sc, PCF8584_S1) & PCF8584_STATUS_BBN)
    411 			return (0);
    412 		delay(1000);
    413 	}
    414 	return (1);
    415 }
    416 
    417 int
    418 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
    419 {
    420 	int		i;
    421 
    422 	for (i = 0; i < 1000; i++) {
    423 		*r = pcfiic_read(sc, PCF8584_S1);
    424 		if ((*r & PCF8584_STATUS_PIN) == 0)
    425 			return (0);
    426 		delay(1000);
    427 	}
    428 	return (1);
    429 }
    430