lpbb.c revision 331722
1/*-
2 * Copyright (c) 1998, 2001 Nicolas Souchu, Marc Bouget
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/dev/ppbus/lpbb.c 331722 2018-03-29 02:50:57Z eadler $");
31
32/*
33 * I2C Bit-Banging over parallel port
34 *
35 * See the Official Philips interface description in lpbb(4)
36 */
37
38#include <sys/param.h>
39#include <sys/bus.h>
40#include <sys/lock.h>
41#include <sys/kernel.h>
42#include <sys/module.h>
43#include <sys/mutex.h>
44#include <sys/systm.h>
45#include <sys/uio.h>
46
47
48#include <dev/ppbus/ppbconf.h>
49#include "ppbus_if.h"
50#include <dev/ppbus/ppbio.h>
51
52#include <dev/iicbus/iiconf.h>
53#include <dev/iicbus/iicbus.h>
54
55#include "iicbb_if.h"
56
57static int lpbb_detect(device_t dev);
58
59static void
60lpbb_identify(driver_t *driver, device_t parent)
61{
62
63	device_t dev;
64
65	dev = device_find_child(parent, "lpbb", -1);
66	if (!dev)
67		BUS_ADD_CHILD(parent, 0, "lpbb", -1);
68}
69
70static int
71lpbb_probe(device_t dev)
72{
73
74	/* Perhaps call this during identify instead? */
75	if (!lpbb_detect(dev))
76		return (ENXIO);
77
78	device_set_desc(dev, "Parallel I2C bit-banging interface");
79
80	return (0);
81}
82
83static int
84lpbb_attach(device_t dev)
85{
86	device_t bitbang;
87
88	/* add generic bit-banging code */
89	bitbang = device_add_child(dev, "iicbb", -1);
90	device_probe_and_attach(bitbang);
91
92	return (0);
93}
94
95static int
96lpbb_callback(device_t dev, int index, caddr_t data)
97{
98	device_t ppbus = device_get_parent(dev);
99	int error = 0;
100	int how;
101
102	switch (index) {
103	case IIC_REQUEST_BUS:
104		/* request the ppbus */
105		how = *(int *)data;
106		ppb_lock(ppbus);
107		error = ppb_request_bus(ppbus, dev, how);
108		ppb_unlock(ppbus);
109		break;
110
111	case IIC_RELEASE_BUS:
112		/* release the ppbus */
113		ppb_lock(ppbus);
114		error = ppb_release_bus(ppbus, dev);
115		ppb_unlock(ppbus);
116		break;
117
118	default:
119		error = EINVAL;
120	}
121
122	return (error);
123}
124
125#define SDA_out 0x80
126#define SCL_out 0x08
127#define SDA_in  0x80
128#define SCL_in  0x08
129#define ALIM    0x20
130#define I2CKEY  0x50
131
132/* Reset bus by setting SDA first and then SCL. */
133static void
134lpbb_reset_bus(device_t dev)
135{
136	device_t ppbus = device_get_parent(dev);
137
138	ppb_assert_locked(ppbus);
139	ppb_wdtr(ppbus, (u_char)~SDA_out);
140	ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
141}
142
143static int
144lpbb_getscl(device_t dev)
145{
146	device_t ppbus = device_get_parent(dev);
147	int rval;
148
149	ppb_lock(ppbus);
150	rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in);
151	ppb_unlock(ppbus);
152	return (rval);
153}
154
155static int
156lpbb_getsda(device_t dev)
157{
158	device_t ppbus = device_get_parent(dev);
159	int rval;
160
161	ppb_lock(ppbus);
162	rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in);
163	ppb_unlock(ppbus);
164	return (rval);
165}
166
167static void
168lpbb_setsda(device_t dev, int val)
169{
170	device_t ppbus = device_get_parent(dev);
171
172	ppb_lock(ppbus);
173	if (val == 0)
174		ppb_wdtr(ppbus, (u_char)SDA_out);
175	else
176		ppb_wdtr(ppbus, (u_char)~SDA_out);
177	ppb_unlock(ppbus);
178}
179
180static void
181lpbb_setscl(device_t dev, int val)
182{
183	device_t ppbus = device_get_parent(dev);
184
185	ppb_lock(ppbus);
186	if (val == 0)
187		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out));
188	else
189		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out));
190	ppb_unlock(ppbus);
191}
192
193static int
194lpbb_detect(device_t dev)
195{
196	device_t ppbus = device_get_parent(dev);
197
198	ppb_lock(ppbus);
199	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
200		ppb_unlock(ppbus);
201		device_printf(dev, "can't allocate ppbus\n");
202		return (0);
203	}
204
205	lpbb_reset_bus(dev);
206
207	if ((ppb_rstr(ppbus) & I2CKEY) ||
208		((ppb_rstr(ppbus) & ALIM) != ALIM)) {
209		ppb_release_bus(ppbus, dev);
210		ppb_unlock(ppbus);
211		return (0);
212	}
213
214	ppb_release_bus(ppbus, dev);
215	ppb_unlock(ppbus);
216
217	return (1);
218}
219
220static int
221lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
222{
223	device_t ppbus = device_get_parent(dev);
224
225	ppb_lock(ppbus);
226	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
227		ppb_unlock(ppbus);
228		device_printf(dev, "can't allocate ppbus\n");
229		return (0);
230	}
231
232	lpbb_reset_bus(dev);
233
234	ppb_release_bus(ppbus, dev);
235	ppb_unlock(ppbus);
236
237	return (IIC_ENOADDR);
238}
239
240static devclass_t lpbb_devclass;
241
242static device_method_t lpbb_methods[] = {
243	/* device interface */
244	DEVMETHOD(device_identify,	lpbb_identify),
245	DEVMETHOD(device_probe,		lpbb_probe),
246	DEVMETHOD(device_attach,	lpbb_attach),
247
248	/* iicbb interface */
249	DEVMETHOD(iicbb_callback,	lpbb_callback),
250	DEVMETHOD(iicbb_setsda,		lpbb_setsda),
251	DEVMETHOD(iicbb_setscl,		lpbb_setscl),
252	DEVMETHOD(iicbb_getsda,		lpbb_getsda),
253	DEVMETHOD(iicbb_getscl,		lpbb_getscl),
254	DEVMETHOD(iicbb_reset,		lpbb_reset),
255
256	DEVMETHOD_END
257};
258
259static driver_t lpbb_driver = {
260	"lpbb",
261	lpbb_methods,
262	1,
263};
264
265DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0);
266DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
267MODULE_DEPEND(lpbb, ppbus, 1, 1, 1);
268MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
269MODULE_VERSION(lpbb, 1);
270