smbconf.c revision 52776
173797Sjhb/*- 268686Sjhb * Copyright (c) 1998 Nicolas Souchu 368686Sjhb * All rights reserved. 468686Sjhb * 568686Sjhb * Redistribution and use in source and binary forms, with or without 668686Sjhb * modification, are permitted provided that the following conditions 768686Sjhb * are met: 868686Sjhb * 1. Redistributions of source code must retain the above copyright 968686Sjhb * notice, this list of conditions and the following disclaimer. 1068686Sjhb * 2. Redistributions in binary form must reproduce the above copyright 1168686Sjhb * notice, this list of conditions and the following disclaimer in the 1268686Sjhb * documentation and/or other materials provided with the distribution. 1368686Sjhb * 1468686Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1568686Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1668686Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1768686Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1868686Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1968686Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2068686Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2168686Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2268686Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2368686Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2468686Sjhb * SUCH DAMAGE. 2568686Sjhb * 2668686Sjhb * $FreeBSD: head/sys/dev/smbus/smbconf.c 52776 1999-11-01 23:15:29Z nsouch $ 27237007Spluknet * 2868686Sjhb */ 2968686Sjhb#include <sys/param.h> 3068686Sjhb#include <sys/systm.h> 3173797Sjhb#include <sys/kernel.h> 32237007Spluknet#include <sys/malloc.h> 3373797Sjhb#include <sys/module.h> 3468686Sjhb#include <sys/bus.h> 3568686Sjhb 3684306Sru#include <dev/smbus/smbconf.h> 3784306Sru#include <dev/smbus/smbus.h> 3884306Sru#include "smbus_if.h" 39237007Spluknet 40237007Spluknet/* 41131735Sru * smbus_intr() 4273797Sjhb */ 4373797Sjhbvoid 44237007Spluknetsmbus_intr(device_t bus, u_char devaddr, char low, char high, int error) 4568686Sjhb{ 4668686Sjhb struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus); 4768686Sjhb 4868686Sjhb /* call owner's intr routine */ 4973797Sjhb if (sc->owner) 5073797Sjhb SMBUS_INTR(sc->owner, devaddr, low, high, error); 5168686Sjhb 52237007Spluknet return; 53237007Spluknet} 5473797Sjhb 55139392Sjkoshy/* 5668686Sjhb * smbus_error() 5768686Sjhb * 5868686Sjhb * Converts an smbus error to a unix error. 5968686Sjhb */ 6073797Sjhbint 6168686Sjhbsmbus_error(int smb_error) 6268686Sjhb{ 6368686Sjhb int error = 0; 6468686Sjhb 6568686Sjhb if (smb_error == SMB_ENOERR) 6668686Sjhb return (0); 6773797Sjhb 6868686Sjhb if (smb_error & (SMB_ENOTSUPP)) { 6968686Sjhb error = ENODEV; 7073797Sjhb } else if (smb_error & (SMB_ENOACK)) { 71237007Spluknet error = ENXIO; 72237007Spluknet } else if (smb_error & (SMB_ETIMEOUT)) { 7368686Sjhb error = EWOULDBLOCK; 74237007Spluknet } else if (smb_error & (SMB_EBUSY)) { 7568686Sjhb error = EBUSY; 76237007Spluknet } else { 7768686Sjhb error = EINVAL; 78237007Spluknet } 79237007Spluknet 80237007Spluknet return (error); 81237007Spluknet} 8268686Sjhb 8368686Sjhb/* 8468686Sjhb * smbus_alloc_bus() 85237007Spluknet * 8668686Sjhb * Allocate a new bus connected to the given parent device 8773797Sjhb */ 8873797Sjhbdevice_t 8973797Sjhbsmbus_alloc_bus(device_t parent) 9073797Sjhb{ 9173797Sjhb device_t child; 9268686Sjhb 9373797Sjhb /* add the bus to the parent */ 9473797Sjhb child = device_add_child(parent, "smbus", -1, NULL); 9568686Sjhb 9668686Sjhb return (child); 9768686Sjhb} 9868686Sjhb 9968686Sjhbstatic int 10068686Sjhbsmbus_poll(struct smbus_softc *sc, int how) 10168686Sjhb{ 10273797Sjhb int error; 10373797Sjhb 104237007Spluknet switch (how) { 10568686Sjhb case (SMB_WAIT | SMB_INTR): 10668686Sjhb error = tsleep(sc, SMBPRI|PCATCH, "smbreq", 0); 10768686Sjhb break; 10868686Sjhb 10973797Sjhb case (SMB_WAIT | SMB_NOINTR): 110139392Sjkoshy error = tsleep(sc, SMBPRI, "smbreq", 0); 11173797Sjhb break; 11273868Sru 11373797Sjhb default: 11473797Sjhb return (EWOULDBLOCK); 11573797Sjhb break; 11668686Sjhb } 11768686Sjhb 118237007Spluknet return (error); 119237007Spluknet} 120237007Spluknet 121237007Spluknet/* 122237007Spluknet * smbus_request_bus() 123237007Spluknet * 124237007Spluknet * Allocate the device to perform transfers. 125237007Spluknet * 12673797Sjhb * how : SMB_WAIT or SMB_DONTWAIT 12768686Sjhb */ 12868686Sjhbint 12968686Sjhbsmbus_request_bus(device_t bus, device_t dev, int how) 13073797Sjhb{ 13168686Sjhb struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus); 13268686Sjhb int s, error = 0; 13368686Sjhb 13468686Sjhb /* first, ask the underlying layers if the request is ok */ 13568686Sjhb do { 136104932Sjhb error = SMBUS_CALLBACK(device_get_parent(bus), 13768686Sjhb SMB_REQUEST_BUS, (caddr_t)&how); 13868686Sjhb if (error) 13968686Sjhb error = smbus_poll(sc, how); 14068686Sjhb } while (error == EWOULDBLOCK); 14168686Sjhb 14268686Sjhb while (!error) { 14368686Sjhb s = splhigh(); 14468686Sjhb if (sc->owner && sc->owner != dev) { 14568686Sjhb splx(s); 146104932Sjhb 147104932Sjhb error = smbus_poll(sc, how); 148104932Sjhb } else { 149107383Sru sc->owner = dev; 15068686Sjhb 15168686Sjhb splx(s); 15268686Sjhb return (0); 153237007Spluknet } 15468686Sjhb 155237007Spluknet /* free any allocated resource */ 156237007Spluknet if (error) 15768686Sjhb SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, 158237007Spluknet (caddr_t)&how); 15968686Sjhb } 160237007Spluknet 16168686Sjhb return (error); 16268686Sjhb} 16368686Sjhb 16492436Sschweikh/* 16568686Sjhb * smbus_release_bus() 16668686Sjhb * 16768686Sjhb * Release the device allocated with smbus_request_dev() 168237007Spluknet */ 169237007Spluknetint 17068686Sjhbsmbus_release_bus(device_t bus, device_t dev) 17168686Sjhb{ 17273797Sjhb struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus); 173237007Spluknet int s, error; 174237007Spluknet 175237007Spluknet /* first, ask the underlying layers if the release is ok */ 17673797Sjhb error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL); 17773797Sjhb 17873797Sjhb if (error) 17973797Sjhb return (error); 18073797Sjhb 18173797Sjhb s = splhigh(); 18273797Sjhb if (sc->owner != dev) { 18373797Sjhb splx(s); 18473797Sjhb return (EACCES); 18573797Sjhb } 18673797Sjhb 18773797Sjhb sc->owner = 0; 18873797Sjhb splx(s); 18973797Sjhb 19073797Sjhb /* wakeup waiting processes */ 191217074Sjhb wakeup(sc); 192217074Sjhb 19373797Sjhb return (0); 19473797Sjhb} 195237007Spluknet 19673797Sjhb/* 19773797Sjhb * smbus_get_addr() 19873797Sjhb * 19973797Sjhb * Get the I2C 7 bits address of the device 20073797Sjhb */ 20173797Sjhbu_char 20273797Sjhbsmbus_get_addr(device_t dev) 20373797Sjhb{ 20473797Sjhb uintptr_t addr; 20573797Sjhb device_t parent = device_get_parent(dev); 20673797Sjhb 207237007Spluknet BUS_READ_IVAR(parent, dev, SMBUS_IVAR_ADDR, &addr); 208237007Spluknet 209237007Spluknet return ((u_char)addr); 21073797Sjhb} 21173797Sjhb