xref: /freebsd/sys/dev/ppbus/lpbb.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
1f7ce69c9SNicolas Souchu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu, Marc Bouget
5f7ce69c9SNicolas Souchu  * All rights reserved.
6f7ce69c9SNicolas Souchu  *
7f7ce69c9SNicolas Souchu  * Redistribution and use in source and binary forms, with or without
8f7ce69c9SNicolas Souchu  * modification, are permitted provided that the following conditions
9f7ce69c9SNicolas Souchu  * are met:
10f7ce69c9SNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
11f7ce69c9SNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
12f7ce69c9SNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
13f7ce69c9SNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
14f7ce69c9SNicolas Souchu  *    documentation and/or other materials provided with the distribution.
15f7ce69c9SNicolas Souchu  *
16f7ce69c9SNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f7ce69c9SNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f7ce69c9SNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f7ce69c9SNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f7ce69c9SNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f7ce69c9SNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f7ce69c9SNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f7ce69c9SNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f7ce69c9SNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f7ce69c9SNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f7ce69c9SNicolas Souchu  * SUCH DAMAGE.
27f7ce69c9SNicolas Souchu  *
28f7ce69c9SNicolas Souchu  *
29f7ce69c9SNicolas Souchu  */
30f7ce69c9SNicolas Souchu 
31aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
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 #include <dev/ppbus/ppbconf.h>
480f210c92SNicolas Souchu #include "ppbus_if.h"
490f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h>
50f7ce69c9SNicolas Souchu 
51f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h>
52f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h>
53f7ce69c9SNicolas Souchu 
54f7ce69c9SNicolas Souchu #include "iicbb_if.h"
55f7ce69c9SNicolas Souchu 
560f210c92SNicolas Souchu static int lpbb_detect(device_t dev);
57f7ce69c9SNicolas Souchu 
580f063508SPeter Wemm static void
590f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent)
600f063508SPeter Wemm {
61f7ce69c9SNicolas Souchu 
62a5c7e3bbSGuido van Rooij 	device_t dev;
63a5c7e3bbSGuido van Rooij 
64ae6b868aSJohn Baldwin 	dev = device_find_child(parent, "lpbb", -1);
65a5c7e3bbSGuido van Rooij 	if (!dev)
66*a05a6804SWarner Losh 		BUS_ADD_CHILD(parent, 0, "lpbb", DEVICE_UNIT_ANY);
670f063508SPeter Wemm }
68f7ce69c9SNicolas Souchu 
69f7ce69c9SNicolas Souchu static int
70f7ce69c9SNicolas Souchu lpbb_probe(device_t dev)
71f7ce69c9SNicolas Souchu {
72f7ce69c9SNicolas Souchu 
730f063508SPeter Wemm 	/* Perhaps call this during identify instead? */
740f210c92SNicolas Souchu 	if (!lpbb_detect(dev))
750f210c92SNicolas Souchu 		return (ENXIO);
76f7ce69c9SNicolas Souchu 
770f063508SPeter Wemm 	device_set_desc(dev, "Parallel I2C bit-banging interface");
780f063508SPeter Wemm 
79f7ce69c9SNicolas Souchu 	return (0);
80f7ce69c9SNicolas Souchu }
81f7ce69c9SNicolas Souchu 
82f7ce69c9SNicolas Souchu static int
83f7ce69c9SNicolas Souchu lpbb_attach(device_t dev)
84f7ce69c9SNicolas Souchu {
85c17d4340SNicolas Souchu 	device_t bitbang;
86f7ce69c9SNicolas Souchu 
87f7ce69c9SNicolas Souchu 	/* add generic bit-banging code */
885b56413dSWarner Losh 	bitbang = device_add_child(dev, "iicbb", DEVICE_UNIT_ANY);
89f7ce69c9SNicolas Souchu 	device_probe_and_attach(bitbang);
90f7ce69c9SNicolas Souchu 
91f7ce69c9SNicolas Souchu 	return (0);
92f7ce69c9SNicolas Souchu }
93f7ce69c9SNicolas Souchu 
94f7ce69c9SNicolas Souchu static int
9534a53f3fSWarner Losh lpbb_callback(device_t dev, int index, caddr_t data)
96f7ce69c9SNicolas Souchu {
970f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
98f7ce69c9SNicolas Souchu 	int error = 0;
99f7ce69c9SNicolas Souchu 	int how;
100f7ce69c9SNicolas Souchu 
101f7ce69c9SNicolas Souchu 	switch (index) {
102f7ce69c9SNicolas Souchu 	case IIC_REQUEST_BUS:
103f7ce69c9SNicolas Souchu 		/* request the ppbus */
104f7ce69c9SNicolas Souchu 		how = *(int *)data;
1052067d312SJohn Baldwin 		ppb_lock(ppbus);
1060f210c92SNicolas Souchu 		error = ppb_request_bus(ppbus, dev, how);
1072067d312SJohn Baldwin 		ppb_unlock(ppbus);
108f7ce69c9SNicolas Souchu 		break;
109f7ce69c9SNicolas Souchu 
110f7ce69c9SNicolas Souchu 	case IIC_RELEASE_BUS:
111f7ce69c9SNicolas Souchu 		/* release the ppbus */
1122067d312SJohn Baldwin 		ppb_lock(ppbus);
1130f210c92SNicolas Souchu 		error = ppb_release_bus(ppbus, dev);
1142067d312SJohn Baldwin 		ppb_unlock(ppbus);
115f7ce69c9SNicolas Souchu 		break;
116f7ce69c9SNicolas Souchu 
117f7ce69c9SNicolas Souchu 	default:
118f7ce69c9SNicolas Souchu 		error = EINVAL;
119f7ce69c9SNicolas Souchu 	}
120f7ce69c9SNicolas Souchu 
121f7ce69c9SNicolas Souchu 	return (error);
122f7ce69c9SNicolas Souchu }
123f7ce69c9SNicolas Souchu 
124f7ce69c9SNicolas Souchu #define SDA_out 0x80
125f7ce69c9SNicolas Souchu #define SCL_out 0x08
126f7ce69c9SNicolas Souchu #define SDA_in  0x80
127f7ce69c9SNicolas Souchu #define SCL_in  0x08
128f7ce69c9SNicolas Souchu #define ALIM    0x20
129f7ce69c9SNicolas Souchu #define I2CKEY  0x50
130f7ce69c9SNicolas Souchu 
1312067d312SJohn Baldwin /* Reset bus by setting SDA first and then SCL. */
1322067d312SJohn Baldwin static void
1332067d312SJohn Baldwin lpbb_reset_bus(device_t dev)
1342067d312SJohn Baldwin {
1352067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
1362067d312SJohn Baldwin 
1372067d312SJohn Baldwin 	ppb_assert_locked(ppbus);
1382067d312SJohn Baldwin 	ppb_wdtr(ppbus, (u_char)~SDA_out);
1392067d312SJohn Baldwin 	ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
1402067d312SJohn Baldwin }
1412067d312SJohn Baldwin 
14213e3657bSJohn Baldwin static int
14313e3657bSJohn Baldwin lpbb_getscl(device_t dev)
144f7ce69c9SNicolas Souchu {
1452067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
14613e3657bSJohn Baldwin 	int rval;
14713e3657bSJohn Baldwin 
1482067d312SJohn Baldwin 	ppb_lock(ppbus);
1492067d312SJohn Baldwin 	rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in);
1502067d312SJohn Baldwin 	ppb_unlock(ppbus);
15113e3657bSJohn Baldwin 	return (rval);
152f7ce69c9SNicolas Souchu }
153f7ce69c9SNicolas Souchu 
15413e3657bSJohn Baldwin static int
15513e3657bSJohn Baldwin lpbb_getsda(device_t dev)
156f7ce69c9SNicolas Souchu {
1572067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
15813e3657bSJohn Baldwin 	int rval;
15913e3657bSJohn Baldwin 
1602067d312SJohn Baldwin 	ppb_lock(ppbus);
1612067d312SJohn Baldwin 	rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in);
1622067d312SJohn Baldwin 	ppb_unlock(ppbus);
16313e3657bSJohn Baldwin 	return (rval);
164c17d4340SNicolas Souchu }
165c17d4340SNicolas Souchu 
16613e3657bSJohn Baldwin static void
16734a53f3fSWarner Losh lpbb_setsda(device_t dev, int val)
168c17d4340SNicolas Souchu {
169c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
170c17d4340SNicolas Souchu 
1712067d312SJohn Baldwin 	ppb_lock(ppbus);
172f7ce69c9SNicolas Souchu 	if (val == 0)
1730f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)SDA_out);
174f7ce69c9SNicolas Souchu 	else
1750f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)~SDA_out);
1762067d312SJohn Baldwin 	ppb_unlock(ppbus);
177f7ce69c9SNicolas Souchu }
178f7ce69c9SNicolas Souchu 
17913e3657bSJohn Baldwin static void
18034a53f3fSWarner Losh lpbb_setscl(device_t dev, int val)
181f7ce69c9SNicolas Souchu {
182c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
183c17d4340SNicolas Souchu 
1842067d312SJohn Baldwin 	ppb_lock(ppbus);
185f7ce69c9SNicolas Souchu 	if (val == 0)
1860f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out));
187f7ce69c9SNicolas Souchu 	else
1880f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
1892067d312SJohn Baldwin 	ppb_unlock(ppbus);
190f7ce69c9SNicolas Souchu }
191f7ce69c9SNicolas Souchu 
19213e3657bSJohn Baldwin static int
19313e3657bSJohn Baldwin lpbb_detect(device_t dev)
194f7ce69c9SNicolas Souchu {
1950f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
1960f210c92SNicolas Souchu 
1972067d312SJohn Baldwin 	ppb_lock(ppbus);
1980f210c92SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
1992067d312SJohn Baldwin 		ppb_unlock(ppbus);
2000f210c92SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
201f7ce69c9SNicolas Souchu 		return (0);
202f7ce69c9SNicolas Souchu 	}
203f7ce69c9SNicolas Souchu 
2042067d312SJohn Baldwin 	lpbb_reset_bus(dev);
205f7ce69c9SNicolas Souchu 
2060f210c92SNicolas Souchu 	if ((ppb_rstr(ppbus) & I2CKEY) ||
2070f210c92SNicolas Souchu 		((ppb_rstr(ppbus) & ALIM) != ALIM)) {
2080f210c92SNicolas Souchu 		ppb_release_bus(ppbus, dev);
2092067d312SJohn Baldwin 		ppb_unlock(ppbus);
210f7ce69c9SNicolas Souchu 		return (0);
211a6530aceSNicolas Souchu 	}
212f7ce69c9SNicolas Souchu 
2130f210c92SNicolas Souchu 	ppb_release_bus(ppbus, dev);
2142067d312SJohn Baldwin 	ppb_unlock(ppbus);
215f7ce69c9SNicolas Souchu 
216f7ce69c9SNicolas Souchu 	return (1);
217f7ce69c9SNicolas Souchu }
218f7ce69c9SNicolas Souchu 
219f7ce69c9SNicolas Souchu static int
220f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
221f7ce69c9SNicolas Souchu {
2220f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
223f7ce69c9SNicolas Souchu 
2242067d312SJohn Baldwin 	ppb_lock(ppbus);
225c17d4340SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
2262067d312SJohn Baldwin 		ppb_unlock(ppbus);
227c17d4340SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
228c17d4340SNicolas Souchu 		return (0);
229c17d4340SNicolas Souchu 	}
230c17d4340SNicolas Souchu 
2312067d312SJohn Baldwin 	lpbb_reset_bus(dev);
232c17d4340SNicolas Souchu 
233c17d4340SNicolas Souchu 	ppb_release_bus(ppbus, dev);
2342067d312SJohn Baldwin 	ppb_unlock(ppbus);
235f7ce69c9SNicolas Souchu 
236f7ce69c9SNicolas Souchu 	return (IIC_ENOADDR);
237f7ce69c9SNicolas Souchu }
238f7ce69c9SNicolas Souchu 
2390f063508SPeter Wemm static device_method_t lpbb_methods[] = {
2400f063508SPeter Wemm 	/* device interface */
2410f063508SPeter Wemm 	DEVMETHOD(device_identify,	lpbb_identify),
2420f063508SPeter Wemm 	DEVMETHOD(device_probe,		lpbb_probe),
2430f063508SPeter Wemm 	DEVMETHOD(device_attach,	lpbb_attach),
2440f063508SPeter Wemm 
2450f063508SPeter Wemm 	/* iicbb interface */
2460f063508SPeter Wemm 	DEVMETHOD(iicbb_callback,	lpbb_callback),
247c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setsda,		lpbb_setsda),
248c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setscl,		lpbb_setscl),
249c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getsda,		lpbb_getsda),
250c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getscl,		lpbb_getscl),
2510f063508SPeter Wemm 	DEVMETHOD(iicbb_reset,		lpbb_reset),
2520f063508SPeter Wemm 
2534b7ec270SMarius Strobl 	DEVMETHOD_END
2540f063508SPeter Wemm };
2550f063508SPeter Wemm 
2560f063508SPeter Wemm static driver_t lpbb_driver = {
2570f063508SPeter Wemm 	"lpbb",
2580f063508SPeter Wemm 	lpbb_methods,
2590f063508SPeter Wemm 	1,
2600f063508SPeter Wemm };
2610f063508SPeter Wemm 
2626d588090SJohn Baldwin DRIVER_MODULE(lpbb, ppbus, lpbb_driver, 0, 0);
2638c13dd83SJohn Baldwin DRIVER_MODULE(iicbb, lpbb, iicbb_driver, 0, 0);
264f5fd5611SRuslan Ermilov MODULE_DEPEND(lpbb, ppbus, 1, 1, 1);
265c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
266c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1);
267