Merge branch 'vendor/FILE'
[dragonfly.git] / sys / bus / smbus / smbconf.c
1 /*-
2  * Copyright (c) 1998, 2001 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.13.10.1 2006/09/22 19:19:16 jhb Exp $
27  * $DragonFly: src/sys/bus/smbus/smbconf.c,v 1.5 2005/06/02 20:40:38 dillon Exp $
28  *
29  */
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/module.h>
33 #include <sys/bus.h>
34 #include <sys/thread2.h>
35
36 #include "smbconf.h"
37 #include "smbus.h"
38 #include "smbus_if.h"
39
40 /*
41  * smbus_intr()
42  */
43 void
44 smbus_intr(device_t bus, u_char devaddr, char low, char high, int error)
45 {
46         struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
47
48         /* call owner's intr routine */
49         if (sc->owner)
50                 SMBUS_INTR(sc->owner, devaddr, low, high, error);
51 }
52
53 /*
54  * smbus_error()
55  *
56  * Converts an smbus error to a unix error.
57  */
58 int
59 smbus_error(int smb_error)
60 {
61         int error = 0;
62
63         if (smb_error == SMB_ENOERR)
64                 return (0);
65         
66         if (smb_error & (SMB_ENOTSUPP))
67                 error = ENODEV;
68         else if (smb_error & (SMB_ENOACK))
69                 error = ENXIO;
70         else if (smb_error & (SMB_ETIMEOUT))
71                 error = EWOULDBLOCK;
72         else if (smb_error & (SMB_EBUSY))
73                 error = EBUSY;
74         else if (smb_error & (SMB_EABORT | SMB_EBUSERR | SMB_ECOLLI))
75                 error = EIO;
76         else
77                 error = EINVAL;
78
79         return (error);
80 }
81
82 static int
83 smbus_poll(struct smbus_softc *sc, int how)
84 {
85         int error;
86
87         switch (how) {
88         case (SMB_WAIT | SMB_INTR):
89                 error = tsleep(sc, PCATCH, "smbreq", 0);
90                 break;
91
92         case (SMB_WAIT | SMB_NOINTR):
93                 error = tsleep(sc, 0, "smbreq", 0);
94                 break;
95
96         default:
97                 error = EWOULDBLOCK;
98                 break;
99         }
100
101         return (error);
102 }
103
104 /*
105  * smbus_request_bus()
106  *
107  * Allocate the device to perform transfers.
108  *
109  * how  : SMB_WAIT or SMB_DONTWAIT
110  */
111 int
112 smbus_request_bus(device_t bus, device_t dev, int how)
113 {
114         struct smbus_softc *sc = device_get_softc(bus);
115         device_t parent;
116         int error;
117
118         /* first, ask the underlying layers if the request is ok */
119         parent = device_get_parent(bus);
120         do {
121                 error = SMBUS_CALLBACK(parent, SMB_REQUEST_BUS, &how);
122                 if (error)
123                         error = smbus_poll(sc, how);
124         } while (error == EWOULDBLOCK);
125
126         while (error == 0) {
127                 crit_enter();
128                 if (sc->owner && sc->owner != dev) {
129                         crit_exit();
130                         error = smbus_poll(sc, how);
131                 } else {
132                         sc->owner = dev;
133                         crit_exit();
134                 }
135
136                 /* free any allocated resource */
137                 if (error)
138                         SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
139         }
140
141         return (error);
142 }
143
144 /*
145  * smbus_release_bus()
146  *
147  * Release the device allocated with smbus_request_dev()
148  */
149 int
150 smbus_release_bus(device_t bus, device_t dev)
151 {
152         struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
153         int error;
154
155         /* first, ask the underlying layers if the release is ok */
156         error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
157
158         if (error)
159                 return (error);
160
161         crit_enter();
162         if (sc->owner == dev) {
163                 sc->owner = NULL;
164
165                 /* wakeup waiting processes */
166                 wakeup(sc);
167         } else {
168                 error = EACCES;
169         }
170         crit_exit();
171
172         /* wakeup waiting processes */
173         wakeup(sc);
174
175         return (error);
176 }