xref: /freebsd/sys/dev/ppbus/lpbb.c (revision 34a53f3f6bc090ffa49fb8c232324c1118087a71)
1f7ce69c9SNicolas Souchu /*-
2c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu, Marc Bouget
3f7ce69c9SNicolas Souchu  * All rights reserved.
4f7ce69c9SNicolas Souchu  *
5f7ce69c9SNicolas Souchu  * Redistribution and use in source and binary forms, with or without
6f7ce69c9SNicolas Souchu  * modification, are permitted provided that the following conditions
7f7ce69c9SNicolas Souchu  * are met:
8f7ce69c9SNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
9f7ce69c9SNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
10f7ce69c9SNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
11f7ce69c9SNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
12f7ce69c9SNicolas Souchu  *    documentation and/or other materials provided with the distribution.
13f7ce69c9SNicolas Souchu  *
14f7ce69c9SNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15f7ce69c9SNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16f7ce69c9SNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17f7ce69c9SNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18f7ce69c9SNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19f7ce69c9SNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20f7ce69c9SNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21f7ce69c9SNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22f7ce69c9SNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23f7ce69c9SNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24f7ce69c9SNicolas Souchu  * SUCH DAMAGE.
25f7ce69c9SNicolas Souchu  *
26f7ce69c9SNicolas Souchu  *
27f7ce69c9SNicolas Souchu  */
28f7ce69c9SNicolas Souchu 
29aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
30aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
31aad970f1SDavid E. O'Brien 
32f7ce69c9SNicolas Souchu /*
33f7ce69c9SNicolas Souchu  * I2C Bit-Banging over parallel port
34f7ce69c9SNicolas Souchu  *
35f7ce69c9SNicolas Souchu  * See the Official Philips interface description in lpbb(4)
36f7ce69c9SNicolas Souchu  */
37f7ce69c9SNicolas Souchu 
38f7ce69c9SNicolas Souchu #include <sys/param.h>
39f7ce69c9SNicolas Souchu #include <sys/bus.h>
4013e3657bSJohn Baldwin #include <sys/lock.h>
4113e3657bSJohn Baldwin #include <sys/kernel.h>
4213e3657bSJohn Baldwin #include <sys/module.h>
4313e3657bSJohn Baldwin #include <sys/mutex.h>
4413e3657bSJohn Baldwin #include <sys/systm.h>
45f7ce69c9SNicolas Souchu #include <sys/uio.h>
46f7ce69c9SNicolas Souchu 
47f7ce69c9SNicolas Souchu 
48f7ce69c9SNicolas Souchu #include <dev/ppbus/ppbconf.h>
490f210c92SNicolas Souchu #include "ppbus_if.h"
500f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h>
51f7ce69c9SNicolas Souchu 
52f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h>
53f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h>
54f7ce69c9SNicolas Souchu 
55f7ce69c9SNicolas Souchu #include "iicbb_if.h"
56f7ce69c9SNicolas Souchu 
570f210c92SNicolas Souchu static int lpbb_detect(device_t dev);
58f7ce69c9SNicolas Souchu 
590f063508SPeter Wemm static void
600f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent)
610f063508SPeter Wemm {
62f7ce69c9SNicolas Souchu 
63a5c7e3bbSGuido van Rooij 	device_t dev;
64a5c7e3bbSGuido van Rooij 
65ae6b868aSJohn Baldwin 	dev = device_find_child(parent, "lpbb", -1);
66a5c7e3bbSGuido van Rooij 	if (!dev)
67338cad62SBernd Walter 		BUS_ADD_CHILD(parent, 0, "lpbb", -1);
680f063508SPeter Wemm }
69f7ce69c9SNicolas Souchu 
70f7ce69c9SNicolas Souchu static int
71f7ce69c9SNicolas Souchu lpbb_probe(device_t dev)
72f7ce69c9SNicolas Souchu {
73f7ce69c9SNicolas Souchu 
740f063508SPeter Wemm 	/* Perhaps call this during identify instead? */
750f210c92SNicolas Souchu 	if (!lpbb_detect(dev))
760f210c92SNicolas Souchu 		return (ENXIO);
77f7ce69c9SNicolas Souchu 
780f063508SPeter Wemm 	device_set_desc(dev, "Parallel I2C bit-banging interface");
790f063508SPeter Wemm 
80f7ce69c9SNicolas Souchu 	return (0);
81f7ce69c9SNicolas Souchu }
82f7ce69c9SNicolas Souchu 
83f7ce69c9SNicolas Souchu static int
84f7ce69c9SNicolas Souchu lpbb_attach(device_t dev)
85f7ce69c9SNicolas Souchu {
86c17d4340SNicolas Souchu 	device_t bitbang;
87f7ce69c9SNicolas Souchu 
88f7ce69c9SNicolas Souchu 	/* add generic bit-banging code */
89fe0d4089SMatthew N. Dodd 	bitbang = device_add_child(dev, "iicbb", -1);
90f7ce69c9SNicolas Souchu 	device_probe_and_attach(bitbang);
91f7ce69c9SNicolas Souchu 
92f7ce69c9SNicolas Souchu 	return (0);
93f7ce69c9SNicolas Souchu }
94f7ce69c9SNicolas Souchu 
95f7ce69c9SNicolas Souchu static int
9634a53f3fSWarner Losh lpbb_callback(device_t dev, int index, caddr_t data)
97f7ce69c9SNicolas Souchu {
980f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
99f7ce69c9SNicolas Souchu 	int error = 0;
100f7ce69c9SNicolas Souchu 	int how;
101f7ce69c9SNicolas Souchu 
102f7ce69c9SNicolas Souchu 	switch (index) {
103f7ce69c9SNicolas Souchu 	case IIC_REQUEST_BUS:
104f7ce69c9SNicolas Souchu 		/* request the ppbus */
105f7ce69c9SNicolas Souchu 		how = *(int *)data;
1062067d312SJohn Baldwin 		ppb_lock(ppbus);
1070f210c92SNicolas Souchu 		error = ppb_request_bus(ppbus, dev, how);
1082067d312SJohn Baldwin 		ppb_unlock(ppbus);
109f7ce69c9SNicolas Souchu 		break;
110f7ce69c9SNicolas Souchu 
111f7ce69c9SNicolas Souchu 	case IIC_RELEASE_BUS:
112f7ce69c9SNicolas Souchu 		/* release the ppbus */
1132067d312SJohn Baldwin 		ppb_lock(ppbus);
1140f210c92SNicolas Souchu 		error = ppb_release_bus(ppbus, dev);
1152067d312SJohn Baldwin 		ppb_unlock(ppbus);
116f7ce69c9SNicolas Souchu 		break;
117f7ce69c9SNicolas Souchu 
118f7ce69c9SNicolas Souchu 	default:
119f7ce69c9SNicolas Souchu 		error = EINVAL;
120f7ce69c9SNicolas Souchu 	}
121f7ce69c9SNicolas Souchu 
122f7ce69c9SNicolas Souchu 	return (error);
123f7ce69c9SNicolas Souchu }
124f7ce69c9SNicolas Souchu 
125f7ce69c9SNicolas Souchu #define SDA_out 0x80
126f7ce69c9SNicolas Souchu #define SCL_out 0x08
127f7ce69c9SNicolas Souchu #define SDA_in  0x80
128f7ce69c9SNicolas Souchu #define SCL_in  0x08
129f7ce69c9SNicolas Souchu #define ALIM    0x20
130f7ce69c9SNicolas Souchu #define I2CKEY  0x50
131f7ce69c9SNicolas Souchu 
1322067d312SJohn Baldwin /* Reset bus by setting SDA first and then SCL. */
1332067d312SJohn Baldwin static void
1342067d312SJohn Baldwin lpbb_reset_bus(device_t dev)
1352067d312SJohn Baldwin {
1362067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
1372067d312SJohn Baldwin 
1382067d312SJohn Baldwin 	ppb_assert_locked(ppbus);
1392067d312SJohn Baldwin 	ppb_wdtr(ppbus, (u_char)~SDA_out);
1402067d312SJohn Baldwin 	ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
1412067d312SJohn Baldwin }
1422067d312SJohn Baldwin 
14313e3657bSJohn Baldwin static int
14413e3657bSJohn Baldwin lpbb_getscl(device_t dev)
145f7ce69c9SNicolas Souchu {
1462067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
14713e3657bSJohn Baldwin 	int rval;
14813e3657bSJohn Baldwin 
1492067d312SJohn Baldwin 	ppb_lock(ppbus);
1502067d312SJohn Baldwin 	rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in);
1512067d312SJohn Baldwin 	ppb_unlock(ppbus);
15213e3657bSJohn Baldwin 	return (rval);
153f7ce69c9SNicolas Souchu }
154f7ce69c9SNicolas Souchu 
15513e3657bSJohn Baldwin static int
15613e3657bSJohn Baldwin lpbb_getsda(device_t dev)
157f7ce69c9SNicolas Souchu {
1582067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
15913e3657bSJohn Baldwin 	int rval;
16013e3657bSJohn Baldwin 
1612067d312SJohn Baldwin 	ppb_lock(ppbus);
1622067d312SJohn Baldwin 	rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in);
1632067d312SJohn Baldwin 	ppb_unlock(ppbus);
16413e3657bSJohn Baldwin 	return (rval);
165c17d4340SNicolas Souchu }
166c17d4340SNicolas Souchu 
16713e3657bSJohn Baldwin static void
16834a53f3fSWarner Losh lpbb_setsda(device_t dev, int val)
169c17d4340SNicolas Souchu {
170c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
171c17d4340SNicolas Souchu 
1722067d312SJohn Baldwin 	ppb_lock(ppbus);
173f7ce69c9SNicolas Souchu 	if (val == 0)
1740f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)SDA_out);
175f7ce69c9SNicolas Souchu 	else
1760f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)~SDA_out);
1772067d312SJohn Baldwin 	ppb_unlock(ppbus);
178f7ce69c9SNicolas Souchu }
179f7ce69c9SNicolas Souchu 
18013e3657bSJohn Baldwin static void
18134a53f3fSWarner Losh lpbb_setscl(device_t dev, int val)
182f7ce69c9SNicolas Souchu {
183c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
184c17d4340SNicolas Souchu 
1852067d312SJohn Baldwin 	ppb_lock(ppbus);
186f7ce69c9SNicolas Souchu 	if (val == 0)
1870f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out));
188f7ce69c9SNicolas Souchu 	else
1890f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
1902067d312SJohn Baldwin 	ppb_unlock(ppbus);
191f7ce69c9SNicolas Souchu }
192f7ce69c9SNicolas Souchu 
19313e3657bSJohn Baldwin static int
19413e3657bSJohn Baldwin lpbb_detect(device_t dev)
195f7ce69c9SNicolas Souchu {
1960f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
1970f210c92SNicolas Souchu 
1982067d312SJohn Baldwin 	ppb_lock(ppbus);
1990f210c92SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
2002067d312SJohn Baldwin 		ppb_unlock(ppbus);
2010f210c92SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
202f7ce69c9SNicolas Souchu 		return (0);
203f7ce69c9SNicolas Souchu 	}
204f7ce69c9SNicolas Souchu 
2052067d312SJohn Baldwin 	lpbb_reset_bus(dev);
206f7ce69c9SNicolas Souchu 
2070f210c92SNicolas Souchu 	if ((ppb_rstr(ppbus) & I2CKEY) ||
2080f210c92SNicolas Souchu 		((ppb_rstr(ppbus) & ALIM) != ALIM)) {
2090f210c92SNicolas Souchu 		ppb_release_bus(ppbus, dev);
2102067d312SJohn Baldwin 		ppb_unlock(ppbus);
211f7ce69c9SNicolas Souchu 		return (0);
212a6530aceSNicolas Souchu 	}
213f7ce69c9SNicolas Souchu 
2140f210c92SNicolas Souchu 	ppb_release_bus(ppbus, dev);
2152067d312SJohn Baldwin 	ppb_unlock(ppbus);
216f7ce69c9SNicolas Souchu 
217f7ce69c9SNicolas Souchu 	return (1);
218f7ce69c9SNicolas Souchu }
219f7ce69c9SNicolas Souchu 
220f7ce69c9SNicolas Souchu static int
221f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
222f7ce69c9SNicolas Souchu {
2230f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
224f7ce69c9SNicolas Souchu 
2252067d312SJohn Baldwin 	ppb_lock(ppbus);
226c17d4340SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
2272067d312SJohn Baldwin 		ppb_unlock(ppbus);
228c17d4340SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
229c17d4340SNicolas Souchu 		return (0);
230c17d4340SNicolas Souchu 	}
231c17d4340SNicolas Souchu 
2322067d312SJohn Baldwin 	lpbb_reset_bus(dev);
233c17d4340SNicolas Souchu 
234c17d4340SNicolas Souchu 	ppb_release_bus(ppbus, dev);
2352067d312SJohn Baldwin 	ppb_unlock(ppbus);
236f7ce69c9SNicolas Souchu 
237f7ce69c9SNicolas Souchu 	return (IIC_ENOADDR);
238f7ce69c9SNicolas Souchu }
239f7ce69c9SNicolas Souchu 
2400f063508SPeter Wemm static devclass_t lpbb_devclass;
2410f063508SPeter Wemm 
2420f063508SPeter Wemm static device_method_t lpbb_methods[] = {
2430f063508SPeter Wemm 	/* device interface */
2440f063508SPeter Wemm 	DEVMETHOD(device_identify,	lpbb_identify),
2450f063508SPeter Wemm 	DEVMETHOD(device_probe,		lpbb_probe),
2460f063508SPeter Wemm 	DEVMETHOD(device_attach,	lpbb_attach),
2470f063508SPeter Wemm 
2480f063508SPeter Wemm 	/* bus interface */
2490f063508SPeter Wemm 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
2500f063508SPeter Wemm 
2510f063508SPeter Wemm 	/* iicbb interface */
2520f063508SPeter Wemm 	DEVMETHOD(iicbb_callback,	lpbb_callback),
253c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setsda,		lpbb_setsda),
254c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setscl,		lpbb_setscl),
255c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getsda,		lpbb_getsda),
256c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getscl,		lpbb_getscl),
2570f063508SPeter Wemm 	DEVMETHOD(iicbb_reset,		lpbb_reset),
2580f063508SPeter Wemm 
2590f063508SPeter Wemm 	{ 0, 0 }
2600f063508SPeter Wemm };
2610f063508SPeter Wemm 
2620f063508SPeter Wemm static driver_t lpbb_driver = {
2630f063508SPeter Wemm 	"lpbb",
2640f063508SPeter Wemm 	lpbb_methods,
2650f063508SPeter Wemm 	1,
2660f063508SPeter Wemm };
2670f063508SPeter Wemm 
2680f210c92SNicolas Souchu DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0);
26913e3657bSJohn Baldwin DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
270f5fd5611SRuslan Ermilov MODULE_DEPEND(lpbb, ppbus, 1, 1, 1);
271c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
272c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1);
273