138776Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu
338776Snsouch * All rights reserved.
438776Snsouch *
538776Snsouch * Redistribution and use in source and binary forms, with or without
638776Snsouch * modification, are permitted provided that the following conditions
738776Snsouch * are met:
838776Snsouch * 1. Redistributions of source code must retain the above copyright
938776Snsouch *    notice, this list of conditions and the following disclaimer.
1038776Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138776Snsouch *    notice, this list of conditions and the following disclaimer in the
1238776Snsouch *    documentation and/or other materials provided with the distribution.
1338776Snsouch *
1438776Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538776Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638776Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738776Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838776Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938776Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038776Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138776Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238776Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338776Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438776Snsouch * SUCH DAMAGE.
2538776Snsouch *
2638776Snsouch *
2738776Snsouch */
28119419Sobrien
29119419Sobrien#include <sys/cdefs.h>
30119419Sobrien__FBSDID("$FreeBSD: stable/10/sys/dev/smbus/smbus.c 308042 2016-10-28 15:02:24Z avg $");
3138776Snsouch#include <sys/param.h>
3238776Snsouch#include <sys/systm.h>
33162234Sjhb#include <sys/lock.h>
34308042Savg#include <sys/malloc.h>
3538776Snsouch#include <sys/module.h>
36162234Sjhb#include <sys/mutex.h>
37308042Savg#include <sys/bus.h>
3838776Snsouch
3938776Snsouch#include <dev/smbus/smbconf.h>
4038776Snsouch#include <dev/smbus/smbus.h>
4138776Snsouch
4238776Snsouch
43308042Savgstruct smbus_ivar
44308042Savg{
45308042Savg	uint8_t	addr;
4638776Snsouch};
4738776Snsouch
4838776Snsouch/*
49308042Savg * Autoconfiguration and support routines for System Management bus
5038776Snsouch */
51308042Savg
5238776Snsouchstatic int
5338776Snsouchsmbus_probe(device_t dev)
5438776Snsouch{
55162234Sjhb
5640785Snsouch	device_set_desc(dev, "System Management Bus");
5742129Snsouch
5840785Snsouch	return (0);
5940785Snsouch}
6040785Snsouch
61153569Srustatic int
62153569Srusmbus_attach(device_t dev)
63153569Sru{
64162234Sjhb	struct smbus_softc *sc = device_get_softc(dev);
65162234Sjhb
66162234Sjhb	mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
67162234Sjhb	bus_generic_probe(dev);
68308042Savg	bus_enumerate_hinted_children(dev);
69153569Sru	bus_generic_attach(dev);
70153569Sru
71153569Sru	return (0);
72153569Sru}
73153569Sru
74162234Sjhbstatic int
75162234Sjhbsmbus_detach(device_t dev)
76162234Sjhb{
77162234Sjhb	struct smbus_softc *sc = device_get_softc(dev);
78162234Sjhb	int error;
79162234Sjhb
80162234Sjhb	error = bus_generic_detach(dev);
81162234Sjhb	if (error)
82162234Sjhb		return (error);
83308042Savg	device_delete_children(dev);
84162234Sjhb	mtx_destroy(&sc->lock);
85162234Sjhb
86162234Sjhb	return (0);
87162234Sjhb}
88162234Sjhb
8938776Snsouchvoid
90189580Simpsmbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
9138776Snsouch{
9238776Snsouch}
9338776Snsouch
94308042Savgstatic device_t
95308042Savgsmbus_add_child(device_t dev, u_int order, const char *name, int unit)
96308042Savg{
97308042Savg	struct smbus_ivar *devi;
98308042Savg	device_t child;
99308042Savg
100308042Savg	child = device_add_child_ordered(dev, order, name, unit);
101308042Savg	if (child == NULL)
102308042Savg		return (child);
103308042Savg	devi = malloc(sizeof(struct smbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
104308042Savg	if (devi == NULL) {
105308042Savg		device_delete_child(dev, child);
106308042Savg		return (NULL);
107308042Savg	}
108308042Savg	device_set_ivars(child, devi);
109308042Savg	return (child);
110308042Savg}
111308042Savg
112308042Savgstatic void
113308042Savgsmbus_hinted_child(device_t bus, const char *dname, int dunit)
114308042Savg{
115308042Savg	struct smbus_ivar *devi;
116308042Savg	device_t child;
117308042Savg	int addr;
118308042Savg
119308042Savg	addr = 0;
120308042Savg	resource_int_value(dname, dunit, "addr", &addr);
121308042Savg	if (addr > UINT8_MAX) {
122308042Savg		device_printf(bus, "ignored incorrect slave address hint 0x%x"
123308042Savg		    " for %s%d\n", addr, dname, dunit);
124308042Savg		return;
125308042Savg	}
126308042Savg	child = BUS_ADD_CHILD(bus, SMBUS_ORDER_HINTED, dname, dunit);
127308042Savg	if (child == NULL)
128308042Savg		return;
129308042Savg	devi = device_get_ivars(child);
130308042Savg	devi->addr = addr;
131308042Savg}
132308042Savg
133308042Savg
134308042Savgstatic int
135308042Savgsmbus_child_location_str(device_t parent, device_t child, char *buf,
136308042Savg    size_t buflen)
137308042Savg{
138308042Savg	struct smbus_ivar *devi;
139308042Savg
140308042Savg	devi = device_get_ivars(child);
141308042Savg	if (devi->addr != 0)
142308042Savg		snprintf(buf, buflen, "addr=0x%x", devi->addr);
143308042Savg	else if (buflen)
144308042Savg		buf[0] = 0;
145308042Savg	return (0);
146308042Savg}
147308042Savg
148308042Savgstatic int
149308042Savgsmbus_print_child(device_t parent, device_t child)
150308042Savg{
151308042Savg	struct smbus_ivar *devi;
152308042Savg	int retval;
153308042Savg
154308042Savg	devi = device_get_ivars(child);
155308042Savg	retval = bus_print_child_header(parent, child);
156308042Savg	if (devi->addr != 0)
157308042Savg		retval += printf(" at addr 0x%x", devi->addr);
158308042Savg	retval += bus_print_child_footer(parent, child);
159308042Savg
160308042Savg	return (retval);
161308042Savg}
162308042Savg
163308042Savgstatic int
164308042Savgsmbus_read_ivar(device_t parent, device_t child, int which, uintptr_t *result)
165308042Savg{
166308042Savg	struct smbus_ivar *devi;
167308042Savg
168308042Savg	devi = device_get_ivars(child);
169308042Savg	switch (which) {
170308042Savg	case SMBUS_IVAR_ADDR:
171308042Savg		if (devi->addr != 0)
172308042Savg			*result = devi->addr;
173308042Savg		else
174308042Savg			*result = -1;
175308042Savg		break;
176308042Savg	default:
177308042Savg		return (ENOENT);
178308042Savg	}
179308042Savg	return (0);
180308042Savg}
181308042Savg
182308042Savgstatic int
183308042Savgsmbus_write_ivar(device_t parent, device_t child, int which, uintptr_t value)
184308042Savg{
185308042Savg	struct smbus_ivar *devi;
186308042Savg
187308042Savg	devi = device_get_ivars(child);
188308042Savg	switch (which) {
189308042Savg	case SMBUS_IVAR_ADDR:
190308042Savg		/* Allow to set but no change the slave address. */
191308042Savg		if (devi->addr != 0)
192308042Savg			return (EINVAL);
193308042Savg		devi->addr = value;
194308042Savg		break;
195308042Savg	default:
196308042Savg		return (ENOENT);
197308042Savg	}
198308042Savg	return (0);
199308042Savg}
200308042Savg
201308042Savgstatic void
202308042Savgsmbus_probe_nomatch(device_t bus, device_t child)
203308042Savg{
204308042Savg	struct smbus_ivar *devi = device_get_ivars(child);
205308042Savg
206308042Savg	/*
207308042Savg	 * Ignore (self-identified) devices without a slave address set.
208308042Savg	 * For example, smb(4).
209308042Savg	 */
210308042Savg	if (devi->addr != 0)
211308042Savg		device_printf(bus, "<unknown device> at addr %#x\n",
212308042Savg		    devi->addr);
213308042Savg}
214308042Savg
215308042Savg/*
216308042Savg * Device methods
217308042Savg */
218308042Savgstatic device_method_t smbus_methods[] = {
219308042Savg        /* device interface */
220308042Savg        DEVMETHOD(device_probe,         smbus_probe),
221308042Savg        DEVMETHOD(device_attach,        smbus_attach),
222308042Savg        DEVMETHOD(device_detach,        smbus_detach),
223308042Savg
224308042Savg	/* bus interface */
225308042Savg	DEVMETHOD(bus_add_child,	smbus_add_child),
226308042Savg	DEVMETHOD(bus_hinted_child,	smbus_hinted_child),
227308042Savg	DEVMETHOD(bus_probe_nomatch,	smbus_probe_nomatch),
228308042Savg	DEVMETHOD(bus_child_location_str, smbus_child_location_str),
229308042Savg	DEVMETHOD(bus_print_child,	smbus_print_child),
230308042Savg	DEVMETHOD(bus_read_ivar,	smbus_read_ivar),
231308042Savg	DEVMETHOD(bus_write_ivar,	smbus_write_ivar),
232308042Savg
233308042Savg	DEVMETHOD_END
234308042Savg};
235308042Savg
236308042Savgdriver_t smbus_driver = {
237308042Savg        "smbus",
238308042Savg        smbus_methods,
239308042Savg        sizeof(struct smbus_softc),
240308042Savg};
241308042Savg
242308042Savgdevclass_t smbus_devclass;
243308042Savg
24493023SnsouchMODULE_VERSION(smbus, SMBUS_MODVER);
245