xref: /freebsd/sys/dev/smbus/smbconf.c (revision fdafd315)
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 
31d70424edSNicolas Souchu #include <sys/param.h>
32d70424edSNicolas Souchu #include <sys/systm.h>
337048a99cSJohn Baldwin #include <sys/lock.h>
34d70424edSNicolas Souchu #include <sys/module.h>
357048a99cSJohn Baldwin #include <sys/mutex.h>
36d70424edSNicolas Souchu #include <sys/bus.h>
37d70424edSNicolas Souchu 
38d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
39d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
40d70424edSNicolas Souchu #include "smbus_if.h"
41d70424edSNicolas Souchu 
42d70424edSNicolas Souchu /*
43d70424edSNicolas Souchu  * smbus_intr()
44d70424edSNicolas Souchu  */
45d70424edSNicolas Souchu void
smbus_intr(device_t bus,u_char devaddr,char low,char high,int error)46d70424edSNicolas Souchu smbus_intr(device_t bus, u_char devaddr, char low, char high, int error)
47d70424edSNicolas Souchu {
487048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(bus);
49d70424edSNicolas Souchu 
50d70424edSNicolas Souchu 	/* call owner's intr routine */
517048a99cSJohn Baldwin 	mtx_lock(&sc->lock);
52d70424edSNicolas Souchu 	if (sc->owner)
53d70424edSNicolas Souchu 		SMBUS_INTR(sc->owner, devaddr, low, high, error);
547048a99cSJohn Baldwin 	mtx_unlock(&sc->lock);
55d70424edSNicolas Souchu }
56d70424edSNicolas Souchu 
57d70424edSNicolas Souchu /*
584012f363SNicolas Souchu  * smbus_error()
594012f363SNicolas Souchu  *
604012f363SNicolas Souchu  * Converts an smbus error to a unix error.
614012f363SNicolas Souchu  */
624012f363SNicolas Souchu int
smbus_error(int smb_error)634012f363SNicolas Souchu smbus_error(int smb_error)
644012f363SNicolas Souchu {
654012f363SNicolas Souchu 	int error = 0;
664012f363SNicolas Souchu 
674012f363SNicolas Souchu 	if (smb_error == SMB_ENOERR)
684012f363SNicolas Souchu 		return (0);
694012f363SNicolas Souchu 
707048a99cSJohn Baldwin 	if (smb_error & (SMB_ENOTSUPP))
714012f363SNicolas Souchu 		error = ENODEV;
727048a99cSJohn Baldwin 	else if (smb_error & (SMB_ENOACK))
734012f363SNicolas Souchu 		error = ENXIO;
747048a99cSJohn Baldwin 	else if (smb_error & (SMB_ETIMEOUT))
754012f363SNicolas Souchu 		error = EWOULDBLOCK;
767048a99cSJohn Baldwin 	else if (smb_error & (SMB_EBUSY))
774012f363SNicolas Souchu 		error = EBUSY;
787048a99cSJohn Baldwin 	else if (smb_error & (SMB_EABORT | SMB_EBUSERR | SMB_ECOLLI))
797048a99cSJohn Baldwin 		error = EIO;
807048a99cSJohn Baldwin 	else
814012f363SNicolas Souchu 		error = EINVAL;
824012f363SNicolas Souchu 
834012f363SNicolas Souchu 	return (error);
844012f363SNicolas Souchu }
854012f363SNicolas Souchu 
863ab1f056SNicolas Souchu static int
smbus_poll(struct smbus_softc * sc,int how)873ab1f056SNicolas Souchu smbus_poll(struct smbus_softc *sc, int how)
88d70424edSNicolas Souchu {
893ab1f056SNicolas Souchu 	int error;
90d70424edSNicolas Souchu 
91d70424edSNicolas Souchu 	switch (how) {
927048a99cSJohn Baldwin 	case SMB_WAIT | SMB_INTR:
937048a99cSJohn Baldwin 		error = msleep(sc, &sc->lock, SMBPRI|PCATCH, "smbreq", 0);
94d70424edSNicolas Souchu 		break;
95d70424edSNicolas Souchu 
967048a99cSJohn Baldwin 	case SMB_WAIT | SMB_NOINTR:
977048a99cSJohn Baldwin 		error = msleep(sc, &sc->lock, SMBPRI, "smbreq", 0);
98d70424edSNicolas Souchu 		break;
99d70424edSNicolas Souchu 
100d70424edSNicolas Souchu 	default:
1017048a99cSJohn Baldwin 		error = EWOULDBLOCK;
102d70424edSNicolas Souchu 		break;
103d70424edSNicolas Souchu 	}
104d70424edSNicolas Souchu 
1053ab1f056SNicolas Souchu 	return (error);
1063ab1f056SNicolas Souchu }
1073ab1f056SNicolas Souchu 
1083ab1f056SNicolas Souchu /*
1093ab1f056SNicolas Souchu  * smbus_request_bus()
1103ab1f056SNicolas Souchu  *
1113ab1f056SNicolas Souchu  * Allocate the device to perform transfers.
1123ab1f056SNicolas Souchu  *
1133ab1f056SNicolas Souchu  * how	: SMB_WAIT or SMB_DONTWAIT
1143ab1f056SNicolas Souchu  */
1153ab1f056SNicolas Souchu int
smbus_request_bus(device_t bus,device_t dev,int how)1163ab1f056SNicolas Souchu smbus_request_bus(device_t bus, device_t dev, int how)
1173ab1f056SNicolas Souchu {
1187048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(bus);
1197048a99cSJohn Baldwin 	device_t parent;
1207048a99cSJohn Baldwin 	int error;
1213ab1f056SNicolas Souchu 
1223ab1f056SNicolas Souchu 	/* first, ask the underlying layers if the request is ok */
1237048a99cSJohn Baldwin 	parent = device_get_parent(bus);
1247048a99cSJohn Baldwin 	mtx_lock(&sc->lock);
1257a191714SNicolas Souchu 	do {
1267048a99cSJohn Baldwin 		mtx_unlock(&sc->lock);
1277048a99cSJohn Baldwin 		error = SMBUS_CALLBACK(parent, SMB_REQUEST_BUS, &how);
1287048a99cSJohn Baldwin 		mtx_lock(&sc->lock);
1297048a99cSJohn Baldwin 
1307a191714SNicolas Souchu 		if (error)
1317a191714SNicolas Souchu 			error = smbus_poll(sc, how);
1327a191714SNicolas Souchu 	} while (error == EWOULDBLOCK);
1333ab1f056SNicolas Souchu 
1347048a99cSJohn Baldwin 	while (error == 0) {
1357048a99cSJohn Baldwin 		if (sc->owner && sc->owner != dev)
1363ab1f056SNicolas Souchu 			error = smbus_poll(sc, how);
1377048a99cSJohn Baldwin 		else {
138d70424edSNicolas Souchu 			sc->owner = dev;
1397048a99cSJohn Baldwin 			break;
140d70424edSNicolas Souchu 		}
141ba81c311SNicolas Souchu 
142ba81c311SNicolas Souchu 		/* free any allocated resource */
1437048a99cSJohn Baldwin 		if (error) {
1447048a99cSJohn Baldwin 			mtx_unlock(&sc->lock);
1457048a99cSJohn Baldwin 			SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
1467048a99cSJohn Baldwin 			return (error);
147d70424edSNicolas Souchu 		}
1487048a99cSJohn Baldwin 	}
1497048a99cSJohn Baldwin 	mtx_unlock(&sc->lock);
150d70424edSNicolas Souchu 
151d70424edSNicolas Souchu 	return (error);
152d70424edSNicolas Souchu }
153d70424edSNicolas Souchu 
154d70424edSNicolas Souchu /*
155d70424edSNicolas Souchu  * smbus_release_bus()
156d70424edSNicolas Souchu  *
157d70424edSNicolas Souchu  * Release the device allocated with smbus_request_dev()
158d70424edSNicolas Souchu  */
159d70424edSNicolas Souchu int
smbus_release_bus(device_t bus,device_t dev)160d70424edSNicolas Souchu smbus_release_bus(device_t bus, device_t dev)
161d70424edSNicolas Souchu {
1627048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(bus);
1637048a99cSJohn Baldwin 	int error;
1643ab1f056SNicolas Souchu 
1653ab1f056SNicolas Souchu 	/* first, ask the underlying layers if the release is ok */
1663ab1f056SNicolas Souchu 	error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
1673ab1f056SNicolas Souchu 
1683ab1f056SNicolas Souchu 	if (error)
1693ab1f056SNicolas Souchu 		return (error);
170d70424edSNicolas Souchu 
1717048a99cSJohn Baldwin 	mtx_lock(&sc->lock);
1727048a99cSJohn Baldwin 	if (sc->owner == dev) {
1737048a99cSJohn Baldwin 		sc->owner = NULL;
174d70424edSNicolas Souchu 
175d70424edSNicolas Souchu 		/* wakeup waiting processes */
176d70424edSNicolas Souchu 		wakeup(sc);
1777048a99cSJohn Baldwin 	} else
1787048a99cSJohn Baldwin 		error = EACCES;
1797048a99cSJohn Baldwin 	mtx_unlock(&sc->lock);
180d70424edSNicolas Souchu 
1817048a99cSJohn Baldwin 	return (error);
182d70424edSNicolas Souchu }
183