xref: /freebsd/sys/dev/ppbus/lpbb.c (revision aad970f1fee9a2a3e5a0f880be9b87c6193b3bd1)
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/kernel.h>
40f7ce69c9SNicolas Souchu #include <sys/systm.h>
41f7ce69c9SNicolas Souchu #include <sys/module.h>
42f7ce69c9SNicolas Souchu #include <sys/bus.h>
43f7ce69c9SNicolas Souchu #include <sys/uio.h>
44f7ce69c9SNicolas Souchu 
45f7ce69c9SNicolas Souchu 
46f7ce69c9SNicolas Souchu #include <dev/ppbus/ppbconf.h>
470f210c92SNicolas Souchu #include "ppbus_if.h"
480f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h>
49f7ce69c9SNicolas Souchu 
50f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h>
51f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h>
52f7ce69c9SNicolas Souchu 
53f7ce69c9SNicolas Souchu #include "iicbb_if.h"
54f7ce69c9SNicolas Souchu 
550f210c92SNicolas Souchu static int lpbb_detect(device_t dev);
56f7ce69c9SNicolas Souchu 
570f063508SPeter Wemm static void
580f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent)
590f063508SPeter Wemm {
60f7ce69c9SNicolas Souchu 
61338cad62SBernd Walter 	BUS_ADD_CHILD(parent, 0, "lpbb", -1);
620f063508SPeter Wemm }
63f7ce69c9SNicolas Souchu 
64f7ce69c9SNicolas Souchu static int
65f7ce69c9SNicolas Souchu lpbb_probe(device_t dev)
66f7ce69c9SNicolas Souchu {
67f7ce69c9SNicolas Souchu 
680f063508SPeter Wemm 	/* Perhaps call this during identify instead? */
690f210c92SNicolas Souchu 	if (!lpbb_detect(dev))
700f210c92SNicolas Souchu 		return (ENXIO);
71f7ce69c9SNicolas Souchu 
720f063508SPeter Wemm 	device_set_desc(dev, "Parallel I2C bit-banging interface");
730f063508SPeter Wemm 
74f7ce69c9SNicolas Souchu 	return (0);
75f7ce69c9SNicolas Souchu }
76f7ce69c9SNicolas Souchu 
77f7ce69c9SNicolas Souchu static int
78f7ce69c9SNicolas Souchu lpbb_attach(device_t dev)
79f7ce69c9SNicolas Souchu {
80c17d4340SNicolas Souchu 	device_t bitbang;
81f7ce69c9SNicolas Souchu 
82f7ce69c9SNicolas Souchu 	/* add generic bit-banging code */
83fe0d4089SMatthew N. Dodd 	bitbang = device_add_child(dev, "iicbb", -1);
84f7ce69c9SNicolas Souchu 	device_probe_and_attach(bitbang);
85f7ce69c9SNicolas Souchu 
86f7ce69c9SNicolas Souchu 	return (0);
87f7ce69c9SNicolas Souchu }
88f7ce69c9SNicolas Souchu 
89f7ce69c9SNicolas Souchu static int
90f7ce69c9SNicolas Souchu lpbb_callback(device_t dev, int index, caddr_t *data)
91f7ce69c9SNicolas Souchu {
920f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
93f7ce69c9SNicolas Souchu 	int error = 0;
94f7ce69c9SNicolas Souchu 	int how;
95f7ce69c9SNicolas Souchu 
96f7ce69c9SNicolas Souchu 	switch (index) {
97f7ce69c9SNicolas Souchu 	case IIC_REQUEST_BUS:
98f7ce69c9SNicolas Souchu 		/* request the ppbus */
99f7ce69c9SNicolas Souchu 		how = *(int *)data;
1000f210c92SNicolas Souchu 		error = ppb_request_bus(ppbus, dev, how);
101f7ce69c9SNicolas Souchu 		break;
102f7ce69c9SNicolas Souchu 
103f7ce69c9SNicolas Souchu 	case IIC_RELEASE_BUS:
104f7ce69c9SNicolas Souchu 		/* release the ppbus */
1050f210c92SNicolas Souchu 		error = ppb_release_bus(ppbus, dev);
106f7ce69c9SNicolas Souchu 		break;
107f7ce69c9SNicolas Souchu 
108f7ce69c9SNicolas Souchu 	default:
109f7ce69c9SNicolas Souchu 		error = EINVAL;
110f7ce69c9SNicolas Souchu 	}
111f7ce69c9SNicolas Souchu 
112f7ce69c9SNicolas Souchu 	return (error);
113f7ce69c9SNicolas Souchu }
114f7ce69c9SNicolas Souchu 
115f7ce69c9SNicolas Souchu #define SDA_out 0x80
116f7ce69c9SNicolas Souchu #define SCL_out 0x08
117f7ce69c9SNicolas Souchu #define SDA_in  0x80
118f7ce69c9SNicolas Souchu #define SCL_in  0x08
119f7ce69c9SNicolas Souchu #define ALIM    0x20
120f7ce69c9SNicolas Souchu #define I2CKEY  0x50
121f7ce69c9SNicolas Souchu 
122c17d4340SNicolas Souchu static int lpbb_getscl(device_t dev)
123f7ce69c9SNicolas Souchu {
124c17d4340SNicolas Souchu 	return ((ppb_rstr(device_get_parent(dev)) & SCL_in) == SCL_in);
125f7ce69c9SNicolas Souchu }
126f7ce69c9SNicolas Souchu 
127c17d4340SNicolas Souchu static int lpbb_getsda(device_t dev)
128f7ce69c9SNicolas Souchu {
129c17d4340SNicolas Souchu 	return ((ppb_rstr(device_get_parent(dev)) & SDA_in) == SDA_in);
130c17d4340SNicolas Souchu }
131c17d4340SNicolas Souchu 
132c17d4340SNicolas Souchu static void lpbb_setsda(device_t dev, char val)
133c17d4340SNicolas Souchu {
134c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
135c17d4340SNicolas Souchu 
136f7ce69c9SNicolas Souchu 	if(val==0)
1370f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)SDA_out);
138f7ce69c9SNicolas Souchu 	else
1390f210c92SNicolas Souchu 		ppb_wdtr(ppbus, (u_char)~SDA_out);
140f7ce69c9SNicolas Souchu }
141f7ce69c9SNicolas Souchu 
142c17d4340SNicolas Souchu static void lpbb_setscl(device_t dev, unsigned char val)
143f7ce69c9SNicolas Souchu {
144c17d4340SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
145c17d4340SNicolas Souchu 
146f7ce69c9SNicolas Souchu 	if(val==0)
1470f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)&~SCL_out));
148f7ce69c9SNicolas Souchu 	else
1490f210c92SNicolas Souchu 		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)|SCL_out));
150f7ce69c9SNicolas Souchu }
151f7ce69c9SNicolas Souchu 
1520f210c92SNicolas Souchu static int lpbb_detect(device_t dev)
153f7ce69c9SNicolas Souchu {
1540f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
1550f210c92SNicolas Souchu 
1560f210c92SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
1570f210c92SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
158f7ce69c9SNicolas Souchu 		return (0);
159f7ce69c9SNicolas Souchu 	}
160f7ce69c9SNicolas Souchu 
161f7ce69c9SNicolas Souchu 	/* reset bus */
162c17d4340SNicolas Souchu 	lpbb_setsda(dev, 1);
163c17d4340SNicolas Souchu 	lpbb_setscl(dev, 1);
164f7ce69c9SNicolas Souchu 
1650f210c92SNicolas Souchu 	if ((ppb_rstr(ppbus) & I2CKEY) ||
1660f210c92SNicolas Souchu 		((ppb_rstr(ppbus) & ALIM) != ALIM)) {
167a6530aceSNicolas Souchu 
1680f210c92SNicolas Souchu 		ppb_release_bus(ppbus, dev);
169f7ce69c9SNicolas Souchu 		return (0);
170a6530aceSNicolas Souchu 	}
171f7ce69c9SNicolas Souchu 
1720f210c92SNicolas Souchu 	ppb_release_bus(ppbus, dev);
173f7ce69c9SNicolas Souchu 
174f7ce69c9SNicolas Souchu 	return (1);
175f7ce69c9SNicolas Souchu }
176f7ce69c9SNicolas Souchu 
177f7ce69c9SNicolas Souchu static int
178f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
179f7ce69c9SNicolas Souchu {
1800f210c92SNicolas Souchu 	device_t ppbus = device_get_parent(dev);
181f7ce69c9SNicolas Souchu 
182c17d4340SNicolas Souchu 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
183c17d4340SNicolas Souchu 		device_printf(dev, "can't allocate ppbus\n");
184c17d4340SNicolas Souchu 		return (0);
185c17d4340SNicolas Souchu 	}
186c17d4340SNicolas Souchu 
187f7ce69c9SNicolas Souchu 	/* reset bus */
188c17d4340SNicolas Souchu 	lpbb_setsda(dev, 1);
189c17d4340SNicolas Souchu 	lpbb_setscl(dev, 1);
190c17d4340SNicolas Souchu 
191c17d4340SNicolas Souchu 	ppb_release_bus(ppbus, dev);
192f7ce69c9SNicolas Souchu 
193f7ce69c9SNicolas Souchu 	return (IIC_ENOADDR);
194f7ce69c9SNicolas Souchu }
195f7ce69c9SNicolas Souchu 
1960f063508SPeter Wemm static devclass_t lpbb_devclass;
1970f063508SPeter Wemm 
1980f063508SPeter Wemm static device_method_t lpbb_methods[] = {
1990f063508SPeter Wemm 	/* device interface */
2000f063508SPeter Wemm 	DEVMETHOD(device_identify,	lpbb_identify),
2010f063508SPeter Wemm 	DEVMETHOD(device_probe,		lpbb_probe),
2020f063508SPeter Wemm 	DEVMETHOD(device_attach,	lpbb_attach),
2030f063508SPeter Wemm 
2040f063508SPeter Wemm 	/* bus interface */
2050f063508SPeter Wemm 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
2060f063508SPeter Wemm 
2070f063508SPeter Wemm 	/* iicbb interface */
2080f063508SPeter Wemm 	DEVMETHOD(iicbb_callback,	lpbb_callback),
209c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setsda,		lpbb_setsda),
210c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_setscl,		lpbb_setscl),
211c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getsda,		lpbb_getsda),
212c17d4340SNicolas Souchu 	DEVMETHOD(iicbb_getscl,		lpbb_getscl),
2130f063508SPeter Wemm 	DEVMETHOD(iicbb_reset,		lpbb_reset),
2140f063508SPeter Wemm 
2150f063508SPeter Wemm 	{ 0, 0 }
2160f063508SPeter Wemm };
2170f063508SPeter Wemm 
2180f063508SPeter Wemm static driver_t lpbb_driver = {
2190f063508SPeter Wemm 	"lpbb",
2200f063508SPeter Wemm 	lpbb_methods,
2210f063508SPeter Wemm 	1,
2220f063508SPeter Wemm };
2230f063508SPeter Wemm 
2240f210c92SNicolas Souchu DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0);
225c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
226c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1);
227