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