xref: /freebsd/sys/dev/smbus/smbconf.c (revision 4012f363f2d0e01549aff0c76ab1ee00a52b985b)
1d70424edSNicolas Souchu /*-
2d70424edSNicolas Souchu  * Copyright (c) 1998 Nicolas Souchu
3d70424edSNicolas Souchu  * All rights reserved.
4d70424edSNicolas Souchu  *
5d70424edSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
6d70424edSNicolas Souchu  * modification, are permitted provided that the following conditions
7d70424edSNicolas Souchu  * are met:
8d70424edSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
9d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
10d70424edSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
11d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
12d70424edSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
13d70424edSNicolas Souchu  *
14d70424edSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d70424edSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d70424edSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d70424edSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d70424edSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d70424edSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d70424edSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d70424edSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d70424edSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d70424edSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d70424edSNicolas Souchu  * SUCH DAMAGE.
25d70424edSNicolas Souchu  *
264012f363SNicolas Souchu  *	$Id: smbconf.c,v 1.5 1999/02/13 17:57:19 nsouch Exp $
27d70424edSNicolas Souchu  *
28d70424edSNicolas Souchu  */
29d70424edSNicolas Souchu #include <sys/param.h>
30d70424edSNicolas Souchu #include <sys/systm.h>
31d70424edSNicolas Souchu #include <sys/kernel.h>
32d70424edSNicolas Souchu #include <sys/malloc.h>
33d70424edSNicolas Souchu #include <sys/module.h>
34d70424edSNicolas Souchu #include <sys/bus.h>
35d70424edSNicolas Souchu 
36d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
37d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
38d70424edSNicolas Souchu #include "smbus_if.h"
39d70424edSNicolas Souchu 
40d70424edSNicolas Souchu /*
41d70424edSNicolas Souchu  * smbus_intr()
42d70424edSNicolas Souchu  */
43d70424edSNicolas Souchu void
44d70424edSNicolas Souchu smbus_intr(device_t bus, u_char devaddr, char low, char high, int error)
45d70424edSNicolas Souchu {
46d70424edSNicolas Souchu 	struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
47d70424edSNicolas Souchu 
48d70424edSNicolas Souchu 	/* call owner's intr routine */
49d70424edSNicolas Souchu 	if (sc->owner)
50d70424edSNicolas Souchu 		SMBUS_INTR(sc->owner, devaddr, low, high, error);
51d70424edSNicolas Souchu 
52d70424edSNicolas Souchu 	return;
53d70424edSNicolas Souchu }
54d70424edSNicolas Souchu 
55d70424edSNicolas Souchu /*
564012f363SNicolas Souchu  * smbus_error()
574012f363SNicolas Souchu  *
584012f363SNicolas Souchu  * Converts an smbus error to a unix error.
594012f363SNicolas Souchu  */
604012f363SNicolas Souchu int
614012f363SNicolas Souchu smbus_error(int smb_error)
624012f363SNicolas Souchu {
634012f363SNicolas Souchu 	int error = 0;
644012f363SNicolas Souchu 
654012f363SNicolas Souchu 	if (smb_error == SMB_ENOERR)
664012f363SNicolas Souchu 		return (0);
674012f363SNicolas Souchu 
684012f363SNicolas Souchu 	if (smb_error & (SMB_ENOTSUPP)) {
694012f363SNicolas Souchu 		error = ENODEV;
704012f363SNicolas Souchu 	} else if (smb_error & (SMB_ENOACK)) {
714012f363SNicolas Souchu 		error = ENXIO;
724012f363SNicolas Souchu 	} else if (smb_error & (SMB_ETIMEOUT)) {
734012f363SNicolas Souchu 		error = EWOULDBLOCK;
744012f363SNicolas Souchu 	} else if (smb_error & (SMB_EBUSY)) {
754012f363SNicolas Souchu 		error = EBUSY;
764012f363SNicolas Souchu 	} else {
774012f363SNicolas Souchu 		error = EINVAL;
784012f363SNicolas Souchu 	}
794012f363SNicolas Souchu 
804012f363SNicolas Souchu 	return (error);
814012f363SNicolas Souchu }
824012f363SNicolas Souchu 
834012f363SNicolas Souchu /*
84d70424edSNicolas Souchu  * smbus_alloc_bus()
85d70424edSNicolas Souchu  *
86d70424edSNicolas Souchu  * Allocate a new bus connected to the given parent device
87d70424edSNicolas Souchu  */
88d70424edSNicolas Souchu device_t
89d70424edSNicolas Souchu smbus_alloc_bus(device_t parent)
90d70424edSNicolas Souchu {
91d70424edSNicolas Souchu 	device_t child;
92d70424edSNicolas Souchu 
93d70424edSNicolas Souchu 	/* add the bus to the parent */
94d70424edSNicolas Souchu 	child = device_add_child(parent, "smbus", -1, NULL);
95d70424edSNicolas Souchu 
96d70424edSNicolas Souchu 	return (child);
97d70424edSNicolas Souchu }
98d70424edSNicolas Souchu 
993ab1f056SNicolas Souchu static int
1003ab1f056SNicolas Souchu smbus_poll(struct smbus_softc *sc, int how)
101d70424edSNicolas Souchu {
1023ab1f056SNicolas Souchu 	int error;
103d70424edSNicolas Souchu 
104d70424edSNicolas Souchu 	switch (how) {
105d70424edSNicolas Souchu 	case (SMB_WAIT | SMB_INTR):
106d70424edSNicolas Souchu 		error = tsleep(sc, SMBPRI|PCATCH, "smbreq", 0);
107d70424edSNicolas Souchu 		break;
108d70424edSNicolas Souchu 
109d70424edSNicolas Souchu 	case (SMB_WAIT | SMB_NOINTR):
110d70424edSNicolas Souchu 		error = tsleep(sc, SMBPRI, "smbreq", 0);
111d70424edSNicolas Souchu 		break;
112d70424edSNicolas Souchu 
113d70424edSNicolas Souchu 	default:
114d70424edSNicolas Souchu 		return (EWOULDBLOCK);
115d70424edSNicolas Souchu 		break;
116d70424edSNicolas Souchu 	}
117d70424edSNicolas Souchu 
1183ab1f056SNicolas Souchu 	return (error);
1193ab1f056SNicolas Souchu }
1203ab1f056SNicolas Souchu 
1213ab1f056SNicolas Souchu /*
1223ab1f056SNicolas Souchu  * smbus_request_bus()
1233ab1f056SNicolas Souchu  *
1243ab1f056SNicolas Souchu  * Allocate the device to perform transfers.
1253ab1f056SNicolas Souchu  *
1263ab1f056SNicolas Souchu  * how	: SMB_WAIT or SMB_DONTWAIT
1273ab1f056SNicolas Souchu  */
1283ab1f056SNicolas Souchu int
1293ab1f056SNicolas Souchu smbus_request_bus(device_t bus, device_t dev, int how)
1303ab1f056SNicolas Souchu {
1313ab1f056SNicolas Souchu 	struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
1323ab1f056SNicolas Souchu 	int s, error = 0;
1333ab1f056SNicolas Souchu 
1343ab1f056SNicolas Souchu 	/* first, ask the underlying layers if the request is ok */
135ba81c311SNicolas Souchu 	error = SMBUS_CALLBACK(device_get_parent(bus), SMB_REQUEST_BUS,
136ba81c311SNicolas Souchu 				(caddr_t)&how);
1373ab1f056SNicolas Souchu 
1383ab1f056SNicolas Souchu 	while (!error) {
1393ab1f056SNicolas Souchu 		s = splhigh();
140ba81c311SNicolas Souchu 		if (sc->owner && sc->owner != dev) {
1413ab1f056SNicolas Souchu 			splx(s);
1423ab1f056SNicolas Souchu 
1433ab1f056SNicolas Souchu 			error = smbus_poll(sc, how);
144d70424edSNicolas Souchu 		} else {
145d70424edSNicolas Souchu 			sc->owner = dev;
146d70424edSNicolas Souchu 
147d70424edSNicolas Souchu 			splx(s);
148d70424edSNicolas Souchu 			return (0);
149d70424edSNicolas Souchu 		}
150ba81c311SNicolas Souchu 
151ba81c311SNicolas Souchu 		/* free any allocated resource */
152ba81c311SNicolas Souchu 		if (error)
153ba81c311SNicolas Souchu 			SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS,
154ba81c311SNicolas Souchu 					(caddr_t)&how);
155d70424edSNicolas Souchu 	}
156d70424edSNicolas Souchu 
157d70424edSNicolas Souchu 	return (error);
158d70424edSNicolas Souchu }
159d70424edSNicolas Souchu 
160d70424edSNicolas Souchu /*
161d70424edSNicolas Souchu  * smbus_release_bus()
162d70424edSNicolas Souchu  *
163d70424edSNicolas Souchu  * Release the device allocated with smbus_request_dev()
164d70424edSNicolas Souchu  */
165d70424edSNicolas Souchu int
166d70424edSNicolas Souchu smbus_release_bus(device_t bus, device_t dev)
167d70424edSNicolas Souchu {
168d70424edSNicolas Souchu 	struct smbus_softc *sc = (struct smbus_softc *)device_get_softc(bus);
1693ab1f056SNicolas Souchu 	int s, error;
1703ab1f056SNicolas Souchu 
1713ab1f056SNicolas Souchu 	/* first, ask the underlying layers if the release is ok */
1723ab1f056SNicolas Souchu 	error = SMBUS_CALLBACK(device_get_parent(bus), SMB_RELEASE_BUS, NULL);
1733ab1f056SNicolas Souchu 
1743ab1f056SNicolas Souchu 	if (error)
1753ab1f056SNicolas Souchu 		return (error);
176d70424edSNicolas Souchu 
177d70424edSNicolas Souchu 	s = splhigh();
178d70424edSNicolas Souchu 	if (sc->owner != dev) {
179d70424edSNicolas Souchu 		splx(s);
180d70424edSNicolas Souchu 		return (EACCES);
181d70424edSNicolas Souchu 	}
182d70424edSNicolas Souchu 
183d70424edSNicolas Souchu 	sc->owner = 0;
184d70424edSNicolas Souchu 	splx(s);
185d70424edSNicolas Souchu 
186d70424edSNicolas Souchu 	/* wakeup waiting processes */
187d70424edSNicolas Souchu 	wakeup(sc);
188d70424edSNicolas Souchu 
189d70424edSNicolas Souchu 	return (0);
190d70424edSNicolas Souchu }
191d70424edSNicolas Souchu 
192d70424edSNicolas Souchu /*
193d70424edSNicolas Souchu  * smbus_get_addr()
194d70424edSNicolas Souchu  *
195d70424edSNicolas Souchu  * Get the I2C 7 bits address of the device
196d70424edSNicolas Souchu  */
197d70424edSNicolas Souchu u_char
198d70424edSNicolas Souchu smbus_get_addr(device_t dev)
199d70424edSNicolas Souchu {
200f8cf96dbSNicolas Souchu 	uintptr_t addr;
201d70424edSNicolas Souchu 	device_t parent = device_get_parent(dev);
202d70424edSNicolas Souchu 
203d70424edSNicolas Souchu 	BUS_READ_IVAR(parent, dev, SMBUS_IVAR_ADDR, &addr);
204d70424edSNicolas Souchu 
205d70424edSNicolas Souchu 	return ((u_char)addr);
206d70424edSNicolas Souchu }
207