Merge from vendor branch BINUTILS:
[dragonfly.git] / sys / bus / iicbus / iiconf.c
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