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