xref: /freebsd/sys/dev/smbus/smbus.c (revision 94dce599114398110c3e2bc6be751b06dfb9e591)
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>
34*94dce599SAndriy Gapon #include <sys/malloc.h>
35d70424edSNicolas Souchu #include <sys/module.h>
367048a99cSJohn Baldwin #include <sys/mutex.h>
37d70424edSNicolas Souchu #include <sys/bus.h>
38d70424edSNicolas Souchu 
39d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
40d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
41d70424edSNicolas Souchu 
42202379afSMichael Gmelin #include "smbus_if.h"
43202379afSMichael Gmelin #include "bus_if.h"
44202379afSMichael Gmelin 
45*94dce599SAndriy Gapon struct smbus_ivar
46*94dce599SAndriy Gapon {
47*94dce599SAndriy Gapon 	uint8_t	addr;
48*94dce599SAndriy Gapon };
49202379afSMichael Gmelin 
50d70424edSNicolas Souchu /*
517048a99cSJohn Baldwin  * Autoconfiguration and support routines for System Management bus
52d70424edSNicolas Souchu  */
53*94dce599SAndriy Gapon static void smbus_probe_device(device_t dev, u_char addr);
54d70424edSNicolas Souchu 
55d70424edSNicolas Souchu static int
56d70424edSNicolas Souchu smbus_probe(device_t dev)
57d70424edSNicolas Souchu {
587048a99cSJohn Baldwin 
593ab1f056SNicolas Souchu 	device_set_desc(dev, "System Management Bus");
60517e2485SNicolas Souchu 
61d70424edSNicolas Souchu 	return (0);
62d70424edSNicolas Souchu }
63d70424edSNicolas Souchu 
649a77af90SRuslan Ermilov static int
659a77af90SRuslan Ermilov smbus_attach(device_t dev)
669a77af90SRuslan Ermilov {
677048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
68202379afSMichael Gmelin 	unsigned char addr;
697048a99cSJohn Baldwin 
707048a99cSJohn Baldwin 	mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
717048a99cSJohn Baldwin 	bus_generic_probe(dev);
72202379afSMichael Gmelin 	for (addr = SMBUS_ADDR_MIN; addr < SMBUS_ADDR_MAX; ++addr) {
73*94dce599SAndriy Gapon 		smbus_probe_device(dev, addr);
74202379afSMichael Gmelin 	}
75*94dce599SAndriy Gapon 	bus_enumerate_hinted_children(dev);
769a77af90SRuslan Ermilov 	bus_generic_attach(dev);
779a77af90SRuslan Ermilov 
789a77af90SRuslan Ermilov 	return (0);
799a77af90SRuslan Ermilov }
809a77af90SRuslan Ermilov 
817048a99cSJohn Baldwin static int
827048a99cSJohn Baldwin smbus_detach(device_t dev)
837048a99cSJohn Baldwin {
847048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
857048a99cSJohn Baldwin 	int error;
867048a99cSJohn Baldwin 
877048a99cSJohn Baldwin 	error = bus_generic_detach(dev);
887048a99cSJohn Baldwin 	if (error)
897048a99cSJohn Baldwin 		return (error);
90*94dce599SAndriy Gapon 	device_delete_children(dev);
917048a99cSJohn Baldwin 	mtx_destroy(&sc->lock);
927048a99cSJohn Baldwin 
937048a99cSJohn Baldwin 	return (0);
947048a99cSJohn Baldwin }
957048a99cSJohn Baldwin 
96d70424edSNicolas Souchu void
97b29df1b2SWarner Losh smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
98d70424edSNicolas Souchu {
99d70424edSNicolas Souchu }
100d70424edSNicolas Souchu 
101202379afSMichael Gmelin static void
102*94dce599SAndriy Gapon smbus_probe_device(device_t dev, u_char addr)
103202379afSMichael Gmelin {
104202379afSMichael Gmelin 	device_t child;
105202379afSMichael Gmelin 	int error;
106202379afSMichael Gmelin 	u_char cmd;
107202379afSMichael Gmelin 	u_char buf[2];
108*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
109202379afSMichael Gmelin 
110202379afSMichael Gmelin 	cmd = 0x01;
111*94dce599SAndriy Gapon 	error = smbus_trans(dev, addr, cmd,
112202379afSMichael Gmelin 			    SMB_TRANS_NOCNT | SMB_TRANS_NOREPORT,
113202379afSMichael Gmelin 			    NULL, 0, buf, 1, NULL);
114202379afSMichael Gmelin 	if (error == 0) {
115202379afSMichael Gmelin 		if (bootverbose)
116*94dce599SAndriy Gapon 			device_printf(dev, "Probed address 0x%02x\n", addr);
117*94dce599SAndriy Gapon 		child = BUS_ADD_CHILD(dev, SMBUS_ORDER_PNP, NULL, -1);
118*94dce599SAndriy Gapon 		if (child == NULL)
119*94dce599SAndriy Gapon 			return;
120*94dce599SAndriy Gapon 		devi = device_get_ivars(child);
121*94dce599SAndriy Gapon 		devi->addr = addr;
122202379afSMichael Gmelin 	}
123202379afSMichael Gmelin }
124202379afSMichael Gmelin 
125*94dce599SAndriy Gapon static device_t
126*94dce599SAndriy Gapon smbus_add_child(device_t dev, u_int order, const char *name, int unit)
127*94dce599SAndriy Gapon {
128*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
129*94dce599SAndriy Gapon 	device_t child;
130*94dce599SAndriy Gapon 
131*94dce599SAndriy Gapon 	child = device_add_child_ordered(dev, order, name, unit);
132*94dce599SAndriy Gapon 	if (child == NULL)
133*94dce599SAndriy Gapon 		return (child);
134*94dce599SAndriy Gapon 	devi = malloc(sizeof(struct smbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
135*94dce599SAndriy Gapon 	if (devi == NULL) {
136*94dce599SAndriy Gapon 		device_delete_child(dev, child);
137*94dce599SAndriy Gapon 		return (NULL);
138*94dce599SAndriy Gapon 	}
139*94dce599SAndriy Gapon 	device_set_ivars(child, devi);
140*94dce599SAndriy Gapon 	return (child);
141*94dce599SAndriy Gapon }
142*94dce599SAndriy Gapon 
143*94dce599SAndriy Gapon static void
144*94dce599SAndriy Gapon smbus_hinted_child(device_t bus, const char *dname, int dunit)
145*94dce599SAndriy Gapon {
146*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
147*94dce599SAndriy Gapon 	device_t child;
148*94dce599SAndriy Gapon 	int addr;
149*94dce599SAndriy Gapon 
150*94dce599SAndriy Gapon 	addr = 0;
151*94dce599SAndriy Gapon 	resource_int_value(dname, dunit, "addr", &addr);
152*94dce599SAndriy Gapon 	if (addr > UINT8_MAX) {
153*94dce599SAndriy Gapon 		device_printf(bus, "ignored incorrect slave address hint 0x%x"
154*94dce599SAndriy Gapon 		    " for %s%d\n", addr, dname, dunit);
155*94dce599SAndriy Gapon 		return;
156*94dce599SAndriy Gapon 	}
157*94dce599SAndriy Gapon 	child = BUS_ADD_CHILD(bus, SMBUS_ORDER_HINTED, dname, dunit);
158*94dce599SAndriy Gapon 	if (child == NULL)
159*94dce599SAndriy Gapon 		return;
160*94dce599SAndriy Gapon 	devi = device_get_ivars(child);
161*94dce599SAndriy Gapon 	devi->addr = addr;
162*94dce599SAndriy Gapon }
163*94dce599SAndriy Gapon 
164*94dce599SAndriy Gapon 
165202379afSMichael Gmelin static int
166202379afSMichael Gmelin smbus_child_location_str(device_t parent, device_t child, char *buf,
167202379afSMichael Gmelin     size_t buflen)
168202379afSMichael Gmelin {
169*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
170202379afSMichael Gmelin 
171*94dce599SAndriy Gapon 	devi = device_get_ivars(child);
172*94dce599SAndriy Gapon 	if (devi->addr != 0)
173*94dce599SAndriy Gapon 		snprintf(buf, buflen, "addr=0x%x", devi->addr);
174202379afSMichael Gmelin 	else if (buflen)
175202379afSMichael Gmelin 		buf[0] = 0;
176202379afSMichael Gmelin 	return (0);
177202379afSMichael Gmelin }
178202379afSMichael Gmelin 
179202379afSMichael Gmelin static int
180202379afSMichael Gmelin smbus_print_child(device_t parent, device_t child)
181202379afSMichael Gmelin {
182*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
183202379afSMichael Gmelin 	int retval;
184202379afSMichael Gmelin 
185*94dce599SAndriy Gapon 	devi = device_get_ivars(child);
186202379afSMichael Gmelin 	retval = bus_print_child_header(parent, child);
187*94dce599SAndriy Gapon 	if (devi->addr != 0)
188*94dce599SAndriy Gapon 		retval += printf(" at addr 0x%x", devi->addr);
189202379afSMichael Gmelin 	retval += bus_print_child_footer(parent, child);
190202379afSMichael Gmelin 
191202379afSMichael Gmelin 	return (retval);
192202379afSMichael Gmelin }
193202379afSMichael Gmelin 
194202379afSMichael Gmelin static int
195*94dce599SAndriy Gapon smbus_read_ivar(device_t parent, device_t child, int which, uintptr_t *result)
196202379afSMichael Gmelin {
197*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
198202379afSMichael Gmelin 
199*94dce599SAndriy Gapon 	devi = device_get_ivars(child);
200202379afSMichael Gmelin 	switch (which) {
201202379afSMichael Gmelin 	case SMBUS_IVAR_ADDR:
202*94dce599SAndriy Gapon 		if (devi->addr != 0)
203*94dce599SAndriy Gapon 			*result = devi->addr;
204*94dce599SAndriy Gapon 		else
205*94dce599SAndriy Gapon 			*result = -1;
206202379afSMichael Gmelin 		break;
207202379afSMichael Gmelin 	default:
208202379afSMichael Gmelin 		return (ENOENT);
209202379afSMichael Gmelin 	}
210202379afSMichael Gmelin 	return (0);
211202379afSMichael Gmelin }
212202379afSMichael Gmelin 
213*94dce599SAndriy Gapon static int
214*94dce599SAndriy Gapon smbus_write_ivar(device_t parent, device_t child, int which, uintptr_t value)
215*94dce599SAndriy Gapon {
216*94dce599SAndriy Gapon 	struct smbus_ivar *devi;
217*94dce599SAndriy Gapon 
218*94dce599SAndriy Gapon 	devi = device_get_ivars(child);
219*94dce599SAndriy Gapon 	switch (which) {
220*94dce599SAndriy Gapon 	case SMBUS_IVAR_ADDR:
221*94dce599SAndriy Gapon 		/* Allow to set but no change the slave address. */
222*94dce599SAndriy Gapon 		if (devi->addr != 0)
223*94dce599SAndriy Gapon 			return (EINVAL);
224*94dce599SAndriy Gapon 		devi->addr = value;
225*94dce599SAndriy Gapon 		break;
226*94dce599SAndriy Gapon 	default:
227*94dce599SAndriy Gapon 		return (ENOENT);
228*94dce599SAndriy Gapon 	}
229*94dce599SAndriy Gapon 	return (0);
230*94dce599SAndriy Gapon }
231*94dce599SAndriy Gapon 
232*94dce599SAndriy Gapon static void
233*94dce599SAndriy Gapon smbus_probe_nomatch(device_t bus, device_t child)
234*94dce599SAndriy Gapon {
235*94dce599SAndriy Gapon 	struct smbus_ivar *devi = device_get_ivars(child);
236*94dce599SAndriy Gapon 
237*94dce599SAndriy Gapon 	/*
238*94dce599SAndriy Gapon 	 * Ignore (self-identified) devices without a slave address set.
239*94dce599SAndriy Gapon 	 * For example, smb(4).
240*94dce599SAndriy Gapon 	 */
241*94dce599SAndriy Gapon 	if (devi->addr != 0)
242*94dce599SAndriy Gapon 		device_printf(bus, "<unknown device> at addr %#x\n",
243*94dce599SAndriy Gapon 		    devi->addr);
244*94dce599SAndriy Gapon }
245*94dce599SAndriy Gapon 
246*94dce599SAndriy Gapon /*
247*94dce599SAndriy Gapon  * Device methods
248*94dce599SAndriy Gapon  */
249*94dce599SAndriy Gapon static device_method_t smbus_methods[] = {
250*94dce599SAndriy Gapon         /* device interface */
251*94dce599SAndriy Gapon         DEVMETHOD(device_probe,         smbus_probe),
252*94dce599SAndriy Gapon         DEVMETHOD(device_attach,        smbus_attach),
253*94dce599SAndriy Gapon         DEVMETHOD(device_detach,        smbus_detach),
254*94dce599SAndriy Gapon 
255*94dce599SAndriy Gapon 	/* bus interface */
256*94dce599SAndriy Gapon 	DEVMETHOD(bus_add_child,	smbus_add_child),
257*94dce599SAndriy Gapon 	DEVMETHOD(bus_hinted_child,	smbus_hinted_child),
258*94dce599SAndriy Gapon 	DEVMETHOD(bus_probe_nomatch,	smbus_probe_nomatch),
259*94dce599SAndriy Gapon 	DEVMETHOD(bus_child_location_str, smbus_child_location_str),
260*94dce599SAndriy Gapon 	DEVMETHOD(bus_print_child,	smbus_print_child),
261*94dce599SAndriy Gapon 	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
262*94dce599SAndriy Gapon 	DEVMETHOD(bus_write_ivar,	smbus_write_ivar),
263*94dce599SAndriy Gapon 
264*94dce599SAndriy Gapon 	DEVMETHOD_END
265*94dce599SAndriy Gapon };
266*94dce599SAndriy Gapon 
267*94dce599SAndriy Gapon driver_t smbus_driver = {
268*94dce599SAndriy Gapon         "smbus",
269*94dce599SAndriy Gapon         smbus_methods,
270*94dce599SAndriy Gapon         sizeof(struct smbus_softc),
271*94dce599SAndriy Gapon };
272*94dce599SAndriy Gapon 
273*94dce599SAndriy Gapon devclass_t smbus_devclass;
274*94dce599SAndriy Gapon 
275c17d4340SNicolas Souchu MODULE_VERSION(smbus, SMBUS_MODVER);
276