xref: /dragonfly/sys/bus/iicbus/iiconf.c (revision 9dbf638f)
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: src/sys/dev/iicbus/iiconf.c,v 1.10 1999/12/03 08:41:02 mdodd Exp $
27  * $DragonFly: src/sys/bus/iicbus/iiconf.c,v 1.4 2003/08/07 21:16:45 dillon Exp $
28  *
29  */
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 
37 #include "iiconf.h"
38 #include "iicbus.h"
39 #include "iicbus_if.h"
40 
41 /*
42  * iicbus_intr()
43  */
44 void
45 iicbus_intr(device_t bus, int event, char *buf)
46 {
47 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
48 
49 	/* call owner's intr routine */
50 	if (sc->owner)
51 		IICBUS_INTR(sc->owner, event, buf);
52 
53 	return;
54 }
55 
56 /*
57  * iicbus_alloc_bus()
58  *
59  * Allocate a new bus connected to the given parent device
60  */
61 device_t
62 iicbus_alloc_bus(device_t parent)
63 {
64 	device_t child;
65 
66 	/* add the bus to the parent */
67 	child = device_add_child(parent, "iicbus", -1);
68 
69 	return (child);
70 }
71 
72 static int
73 iicbus_poll(struct iicbus_softc *sc, int how)
74 {
75 	int error;
76 
77 	switch (how) {
78 	case (IIC_WAIT | IIC_INTR):
79 		error = tsleep(sc, PCATCH, "iicreq", 0);
80 		break;
81 
82 	case (IIC_WAIT | IIC_NOINTR):
83 		error = tsleep(sc, 0, "iicreq", 0);
84 		break;
85 
86 	default:
87 		return (EWOULDBLOCK);
88 		break;
89 	}
90 
91 	return (error);
92 }
93 
94 /*
95  * iicbus_request_bus()
96  *
97  * Allocate the device to perform transfers.
98  *
99  * how	: IIC_WAIT or IIC_DONTWAIT
100  */
101 int
102 iicbus_request_bus(device_t bus, device_t dev, int how)
103 {
104 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
105 	int s, error = 0;
106 
107 	/* first, ask the underlying layers if the request is ok */
108 	do {
109 		error = IICBUS_CALLBACK(device_get_parent(bus),
110 						IIC_REQUEST_BUS, (caddr_t)&how);
111 		if (error)
112 			error = iicbus_poll(sc, how);
113 	} while (error == EWOULDBLOCK);
114 
115 	while (!error) {
116 		s = splhigh();
117 		if (sc->owner && sc->owner != dev) {
118 			splx(s);
119 
120 			error = iicbus_poll(sc, how);
121 		} else {
122 			sc->owner = dev;
123 
124 			splx(s);
125 			return (0);
126 		}
127 
128 		/* free any allocated resource */
129 		if (error)
130 			IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS,
131 					(caddr_t)&how);
132 	}
133 
134 	return (error);
135 }
136 
137 /*
138  * iicbus_release_bus()
139  *
140  * Release the device allocated with iicbus_request_dev()
141  */
142 int
143 iicbus_release_bus(device_t bus, device_t dev)
144 {
145 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
146 	int s, error;
147 
148 	/* first, ask the underlying layers if the release is ok */
149 	error = IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL);
150 
151 	if (error)
152 		return (error);
153 
154 	s = splhigh();
155 	if (sc->owner != dev) {
156 		splx(s);
157 		return (EACCES);
158 	}
159 
160 	sc->owner = 0;
161 	splx(s);
162 
163 	/* wakeup waiting processes */
164 	wakeup(sc);
165 
166 	return (0);
167 }
168 
169 /*
170  * iicbus_started()
171  *
172  * Test if the iicbus is started by the controller
173  */
174 int
175 iicbus_started(device_t bus)
176 {
177 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
178 
179 	return (sc->started);
180 }
181 
182 /*
183  * iicbus_start()
184  *
185  * Send start condition to the slave addressed by 'slave'
186  */
187 int
188 iicbus_start(device_t bus, u_char slave, int timeout)
189 {
190 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
191 	int error = 0;
192 
193 	if (sc->started)
194 		return (EINVAL);		/* bus already started */
195 
196 	if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout)))
197 		sc->started = slave;
198 	else
199 		sc->started = 0;
200 
201 	return (error);
202 }
203 
204 /*
205  * iicbus_repeated_start()
206  *
207  * Send start condition to the slave addressed by 'slave'
208  */
209 int
210 iicbus_repeated_start(device_t bus, u_char slave, int timeout)
211 {
212 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
213 	int error = 0;
214 
215 	if (!sc->started)
216 		return (EINVAL);     /* bus should have been already started */
217 
218 	if (!(error = IICBUS_REPEATED_START(device_get_parent(bus), slave, timeout)))
219 		sc->started = slave;
220 	else
221 		sc->started = 0;
222 
223 	return (error);
224 }
225 
226 /*
227  * iicbus_stop()
228  *
229  * Send stop condition to the bus
230  */
231 int
232 iicbus_stop(device_t bus)
233 {
234 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
235 	int error = 0;
236 
237 	if (!sc->started)
238 		return (EINVAL);		/* bus not started */
239 
240 	error = IICBUS_STOP(device_get_parent(bus));
241 
242 	/* refuse any further access */
243 	sc->started = 0;
244 
245 	return (error);
246 }
247 
248 /*
249  * iicbus_write()
250  *
251  * Write a block of data to the slave previously started by
252  * iicbus_start() call
253  */
254 int
255 iicbus_write(device_t bus, char *buf, int len, int *sent, int timeout)
256 {
257 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
258 
259 	/* a slave must have been started with the appropriate address */
260 	if (!sc->started || (sc->started & LSB))
261 		return (EINVAL);
262 
263 	return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout));
264 }
265 
266 /*
267  * iicbus_read()
268  *
269  * Read a block of data from the slave previously started by
270  * iicbus_read() call
271  */
272 int
273 iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay)
274 {
275 	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
276 
277 	/* a slave must have been started with the appropriate address */
278 	if (!sc->started || !(sc->started & LSB))
279 		return (EINVAL);
280 
281 	return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, delay));
282 }
283 
284 /*
285  * iicbus_write_byte()
286  *
287  * Write a byte to the slave previously started by iicbus_start() call
288  */
289 int
290 iicbus_write_byte(device_t bus, char byte, int timeout)
291 {
292 	char data = byte;
293 	int sent;
294 
295 	return (iicbus_write(bus, &data, 1, &sent, timeout));
296 }
297 
298 /*
299  * iicbus_read_byte()
300  *
301  * Read a byte from the slave previously started by iicbus_start() call
302  */
303 int
304 iicbus_read_byte(device_t bus, char *byte, int timeout)
305 {
306 	int read;
307 
308 	return (iicbus_read(bus, byte, 1, &read, IIC_LAST_READ, timeout));
309 }
310 
311 /*
312  * iicbus_block_write()
313  *
314  * Write a block of data to slave ; start/stop protocol managed
315  */
316 int
317 iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent)
318 {
319 	u_char addr = slave & ~LSB;
320 	int error;
321 
322 	if ((error = iicbus_start(bus, addr, 0)))
323 		return (error);
324 
325 	error = iicbus_write(bus, buf, len, sent, 0);
326 
327 	iicbus_stop(bus);
328 
329 	return (error);
330 }
331 
332 /*
333  * iicbus_block_read()
334  *
335  * Read a block of data from slave ; start/stop protocol managed
336  */
337 int
338 iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read)
339 {
340 	u_char addr = slave | LSB;
341 	int error;
342 
343 	if ((error = iicbus_start(bus, addr, 0)))
344 		return (error);
345 
346 	error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0);
347 
348 	iicbus_stop(bus);
349 
350 	return (error);
351 }
352 
353 /*
354  * iicbus_get_addr()
355  *
356  * Get the I2C 7 bits address of the device
357  */
358 u_char
359 iicbus_get_addr(device_t dev)
360 {
361 	uintptr_t addr;
362 	device_t parent = device_get_parent(dev);
363 
364 	BUS_READ_IVAR(parent, dev, IICBUS_IVAR_ADDR, &addr);
365 
366 	return ((u_char)addr);
367 }
368 
369