xref: /freebsd/sys/dev/smbus/smbus.c (revision b196276c20b577b364372f1aa1a646b9ce34bf5c)
1d70424edSNicolas Souchu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
31d70424edSNicolas Souchu #include <sys/param.h>
32d70424edSNicolas Souchu #include <sys/systm.h>
33ddfc9c4cSWarner Losh #include <sys/bus.h>
347048a99cSJohn Baldwin #include <sys/lock.h>
3594dce599SAndriy Gapon #include <sys/malloc.h>
36d70424edSNicolas Souchu #include <sys/module.h>
377048a99cSJohn Baldwin #include <sys/mutex.h>
38ddfc9c4cSWarner Losh #include <sys/sbuf.h>
39d70424edSNicolas Souchu 
40d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
41d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
42d70424edSNicolas Souchu 
43202379afSMichael Gmelin #include "smbus_if.h"
44202379afSMichael Gmelin #include "bus_if.h"
45202379afSMichael Gmelin 
4694dce599SAndriy Gapon struct smbus_ivar
4794dce599SAndriy Gapon {
4894dce599SAndriy Gapon 	uint8_t	addr;
4994dce599SAndriy Gapon };
50202379afSMichael Gmelin 
51d70424edSNicolas Souchu /*
527048a99cSJohn Baldwin  * Autoconfiguration and support routines for System Management bus
53d70424edSNicolas Souchu  */
54d70424edSNicolas Souchu 
55d70424edSNicolas Souchu static int
smbus_probe(device_t dev)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
smbus_attach(device_t dev)659a77af90SRuslan Ermilov smbus_attach(device_t dev)
669a77af90SRuslan Ermilov {
677048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
687048a99cSJohn Baldwin 
697048a99cSJohn Baldwin 	mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
70723da5d9SJohn Baldwin 	bus_identify_children(dev);
7194dce599SAndriy Gapon 	bus_enumerate_hinted_children(dev);
72*18250ec6SJohn Baldwin 	bus_attach_children(dev);
739a77af90SRuslan Ermilov 
749a77af90SRuslan Ermilov 	return (0);
759a77af90SRuslan Ermilov }
769a77af90SRuslan Ermilov 
777048a99cSJohn Baldwin static int
smbus_detach(device_t dev)787048a99cSJohn Baldwin smbus_detach(device_t dev)
797048a99cSJohn Baldwin {
807048a99cSJohn Baldwin 	struct smbus_softc *sc = device_get_softc(dev);
817048a99cSJohn Baldwin 	int error;
827048a99cSJohn Baldwin 
837048a99cSJohn Baldwin 	error = bus_generic_detach(dev);
847048a99cSJohn Baldwin 	if (error)
857048a99cSJohn Baldwin 		return (error);
867048a99cSJohn Baldwin 	mtx_destroy(&sc->lock);
877048a99cSJohn Baldwin 
887048a99cSJohn Baldwin 	return (0);
897048a99cSJohn Baldwin }
907048a99cSJohn Baldwin 
91d70424edSNicolas Souchu void
smbus_generic_intr(device_t dev,u_char devaddr,char low,char high,int err)92b29df1b2SWarner Losh smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
93d70424edSNicolas Souchu {
94d70424edSNicolas Souchu }
95d70424edSNicolas Souchu 
9694dce599SAndriy Gapon static device_t
smbus_add_child(device_t dev,u_int order,const char * name,int unit)9794dce599SAndriy Gapon smbus_add_child(device_t dev, u_int order, const char *name, int unit)
9894dce599SAndriy Gapon {
9994dce599SAndriy Gapon 	struct smbus_ivar *devi;
10094dce599SAndriy Gapon 	device_t child;
10194dce599SAndriy Gapon 
10294dce599SAndriy Gapon 	child = device_add_child_ordered(dev, order, name, unit);
10394dce599SAndriy Gapon 	if (child == NULL)
10494dce599SAndriy Gapon 		return (child);
10594dce599SAndriy Gapon 	devi = malloc(sizeof(struct smbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
10694dce599SAndriy Gapon 	if (devi == NULL) {
10794dce599SAndriy Gapon 		device_delete_child(dev, child);
10894dce599SAndriy Gapon 		return (NULL);
10994dce599SAndriy Gapon 	}
11094dce599SAndriy Gapon 	device_set_ivars(child, devi);
11194dce599SAndriy Gapon 	return (child);
11294dce599SAndriy Gapon }
11394dce599SAndriy Gapon 
11494dce599SAndriy Gapon static void
smbus_child_deleted(device_t dev,device_t child)11503f641f9SJohn Baldwin smbus_child_deleted(device_t dev, device_t child)
11603f641f9SJohn Baldwin {
11703f641f9SJohn Baldwin 	free(device_get_ivars(child), M_DEVBUF);
11803f641f9SJohn Baldwin }
11903f641f9SJohn Baldwin 
12003f641f9SJohn Baldwin static void
smbus_hinted_child(device_t bus,const char * dname,int dunit)12194dce599SAndriy Gapon smbus_hinted_child(device_t bus, const char *dname, int dunit)
12294dce599SAndriy Gapon {
12394dce599SAndriy Gapon 	struct smbus_ivar *devi;
12494dce599SAndriy Gapon 	device_t child;
12594dce599SAndriy Gapon 	int addr;
12694dce599SAndriy Gapon 
12794dce599SAndriy Gapon 	addr = 0;
12894dce599SAndriy Gapon 	resource_int_value(dname, dunit, "addr", &addr);
12994dce599SAndriy Gapon 	if (addr > UINT8_MAX) {
13094dce599SAndriy Gapon 		device_printf(bus, "ignored incorrect slave address hint 0x%x"
13194dce599SAndriy Gapon 		    " for %s%d\n", addr, dname, dunit);
13294dce599SAndriy Gapon 		return;
13394dce599SAndriy Gapon 	}
13494dce599SAndriy Gapon 	child = BUS_ADD_CHILD(bus, SMBUS_ORDER_HINTED, dname, dunit);
13594dce599SAndriy Gapon 	if (child == NULL)
13694dce599SAndriy Gapon 		return;
13794dce599SAndriy Gapon 	devi = device_get_ivars(child);
13894dce599SAndriy Gapon 	devi->addr = addr;
13994dce599SAndriy Gapon }
14094dce599SAndriy Gapon 
141202379afSMichael Gmelin static int
smbus_child_location(device_t parent,device_t child,struct sbuf * sb)142ddfc9c4cSWarner Losh smbus_child_location(device_t parent, device_t child, struct sbuf *sb)
143202379afSMichael Gmelin {
14494dce599SAndriy Gapon 	struct smbus_ivar *devi;
145202379afSMichael Gmelin 
14694dce599SAndriy Gapon 	devi = device_get_ivars(child);
14794dce599SAndriy Gapon 	if (devi->addr != 0)
148ddfc9c4cSWarner Losh 		sbuf_printf(sb, "addr=0x%x", devi->addr);
149202379afSMichael Gmelin 	return (0);
150202379afSMichael Gmelin }
151202379afSMichael Gmelin 
152202379afSMichael Gmelin static int
smbus_print_child(device_t parent,device_t child)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
smbus_read_ivar(device_t parent,device_t child,int which,uintptr_t * result)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
smbus_write_ivar(device_t parent,device_t child,int which,uintptr_t value)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
smbus_probe_nomatch(device_t bus,device_t child)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),
23003f641f9SJohn Baldwin 	DEVMETHOD(bus_child_deleted,	smbus_child_deleted),
23194dce599SAndriy Gapon 	DEVMETHOD(bus_hinted_child,	smbus_hinted_child),
23294dce599SAndriy Gapon 	DEVMETHOD(bus_probe_nomatch,	smbus_probe_nomatch),
233ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_location,	smbus_child_location),
23494dce599SAndriy Gapon 	DEVMETHOD(bus_print_child,	smbus_print_child),
23594dce599SAndriy Gapon 	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
23694dce599SAndriy Gapon 	DEVMETHOD(bus_write_ivar,	smbus_write_ivar),
23794dce599SAndriy Gapon 
23894dce599SAndriy Gapon 	DEVMETHOD_END
23994dce599SAndriy Gapon };
24094dce599SAndriy Gapon 
24194dce599SAndriy Gapon driver_t smbus_driver = {
24294dce599SAndriy Gapon         "smbus",
24394dce599SAndriy Gapon         smbus_methods,
24494dce599SAndriy Gapon         sizeof(struct smbus_softc),
24594dce599SAndriy Gapon };
24694dce599SAndriy Gapon 
247c17d4340SNicolas Souchu MODULE_VERSION(smbus, SMBUS_MODVER);
248