Home | History | Annotate | Line # | Download | only in ic
pcf8584.c revision 1.16
      1 /*	$NetBSD: pcf8584.c,v 1.16 2019/12/22 23:23: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 (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/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 
     39 void		pcfiic_init(struct pcfiic_softc *);
     40 int		pcfiic_i2c_acquire_bus(void *, int);
     41 void		pcfiic_i2c_release_bus(void *, int);
     42 int		pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
     43 		    size_t, void *, size_t, int);
     44 
     45 int		pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
     46 		    size_t, const u_int8_t *, size_t);
     47 int		pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
     48 		    size_t);
     49 
     50 u_int8_t	pcfiic_read(struct pcfiic_softc *, bus_size_t);
     51 void		pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
     52 void		pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
     53 int		pcfiic_wait_BBN(struct pcfiic_softc *);
     54 int		pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
     55 
     56 void
     57 pcfiic_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 
     73 void
     74 pcfiic_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);
    103 }
    104 
    105 int
    106 pcfiic_intr(void *arg)
    107 {
    108 	return (0);
    109 }
    110 
    111 int
    112 pcfiic_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 (cold || 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 
    143 int
    144 pcfiic_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 
    176 int
    177 pcfiic_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 
    212 u_int8_t
    213 pcfiic_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 
    220 void
    221 pcfiic_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 
    227 void
    228 pcfiic_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 
    235 int
    236 pcfiic_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 
    248 int
    249 pcfiic_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