xref: /freebsd/sys/dev/smbus/smbus.c (revision 202379af847e82a654964f69aaa3435212732eff)
1d70424edSNicolas Souchu /*-
2c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 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  *
26d70424edSNicolas Souchu  *
27d70424edSNicolas Souchu  */
28945ff31aSDavid E. O'Brien 
29945ff31aSDavid E. O'Brien #include <sys/cdefs.h>
30945ff31aSDavid E. O'Brien __FBSDID("$FreeBSD$");
31d70424edSNicolas Souchu #include <sys/param.h>
32d70424edSNicolas Souchu #include <sys/systm.h>
337048a99cSJohn Baldwin #include <sys/lock.h>
34d70424edSNicolas Souchu #include <sys/module.h>
357048a99cSJohn Baldwin #include <sys/mutex.h>
36d70424edSNicolas Souchu #include <sys/bus.h>
37d70424edSNicolas Souchu 
38d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
39d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
40d70424edSNicolas Souchu 
41*202379afSMichael Gmelin #include "smbus_if.h"
42*202379afSMichael Gmelin #include "bus_if.h"
43*202379afSMichael Gmelin 
44*202379afSMichael Gmelin 
45d70424edSNicolas Souchu /*
467048a99cSJohn Baldwin  * Autoconfiguration and support routines for System Management bus
47d70424edSNicolas Souchu  */
48d70424edSNicolas Souchu 
49d70424edSNicolas Souchu /*
50d70424edSNicolas Souchu  * Device methods
51d70424edSNicolas Souchu  */
52d70424edSNicolas Souchu static int smbus_probe(device_t);
539a77af90SRuslan Ermilov static int smbus_attach(device_t);
547048a99cSJohn Baldwin static int smbus_detach(device_t);
55d70424edSNicolas Souchu 
56*202379afSMichael Gmelin static int smbus_child_location_str(device_t parent, device_t child,
57*202379afSMichael Gmelin 		    char *buf, size_t buflen);
58*202379afSMichael Gmelin static int smbus_print_child(device_t parent, device_t child);
59*202379afSMichael Gmelin static void smbus_probe_device(device_t dev, u_char* addr);
60*202379afSMichael Gmelin static int smbus_read_ivar(device_t parent, device_t child, int which,
61*202379afSMichael Gmelin 		    uintptr_t *result);
62*202379afSMichael Gmelin 
63d70424edSNicolas Souchu static device_method_t smbus_methods[] = {
64d70424edSNicolas Souchu         /* device interface */
65d70424edSNicolas Souchu         DEVMETHOD(device_probe,         smbus_probe),
669a77af90SRuslan Ermilov         DEVMETHOD(device_attach,        smbus_attach),
677048a99cSJohn Baldwin         DEVMETHOD(device_detach,        smbus_detach),
68d70424edSNicolas Souchu 
69d70424edSNicolas Souchu 	/* bus interface */
703bb00f61SJohn Baldwin 	DEVMETHOD(bus_add_child,	bus_generic_add_child),
71*202379afSMichael Gmelin 	DEVMETHOD(bus_child_location_str, smbus_child_location_str),
72*202379afSMichael Gmelin 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
73*202379afSMichael Gmelin 	DEVMETHOD(bus_print_child,	smbus_print_child),
74*202379afSMichael Gmelin 	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
75d70424edSNicolas Souchu 
764b7ec270SMarius Strobl 	DEVMETHOD_END
77d70424edSNicolas Souchu };
78d70424edSNicolas Souchu 
797048a99cSJohn Baldwin driver_t smbus_driver = {
80d70424edSNicolas Souchu         "smbus",
81d70424edSNicolas Souchu         smbus_methods,
82d70424edSNicolas Souchu         sizeof(struct smbus_softc),
83d70424edSNicolas Souchu };
84d70424edSNicolas Souchu 
857048a99cSJohn Baldwin devclass_t smbus_devclass;
867048a99cSJohn Baldwin 
87d70424edSNicolas Souchu /*
88d70424edSNicolas Souchu  * At 'probe' time, we add all the devices which we know about to the
89d70424edSNicolas Souchu  * bus.  The generic attach routine will probe and attach them if they
90d70424edSNicolas Souchu  * are alive.
91d70424edSNicolas Souchu  */
92d70424edSNicolas Souchu static int
93d70424edSNicolas Souchu smbus_probe(device_t dev)
94d70424edSNicolas Souchu {
957048a99cSJohn Baldwin 
963ab1f056SNicolas Souchu 	device_set_desc(dev, "System Management Bus");
97517e2485SNicolas Souchu 
98d70424edSNicolas Souchu 	return (0);
99d70424edSNicolas Souchu }
100d70424edSNicolas Souchu 
1019a77af90SRuslan Ermilov static int
1029a77af90SRuslan Ermilov smbus_attach(device_t dev)
1039a77af90SRuslan Ermilov {
1047048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
105*202379afSMichael Gmelin 	unsigned char addr;
1067048a99cSJohn Baldwin 
1077048a99cSJohn Baldwin 	mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
1087048a99cSJohn Baldwin 	bus_generic_probe(dev);
109*202379afSMichael Gmelin 	for (addr = SMBUS_ADDR_MIN; addr < SMBUS_ADDR_MAX; ++addr) {
110*202379afSMichael Gmelin 		sc->addrs[addr] = addr;
111*202379afSMichael Gmelin 		smbus_probe_device(dev, &sc->addrs[addr]);
112*202379afSMichael Gmelin 	}
1139a77af90SRuslan Ermilov 	bus_generic_attach(dev);
1149a77af90SRuslan Ermilov 
1159a77af90SRuslan Ermilov 	return (0);
1169a77af90SRuslan Ermilov }
1179a77af90SRuslan Ermilov 
1187048a99cSJohn Baldwin static int
1197048a99cSJohn Baldwin smbus_detach(device_t dev)
1207048a99cSJohn Baldwin {
1217048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
1227048a99cSJohn Baldwin 	int error;
1237048a99cSJohn Baldwin 
1247048a99cSJohn Baldwin 	error = bus_generic_detach(dev);
1257048a99cSJohn Baldwin 	if (error)
1267048a99cSJohn Baldwin 		return (error);
1277048a99cSJohn Baldwin 	mtx_destroy(&sc->lock);
1287048a99cSJohn Baldwin 
1297048a99cSJohn Baldwin 	return (0);
1307048a99cSJohn Baldwin }
1317048a99cSJohn Baldwin 
132d70424edSNicolas Souchu void
133b29df1b2SWarner Losh smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
134d70424edSNicolas Souchu {
135d70424edSNicolas Souchu }
136d70424edSNicolas Souchu 
137*202379afSMichael Gmelin static void
138*202379afSMichael Gmelin smbus_probe_device(device_t dev, u_char* addr)
139*202379afSMichael Gmelin {
140*202379afSMichael Gmelin 	device_t child;
141*202379afSMichael Gmelin 	int error;
142*202379afSMichael Gmelin 	u_char cmd;
143*202379afSMichael Gmelin 	u_char buf[2];
144*202379afSMichael Gmelin 
145*202379afSMichael Gmelin 	cmd = 0x01;
146*202379afSMichael Gmelin 	error = smbus_trans(dev, *addr, cmd,
147*202379afSMichael Gmelin 			    SMB_TRANS_NOCNT | SMB_TRANS_NOREPORT,
148*202379afSMichael Gmelin 			    NULL, 0, buf, 1, NULL);
149*202379afSMichael Gmelin 	if (error == 0) {
150*202379afSMichael Gmelin 		if (bootverbose)
151*202379afSMichael Gmelin 			device_printf(dev, "Probed address 0x%02x\n", *addr);
152*202379afSMichael Gmelin 		child = device_add_child(dev, NULL, -1);
153*202379afSMichael Gmelin 		device_set_ivars(child, addr);
154*202379afSMichael Gmelin 	}
155*202379afSMichael Gmelin }
156*202379afSMichael Gmelin 
157*202379afSMichael Gmelin static int
158*202379afSMichael Gmelin smbus_child_location_str(device_t parent, device_t child, char *buf,
159*202379afSMichael Gmelin     size_t buflen)
160*202379afSMichael Gmelin {
161*202379afSMichael Gmelin 	unsigned char *addr;
162*202379afSMichael Gmelin 
163*202379afSMichael Gmelin 	addr = device_get_ivars(child);
164*202379afSMichael Gmelin 	if (addr)
165*202379afSMichael Gmelin 		snprintf(buf, buflen, "addr=0x%x", *addr);
166*202379afSMichael Gmelin 	else if (buflen)
167*202379afSMichael Gmelin 		buf[0] = 0;
168*202379afSMichael Gmelin 	return (0);
169*202379afSMichael Gmelin }
170*202379afSMichael Gmelin 
171*202379afSMichael Gmelin static int
172*202379afSMichael Gmelin smbus_print_child(device_t parent, device_t child)
173*202379afSMichael Gmelin {
174*202379afSMichael Gmelin 	unsigned char *addr;
175*202379afSMichael Gmelin 	int retval;
176*202379afSMichael Gmelin 
177*202379afSMichael Gmelin 	addr = device_get_ivars(child);
178*202379afSMichael Gmelin 	retval = bus_print_child_header(parent, child);
179*202379afSMichael Gmelin 	if (addr)
180*202379afSMichael Gmelin 		retval += printf(" at addr 0x%x", *addr);
181*202379afSMichael Gmelin 	retval += bus_print_child_footer(parent, child);
182*202379afSMichael Gmelin 
183*202379afSMichael Gmelin 	return (retval);
184*202379afSMichael Gmelin }
185*202379afSMichael Gmelin 
186*202379afSMichael Gmelin static int
187*202379afSMichael Gmelin smbus_read_ivar(device_t parent, device_t child, int which,
188*202379afSMichael Gmelin     uintptr_t *result)
189*202379afSMichael Gmelin {
190*202379afSMichael Gmelin 	unsigned char *addr;
191*202379afSMichael Gmelin 
192*202379afSMichael Gmelin 	addr = device_get_ivars(child);
193*202379afSMichael Gmelin 	switch (which) {
194*202379afSMichael Gmelin 	case SMBUS_IVAR_ADDR:
195*202379afSMichael Gmelin 		*result = (addr == NULL) ? -1 : *addr;
196*202379afSMichael Gmelin 		break;
197*202379afSMichael Gmelin 	default:
198*202379afSMichael Gmelin 		return (ENOENT);
199*202379afSMichael Gmelin 	}
200*202379afSMichael Gmelin 	return (0);
201*202379afSMichael Gmelin }
202*202379afSMichael Gmelin 
203c17d4340SNicolas Souchu MODULE_VERSION(smbus, SMBUS_MODVER);
204