xref: /freebsd/sys/dev/smbus/smbconf.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
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  *
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 
36 #include <dev/smbus/smbconf.h>
37 #include <dev/smbus/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 	return;
53 }
54 
55 /*
56  * smbus_error()
57  *
58  * Converts an smbus error to a unix error.
59  */
60 int
61 smbus_error(int smb_error)
62 {
63 	int error = 0;
64 
65 	if (smb_error == SMB_ENOERR)
66 		return (0);
67 
68 	if (smb_error & (SMB_ENOTSUPP)) {
69 		error = ENODEV;
70 	} else if (smb_error & (SMB_ENOACK)) {
71 		error = ENXIO;
72 	} else if (smb_error & (SMB_ETIMEOUT)) {
73 		error = EWOULDBLOCK;
74 	} else if (smb_error & (SMB_EBUSY)) {
75 		error = EBUSY;
76 	} else {
77 		error = EINVAL;
78 	}
79 
80 	return (error);
81 }
82 
83 static int
84 smbus_poll(struct smbus_softc *sc, int how)
85 {
86 	int error;
87 
88 	switch (how) {
89 	case (SMB_WAIT | SMB_INTR):
90 		error = tsleep(sc, SMBPRI|PCATCH, "smbreq", 0);
91 		break;
92 
93 	case (SMB_WAIT | SMB_NOINTR):
94 		error = tsleep(sc, SMBPRI, "smbreq", 0);
95 		break;
96 
97 	default:
98 		return (EWOULDBLOCK);
99 		break;
100 	}
101 
102 	return (error);
103 }
104 
105 /*
106  * smbus_request_bus()
107  *
108  * Allocate the device to perform transfers.
109  *
110  * how	: SMB_WAIT or SMB_DONTWAIT
111  */
112 int
113 smbus_request_bus(device_t bus, device_t dev, int how)
114 {
115 	struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
116 	int s, error = 0;
117 
118 	/* first, ask the underlying layers if the request is ok */
119 	do {
120 		error = SMBUS_CALLBACK(device_get_parent(bus),
121 						SMB_REQUEST_BUS, (caddr_t)&how);
122 		if (error)
123 			error = smbus_poll(sc, how);
124 	} while (error == EWOULDBLOCK);
125 
126 	while (!error) {
127 		s = splhigh();
128 		if (sc->owner && sc->owner != dev) {
129 			splx(s);
130 
131 			error = smbus_poll(sc, how);
132 		} else {
133 			sc->owner = dev;
134 
135 			splx(s);
136 			return (0);
137 		}
138 
139 		/* free any allocated resource */
140 		if (error)
141 			SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS,
142 					(caddr_t)&how);
143 	}
144 
145 	return (error);
146 }
147 
148 /*
149  * smbus_release_bus()
150  *
151  * Release the device allocated with smbus_request_dev()
152  */
153 int
154 smbus_release_bus(device_t bus, device_t dev)
155 {
156 	struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
157 	int s, error;
158 
159 	/* first, ask the underlying layers if the release is ok */
160 	error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
161 
162 	if (error)
163 		return (error);
164 
165 	s = splhigh();
166 	if (sc->owner != dev) {
167 		splx(s);
168 		return (EACCES);
169 	}
170 
171 	sc->owner = 0;
172 	splx(s);
173 
174 	/* wakeup waiting processes */
175 	wakeup(sc);
176 
177 	return (0);
178 }
179