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