iicbus.c revision 167856
138774Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu
338774Snsouch * All rights reserved.
438774Snsouch *
538774Snsouch * Redistribution and use in source and binary forms, with or without
638774Snsouch * modification, are permitted provided that the following conditions
738774Snsouch * are met:
838774Snsouch * 1. Redistributions of source code must retain the above copyright
938774Snsouch *    notice, this list of conditions and the following disclaimer.
1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138774Snsouch *    notice, this list of conditions and the following disclaimer in the
1238774Snsouch *    documentation and/or other materials provided with the distribution.
1338774Snsouch *
1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738774Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438774Snsouch * SUCH DAMAGE.
2538774Snsouch */
2638774Snsouch
27119418Sobrien#include <sys/cdefs.h>
28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/iicbus/iicbus.c 167856 2007-03-23 23:08:28Z imp $");
29119418Sobrien
3038774Snsouch/*
3138774Snsouch * Autoconfiguration and support routines for the Philips serial I2C bus
3238774Snsouch */
3338774Snsouch
3438774Snsouch#include <sys/param.h>
3538774Snsouch#include <sys/systm.h>
3638774Snsouch#include <sys/kernel.h>
37167856Simp#include <sys/malloc.h>
3838774Snsouch#include <sys/module.h>
3938774Snsouch#include <sys/bus.h>
4038774Snsouch
4138774Snsouch#include <dev/iicbus/iiconf.h>
4238774Snsouch#include <dev/iicbus/iicbus.h>
4338774Snsouch
4438774Snsouch#include "iicbus_if.h"
4538774Snsouch
46129152Sjoerg/* See comments below for why auto-scanning is a bad idea. */
47129152Sjoerg#define SCAN_IICBUS 0
48129152Sjoerg
4940782Snsouchstatic int
5040782Snsouchiicbus_probe(device_t dev)
5140782Snsouch{
52160372Simp
5342442Snsouch	device_set_desc(dev, "Philips I2C bus");
5440782Snsouch	return (0);
5540782Snsouch}
5640782Snsouch
57129152Sjoerg#if SCAN_IICBUS
5840782Snsouchstatic int
5940782Snsouchiic_probe_device(device_t dev, u_char addr)
6040782Snsouch{
6140782Snsouch	int count;
6240782Snsouch	char byte;
6340782Snsouch
6440782Snsouch	if ((addr & 1) == 0) {
6540782Snsouch		/* is device writable? */
6640782Snsouch		if (!iicbus_start(dev, (u_char)addr, 0)) {
6740782Snsouch			iicbus_stop(dev);
6840782Snsouch			return (1);
6940782Snsouch		}
7040782Snsouch	} else {
7140782Snsouch		/* is device readable? */
7240782Snsouch		if (!iicbus_block_read(dev, (u_char)addr, &byte, 1, &count))
7340782Snsouch			return (1);
7440782Snsouch	}
7540782Snsouch
7640782Snsouch	return (0);
7740782Snsouch}
7842442Snsouch#endif
7940782Snsouch
8038774Snsouch/*
8140782Snsouch * We add all the devices which we know about.
8240782Snsouch * The generic attach routine will attach them if they are alive.
8338774Snsouch */
8438774Snsouchstatic int
8540782Snsouchiicbus_attach(device_t dev)
8638774Snsouch{
87129152Sjoerg#if SCAN_IICBUS
88129152Sjoerg	unsigned char addr;
89129152Sjoerg#endif
90167856Simp	struct iicbus_softc *sc = IICBUS_SOFTC(dev);
91129152Sjoerg
92167856Simp	sc->dev = dev;
9340782Snsouch	iicbus_reset(dev, IIC_FASTEST, 0, NULL);
9438774Snsouch
9542442Snsouch	/* device probing is meaningless since the bus is supposed to be
9642442Snsouch	 * hot-plug. Moreover, some I2C chips do not appreciate random
9742442Snsouch	 * accesses like stop after start to fast, reads for less than
9842442Snsouch	 * x bytes...
9942442Snsouch	 */
100129152Sjoerg#if SCAN_IICBUS
10140782Snsouch	printf("Probing for devices on iicbus%d:", device_get_unit(dev));
10238774Snsouch
10340782Snsouch	/* probe any devices */
104129152Sjoerg	for (addr = 16; addr < 240; addr++) {
10540782Snsouch		if (iic_probe_device(dev, (u_char)addr)) {
10640782Snsouch			printf(" <%x>", addr);
10740782Snsouch		}
10840782Snsouch	}
10940782Snsouch	printf("\n");
11042442Snsouch#endif
111167856Simp	/* Always attach the iicsmb children */
112167856Simp	BUS_ADD_CHILD(dev, 0, "iicsmb", -1);
11393023Snsouch	/* attach any known device */
114167856Simp	BUS_ADD_CHILD(dev, 0, "iic", -1);
115167856Simp	/* Attach the wired devices via hints */
116167856Simp	bus_enumerate_hinted_children(dev);
117167856Simp	/* Now probe and attach them */
11838774Snsouch	bus_generic_attach(dev);
11938774Snsouch        return (0);
12038774Snsouch}
12193023Snsouch
12293023Snsouchstatic int
12393023Snsouchiicbus_detach(device_t dev)
12493023Snsouch{
125160372Simp
12693023Snsouch	iicbus_reset(dev, IIC_FASTEST, 0, NULL);
12793023Snsouch	bus_generic_detach(dev);
12893023Snsouch	return (0);
12993023Snsouch}
13093023Snsouch
13193023Snsouchstatic int
132167856Simpiicbus_print_child(device_t dev, device_t child)
13393023Snsouch{
134167856Simp	struct iicbus_ivar *devi = IICBUS_IVAR(child);
135167856Simp	int retval = 0;
136160372Simp
137167856Simp	retval += bus_print_child_header(dev, child);
138167856Simp	if (devi->addr != 0)
139167856Simp		retval += printf(" at addr %#x", devi->addr);
140167856Simp	retval += bus_print_child_footer(dev, child);
141167856Simp
142167856Simp	return (retval);
143167856Simp}
144167856Simp
145167856Simpstatic void
146167856Simpiicbus_probe_nomatch(device_t bus, device_t child)
147167856Simp{
148167856Simp	struct iicbus_ivar *devi = IICBUS_IVAR(child);
149167856Simp
150167856Simp	device_printf(bus, "<unknown card>");
151167856Simp	printf(" at addr %#x\n", devi->addr);
152167856Simp	return;
153167856Simp}
154167856Simp
155167856Simpstatic int
156167856Simpiicbus_child_location_str(device_t bus, device_t child, char *buf,
157167856Simp    size_t buflen)
158167856Simp{
159167856Simp	struct iicbus_ivar *devi = IICBUS_IVAR(child);
160167856Simp
161167856Simp	snprintf(buf, buflen, "addr=%#x", devi->addr);
16293023Snsouch	return (0);
16393023Snsouch}
16493023Snsouch
165167856Simpstatic int
166167856Simpiicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
167167856Simp    size_t buflen)
168167856Simp{
169167856Simp	*buf = '\0';
170167856Simp	return (0);
171167856Simp}
172167856Simp
173167856Simpstatic int
174167856Simpiicbus_read_ivar(device_t bus, device_t child, int which, u_char *result)
175167856Simp{
176167856Simp	struct iicbus_ivar *devi = IICBUS_IVAR(child);
177167856Simp
178167856Simp	switch (which) {
179167856Simp	default:
180167856Simp		return (EINVAL);
181167856Simp	case IICBUS_IVAR_ADDR:
182167856Simp		*(uint32_t *)result = devi->addr;
183167856Simp		break;
184167856Simp	}
185167856Simp	return (0);
186167856Simp}
187167856Simp
188167856Simpstatic device_t
189167856Simpiicbus_add_child(device_t dev, int order, const char *name, int unit)
190167856Simp{
191167856Simp	device_t child;
192167856Simp	struct iicbus_ivar *devi;
193167856Simp
194167856Simp	child = device_add_child_ordered(dev, order, name, unit);
195167856Simp	if (child == NULL)
196167856Simp		return (child);
197167856Simp	devi = malloc(sizeof(struct iicbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
198167856Simp	if (devi == NULL) {
199167856Simp		device_delete_child(dev, child);
200167856Simp		return (0);
201167856Simp	}
202167856Simp	device_set_ivars(child, devi);
203167856Simp	return (child);
204167856Simp}
205167856Simp
206167856Simpstatic void
207167856Simpiicbus_hinted_child(device_t bus, const char *dname, int dunit)
208167856Simp{
209167856Simp	device_t child;
210167856Simp	struct iicbus_ivar *devi;
211167856Simp
212167856Simp	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
213167856Simp	devi = IICBUS_IVAR(child);
214167856Simp	resource_int_value(dname, dunit, "addr", &devi->addr);
215167856Simp}
216167856Simp
21738774Snsouchint
21838774Snsouchiicbus_generic_intr(device_t dev, int event, char *buf)
21938774Snsouch{
220160372Simp
22138774Snsouch	return (0);
22238774Snsouch}
22338774Snsouch
22440782Snsouchint
22540782Snsouchiicbus_null_callback(device_t dev, int index, caddr_t data)
22640782Snsouch{
227160372Simp
22840782Snsouch	return (0);
22940782Snsouch}
23040782Snsouch
23140782Snsouchint
23240782Snsouchiicbus_null_repeated_start(device_t dev, u_char addr)
23340782Snsouch{
234160372Simp
23540782Snsouch	return (IIC_ENOTSUPP);
23640782Snsouch}
23740782Snsouch
238167856Simpstatic device_method_t iicbus_methods[] = {
239167856Simp        /* device interface */
240167856Simp        DEVMETHOD(device_probe,         iicbus_probe),
241167856Simp        DEVMETHOD(device_attach,        iicbus_attach),
242167856Simp        DEVMETHOD(device_detach,        iicbus_detach),
243167856Simp
244167856Simp        /* bus interface */
245167856Simp        DEVMETHOD(bus_add_child,	iicbus_add_child),
246167856Simp	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
247167856Simp        DEVMETHOD(bus_print_child,      iicbus_print_child),
248167856Simp	DEVMETHOD(bus_probe_nomatch,	iicbus_probe_nomatch),
249167856Simp	DEVMETHOD(bus_read_ivar,	iicbus_read_ivar),
250167856Simp	DEVMETHOD(bus_child_pnpinfo_str, iicbus_child_pnpinfo_str),
251167856Simp	DEVMETHOD(bus_child_location_str, iicbus_child_location_str),
252167856Simp	DEVMETHOD(bus_hinted_child,	iicbus_hinted_child),
253167856Simp
254167856Simp	/* iicbus interface */
255167856Simp	DEVMETHOD(iicbus_transfer,	iicbus_transfer),
256167856Simp
257167856Simp        { 0, 0 }
258167856Simp};
259167856Simp
260167856Simpdriver_t iicbus_driver = {
261167856Simp        "iicbus",
262167856Simp        iicbus_methods,
263167856Simp        sizeof(struct iicbus_softc),
264167856Simp};
265167856Simp
266167856Simpdevclass_t iicbus_devclass;
267167856Simp
268129778SjoergDRIVER_MODULE(iicbus, envctrl, iicbus_driver, iicbus_devclass, 0, 0);
26940782SnsouchDRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0);
27093023SnsouchMODULE_VERSION(iicbus, IICBUS_MODVER);
271