iiconf.c revision 59760
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 59760 2000-04-29 15:36:14Z phk $
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 */
41void
42iicbus_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 */
58device_t
59iicbus_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
69static int
70iicbus_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 */
98int
99iicbus_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 */
139int
140iicbus_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 */
171int
172iicbus_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 */
184int
185iicbus_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 */
206int
207iicbus_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 */
228int
229iicbus_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 */
251int
252iicbus_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 */
269int
270iicbus_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 */
286int
287iicbus_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 */
300int
301iicbus_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 */
313int
314iicbus_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 */
334int
335iicbus_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 */
355u_char
356iicbus_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