138776Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu
338776Snsouch * All rights reserved.
438776Snsouch *
538776Snsouch * Redistribution and use in source and binary forms, with or without
638776Snsouch * modification, are permitted provided that the following conditions
738776Snsouch * are met:
838776Snsouch * 1. Redistributions of source code must retain the above copyright
938776Snsouch *    notice, this list of conditions and the following disclaimer.
1038776Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138776Snsouch *    notice, this list of conditions and the following disclaimer in the
1238776Snsouch *    documentation and/or other materials provided with the distribution.
1338776Snsouch *
1438776Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538776Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638776Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738776Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838776Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938776Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038776Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138776Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238776Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338776Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438776Snsouch * SUCH DAMAGE.
2538776Snsouch *
2638776Snsouch *
2738776Snsouch */
28119419Sobrien
29119419Sobrien#include <sys/cdefs.h>
30119419Sobrien__FBSDID("$FreeBSD$");
3138776Snsouch#include <sys/param.h>
3238776Snsouch#include <sys/systm.h>
33162234Sjhb#include <sys/lock.h>
3438776Snsouch#include <sys/module.h>
35162234Sjhb#include <sys/mutex.h>
3638776Snsouch#include <sys/bus.h>
3738776Snsouch
3838776Snsouch#include <dev/smbus/smbconf.h>
3938776Snsouch#include <dev/smbus/smbus.h>
4038776Snsouch#include "smbus_if.h"
4138776Snsouch
4238776Snsouch/*
4338776Snsouch * smbus_intr()
4438776Snsouch */
4538776Snsouchvoid
4638776Snsouchsmbus_intr(device_t bus, u_char devaddr, char low, char high, int error)
4738776Snsouch{
48162234Sjhb	struct smbus_softc *sc = device_get_softc(bus);
4938776Snsouch
5038776Snsouch	/* call owner's intr routine */
51162234Sjhb	mtx_lock(&sc->lock);
5238776Snsouch	if (sc->owner)
5338776Snsouch		SMBUS_INTR(sc->owner, devaddr, low, high, error);
54162234Sjhb	mtx_unlock(&sc->lock);
5538776Snsouch}
5638776Snsouch
5738776Snsouch/*
5843998Snsouch * smbus_error()
5943998Snsouch *
6043998Snsouch * Converts an smbus error to a unix error.
6143998Snsouch */
6243998Snsouchint
6343998Snsouchsmbus_error(int smb_error)
6443998Snsouch{
6543998Snsouch	int error = 0;
6643998Snsouch
6743998Snsouch	if (smb_error == SMB_ENOERR)
6843998Snsouch		return (0);
6943998Snsouch
70162234Sjhb	if (smb_error & (SMB_ENOTSUPP))
7143998Snsouch		error = ENODEV;
72162234Sjhb	else if (smb_error & (SMB_ENOACK))
7343998Snsouch		error = ENXIO;
74162234Sjhb	else if (smb_error & (SMB_ETIMEOUT))
7543998Snsouch		error = EWOULDBLOCK;
76162234Sjhb	else if (smb_error & (SMB_EBUSY))
7743998Snsouch		error = EBUSY;
78162234Sjhb	else if (smb_error & (SMB_EABORT | SMB_EBUSERR | SMB_ECOLLI))
79162234Sjhb		error = EIO;
80162234Sjhb	else
8143998Snsouch		error = EINVAL;
8243998Snsouch
8343998Snsouch	return (error);
8443998Snsouch}
8543998Snsouch
8640785Snsouchstatic int
8740785Snsouchsmbus_poll(struct smbus_softc *sc, int how)
8840785Snsouch{
8940785Snsouch	int error;
9040785Snsouch
9140785Snsouch	switch (how) {
92162234Sjhb	case SMB_WAIT | SMB_INTR:
93162234Sjhb		error = msleep(sc, &sc->lock, SMBPRI|PCATCH, "smbreq", 0);
9440785Snsouch		break;
9540785Snsouch
96162234Sjhb	case SMB_WAIT | SMB_NOINTR:
97162234Sjhb		error = msleep(sc, &sc->lock, SMBPRI, "smbreq", 0);
9840785Snsouch		break;
9940785Snsouch
10040785Snsouch	default:
101162234Sjhb		error = EWOULDBLOCK;
10240785Snsouch		break;
10340785Snsouch	}
10440785Snsouch
10540785Snsouch	return (error);
10640785Snsouch}
10740785Snsouch
10838776Snsouch/*
10938776Snsouch * smbus_request_bus()
11038776Snsouch *
11138776Snsouch * Allocate the device to perform transfers.
11238776Snsouch *
11338776Snsouch * how	: SMB_WAIT or SMB_DONTWAIT
11438776Snsouch */
11538776Snsouchint
11638776Snsouchsmbus_request_bus(device_t bus, device_t dev, int how)
11738776Snsouch{
118162234Sjhb	struct smbus_softc *sc = device_get_softc(bus);
119162234Sjhb	device_t parent;
120162234Sjhb	int error;
12138776Snsouch
12240785Snsouch	/* first, ask the underlying layers if the request is ok */
123162234Sjhb	parent = device_get_parent(bus);
124162234Sjhb	mtx_lock(&sc->lock);
12552776Snsouch	do {
126162234Sjhb		mtx_unlock(&sc->lock);
127162234Sjhb		error = SMBUS_CALLBACK(parent, SMB_REQUEST_BUS, &how);
128162234Sjhb		mtx_lock(&sc->lock);
129162234Sjhb
13052776Snsouch		if (error)
13152776Snsouch			error = smbus_poll(sc, how);
13252776Snsouch	} while (error == EWOULDBLOCK);
13340785Snsouch
134162234Sjhb	while (error == 0) {
135162234Sjhb		if (sc->owner && sc->owner != dev)
13640785Snsouch			error = smbus_poll(sc, how);
137162234Sjhb		else {
13838776Snsouch			sc->owner = dev;
139162234Sjhb			break;
14038776Snsouch		}
14143975Snsouch
14243975Snsouch		/* free any allocated resource */
143162234Sjhb		if (error) {
144162234Sjhb			mtx_unlock(&sc->lock);
145162234Sjhb			SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
146162234Sjhb			return (error);
147162234Sjhb		}
14838776Snsouch	}
149162234Sjhb	mtx_unlock(&sc->lock);
15038776Snsouch
15138776Snsouch	return (error);
15238776Snsouch}
15338776Snsouch
15438776Snsouch/*
15538776Snsouch * smbus_release_bus()
15638776Snsouch *
15738776Snsouch * Release the device allocated with smbus_request_dev()
15838776Snsouch */
15938776Snsouchint
16038776Snsouchsmbus_release_bus(device_t bus, device_t dev)
16138776Snsouch{
162162234Sjhb	struct smbus_softc *sc = device_get_softc(bus);
163162234Sjhb	int error;
16438776Snsouch
16540785Snsouch	/* first, ask the underlying layers if the release is ok */
16640785Snsouch	error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
16740785Snsouch
16840785Snsouch	if (error)
16940785Snsouch		return (error);
17040785Snsouch
171162234Sjhb	mtx_lock(&sc->lock);
172162234Sjhb	if (sc->owner == dev) {
173162234Sjhb		sc->owner = NULL;
17438776Snsouch
175162234Sjhb		/* wakeup waiting processes */
176162234Sjhb		wakeup(sc);
177162234Sjhb	} else
178162234Sjhb		error = EACCES;
179162234Sjhb	mtx_unlock(&sc->lock);
18038776Snsouch
181162234Sjhb	return (error);
18238776Snsouch}
183