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