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