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