xref: /dragonfly/sys/bus/smbus/smbconf.c (revision d316587fb7e2095feadd275045648efba8e09370)
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
smbus_intr(device_t bus,u_char devaddr,char low,char high,int error)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
smbus_error(int smb_error)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
smbus_poll(struct smbus_softc * sc,int how)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
smbus_request_bus(device_t bus,device_t dev,int how)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                               if (error == EWOULDBLOCK)
132                                         error = 0;
133                     } else {
134                               sc->owner = dev;
135                               crit_exit();
136                     }
137                     if (error == 0)
138                               break;
139                     /* free any allocated resource */
140                     SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
141           }
142 
143           return (error);
144 }
145 
146 /*
147  * smbus_release_bus()
148  *
149  * Release the device allocated with smbus_request_dev()
150  */
151 int
smbus_release_bus(device_t bus,device_t dev)152 smbus_release_bus(device_t bus, device_t dev)
153 {
154           struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
155           device_t parent;
156           int error;
157 
158           parent = device_get_parent(bus);
159           /* first, ask the underlying layers if the release is ok */
160           error = SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, NULL);
161 
162           if (error)
163                     return (error);
164 
165           crit_enter();
166           if (sc->owner == dev) {
167                     sc->owner = NULL;
168 
169                     /* wakeup waiting processes */
170                     wakeup(sc);
171           } else {
172                     error = EACCES;
173           }
174           crit_exit();
175 
176           /* wakeup waiting processes */
177           wakeup(sc);
178 
179           return (error);
180 }
181