xref: /freebsd/sys/dev/smbus/smbconf.c (revision 4d846d26)
1d70424edSNicolas Souchu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
5d70424edSNicolas Souchu  * All rights reserved.
6d70424edSNicolas Souchu  *
7d70424edSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
8d70424edSNicolas Souchu  * modification, are permitted provided that the following conditions
9d70424edSNicolas Souchu  * are met:
10d70424edSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
11d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
12d70424edSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
13d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
14d70424edSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
15d70424edSNicolas Souchu  *
16d70424edSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17d70424edSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d70424edSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d70424edSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20d70424edSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d70424edSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d70424edSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d70424edSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d70424edSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d70424edSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d70424edSNicolas Souchu  * SUCH DAMAGE.
27d70424edSNicolas Souchu  *
28d70424edSNicolas Souchu  *
29d70424edSNicolas Souchu  */
30945ff31aSDavid E. O'Brien 
31945ff31aSDavid E. O'Brien #include <sys/cdefs.h>
32945ff31aSDavid E. O'Brien __FBSDID("$FreeBSD$");
33d70424edSNicolas Souchu #include <sys/param.h>
34d70424edSNicolas Souchu #include <sys/systm.h>
357048a99cSJohn Baldwin #include <sys/lock.h>
36d70424edSNicolas Souchu #include <sys/module.h>
377048a99cSJohn Baldwin #include <sys/mutex.h>
38d70424edSNicolas Souchu #include <sys/bus.h>
39d70424edSNicolas Souchu 
40d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
41d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
42d70424edSNicolas Souchu #include "smbus_if.h"
43d70424edSNicolas Souchu 
44d70424edSNicolas Souchu /*
45d70424edSNicolas Souchu  * smbus_intr()
46d70424edSNicolas Souchu  */
47d70424edSNicolas Souchu void
48d70424edSNicolas Souchu smbus_intr(device_t bus, u_char devaddr, char low, char high, int error)
49d70424edSNicolas Souchu {
507048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(bus);
51d70424edSNicolas Souchu 
52d70424edSNicolas Souchu 	/* call owner's intr routine */
537048a99cSJohn Baldwin 	mtx_lock(&sc->lock);
54d70424edSNicolas Souchu 	if (sc->owner)
55d70424edSNicolas Souchu 		SMBUS_INTR(sc->owner, devaddr, low, high, error);
567048a99cSJohn Baldwin 	mtx_unlock(&sc->lock);
57d70424edSNicolas Souchu }
58d70424edSNicolas Souchu 
59d70424edSNicolas Souchu /*
604012f363SNicolas Souchu  * smbus_error()
614012f363SNicolas Souchu  *
624012f363SNicolas Souchu  * Converts an smbus error to a unix error.
634012f363SNicolas Souchu  */
644012f363SNicolas Souchu int
654012f363SNicolas Souchu smbus_error(int smb_error)
664012f363SNicolas Souchu {
674012f363SNicolas Souchu 	int error = 0;
684012f363SNicolas Souchu 
694012f363SNicolas Souchu 	if (smb_error == SMB_ENOERR)
704012f363SNicolas Souchu 		return (0);
714012f363SNicolas Souchu 
727048a99cSJohn Baldwin 	if (smb_error & (SMB_ENOTSUPP))
734012f363SNicolas Souchu 		error = ENODEV;
747048a99cSJohn Baldwin 	else if (smb_error & (SMB_ENOACK))
754012f363SNicolas Souchu 		error = ENXIO;
767048a99cSJohn Baldwin 	else if (smb_error & (SMB_ETIMEOUT))
774012f363SNicolas Souchu 		error = EWOULDBLOCK;
787048a99cSJohn Baldwin 	else if (smb_error & (SMB_EBUSY))
794012f363SNicolas Souchu 		error = EBUSY;
807048a99cSJohn Baldwin 	else if (smb_error & (SMB_EABORT | SMB_EBUSERR | SMB_ECOLLI))
817048a99cSJohn Baldwin 		error = EIO;
827048a99cSJohn Baldwin 	else
834012f363SNicolas Souchu 		error = EINVAL;
844012f363SNicolas Souchu 
854012f363SNicolas Souchu 	return (error);
864012f363SNicolas Souchu }
874012f363SNicolas Souchu 
883ab1f056SNicolas Souchu static int
893ab1f056SNicolas Souchu smbus_poll(struct smbus_softc *sc, int how)
90d70424edSNicolas Souchu {
913ab1f056SNicolas Souchu 	int error;
92d70424edSNicolas Souchu 
93d70424edSNicolas Souchu 	switch (how) {
947048a99cSJohn Baldwin 	case SMB_WAIT | SMB_INTR:
957048a99cSJohn Baldwin 		error = msleep(sc, &sc->lock, SMBPRI|PCATCH, "smbreq", 0);
96d70424edSNicolas Souchu 		break;
97d70424edSNicolas Souchu 
987048a99cSJohn Baldwin 	case SMB_WAIT | SMB_NOINTR:
997048a99cSJohn Baldwin 		error = msleep(sc, &sc->lock, SMBPRI, "smbreq", 0);
100d70424edSNicolas Souchu 		break;
101d70424edSNicolas Souchu 
102d70424edSNicolas Souchu 	default:
1037048a99cSJohn Baldwin 		error = EWOULDBLOCK;
104d70424edSNicolas Souchu 		break;
105d70424edSNicolas Souchu 	}
106d70424edSNicolas Souchu 
1073ab1f056SNicolas Souchu 	return (error);
1083ab1f056SNicolas Souchu }
1093ab1f056SNicolas Souchu 
1103ab1f056SNicolas Souchu /*
1113ab1f056SNicolas Souchu  * smbus_request_bus()
1123ab1f056SNicolas Souchu  *
1133ab1f056SNicolas Souchu  * Allocate the device to perform transfers.
1143ab1f056SNicolas Souchu  *
1153ab1f056SNicolas Souchu  * how	: SMB_WAIT or SMB_DONTWAIT
1163ab1f056SNicolas Souchu  */
1173ab1f056SNicolas Souchu int
1183ab1f056SNicolas Souchu smbus_request_bus(device_t bus, device_t dev, int how)
1193ab1f056SNicolas Souchu {
1207048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(bus);
1217048a99cSJohn Baldwin 	device_t parent;
1227048a99cSJohn Baldwin 	int error;
1233ab1f056SNicolas Souchu 
1243ab1f056SNicolas Souchu 	/* first, ask the underlying layers if the request is ok */
1257048a99cSJohn Baldwin 	parent = device_get_parent(bus);
1267048a99cSJohn Baldwin 	mtx_lock(&sc->lock);
1277a191714SNicolas Souchu 	do {
1287048a99cSJohn Baldwin 		mtx_unlock(&sc->lock);
1297048a99cSJohn Baldwin 		error = SMBUS_CALLBACK(parent, SMB_REQUEST_BUS, &how);
1307048a99cSJohn Baldwin 		mtx_lock(&sc->lock);
1317048a99cSJohn Baldwin 
1327a191714SNicolas Souchu 		if (error)
1337a191714SNicolas Souchu 			error = smbus_poll(sc, how);
1347a191714SNicolas Souchu 	} while (error == EWOULDBLOCK);
1353ab1f056SNicolas Souchu 
1367048a99cSJohn Baldwin 	while (error == 0) {
1377048a99cSJohn Baldwin 		if (sc->owner && sc->owner != dev)
1383ab1f056SNicolas Souchu 			error = smbus_poll(sc, how);
1397048a99cSJohn Baldwin 		else {
140d70424edSNicolas Souchu 			sc->owner = dev;
1417048a99cSJohn Baldwin 			break;
142d70424edSNicolas Souchu 		}
143ba81c311SNicolas Souchu 
144ba81c311SNicolas Souchu 		/* free any allocated resource */
1457048a99cSJohn Baldwin 		if (error) {
1467048a99cSJohn Baldwin 			mtx_unlock(&sc->lock);
1477048a99cSJohn Baldwin 			SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
1487048a99cSJohn Baldwin 			return (error);
149d70424edSNicolas Souchu 		}
1507048a99cSJohn Baldwin 	}
1517048a99cSJohn Baldwin 	mtx_unlock(&sc->lock);
152d70424edSNicolas Souchu 
153d70424edSNicolas Souchu 	return (error);
154d70424edSNicolas Souchu }
155d70424edSNicolas Souchu 
156d70424edSNicolas Souchu /*
157d70424edSNicolas Souchu  * smbus_release_bus()
158d70424edSNicolas Souchu  *
159d70424edSNicolas Souchu  * Release the device allocated with smbus_request_dev()
160d70424edSNicolas Souchu  */
161d70424edSNicolas Souchu int
162d70424edSNicolas Souchu smbus_release_bus(device_t bus, device_t dev)
163d70424edSNicolas Souchu {
1647048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(bus);
1657048a99cSJohn Baldwin 	int error;
1663ab1f056SNicolas Souchu 
1673ab1f056SNicolas Souchu 	/* first, ask the underlying layers if the release is ok */
1683ab1f056SNicolas Souchu 	error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
1693ab1f056SNicolas Souchu 
1703ab1f056SNicolas Souchu 	if (error)
1713ab1f056SNicolas Souchu 		return (error);
172d70424edSNicolas Souchu 
1737048a99cSJohn Baldwin 	mtx_lock(&sc->lock);
1747048a99cSJohn Baldwin 	if (sc->owner == dev) {
1757048a99cSJohn Baldwin 		sc->owner = NULL;
176d70424edSNicolas Souchu 
177d70424edSNicolas Souchu 		/* wakeup waiting processes */
178d70424edSNicolas Souchu 		wakeup(sc);
1797048a99cSJohn Baldwin 	} else
1807048a99cSJohn Baldwin 		error = EACCES;
1817048a99cSJohn Baldwin 	mtx_unlock(&sc->lock);
182d70424edSNicolas Souchu 
1837048a99cSJohn Baldwin 	return (error);
184d70424edSNicolas Souchu }
185