xref: /freebsd/sys/dev/ppbus/lpbb.c (revision 6d5880906ecc768646ba7b0cbaba4babc27a44d8)
1f7ce69c9SNicolas Souchu /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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>
32aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
33aad970f1SDavid E. O'Brien 
34f7ce69c9SNicolas Souchu /*
35f7ce69c9SNicolas Souchu  * I2C Bit-Banging over parallel port
36f7ce69c9SNicolas Souchu  *
37f7ce69c9SNicolas Souchu  * See the Official Philips interface description in lpbb(4)
38f7ce69c9SNicolas Souchu  */
39f7ce69c9SNicolas Souchu 
40f7ce69c9SNicolas Souchu #include <sys/param.h>
41f7ce69c9SNicolas Souchu #include <sys/bus.h>
4213e3657bSJohn Baldwin #include <sys/lock.h>
4313e3657bSJohn Baldwin #include <sys/kernel.h>
4413e3657bSJohn Baldwin #include <sys/module.h>
4513e3657bSJohn Baldwin #include <sys/mutex.h>
4613e3657bSJohn Baldwin #include <sys/systm.h>
47f7ce69c9SNicolas Souchu #include <sys/uio.h>
48f7ce69c9SNicolas Souchu 
49f7ce69c9SNicolas Souchu #include <dev/ppbus/ppbconf.h>
500f210c92SNicolas Souchu #include "ppbus_if.h"
510f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h>
52f7ce69c9SNicolas Souchu 
53f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h>
54f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h>
55f7ce69c9SNicolas Souchu 
56f7ce69c9SNicolas Souchu #include "iicbb_if.h"
57f7ce69c9SNicolas Souchu 
580f210c92SNicolas Souchu static int lpbb_detect(device_t dev);
59f7ce69c9SNicolas Souchu 
600f063508SPeter Wemm static void
610f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent)
620f063508SPeter Wemm {
63f7ce69c9SNicolas Souchu 
64a5c7e3bbSGuido van Rooij 	device_t dev;
65a5c7e3bbSGuido van Rooij 
66ae6b868aSJohn Baldwin 	dev = device_find_child(parent, "lpbb", -1);
67a5c7e3bbSGuido van Rooij 	if (!dev)
68338cad62SBernd Walter 		BUS_ADD_CHILD(parent, 0, "lpbb", -1);
690f063508SPeter Wemm }
70f7ce69c9SNicolas Souchu 
71f7ce69c9SNicolas Souchu static int
72f7ce69c9SNicolas Souchu lpbb_probe(device_t dev)
73f7ce69c9SNicolas Souchu {
74f7ce69c9SNicolas Souchu 
750f063508SPeter Wemm 	/* Perhaps call this during identify instead? */
760f210c92SNicolas Souchu 	if (!lpbb_detect(dev))
770f210c92SNicolas Souchu 		return (ENXIO);
78f7ce69c9SNicolas Souchu 
790f063508SPeter Wemm 	device_set_desc(dev, "Parallel I2C bit-banging interface");
800f063508SPeter Wemm 
81f7ce69c9SNicolas Souchu 	return (0);
82f7ce69c9SNicolas Souchu }
83f7ce69c9SNicolas Souchu 
84f7ce69c9SNicolas Souchu static int
85f7ce69c9SNicolas Souchu lpbb_attach(device_t dev)
86f7ce69c9SNicolas Souchu {
87c17d4340SNicolas Souchu 	device_t bitbang;
88f7ce69c9SNicolas Souchu 
89f7ce69c9SNicolas Souchu 	/* add generic bit-banging code */
90fe0d4089SMatthew N. Dodd 	bitbang = device_add_child(dev, "iicbb", -1);
91f7ce69c9SNicolas Souchu 	device_probe_and_attach(bitbang);
92f7ce69c9SNicolas Souchu 
93f7ce69c9SNicolas Souchu 	return (0);
94f7ce69c9SNicolas Souchu }
95f7ce69c9SNicolas Souchu 
96f7ce69c9SNicolas Souchu static int
9734a53f3fSWarner Losh lpbb_callback(device_t dev, int index, caddr_t data)
98f7ce69c9SNicolas Souchu {
990f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
100f7ce69c9SNicolas Souchu 	int error = 0;
101f7ce69c9SNicolas Souchu 	int how;
102f7ce69c9SNicolas Souchu 
103f7ce69c9SNicolas Souchu 	switch (index) {
104f7ce69c9SNicolas Souchu 	case IIC_REQUEST_BUS:
105f7ce69c9SNicolas Souchu 		/* request the ppbus */
106f7ce69c9SNicolas Souchu 		how = *(int *)data;
1072067d312SJohn Baldwin 		ppb_lock(ppbus);
1080f210c92SNicolas Souchu 		error = ppb_request_bus(ppbus, dev, how);
1092067d312SJohn Baldwin 		ppb_unlock(ppbus);
110f7ce69c9SNicolas Souchu 		break;
111f7ce69c9SNicolas Souchu 
112f7ce69c9SNicolas Souchu 	case IIC_RELEASE_BUS:
113f7ce69c9SNicolas Souchu 		/* release the ppbus */
1142067d312SJohn Baldwin 		ppb_lock(ppbus);
1150f210c92SNicolas Souchu 		error = ppb_release_bus(ppbus, dev);
1162067d312SJohn Baldwin 		ppb_unlock(ppbus);
117f7ce69c9SNicolas Souchu 		break;
118f7ce69c9SNicolas Souchu 
119f7ce69c9SNicolas Souchu 	default:
120f7ce69c9SNicolas Souchu 		error = EINVAL;
121f7ce69c9SNicolas Souchu 	}
122f7ce69c9SNicolas Souchu 
123f7ce69c9SNicolas Souchu 	return (error);
124f7ce69c9SNicolas Souchu }
125f7ce69c9SNicolas Souchu 
126f7ce69c9SNicolas Souchu #define SDA_out 0x80
127f7ce69c9SNicolas Souchu #define SCL_out 0x08
128f7ce69c9SNicolas Souchu #define SDA_in  0x80
129f7ce69c9SNicolas Souchu #define SCL_in  0x08
130f7ce69c9SNicolas Souchu #define ALIM    0x20
131f7ce69c9SNicolas Souchu #define I2CKEY  0x50
132f7ce69c9SNicolas Souchu 
1332067d312SJohn Baldwin /* Reset bus by setting SDA first and then SCL. */
1342067d312SJohn Baldwin static void
1352067d312SJohn Baldwin lpbb_reset_bus(device_t dev)
1362067d312SJohn Baldwin {
1372067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
1382067d312SJohn Baldwin 
1392067d312SJohn Baldwin 	ppb_assert_locked(ppbus);
1402067d312SJohn Baldwin 	ppb_wdtr(ppbus, (u_char)~SDA_out);
1412067d312SJohn Baldwin 	ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
1422067d312SJohn Baldwin }
1432067d312SJohn Baldwin 
14413e3657bSJohn Baldwin static int
14513e3657bSJohn Baldwin lpbb_getscl(device_t dev)
146f7ce69c9SNicolas Souchu {
1472067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
14813e3657bSJohn Baldwin 	int rval;
14913e3657bSJohn Baldwin 
1502067d312SJohn Baldwin 	ppb_lock(ppbus);
1512067d312SJohn Baldwin 	rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in);
1522067d312SJohn Baldwin 	ppb_unlock(ppbus);
15313e3657bSJohn Baldwin 	return (rval);
154f7ce69c9SNicolas Souchu }
155f7ce69c9SNicolas Souchu 
15613e3657bSJohn Baldwin static int
15713e3657bSJohn Baldwin lpbb_getsda(device_t dev)
158f7ce69c9SNicolas Souchu {
1592067d312SJohn Baldwin 	device_t ppbus = device_get_parent(dev);
16013e3657bSJohn Baldwin 	int rval;
16113e3657bSJohn Baldwin 
1622067d312SJohn Baldwin 	ppb_lock(ppbus);
1632067d312SJohn Baldwin 	rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in);
1642067d312SJohn Baldwin 	ppb_unlock(ppbus);
16513e3657bSJohn Baldwin 	return (rval);
166c17d4340SNicolas Souchu }
167c17d4340SNicolas Souchu 
16813e3657bSJohn Baldwin static void
16934a53f3fSWarner Losh lpbb_setsda(device_t dev, int val)
170c17d4340SNicolas Souchu {
171c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
172c17d4340SNicolas Souchu 
1732067d312SJohn Baldwin 	ppb_lock(ppbus);
174f7ce69c9SNicolas Souchu 	if (val == 0)
1750f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)SDA_out);
176f7ce69c9SNicolas Souchu 	else
1770f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)~SDA_out);
1782067d312SJohn Baldwin 	ppb_unlock(ppbus);
179f7ce69c9SNicolas Souchu }
180f7ce69c9SNicolas Souchu 
18113e3657bSJohn Baldwin static void
18234a53f3fSWarner Losh lpbb_setscl(device_t dev, int val)
183f7ce69c9SNicolas Souchu {
184c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
185c17d4340SNicolas Souchu 
1862067d312SJohn Baldwin 	ppb_lock(ppbus);
187f7ce69c9SNicolas Souchu 	if (val == 0)
1880f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out));
189f7ce69c9SNicolas Souchu 	else
1900f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
1912067d312SJohn Baldwin 	ppb_unlock(ppbus);
192f7ce69c9SNicolas Souchu }
193f7ce69c9SNicolas Souchu 
19413e3657bSJohn Baldwin static int
19513e3657bSJohn Baldwin lpbb_detect(device_t dev)
196f7ce69c9SNicolas Souchu {
1970f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
1980f210c92SNicolas Souchu 
1992067d312SJohn Baldwin 	ppb_lock(ppbus);
2000f210c92SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
2012067d312SJohn Baldwin 		ppb_unlock(ppbus);
2020f210c92SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
203f7ce69c9SNicolas Souchu 		return (0);
204f7ce69c9SNicolas Souchu 	}
205f7ce69c9SNicolas Souchu 
2062067d312SJohn Baldwin 	lpbb_reset_bus(dev);
207f7ce69c9SNicolas Souchu 
2080f210c92SNicolas Souchu 	if ((ppb_rstr(ppbus) & I2CKEY) ||
2090f210c92SNicolas Souchu 		((ppb_rstr(ppbus) & ALIM) != ALIM)) {
2100f210c92SNicolas Souchu 		ppb_release_bus(ppbus, dev);
2112067d312SJohn Baldwin 		ppb_unlock(ppbus);
212f7ce69c9SNicolas Souchu 		return (0);
213a6530aceSNicolas Souchu 	}
214f7ce69c9SNicolas Souchu 
2150f210c92SNicolas Souchu 	ppb_release_bus(ppbus, dev);
2162067d312SJohn Baldwin 	ppb_unlock(ppbus);
217f7ce69c9SNicolas Souchu 
218f7ce69c9SNicolas Souchu 	return (1);
219f7ce69c9SNicolas Souchu }
220f7ce69c9SNicolas Souchu 
221f7ce69c9SNicolas Souchu static int
222f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
223f7ce69c9SNicolas Souchu {
2240f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
225f7ce69c9SNicolas Souchu 
2262067d312SJohn Baldwin 	ppb_lock(ppbus);
227c17d4340SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
2282067d312SJohn Baldwin 		ppb_unlock(ppbus);
229c17d4340SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
230c17d4340SNicolas Souchu 		return (0);
231c17d4340SNicolas Souchu 	}
232c17d4340SNicolas Souchu 
2332067d312SJohn Baldwin 	lpbb_reset_bus(dev);
234c17d4340SNicolas Souchu 
235c17d4340SNicolas Souchu 	ppb_release_bus(ppbus, dev);
2362067d312SJohn Baldwin 	ppb_unlock(ppbus);
237f7ce69c9SNicolas Souchu 
238f7ce69c9SNicolas Souchu 	return (IIC_ENOADDR);
239f7ce69c9SNicolas Souchu }
240f7ce69c9SNicolas Souchu 
2410f063508SPeter Wemm static device_method_t lpbb_methods[] = {
2420f063508SPeter Wemm 	/* device interface */
2430f063508SPeter Wemm 	DEVMETHOD(device_identify,	lpbb_identify),
2440f063508SPeter Wemm 	DEVMETHOD(device_probe,		lpbb_probe),
2450f063508SPeter Wemm 	DEVMETHOD(device_attach,	lpbb_attach),
2460f063508SPeter Wemm 
2470f063508SPeter Wemm 	/* iicbb interface */
2480f063508SPeter Wemm 	DEVMETHOD(iicbb_callback,	lpbb_callback),
249c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setsda,		lpbb_setsda),
250c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setscl,		lpbb_setscl),
251c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getsda,		lpbb_getsda),
252c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getscl,		lpbb_getscl),
2530f063508SPeter Wemm 	DEVMETHOD(iicbb_reset,		lpbb_reset),
2540f063508SPeter Wemm 
2554b7ec270SMarius Strobl 	DEVMETHOD_END
2560f063508SPeter Wemm };
2570f063508SPeter Wemm 
2580f063508SPeter Wemm static driver_t lpbb_driver = {
2590f063508SPeter Wemm 	"lpbb",
2600f063508SPeter Wemm 	lpbb_methods,
2610f063508SPeter Wemm 	1,
2620f063508SPeter Wemm };
2630f063508SPeter Wemm 
264*6d588090SJohn Baldwin DRIVER_MODULE(lpbb, ppbus, lpbb_driver, 0, 0);
2658c13dd83SJohn Baldwin DRIVER_MODULE(iicbb, lpbb, iicbb_driver, 0, 0);
266f5fd5611SRuslan Ermilov MODULE_DEPEND(lpbb, ppbus, 1, 1, 1);
267c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
268c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1);
269