xref: /netbsd-src/sys/dev/ic/pcf8584.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /*	$NetBSD: pcf8584.c,v 1.15 2016/01/11 18:24:56 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@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 registers */
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, const u_int8_t *, 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 	rw_enter(&sc->sc_lock, RW_WRITER);
120 	return 0;
121 }
122 
123 void
124 pcfiic_i2c_release_bus(void *arg, int flags)
125 {
126 	struct pcfiic_softc	*sc = arg;
127 
128 	rw_exit(&sc->sc_lock);
129 }
130 
131 int
132 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
133     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
134 {
135 	struct pcfiic_softc	*sc = arg;
136 	int			ret = 0;
137 
138 #if 0
139         printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
140             device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags);
141 #endif
142 
143 	if (cold || sc->sc_poll)
144 		flags |= I2C_F_POLL;
145 
146 	if (sc->sc_master)
147 		pcfiic_choose_bus(sc, addr >> 7);
148 
149 	/*
150 	 * If we are writing, write address, cmdbuf, buf.
151 	 * If we are reading, write address, cmdbuf, then read address, buf.
152 	 */
153 	if (I2C_OP_WRITE_P(op)) {
154 		ret = pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, buf, len);
155 	} else {
156 		if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, NULL, 0) != 0)
157 			return (1);
158 		ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
159 	}
160 	return (ret);
161 }
162 
163 int
164 pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *cmdbuf,
165     size_t cmdlen, const u_int8_t *buf, size_t len)
166 {
167 	int			i, err = 0;
168 	volatile u_int8_t	r;
169 
170 	if (pcfiic_wait_BBN(sc) != 0)
171 		return (1);
172 
173 	pcfiic_write(sc, PCF8584_S0, addr << 1);
174 	pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START);
175 
176 	for (i = 0; i <= cmdlen + len; i++) {
177 		if (pcfiic_wait_pin(sc, &r) != 0) {
178 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
179 			return (1);
180 		}
181 
182 		if (r & PCF8584_STATUS_LRB) {
183 			err = 1;
184 			break;
185 		}
186 
187 		if (i < cmdlen)
188 			pcfiic_write(sc, PCF8584_S0, cmdbuf[i]);
189 		else if (i < cmdlen + len)
190 			pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]);
191 	}
192 	pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
193 	return (err);
194 }
195 
196 int
197 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
198 {
199 	int			i = 0, err = 0;
200 	volatile u_int8_t	r;
201 
202 	if (pcfiic_wait_BBN(sc) != 0)
203 		return (1);
204 
205 	pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01);
206 	pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START);
207 
208 	for (i = 0; i <= len; i++) {
209 		if (pcfiic_wait_pin(sc, &r) != 0) {
210 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
211 			return (1);
212 		}
213 
214 		if ((i != len) && (r & PCF8584_STATUS_LRB)) {
215 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
216 			return (1);
217 		}
218 
219 		if (i == len - 1) {
220 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK);
221 		} else if (i == len) {
222 			pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
223 		}
224 
225 		r = pcfiic_read(sc, PCF8584_S0);
226 		if (i > 0)
227 			buf[i - 1] = r;
228 	}
229 	return (err);
230 }
231 
232 u_int8_t
233 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
234 {
235 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
236 	    BUS_SPACE_BARRIER_READ);
237 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
238 }
239 
240 void
241 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
242 {
243 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
244 	(void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF8584_S1);
245 }
246 
247 void
248 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
249 {
250 	bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
251 	bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
252 	    BUS_SPACE_BARRIER_WRITE);
253 }
254 
255 int
256 pcfiic_wait_BBN(struct pcfiic_softc *sc)
257 {
258 	int		i;
259 
260 	for (i = 0; i < 1000; i++) {
261 		if (pcfiic_read(sc, PCF8584_S1) & PCF8584_STATUS_BBN)
262 			return (0);
263 		delay(1000);
264 	}
265 	return (1);
266 }
267 
268 int
269 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
270 {
271 	int		i;
272 
273 	for (i = 0; i < 1000; i++) {
274 		*r = pcfiic_read(sc, PCF8584_S1);
275 		if ((*r & PCF8584_STATUS_PIN) == 0)
276 			return (0);
277 		delay(1000);
278 	}
279 	return (1);
280 }
281