xref: /openbsd-src/sys/dev/ic/pcf8584.c (revision a6cacc5311a67b4bdd4df5bd6213de2a4e75c5bc)
1*a6cacc53Skettenis /*	$OpenBSD: pcf8584.c,v 1.11 2010/08/01 18:48:41 kettenis Exp $ */
2c74283e0Sdlg 
3c74283e0Sdlg /*
4c74283e0Sdlg  * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
5c74283e0Sdlg  *
6c74283e0Sdlg  * Permission to use, copy, modify, and distribute this software for any
7c74283e0Sdlg  * purpose with or without fee is hereby granted, provided that the above
8c74283e0Sdlg  * copyright notice and this permission notice appear in all copies.
9c74283e0Sdlg  *
10c74283e0Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c74283e0Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c74283e0Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c74283e0Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c74283e0Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c74283e0Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c74283e0Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c74283e0Sdlg  */
18c74283e0Sdlg 
19c74283e0Sdlg #include <sys/param.h>
20c74283e0Sdlg #include <sys/systm.h>
21c74283e0Sdlg #include <sys/device.h>
22c74283e0Sdlg #include <sys/malloc.h>
23c74283e0Sdlg #include <sys/kernel.h>
24ee945c99Sjsg #include <sys/rwlock.h>
25c74283e0Sdlg #include <sys/proc.h>
26c74283e0Sdlg 
27c74283e0Sdlg #include <machine/bus.h>
28c74283e0Sdlg 
29c74283e0Sdlg #include <dev/i2c/i2cvar.h>
30c74283e0Sdlg 
31c74283e0Sdlg #include <dev/ic/pcf8584var.h>
32c74283e0Sdlg 
33c74283e0Sdlg #define PCF_S0			0x00
34c74283e0Sdlg #define PCF_S1			0x01
35c74283e0Sdlg #define PCF_S2			0x02
36c74283e0Sdlg #define PCF_S3			0x03
37c74283e0Sdlg 
38c74283e0Sdlg #define PCF_CTRL_ACK		(1<<0)
39c74283e0Sdlg #define PCF_CTRL_STO		(1<<1)
40c74283e0Sdlg #define PCF_CTRL_STA		(1<<2)
41c74283e0Sdlg #define PCF_CTRL_ENI		(1<<3)
42c74283e0Sdlg #define PCF_CTRL_ES2		(1<<4)
43c74283e0Sdlg #define PCF_CTRL_ES1		(1<<5)
44c74283e0Sdlg #define PCF_CTRL_ESO		(1<<6)
45c74283e0Sdlg #define PCF_CTRL_PIN		(1<<7)
46c74283e0Sdlg 
47c74283e0Sdlg #define PCF_CTRL_START		(PCF_CTRL_PIN | PCF_CTRL_ESO | \
48c74283e0Sdlg     PCF_CTRL_STA | PCF_CTRL_ACK)
49c74283e0Sdlg #define PCF_CTRL_STOP		(PCF_CTRL_PIN | PCF_CTRL_ESO | \
50c74283e0Sdlg     PCF_CTRL_STO | PCF_CTRL_ACK)
51c74283e0Sdlg #define PCF_CTRL_REPSTART	(PCF_CTRL_ESO | PCF_CTRL_STA | PCF_CTRL_ACK)
52c74283e0Sdlg #define PCF_CTRL_IDLE		(PCF_CTRL_PIN | PCF_CTRL_ESO | PCF_CTRL_ACK)
53c74283e0Sdlg 
54c74283e0Sdlg #define PCF_STAT_nBB		(1<<0)
55c74283e0Sdlg #define PCF_STAT_LAB		(1<<1)
56c74283e0Sdlg #define PCF_STAT_AAS		(1<<2)
57c74283e0Sdlg #define PCF_STAT_AD0		(1<<3)
58c74283e0Sdlg #define PCF_STAT_LRB		(1<<3)
59c74283e0Sdlg #define PCF_STAT_BER		(1<<4)
60c74283e0Sdlg #define PCF_STAT_STS		(1<<5)
61c74283e0Sdlg #define PCF_STAT_PIN		(1<<7)
62c74283e0Sdlg 
63c74283e0Sdlg struct cfdriver pcfiic_cd = {
64c74283e0Sdlg 	NULL, "pcfiic", DV_DULL
65c74283e0Sdlg };
66c74283e0Sdlg 
673e5c8021Sderaadt void		pcfiic_init(struct pcfiic_softc *);
68c74283e0Sdlg int		pcfiic_i2c_acquire_bus(void *, int);
69c74283e0Sdlg void		pcfiic_i2c_release_bus(void *, int);
70c74283e0Sdlg int		pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
71c74283e0Sdlg 		    size_t, void *, size_t, int);
72c74283e0Sdlg 
73c74283e0Sdlg int		pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
74c74283e0Sdlg 		    size_t);
75c74283e0Sdlg int		pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
76c74283e0Sdlg 		    size_t);
77c74283e0Sdlg 
78b3b2c8a0Skettenis u_int8_t	pcfiic_read(struct pcfiic_softc *, bus_size_t);
79b3b2c8a0Skettenis void		pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
809c6a5656Sderaadt void		pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
81c74283e0Sdlg int		pcfiic_wait_nBB(struct pcfiic_softc *);
82c74283e0Sdlg int		pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
83c74283e0Sdlg 
84c74283e0Sdlg void
pcfiic_init(struct pcfiic_softc * sc)853e5c8021Sderaadt pcfiic_init(struct pcfiic_softc *sc)
863e5c8021Sderaadt {
873e5c8021Sderaadt 	/* init S1 */
883e5c8021Sderaadt 	pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN);
893e5c8021Sderaadt 	/* own address */
903e5c8021Sderaadt 	pcfiic_write(sc, PCF_S0, sc->sc_addr);
913e5c8021Sderaadt 
923e5c8021Sderaadt 	/* select clock reg */
933e5c8021Sderaadt 	pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1);
943e5c8021Sderaadt 	pcfiic_write(sc, PCF_S0, sc->sc_clock);
953e5c8021Sderaadt 
963e5c8021Sderaadt 	pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE);
973e5c8021Sderaadt 
983e5c8021Sderaadt 	delay(200000);	/* Multi-Master mode, wait for longest i2c message */
993e5c8021Sderaadt }
1003e5c8021Sderaadt 
1013e5c8021Sderaadt void
pcfiic_attach(struct pcfiic_softc * sc,i2c_addr_t addr,u_int8_t clock,int swapregs,void (* scan_func)(struct device *,struct i2cbus_attach_args *,void *),void * scan_arg)1023e5c8021Sderaadt pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock,
1033e5c8021Sderaadt     int swapregs,
104c74283e0Sdlg     void (*scan_func)(struct device *, struct i2cbus_attach_args *, void *),
105c74283e0Sdlg     void *scan_arg)
106c74283e0Sdlg {
107c74283e0Sdlg 	struct i2cbus_attach_args		iba;
108c74283e0Sdlg 
1093e5c8021Sderaadt 	if (swapregs) {
1103e5c8021Sderaadt 		sc->sc_regmap[PCF_S1] = PCF_S0;
1113e5c8021Sderaadt 		sc->sc_regmap[PCF_S0] = PCF_S1;
1123e5c8021Sderaadt 	} else {
1133e5c8021Sderaadt 		sc->sc_regmap[PCF_S0] = PCF_S0;
1143e5c8021Sderaadt 		sc->sc_regmap[PCF_S1] = PCF_S1;
1153e5c8021Sderaadt 	}
1163e5c8021Sderaadt 	sc->sc_clock = clock;
1173e5c8021Sderaadt 	sc->sc_addr = addr;
118c74283e0Sdlg 
1193e5c8021Sderaadt 	pcfiic_init(sc);
120c74283e0Sdlg 
121e18683d3Sdlg 	printf("\n");
122c74283e0Sdlg 
1239c6a5656Sderaadt 	if (sc->sc_master)
1249c6a5656Sderaadt 		pcfiic_choose_bus(sc, 0);
1259c6a5656Sderaadt 
126ee945c99Sjsg 	rw_init(&sc->sc_lock, "iiclk");
127c74283e0Sdlg 	sc->sc_i2c.ic_cookie = sc;
128c74283e0Sdlg 	sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus;
129c74283e0Sdlg 	sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus;
130c74283e0Sdlg 	sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
131c74283e0Sdlg 
132c74283e0Sdlg 	bzero(&iba, sizeof(iba));
133c74283e0Sdlg 	iba.iba_name = "iic";
134c74283e0Sdlg 	iba.iba_tag = &sc->sc_i2c;
135c74283e0Sdlg 	iba.iba_bus_scan = scan_func;
136c74283e0Sdlg 	iba.iba_bus_scan_arg = scan_arg;
137c74283e0Sdlg 	config_found(&sc->sc_dev, &iba, iicbus_print);
138c74283e0Sdlg }
139c74283e0Sdlg 
140c74283e0Sdlg int
pcfiic_intr(void * arg)141c74283e0Sdlg pcfiic_intr(void *arg)
142c74283e0Sdlg {
143c74283e0Sdlg 	return (0);
144c74283e0Sdlg }
145c74283e0Sdlg 
146c74283e0Sdlg int
pcfiic_i2c_acquire_bus(void * arg,int flags)147c74283e0Sdlg pcfiic_i2c_acquire_bus(void *arg, int flags)
148c74283e0Sdlg {
149c74283e0Sdlg 	struct pcfiic_softc	*sc = arg;
150c74283e0Sdlg 
151c74283e0Sdlg 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
152c74283e0Sdlg 		return (0);
153c74283e0Sdlg 
154ee945c99Sjsg 	return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
155c74283e0Sdlg }
156c74283e0Sdlg 
157c74283e0Sdlg void
pcfiic_i2c_release_bus(void * arg,int flags)158c74283e0Sdlg pcfiic_i2c_release_bus(void *arg, int flags)
159c74283e0Sdlg {
160c74283e0Sdlg 	struct pcfiic_softc	*sc = arg;
161c74283e0Sdlg 
162c74283e0Sdlg 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
163c74283e0Sdlg 		return;
164c74283e0Sdlg 
165ee945c99Sjsg 	rw_exit(&sc->sc_lock);
166c74283e0Sdlg }
167c74283e0Sdlg 
168c74283e0Sdlg int
pcfiic_i2c_exec(void * arg,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)169c74283e0Sdlg pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
170c74283e0Sdlg     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
171c74283e0Sdlg {
172c74283e0Sdlg 	struct pcfiic_softc	*sc = arg;
173c74283e0Sdlg 	int			ret = 0;
174c74283e0Sdlg 
175c74283e0Sdlg #if 0
176c74283e0Sdlg         printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
177c74283e0Sdlg             sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
178c74283e0Sdlg #endif
179c74283e0Sdlg 
180c74283e0Sdlg 	if (cold || sc->sc_poll)
181c74283e0Sdlg 		flags |= I2C_F_POLL;
182c74283e0Sdlg 
1839c6a5656Sderaadt 	if (sc->sc_master)
1849c6a5656Sderaadt 		pcfiic_choose_bus(sc, addr >> 7);
1859c6a5656Sderaadt 
186c265439bSderaadt 	if (cmdlen > 0)
1879c6a5656Sderaadt 		if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0)
188c74283e0Sdlg 			return (1);
189c74283e0Sdlg 
190c74283e0Sdlg 	if (len > 0) {
191c74283e0Sdlg 		if (I2C_OP_WRITE_P(op))
1929c6a5656Sderaadt 			ret = pcfiic_xmit(sc, addr & 0x7f, buf, len);
193c74283e0Sdlg 		else
1949c6a5656Sderaadt 			ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
195c74283e0Sdlg 	}
196c74283e0Sdlg 	return (ret);
197c74283e0Sdlg }
198c74283e0Sdlg 
199c74283e0Sdlg int
pcfiic_xmit(struct pcfiic_softc * sc,u_int8_t addr,const u_int8_t * buf,size_t len)200c74283e0Sdlg pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
201c74283e0Sdlg     size_t len)
202c74283e0Sdlg {
2033e5c8021Sderaadt 	int			i, err = 0;
204c74283e0Sdlg 	volatile u_int8_t	r;
205c74283e0Sdlg 
2063e5c8021Sderaadt 	if (pcfiic_wait_nBB(sc) != 0)
207e5eda077Sderaadt 		return (1);
208c74283e0Sdlg 
209c74283e0Sdlg 	pcfiic_write(sc, PCF_S0, addr << 1);
2109c6a5656Sderaadt 	pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
211c74283e0Sdlg 
212c74283e0Sdlg 	for (i = 0; i <= len; i++) {
2133e5c8021Sderaadt 		if (pcfiic_wait_pin(sc, &r) != 0) {
2143e5c8021Sderaadt 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
2153e5c8021Sderaadt 			return (1);
216e5eda077Sderaadt 		}
217c74283e0Sdlg 
218c74283e0Sdlg 		if (r & PCF_STAT_LRB) {
219c74283e0Sdlg 			err = 1;
220c74283e0Sdlg 			break;
221c74283e0Sdlg 		}
222c74283e0Sdlg 
223c74283e0Sdlg 		if (i < len)
224c74283e0Sdlg 			pcfiic_write(sc, PCF_S0, buf[i]);
225c74283e0Sdlg 	}
2269c6a5656Sderaadt 	pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
227c74283e0Sdlg 	return (err);
228c74283e0Sdlg }
229c74283e0Sdlg 
230c74283e0Sdlg int
pcfiic_recv(struct pcfiic_softc * sc,u_int8_t addr,u_int8_t * buf,size_t len)231c74283e0Sdlg pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
232c74283e0Sdlg {
233c265439bSderaadt 	int			i = 0, err = 0;
234c74283e0Sdlg 	volatile u_int8_t	r;
235c74283e0Sdlg 
236c74283e0Sdlg 	if (pcfiic_wait_nBB(sc) != 0)
237c74283e0Sdlg 		return (1);
238c74283e0Sdlg 
2393e5c8021Sderaadt 	pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01);
2409c6a5656Sderaadt 	pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
241c74283e0Sdlg 
242c74283e0Sdlg 	for (i = 0; i <= len; i++) {
243c74283e0Sdlg 		if (pcfiic_wait_pin(sc, &r) != 0) {
2449c6a5656Sderaadt 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
245c74283e0Sdlg 			return (1);
246c74283e0Sdlg 		}
247c74283e0Sdlg 
248c74283e0Sdlg 		if ((i != len) && (r & PCF_STAT_LRB)) {
2499c6a5656Sderaadt 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
250c74283e0Sdlg 			return (1);
251c74283e0Sdlg 		}
252c74283e0Sdlg 
253c74283e0Sdlg 		if (i == len - 1) {
254c74283e0Sdlg 			pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO);
255c74283e0Sdlg 		} else if (i == len) {
2569c6a5656Sderaadt 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
257c74283e0Sdlg 		}
258c74283e0Sdlg 
259c74283e0Sdlg 		r = pcfiic_read(sc, PCF_S0);
260c74283e0Sdlg 		if (i > 0)
261c74283e0Sdlg 			buf[i - 1] = r;
262c74283e0Sdlg 	}
263c74283e0Sdlg 	return (err);
264c74283e0Sdlg }
265c74283e0Sdlg 
266b3b2c8a0Skettenis u_int8_t
pcfiic_read(struct pcfiic_softc * sc,bus_size_t r)267c74283e0Sdlg pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
268c74283e0Sdlg {
2693e5c8021Sderaadt 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
270c74283e0Sdlg 	    BUS_SPACE_BARRIER_READ);
2713e5c8021Sderaadt 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
272c74283e0Sdlg }
273c74283e0Sdlg 
274b3b2c8a0Skettenis void
pcfiic_write(struct pcfiic_softc * sc,bus_size_t r,u_int8_t v)275c74283e0Sdlg pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
276c74283e0Sdlg {
2773e5c8021Sderaadt 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
278*a6cacc53Skettenis 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, 4,
279*a6cacc53Skettenis 	    BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
280*a6cacc53Skettenis 	bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[PCF_S1]);
281c74283e0Sdlg }
282c74283e0Sdlg 
2839c6a5656Sderaadt void
pcfiic_choose_bus(struct pcfiic_softc * sc,u_int8_t bus)2849c6a5656Sderaadt pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
2859c6a5656Sderaadt {
2869c6a5656Sderaadt 	bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
2879c6a5656Sderaadt 	bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
2889c6a5656Sderaadt 	    BUS_SPACE_BARRIER_WRITE);
2899c6a5656Sderaadt }
2909c6a5656Sderaadt 
291c74283e0Sdlg int
pcfiic_wait_nBB(struct pcfiic_softc * sc)292c74283e0Sdlg pcfiic_wait_nBB(struct pcfiic_softc *sc)
293c74283e0Sdlg {
294c74283e0Sdlg 	int		i;
295c74283e0Sdlg 
296c74283e0Sdlg 	for (i = 0; i < 1000; i++) {
297c74283e0Sdlg 		if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
298c74283e0Sdlg 			return (0);
299c74283e0Sdlg 		delay(1000);
300c74283e0Sdlg 	}
301c74283e0Sdlg 	return (1);
302c74283e0Sdlg }
303c74283e0Sdlg 
304c74283e0Sdlg int
pcfiic_wait_pin(struct pcfiic_softc * sc,volatile u_int8_t * r)305c74283e0Sdlg pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
306c74283e0Sdlg {
307c74283e0Sdlg 	int		i;
308c74283e0Sdlg 
309c74283e0Sdlg 	for (i = 0; i < 1000; i++) {
310c74283e0Sdlg 		*r = pcfiic_read(sc, PCF_S1);
311c74283e0Sdlg 		if ((*r & PCF_STAT_PIN) == 0)
312c74283e0Sdlg 			return (0);
313c74283e0Sdlg 		delay(1000);
314c74283e0Sdlg 	}
315c74283e0Sdlg 	return (1);
316c74283e0Sdlg }
317