1d70424edSNicolas Souchu /*-
2*4d846d26SWarner 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