xref: /freebsd/sys/dev/smbus/smbus.c (revision fdafd315)
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);
707048a99cSJohn Baldwin 	bus_generic_probe(dev);
7194dce599SAndriy Gapon 	bus_enumerate_hinted_children(dev);
729a77af90SRuslan Ermilov 	bus_generic_attach(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);
8694dce599SAndriy Gapon 	device_delete_children(dev);
877048a99cSJohn Baldwin 	mtx_destroy(&sc->lock);
887048a99cSJohn Baldwin 
897048a99cSJohn Baldwin 	return (0);
907048a99cSJohn Baldwin }
917048a99cSJohn Baldwin 
92d70424edSNicolas Souchu void
smbus_generic_intr(device_t dev,u_char devaddr,char low,char high,int err)93b29df1b2SWarner Losh smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
94d70424edSNicolas Souchu {
95d70424edSNicolas Souchu }
96d70424edSNicolas Souchu 
9794dce599SAndriy Gapon static device_t
smbus_add_child(device_t dev,u_int order,const char * name,int unit)9894dce599SAndriy Gapon smbus_add_child(device_t dev, u_int order, const char *name, int unit)
9994dce599SAndriy Gapon {
10094dce599SAndriy Gapon 	struct smbus_ivar *devi;
10194dce599SAndriy Gapon 	device_t child;
10294dce599SAndriy Gapon 
10394dce599SAndriy Gapon 	child = device_add_child_ordered(dev, order, name, unit);
10494dce599SAndriy Gapon 	if (child == NULL)
10594dce599SAndriy Gapon 		return (child);
10694dce599SAndriy Gapon 	devi = malloc(sizeof(struct smbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
10794dce599SAndriy Gapon 	if (devi == NULL) {
10894dce599SAndriy Gapon 		device_delete_child(dev, child);
10994dce599SAndriy Gapon 		return (NULL);
11094dce599SAndriy Gapon 	}
11194dce599SAndriy Gapon 	device_set_ivars(child, devi);
11294dce599SAndriy Gapon 	return (child);
11394dce599SAndriy Gapon }
11494dce599SAndriy Gapon 
11594dce599SAndriy Gapon static void
smbus_hinted_child(device_t bus,const char * dname,int dunit)11694dce599SAndriy Gapon smbus_hinted_child(device_t bus, const char *dname, int dunit)
11794dce599SAndriy Gapon {
11894dce599SAndriy Gapon 	struct smbus_ivar *devi;
11994dce599SAndriy Gapon 	device_t child;
12094dce599SAndriy Gapon 	int addr;
12194dce599SAndriy Gapon 
12294dce599SAndriy Gapon 	addr = 0;
12394dce599SAndriy Gapon 	resource_int_value(dname, dunit, "addr", &addr);
12494dce599SAndriy Gapon 	if (addr > UINT8_MAX) {
12594dce599SAndriy Gapon 		device_printf(bus, "ignored incorrect slave address hint 0x%x"
12694dce599SAndriy Gapon 		    " for %s%d\n", addr, dname, dunit);
12794dce599SAndriy Gapon 		return;
12894dce599SAndriy Gapon 	}
12994dce599SAndriy Gapon 	child = BUS_ADD_CHILD(bus, SMBUS_ORDER_HINTED, dname, dunit);
13094dce599SAndriy Gapon 	if (child == NULL)
13194dce599SAndriy Gapon 		return;
13294dce599SAndriy Gapon 	devi = device_get_ivars(child);
13394dce599SAndriy Gapon 	devi->addr = addr;
13494dce599SAndriy Gapon }
13594dce599SAndriy Gapon 
136202379afSMichael Gmelin static int
smbus_child_location(device_t parent,device_t child,struct sbuf * sb)137ddfc9c4cSWarner Losh smbus_child_location(device_t parent, device_t child, struct sbuf *sb)
138202379afSMichael Gmelin {
13994dce599SAndriy Gapon 	struct smbus_ivar *devi;
140202379afSMichael Gmelin 
14194dce599SAndriy Gapon 	devi = device_get_ivars(child);
14294dce599SAndriy Gapon 	if (devi->addr != 0)
143ddfc9c4cSWarner Losh 		sbuf_printf(sb, "addr=0x%x", devi->addr);
144202379afSMichael Gmelin 	return (0);
145202379afSMichael Gmelin }
146202379afSMichael Gmelin 
147202379afSMichael Gmelin static int
smbus_print_child(device_t parent,device_t child)148202379afSMichael Gmelin smbus_print_child(device_t parent, device_t child)
149202379afSMichael Gmelin {
15094dce599SAndriy Gapon 	struct smbus_ivar *devi;
151202379afSMichael Gmelin 	int retval;
152202379afSMichael Gmelin 
15394dce599SAndriy Gapon 	devi = device_get_ivars(child);
154202379afSMichael Gmelin 	retval = bus_print_child_header(parent, child);
15594dce599SAndriy Gapon 	if (devi->addr != 0)
15694dce599SAndriy Gapon 		retval += printf(" at addr 0x%x", devi->addr);
157202379afSMichael Gmelin 	retval += bus_print_child_footer(parent, child);
158202379afSMichael Gmelin 
159202379afSMichael Gmelin 	return (retval);
160202379afSMichael Gmelin }
161202379afSMichael Gmelin 
162202379afSMichael Gmelin static int
smbus_read_ivar(device_t parent,device_t child,int which,uintptr_t * result)16394dce599SAndriy Gapon smbus_read_ivar(device_t parent, device_t child, int which, uintptr_t *result)
164202379afSMichael Gmelin {
16594dce599SAndriy Gapon 	struct smbus_ivar *devi;
166202379afSMichael Gmelin 
16794dce599SAndriy Gapon 	devi = device_get_ivars(child);
168202379afSMichael Gmelin 	switch (which) {
169202379afSMichael Gmelin 	case SMBUS_IVAR_ADDR:
17094dce599SAndriy Gapon 		if (devi->addr != 0)
17194dce599SAndriy Gapon 			*result = devi->addr;
17294dce599SAndriy Gapon 		else
17394dce599SAndriy Gapon 			*result = -1;
174202379afSMichael Gmelin 		break;
175202379afSMichael Gmelin 	default:
176202379afSMichael Gmelin 		return (ENOENT);
177202379afSMichael Gmelin 	}
178202379afSMichael Gmelin 	return (0);
179202379afSMichael Gmelin }
180202379afSMichael Gmelin 
18194dce599SAndriy Gapon static int
smbus_write_ivar(device_t parent,device_t child,int which,uintptr_t value)18294dce599SAndriy Gapon smbus_write_ivar(device_t parent, device_t child, int which, uintptr_t value)
18394dce599SAndriy Gapon {
18494dce599SAndriy Gapon 	struct smbus_ivar *devi;
18594dce599SAndriy Gapon 
18694dce599SAndriy Gapon 	devi = device_get_ivars(child);
18794dce599SAndriy Gapon 	switch (which) {
18894dce599SAndriy Gapon 	case SMBUS_IVAR_ADDR:
18994dce599SAndriy Gapon 		/* Allow to set but no change the slave address. */
19094dce599SAndriy Gapon 		if (devi->addr != 0)
19194dce599SAndriy Gapon 			return (EINVAL);
19294dce599SAndriy Gapon 		devi->addr = value;
19394dce599SAndriy Gapon 		break;
19494dce599SAndriy Gapon 	default:
19594dce599SAndriy Gapon 		return (ENOENT);
19694dce599SAndriy Gapon 	}
19794dce599SAndriy Gapon 	return (0);
19894dce599SAndriy Gapon }
19994dce599SAndriy Gapon 
20094dce599SAndriy Gapon static void
smbus_probe_nomatch(device_t bus,device_t child)20194dce599SAndriy Gapon smbus_probe_nomatch(device_t bus, device_t child)
20294dce599SAndriy Gapon {
20394dce599SAndriy Gapon 	struct smbus_ivar *devi = device_get_ivars(child);
20494dce599SAndriy Gapon 
20594dce599SAndriy Gapon 	/*
20694dce599SAndriy Gapon 	 * Ignore (self-identified) devices without a slave address set.
20794dce599SAndriy Gapon 	 * For example, smb(4).
20894dce599SAndriy Gapon 	 */
20994dce599SAndriy Gapon 	if (devi->addr != 0)
21094dce599SAndriy Gapon 		device_printf(bus, "<unknown device> at addr %#x\n",
21194dce599SAndriy Gapon 		    devi->addr);
21294dce599SAndriy Gapon }
21394dce599SAndriy Gapon 
21494dce599SAndriy Gapon /*
21594dce599SAndriy Gapon  * Device methods
21694dce599SAndriy Gapon  */
21794dce599SAndriy Gapon static device_method_t smbus_methods[] = {
21894dce599SAndriy Gapon         /* device interface */
21994dce599SAndriy Gapon         DEVMETHOD(device_probe,         smbus_probe),
22094dce599SAndriy Gapon         DEVMETHOD(device_attach,        smbus_attach),
22194dce599SAndriy Gapon         DEVMETHOD(device_detach,        smbus_detach),
22294dce599SAndriy Gapon 
22394dce599SAndriy Gapon 	/* bus interface */
22494dce599SAndriy Gapon 	DEVMETHOD(bus_add_child,	smbus_add_child),
22594dce599SAndriy Gapon 	DEVMETHOD(bus_hinted_child,	smbus_hinted_child),
22694dce599SAndriy Gapon 	DEVMETHOD(bus_probe_nomatch,	smbus_probe_nomatch),
227ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_location,	smbus_child_location),
22894dce599SAndriy Gapon 	DEVMETHOD(bus_print_child,	smbus_print_child),
22994dce599SAndriy Gapon 	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
23094dce599SAndriy Gapon 	DEVMETHOD(bus_write_ivar,	smbus_write_ivar),
23194dce599SAndriy Gapon 
23294dce599SAndriy Gapon 	DEVMETHOD_END
23394dce599SAndriy Gapon };
23494dce599SAndriy Gapon 
23594dce599SAndriy Gapon driver_t smbus_driver = {
23694dce599SAndriy Gapon         "smbus",
23794dce599SAndriy Gapon         smbus_methods,
23894dce599SAndriy Gapon         sizeof(struct smbus_softc),
23994dce599SAndriy Gapon };
24094dce599SAndriy Gapon 
241c17d4340SNicolas Souchu MODULE_VERSION(smbus, SMBUS_MODVER);
242