Merge from vendor branch READLINE:
[dragonfly.git] / sys / bus / smbus / smbconf.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/smbus/smbconf.c,v 1.9 1999/12/03 08:41:08 mdodd Exp $
27  * $DragonFly: src/sys/bus/smbus/smbconf.c,v 1.4 2003/08/07 21:16:47 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 "smbconf.h"
38 #include "smbus.h"
39 #include "smbus_if.h"
40
41 /*
42  * smbus_intr()
43  */
44 void
45 smbus_intr(device_t bus, u_char devaddr, char low, char high, int error)
46 {
47         struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
48
49         /* call owner's intr routine */
50         if (sc->owner)
51                 SMBUS_INTR(sc->owner, devaddr, low, high, error);
52
53         return;
54 }
55
56 /*
57  * smbus_error()
58  *
59  * Converts an smbus error to a unix error.
60  */
61 int
62 smbus_error(int smb_error)
63 {
64         int error = 0;
65
66         if (smb_error == SMB_ENOERR)
67                 return (0);
68         
69         if (smb_error & (SMB_ENOTSUPP)) {
70                 error = ENODEV;
71         } else if (smb_error & (SMB_ENOACK)) {
72                 error = ENXIO;
73         } else if (smb_error & (SMB_ETIMEOUT)) {
74                 error = EWOULDBLOCK;
75         } else if (smb_error & (SMB_EBUSY)) {
76                 error = EBUSY;
77         } else {
78                 error = EINVAL;
79         }
80
81         return (error);
82 }
83
84 /*
85  * smbus_alloc_bus()
86  *
87  * Allocate a new bus connected to the given parent device
88  */
89 device_t
90 smbus_alloc_bus(device_t parent)
91 {
92         device_t child;
93
94         /* add the bus to the parent */
95         child = device_add_child(parent, "smbus", -1);
96
97         return (child);
98 }
99
100 static int
101 smbus_poll(struct smbus_softc *sc, int how)
102 {
103         int error;
104
105         switch (how) {
106         case (SMB_WAIT | SMB_INTR):
107                 error = tsleep(sc, PCATCH, "smbreq", 0);
108                 break;
109
110         case (SMB_WAIT | SMB_NOINTR):
111                 error = tsleep(sc, 0, "smbreq", 0);
112                 break;
113
114         default:
115                 return (EWOULDBLOCK);
116                 break;
117         }
118
119         return (error);
120 }
121
122 /*
123  * smbus_request_bus()
124  *
125  * Allocate the device to perform transfers.
126  *
127  * how  : SMB_WAIT or SMB_DONTWAIT
128  */
129 int
130 smbus_request_bus(device_t bus, device_t dev, int how)
131 {
132         struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
133         int s, error = 0;
134
135         /* first, ask the underlying layers if the request is ok */
136         do {
137                 error = SMBUS_CALLBACK(device_get_parent(bus),
138                                                 SMB_REQUEST_BUS, (caddr_t)&how);
139                 if (error)
140                         error = smbus_poll(sc, how);
141         } while (error == EWOULDBLOCK);
142
143         while (!error) {
144                 s = splhigh();  
145                 if (sc->owner && sc->owner != dev) {
146                         splx(s);
147
148                         error = smbus_poll(sc, how);
149                 } else {
150                         sc->owner = dev;
151
152                         splx(s);
153                         return (0);
154                 }
155
156                 /* free any allocated resource */
157                 if (error)
158                         SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS,
159                                         (caddr_t)&how);
160         }
161
162         return (error);
163 }
164
165 /*
166  * smbus_release_bus()
167  *
168  * Release the device allocated with smbus_request_dev()
169  */
170 int
171 smbus_release_bus(device_t bus, device_t dev)
172 {
173         struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
174         int s, error;
175
176         /* first, ask the underlying layers if the release is ok */
177         error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
178
179         if (error)
180                 return (error);
181
182         s = splhigh();
183         if (sc->owner != dev) {
184                 splx(s);
185                 return (EACCES);
186         }
187
188         sc->owner = 0;
189         splx(s);
190
191         /* wakeup waiting processes */
192         wakeup(sc);
193
194         return (0);
195 }
196
197 /*
198  * smbus_get_addr()
199  *
200  * Get the I2C 7 bits address of the device
201  */
202 u_char
203 smbus_get_addr(device_t dev)
204 {
205         uintptr_t addr;
206         device_t parent = device_get_parent(dev);
207
208         BUS_READ_IVAR(parent, dev, SMBUS_IVAR_ADDR, &addr);
209
210         return ((u_char)addr);
211 }