xref: /freebsd/sys/dev/smbus/smbus.c (revision 718cf2cc)
1d70424edSNicolas Souchu /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
5d70424edSNicolas Souchu  * All rights reserved.
6d70424edSNicolas Souchu  *
7d70424edSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
8d70424edSNicolas Souchu  * modification, are permitted provided that the following conditions
9d70424edSNicolas Souchu  * are met:
10d70424edSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
11d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
12d70424edSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
13d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
14d70424edSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
15d70424edSNicolas Souchu  *
16d70424edSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17d70424edSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d70424edSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d70424edSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20d70424edSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d70424edSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d70424edSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d70424edSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d70424edSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d70424edSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d70424edSNicolas Souchu  * SUCH DAMAGE.
27d70424edSNicolas Souchu  *
28d70424edSNicolas Souchu  *
29d70424edSNicolas Souchu  */
30945ff31aSDavid E. O'Brien 
31945ff31aSDavid E. O'Brien #include <sys/cdefs.h>
32945ff31aSDavid E. O'Brien __FBSDID("$FreeBSD$");
33d70424edSNicolas Souchu #include <sys/param.h>
34d70424edSNicolas Souchu #include <sys/systm.h>
357048a99cSJohn Baldwin #include <sys/lock.h>
3694dce599SAndriy Gapon #include <sys/malloc.h>
37d70424edSNicolas Souchu #include <sys/module.h>
387048a99cSJohn Baldwin #include <sys/mutex.h>
39d70424edSNicolas Souchu #include <sys/bus.h>
40d70424edSNicolas Souchu 
41d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
42d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
43d70424edSNicolas Souchu 
44202379afSMichael Gmelin #include "smbus_if.h"
45202379afSMichael Gmelin #include "bus_if.h"
46202379afSMichael Gmelin 
4794dce599SAndriy Gapon struct smbus_ivar
4894dce599SAndriy Gapon {
4994dce599SAndriy Gapon 	uint8_t	addr;
5094dce599SAndriy Gapon };
51202379afSMichael Gmelin 
52d70424edSNicolas Souchu /*
537048a99cSJohn Baldwin  * Autoconfiguration and support routines for System Management bus
54d70424edSNicolas Souchu  */
55d70424edSNicolas Souchu 
56d70424edSNicolas Souchu static int
57d70424edSNicolas Souchu smbus_probe(device_t dev)
58d70424edSNicolas Souchu {
597048a99cSJohn Baldwin 
603ab1f056SNicolas Souchu 	device_set_desc(dev, "System Management Bus");
61517e2485SNicolas Souchu 
62d70424edSNicolas Souchu 	return (0);
63d70424edSNicolas Souchu }
64d70424edSNicolas Souchu 
659a77af90SRuslan Ermilov static int
669a77af90SRuslan Ermilov smbus_attach(device_t dev)
679a77af90SRuslan Ermilov {
687048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
697048a99cSJohn Baldwin 
707048a99cSJohn Baldwin 	mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
717048a99cSJohn Baldwin 	bus_generic_probe(dev);
7294dce599SAndriy Gapon 	bus_enumerate_hinted_children(dev);
739a77af90SRuslan Ermilov 	bus_generic_attach(dev);
749a77af90SRuslan Ermilov 
759a77af90SRuslan Ermilov 	return (0);
769a77af90SRuslan Ermilov }
779a77af90SRuslan Ermilov 
787048a99cSJohn Baldwin static int
797048a99cSJohn Baldwin smbus_detach(device_t dev)
807048a99cSJohn Baldwin {
817048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
827048a99cSJohn Baldwin 	int error;
837048a99cSJohn Baldwin 
847048a99cSJohn Baldwin 	error = bus_generic_detach(dev);
857048a99cSJohn Baldwin 	if (error)
867048a99cSJohn Baldwin 		return (error);
8794dce599SAndriy Gapon 	device_delete_children(dev);
887048a99cSJohn Baldwin 	mtx_destroy(&sc->lock);
897048a99cSJohn Baldwin 
907048a99cSJohn Baldwin 	return (0);
917048a99cSJohn Baldwin }
927048a99cSJohn Baldwin 
93d70424edSNicolas Souchu void
94b29df1b2SWarner Losh smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
95d70424edSNicolas Souchu {
96d70424edSNicolas Souchu }
97d70424edSNicolas Souchu 
9894dce599SAndriy Gapon static device_t
9994dce599SAndriy Gapon smbus_add_child(device_t dev, u_int order, const char *name, int unit)
10094dce599SAndriy Gapon {
10194dce599SAndriy Gapon 	struct smbus_ivar *devi;
10294dce599SAndriy Gapon 	device_t child;
10394dce599SAndriy Gapon 
10494dce599SAndriy Gapon 	child = device_add_child_ordered(dev, order, name, unit);
10594dce599SAndriy Gapon 	if (child == NULL)
10694dce599SAndriy Gapon 		return (child);
10794dce599SAndriy Gapon 	devi = malloc(sizeof(struct smbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
10894dce599SAndriy Gapon 	if (devi == NULL) {
10994dce599SAndriy Gapon 		device_delete_child(dev, child);
11094dce599SAndriy Gapon 		return (NULL);
11194dce599SAndriy Gapon 	}
11294dce599SAndriy Gapon 	device_set_ivars(child, devi);
11394dce599SAndriy Gapon 	return (child);
11494dce599SAndriy Gapon }
11594dce599SAndriy Gapon 
11694dce599SAndriy Gapon static void
11794dce599SAndriy Gapon smbus_hinted_child(device_t bus, const char *dname, int dunit)
11894dce599SAndriy Gapon {
11994dce599SAndriy Gapon 	struct smbus_ivar *devi;
12094dce599SAndriy Gapon 	device_t child;
12194dce599SAndriy Gapon 	int addr;
12294dce599SAndriy Gapon 
12394dce599SAndriy Gapon 	addr = 0;
12494dce599SAndriy Gapon 	resource_int_value(dname, dunit, "addr", &addr);
12594dce599SAndriy Gapon 	if (addr > UINT8_MAX) {
12694dce599SAndriy Gapon 		device_printf(bus, "ignored incorrect slave address hint 0x%x"
12794dce599SAndriy Gapon 		    " for %s%d\n", addr, dname, dunit);
12894dce599SAndriy Gapon 		return;
12994dce599SAndriy Gapon 	}
13094dce599SAndriy Gapon 	child = BUS_ADD_CHILD(bus, SMBUS_ORDER_HINTED, dname, dunit);
13194dce599SAndriy Gapon 	if (child == NULL)
13294dce599SAndriy Gapon 		return;
13394dce599SAndriy Gapon 	devi = device_get_ivars(child);
13494dce599SAndriy Gapon 	devi->addr = addr;
13594dce599SAndriy Gapon }
13694dce599SAndriy Gapon 
13794dce599SAndriy Gapon 
138202379afSMichael Gmelin static int
139202379afSMichael Gmelin smbus_child_location_str(device_t parent, device_t child, char *buf,
140202379afSMichael Gmelin     size_t buflen)
141202379afSMichael Gmelin {
14294dce599SAndriy Gapon 	struct smbus_ivar *devi;
143202379afSMichael Gmelin 
14494dce599SAndriy Gapon 	devi = device_get_ivars(child);
14594dce599SAndriy Gapon 	if (devi->addr != 0)
14694dce599SAndriy Gapon 		snprintf(buf, buflen, "addr=0x%x", devi->addr);
147202379afSMichael Gmelin 	else if (buflen)
148202379afSMichael Gmelin 		buf[0] = 0;
149202379afSMichael Gmelin 	return (0);
150202379afSMichael Gmelin }
151202379afSMichael Gmelin 
152202379afSMichael Gmelin static int
153202379afSMichael Gmelin smbus_print_child(device_t parent, device_t child)
154202379afSMichael Gmelin {
15594dce599SAndriy Gapon 	struct smbus_ivar *devi;
156202379afSMichael Gmelin 	int retval;
157202379afSMichael Gmelin 
15894dce599SAndriy Gapon 	devi = device_get_ivars(child);
159202379afSMichael Gmelin 	retval = bus_print_child_header(parent, child);
16094dce599SAndriy Gapon 	if (devi->addr != 0)
16194dce599SAndriy Gapon 		retval += printf(" at addr 0x%x", devi->addr);
162202379afSMichael Gmelin 	retval += bus_print_child_footer(parent, child);
163202379afSMichael Gmelin 
164202379afSMichael Gmelin 	return (retval);
165202379afSMichael Gmelin }
166202379afSMichael Gmelin 
167202379afSMichael Gmelin static int
16894dce599SAndriy Gapon smbus_read_ivar(device_t parent, device_t child, int which, uintptr_t *result)
169202379afSMichael Gmelin {
17094dce599SAndriy Gapon 	struct smbus_ivar *devi;
171202379afSMichael Gmelin 
17294dce599SAndriy Gapon 	devi = device_get_ivars(child);
173202379afSMichael Gmelin 	switch (which) {
174202379afSMichael Gmelin 	case SMBUS_IVAR_ADDR:
17594dce599SAndriy Gapon 		if (devi->addr != 0)
17694dce599SAndriy Gapon 			*result = devi->addr;
17794dce599SAndriy Gapon 		else
17894dce599SAndriy Gapon 			*result = -1;
179202379afSMichael Gmelin 		break;
180202379afSMichael Gmelin 	default:
181202379afSMichael Gmelin 		return (ENOENT);
182202379afSMichael Gmelin 	}
183202379afSMichael Gmelin 	return (0);
184202379afSMichael Gmelin }
185202379afSMichael Gmelin 
18694dce599SAndriy Gapon static int
18794dce599SAndriy Gapon smbus_write_ivar(device_t parent, device_t child, int which, uintptr_t value)
18894dce599SAndriy Gapon {
18994dce599SAndriy Gapon 	struct smbus_ivar *devi;
19094dce599SAndriy Gapon 
19194dce599SAndriy Gapon 	devi = device_get_ivars(child);
19294dce599SAndriy Gapon 	switch (which) {
19394dce599SAndriy Gapon 	case SMBUS_IVAR_ADDR:
19494dce599SAndriy Gapon 		/* Allow to set but no change the slave address. */
19594dce599SAndriy Gapon 		if (devi->addr != 0)
19694dce599SAndriy Gapon 			return (EINVAL);
19794dce599SAndriy Gapon 		devi->addr = value;
19894dce599SAndriy Gapon 		break;
19994dce599SAndriy Gapon 	default:
20094dce599SAndriy Gapon 		return (ENOENT);
20194dce599SAndriy Gapon 	}
20294dce599SAndriy Gapon 	return (0);
20394dce599SAndriy Gapon }
20494dce599SAndriy Gapon 
20594dce599SAndriy Gapon static void
20694dce599SAndriy Gapon smbus_probe_nomatch(device_t bus, device_t child)
20794dce599SAndriy Gapon {
20894dce599SAndriy Gapon 	struct smbus_ivar *devi = device_get_ivars(child);
20994dce599SAndriy Gapon 
21094dce599SAndriy Gapon 	/*
21194dce599SAndriy Gapon 	 * Ignore (self-identified) devices without a slave address set.
21294dce599SAndriy Gapon 	 * For example, smb(4).
21394dce599SAndriy Gapon 	 */
21494dce599SAndriy Gapon 	if (devi->addr != 0)
21594dce599SAndriy Gapon 		device_printf(bus, "<unknown device> at addr %#x\n",
21694dce599SAndriy Gapon 		    devi->addr);
21794dce599SAndriy Gapon }
21894dce599SAndriy Gapon 
21994dce599SAndriy Gapon /*
22094dce599SAndriy Gapon  * Device methods
22194dce599SAndriy Gapon  */
22294dce599SAndriy Gapon static device_method_t smbus_methods[] = {
22394dce599SAndriy Gapon         /* device interface */
22494dce599SAndriy Gapon         DEVMETHOD(device_probe,         smbus_probe),
22594dce599SAndriy Gapon         DEVMETHOD(device_attach,        smbus_attach),
22694dce599SAndriy Gapon         DEVMETHOD(device_detach,        smbus_detach),
22794dce599SAndriy Gapon 
22894dce599SAndriy Gapon 	/* bus interface */
22994dce599SAndriy Gapon 	DEVMETHOD(bus_add_child,	smbus_add_child),
23094dce599SAndriy Gapon 	DEVMETHOD(bus_hinted_child,	smbus_hinted_child),
23194dce599SAndriy Gapon 	DEVMETHOD(bus_probe_nomatch,	smbus_probe_nomatch),
23294dce599SAndriy Gapon 	DEVMETHOD(bus_child_location_str, smbus_child_location_str),
23394dce599SAndriy Gapon 	DEVMETHOD(bus_print_child,	smbus_print_child),
23494dce599SAndriy Gapon 	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
23594dce599SAndriy Gapon 	DEVMETHOD(bus_write_ivar,	smbus_write_ivar),
23694dce599SAndriy Gapon 
23794dce599SAndriy Gapon 	DEVMETHOD_END
23894dce599SAndriy Gapon };
23994dce599SAndriy Gapon 
24094dce599SAndriy Gapon driver_t smbus_driver = {
24194dce599SAndriy Gapon         "smbus",
24294dce599SAndriy Gapon         smbus_methods,
24394dce599SAndriy Gapon         sizeof(struct smbus_softc),
24494dce599SAndriy Gapon };
24594dce599SAndriy Gapon 
24694dce599SAndriy Gapon devclass_t smbus_devclass;
24794dce599SAndriy Gapon 
248c17d4340SNicolas Souchu MODULE_VERSION(smbus, SMBUS_MODVER);
249