xref: /freebsd/sys/dev/iicbus/iiconf.c (revision 70fe064ad7cab6c0444b91622f60ec6a462f308a)
1 /*-
2  * Copyright (c) 1998 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/module.h>
32 #include <sys/bus.h>
33 
34 #include <dev/iicbus/iiconf.h>
35 #include <dev/iicbus/iicbus.h>
36 #include "iicbus_if.h"
37 
38 /*
39  * iicbus_intr()
40  */
41 void
42 iicbus_intr(device_t bus, int event, char *buf)
43 {
44 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
45 
46 	/* call owner's intr routine */
47 	if (sc->owner)
48 		IICBUS_INTR(sc->owner, event, buf);
49 
50 	return;
51 }
52 
53 /*
54  * iicbus_alloc_bus()
55  *
56  * Allocate a new bus connected to the given parent device
57  */
58 device_t
59 iicbus_alloc_bus(device_t parent)
60 {
61 	device_t child;
62 
63 	/* add the bus to the parent */
64 	child = device_add_child(parent, "iicbus", -1);
65 
66 	return (child);
67 }
68 
69 static int
70 iicbus_poll(struct iicbus_softc *sc, int how)
71 {
72 	int error;
73 
74 	switch (how) {
75 	case (IIC_WAIT | IIC_INTR):
76 		error = tsleep(sc, IICPRI|PCATCH, "iicreq", 0);
77 		break;
78 
79 	case (IIC_WAIT | IIC_NOINTR):
80 		error = tsleep(sc, IICPRI, "iicreq", 0);
81 		break;
82 
83 	default:
84 		return (EWOULDBLOCK);
85 		break;
86 	}
87 
88 	return (error);
89 }
90 
91 /*
92  * iicbus_request_bus()
93  *
94  * Allocate the device to perform transfers.
95  *
96  * how	: IIC_WAIT or IIC_DONTWAIT
97  */
98 int
99 iicbus_request_bus(device_t bus, device_t dev, int how)
100 {
101 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
102 	int s, error = 0;
103 
104 	/* first, ask the underlying layers if the request is ok */
105 	do {
106 		error = IICBUS_CALLBACK(device_get_parent(bus),
107 						IIC_REQUEST_BUS, (caddr_t)&how);
108 		if (error)
109 			error = iicbus_poll(sc, how);
110 	} while (error == EWOULDBLOCK);
111 
112 	while (!error) {
113 		s = splhigh();
114 		if (sc->owner && sc->owner != dev) {
115 			splx(s);
116 
117 			error = iicbus_poll(sc, how);
118 		} else {
119 			sc->owner = dev;
120 
121 			splx(s);
122 			return (0);
123 		}
124 
125 		/* free any allocated resource */
126 		if (error)
127 			IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS,
128 					(caddr_t)&how);
129 	}
130 
131 	return (error);
132 }
133 
134 /*
135  * iicbus_release_bus()
136  *
137  * Release the device allocated with iicbus_request_dev()
138  */
139 int
140 iicbus_release_bus(device_t bus, device_t dev)
141 {
142 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
143 	int s, error;
144 
145 	/* first, ask the underlying layers if the release is ok */
146 	error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL);
147 
148 	if (error)
149 		return (error);
150 
151 	s = splhigh();
152 	if (sc->owner != dev) {
153 		splx(s);
154 		return (EACCES);
155 	}
156 
157 	sc->owner = 0;
158 	splx(s);
159 
160 	/* wakeup waiting processes */
161 	wakeup(sc);
162 
163 	return (0);
164 }
165 
166 /*
167  * iicbus_started()
168  *
169  * Test if the iicbus is started by the controller
170  */
171 int
172 iicbus_started(device_t bus)
173 {
174 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
175 
176 	return (sc->started);
177 }
178 
179 /*
180  * iicbus_start()
181  *
182  * Send start condition to the slave addressed by 'slave'
183  */
184 int
185 iicbus_start(device_t bus, u_char slave, int timeout)
186 {
187 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
188 	int error = 0;
189 
190 	if (sc->started)
191 		return (EINVAL);		/* bus already started */
192 
193 	if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout)))
194 		sc->started = slave;
195 	else
196 		sc->started = 0;
197 
198 	return (error);
199 }
200 
201 /*
202  * iicbus_repeated_start()
203  *
204  * Send start condition to the slave addressed by 'slave'
205  */
206 int
207 iicbus_repeated_start(device_t bus, u_char slave, int timeout)
208 {
209 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
210 	int error = 0;
211 
212 	if (!sc->started)
213 		return (EINVAL);     /* bus should have been already started */
214 
215 	if (!(error = IICBUS_REPEATED_START(device_get_parent(bus), slave, timeout)))
216 		sc->started = slave;
217 	else
218 		sc->started = 0;
219 
220 	return (error);
221 }
222 
223 /*
224  * iicbus_stop()
225  *
226  * Send stop condition to the bus
227  */
228 int
229 iicbus_stop(device_t bus)
230 {
231 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
232 	int error = 0;
233 
234 	if (!sc->started)
235 		return (EINVAL);		/* bus not started */
236 
237 	error = IICBUS_STOP(device_get_parent(bus));
238 
239 	/* refuse any further access */
240 	sc->started = 0;
241 
242 	return (error);
243 }
244 
245 /*
246  * iicbus_write()
247  *
248  * Write a block of data to the slave previously started by
249  * iicbus_start() call
250  */
251 int
252 iicbus_write(device_t bus, char *buf, int len, int *sent, int timeout)
253 {
254 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
255 
256 	/* a slave must have been started with the appropriate address */
257 	if (!sc->started || (sc->started & LSB))
258 		return (EINVAL);
259 
260 	return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout));
261 }
262 
263 /*
264  * iicbus_read()
265  *
266  * Read a block of data from the slave previously started by
267  * iicbus_read() call
268  */
269 int
270 iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay)
271 {
272 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
273 
274 	/* a slave must have been started with the appropriate address */
275 	if (!sc->started || !(sc->started & LSB))
276 		return (EINVAL);
277 
278 	return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, delay));
279 }
280 
281 /*
282  * iicbus_write_byte()
283  *
284  * Write a byte to the slave previously started by iicbus_start() call
285  */
286 int
287 iicbus_write_byte(device_t bus, char byte, int timeout)
288 {
289 	char data = byte;
290 	int sent;
291 
292 	return (iicbus_write(bus, &data, 1, &sent, timeout));
293 }
294 
295 /*
296  * iicbus_read_byte()
297  *
298  * Read a byte from the slave previously started by iicbus_start() call
299  */
300 int
301 iicbus_read_byte(device_t bus, char *byte, int timeout)
302 {
303 	int read;
304 
305 	return (iicbus_read(bus, byte, 1, &read, IIC_LAST_READ, timeout));
306 }
307 
308 /*
309  * iicbus_block_write()
310  *
311  * Write a block of data to slave ; start/stop protocol managed
312  */
313 int
314 iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent)
315 {
316 	u_char addr = slave & ~LSB;
317 	int error;
318 
319 	if ((error = iicbus_start(bus, addr, 0)))
320 		return (error);
321 
322 	error = iicbus_write(bus, buf, len, sent, 0);
323 
324 	iicbus_stop(bus);
325 
326 	return (error);
327 }
328 
329 /*
330  * iicbus_block_read()
331  *
332  * Read a block of data from slave ; start/stop protocol managed
333  */
334 int
335 iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read)
336 {
337 	u_char addr = slave | LSB;
338 	int error;
339 
340 	if ((error = iicbus_start(bus, addr, 0)))
341 		return (error);
342 
343 	error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0);
344 
345 	iicbus_stop(bus);
346 
347 	return (error);
348 }
349 
350 /*
351  * iicbus_get_addr()
352  *
353  * Get the I2C 7 bits address of the device
354  */
355 u_char
356 iicbus_get_addr(device_t dev)
357 {
358 	uintptr_t addr;
359 	device_t parent = device_get_parent(dev);
360 
361 	BUS_READ_IVAR(parent, dev, IICBUS_IVAR_ADDR, &addr);
362 
363 	return ((u_char)addr);
364 }
365 
366