xref: /openbsd/sys/dev/ic/pcf8584.c (revision e5dd7070)
1 /*	$OpenBSD: pcf8584.c,v 1.11 2010/08/01 18:48:41 kettenis Exp $ */
2 
3 /*
4  * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/kernel.h>
24 #include <sys/rwlock.h>
25 #include <sys/proc.h>
26 
27 #include <machine/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 struct cfdriver pcfiic_cd = {
64 	NULL, "pcfiic", DV_DULL
65 };
66 
67 void		pcfiic_init(struct pcfiic_softc *);
68 int		pcfiic_i2c_acquire_bus(void *, int);
69 void		pcfiic_i2c_release_bus(void *, int);
70 int		pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
71 		    size_t, void *, size_t, int);
72 
73 int		pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
74 		    size_t);
75 int		pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
76 		    size_t);
77 
78 u_int8_t	pcfiic_read(struct pcfiic_softc *, bus_size_t);
79 void		pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
80 void		pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
81 int		pcfiic_wait_nBB(struct pcfiic_softc *);
82 int		pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
83 
84 void
85 pcfiic_init(struct pcfiic_softc *sc)
86 {
87 	/* init S1 */
88 	pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN);
89 	/* own address */
90 	pcfiic_write(sc, PCF_S0, sc->sc_addr);
91 
92 	/* select clock reg */
93 	pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1);
94 	pcfiic_write(sc, PCF_S0, sc->sc_clock);
95 
96 	pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE);
97 
98 	delay(200000);	/* Multi-Master mode, wait for longest i2c message */
99 }
100 
101 void
102 pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock,
103     int swapregs,
104     void (*scan_func)(struct device *, struct i2cbus_attach_args *, void *),
105     void *scan_arg)
106 {
107 	struct i2cbus_attach_args		iba;
108 
109 	if (swapregs) {
110 		sc->sc_regmap[PCF_S1] = PCF_S0;
111 		sc->sc_regmap[PCF_S0] = PCF_S1;
112 	} else {
113 		sc->sc_regmap[PCF_S0] = PCF_S0;
114 		sc->sc_regmap[PCF_S1] = PCF_S1;
115 	}
116 	sc->sc_clock = clock;
117 	sc->sc_addr = addr;
118 
119 	pcfiic_init(sc);
120 
121 	printf("\n");
122 
123 	if (sc->sc_master)
124 		pcfiic_choose_bus(sc, 0);
125 
126 	rw_init(&sc->sc_lock, "iiclk");
127 	sc->sc_i2c.ic_cookie = sc;
128 	sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus;
129 	sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus;
130 	sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
131 
132 	bzero(&iba, sizeof(iba));
133 	iba.iba_name = "iic";
134 	iba.iba_tag = &sc->sc_i2c;
135 	iba.iba_bus_scan = scan_func;
136 	iba.iba_bus_scan_arg = scan_arg;
137 	config_found(&sc->sc_dev, &iba, iicbus_print);
138 }
139 
140 int
141 pcfiic_intr(void *arg)
142 {
143 	return (0);
144 }
145 
146 int
147 pcfiic_i2c_acquire_bus(void *arg, int flags)
148 {
149 	struct pcfiic_softc	*sc = arg;
150 
151 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
152 		return (0);
153 
154 	return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
155 }
156 
157 void
158 pcfiic_i2c_release_bus(void *arg, int flags)
159 {
160 	struct pcfiic_softc	*sc = arg;
161 
162 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
163 		return;
164 
165 	rw_exit(&sc->sc_lock);
166 }
167 
168 int
169 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
170     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
171 {
172 	struct pcfiic_softc	*sc = arg;
173 	int			ret = 0;
174 
175 #if 0
176         printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
177             sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
178 #endif
179 
180 	if (cold || sc->sc_poll)
181 		flags |= I2C_F_POLL;
182 
183 	if (sc->sc_master)
184 		pcfiic_choose_bus(sc, addr >> 7);
185 
186 	if (cmdlen > 0)
187 		if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0)
188 			return (1);
189 
190 	if (len > 0) {
191 		if (I2C_OP_WRITE_P(op))
192 			ret = pcfiic_xmit(sc, addr & 0x7f, buf, len);
193 		else
194 			ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
195 	}
196 	return (ret);
197 }
198 
199 int
200 pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
201     size_t len)
202 {
203 	int			i, err = 0;
204 	volatile u_int8_t	r;
205 
206 	if (pcfiic_wait_nBB(sc) != 0)
207 		return (1);
208 
209 	pcfiic_write(sc, PCF_S0, addr << 1);
210 	pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
211 
212 	for (i = 0; i <= len; i++) {
213 		if (pcfiic_wait_pin(sc, &r) != 0) {
214 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
215 			return (1);
216 		}
217 
218 		if (r & PCF_STAT_LRB) {
219 			err = 1;
220 			break;
221 		}
222 
223 		if (i < len)
224 			pcfiic_write(sc, PCF_S0, buf[i]);
225 	}
226 	pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
227 	return (err);
228 }
229 
230 int
231 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
232 {
233 	int			i = 0, err = 0;
234 	volatile u_int8_t	r;
235 
236 	if (pcfiic_wait_nBB(sc) != 0)
237 		return (1);
238 
239 	pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01);
240 	pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
241 
242 	for (i = 0; i <= len; i++) {
243 		if (pcfiic_wait_pin(sc, &r) != 0) {
244 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
245 			return (1);
246 		}
247 
248 		if ((i != len) && (r & PCF_STAT_LRB)) {
249 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
250 			return (1);
251 		}
252 
253 		if (i == len - 1) {
254 			pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO);
255 		} else if (i == len) {
256 			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
257 		}
258 
259 		r = pcfiic_read(sc, PCF_S0);
260 		if (i > 0)
261 			buf[i - 1] = r;
262 	}
263 	return (err);
264 }
265 
266 u_int8_t
267 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
268 {
269 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
270 	    BUS_SPACE_BARRIER_READ);
271 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
272 }
273 
274 void
275 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
276 {
277 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
278 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, 4,
279 	    BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
280 	bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[PCF_S1]);
281 }
282 
283 void
284 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
285 {
286 	bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
287 	bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
288 	    BUS_SPACE_BARRIER_WRITE);
289 }
290 
291 int
292 pcfiic_wait_nBB(struct pcfiic_softc *sc)
293 {
294 	int		i;
295 
296 	for (i = 0; i < 1000; i++) {
297 		if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
298 			return (0);
299 		delay(1000);
300 	}
301 	return (1);
302 }
303 
304 int
305 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
306 {
307 	int		i;
308 
309 	for (i = 0; i < 1000; i++) {
310 		*r = pcfiic_read(sc, PCF_S1);
311 		if ((*r & PCF_STAT_PIN) == 0)
312 			return (0);
313 		delay(1000);
314 	}
315 	return (1);
316 }
317