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