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