lpbb.c revision 94154
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 * $FreeBSD: head/sys/dev/ppbus/lpbb.c 94154 2002-04-07 22:06:20Z ticso $
27 *
28 */
29
30/*
31 * I2C Bit-Banging over parallel port
32 *
33 * See the Official Philips interface description in lpbb(4)
34 */
35
36#include <sys/param.h>
37#include <sys/kernel.h>
38#include <sys/systm.h>
39#include <sys/module.h>
40#include <sys/bus.h>
41#include <sys/uio.h>
42
43
44#include <dev/ppbus/ppbconf.h>
45#include "ppbus_if.h"
46#include <dev/ppbus/ppbio.h>
47
48#include <dev/iicbus/iiconf.h>
49#include <dev/iicbus/iicbus.h>
50
51#include "iicbb_if.h"
52
53static int lpbb_detect(device_t dev);
54
55static void
56lpbb_identify(driver_t *driver, device_t parent)
57{
58
59	BUS_ADD_CHILD(parent, 0, "lpbb", -1);
60}
61
62static int
63lpbb_probe(device_t dev)
64{
65
66	/* Perhaps call this during identify instead? */
67	if (!lpbb_detect(dev))
68		return (ENXIO);
69
70	device_set_desc(dev, "Parallel I2C bit-banging interface");
71
72	return (0);
73}
74
75static int
76lpbb_attach(device_t dev)
77{
78	device_t bitbang;
79
80	/* add generic bit-banging code */
81	bitbang = device_add_child(dev, "iicbb", -1);
82	device_probe_and_attach(bitbang);
83
84	return (0);
85}
86
87static int
88lpbb_callback(device_t dev, int index, caddr_t *data)
89{
90	device_t ppbus = device_get_parent(dev);
91	int error = 0;
92	int how;
93
94	switch (index) {
95	case IIC_REQUEST_BUS:
96		/* request the ppbus */
97		how = *(int *)data;
98		error = ppb_request_bus(ppbus, dev, how);
99		break;
100
101	case IIC_RELEASE_BUS:
102		/* release the ppbus */
103		error = ppb_release_bus(ppbus, dev);
104		break;
105
106	default:
107		error = EINVAL;
108	}
109
110	return (error);
111}
112
113#define SDA_out 0x80
114#define SCL_out 0x08
115#define SDA_in  0x80
116#define SCL_in  0x08
117#define ALIM    0x20
118#define I2CKEY  0x50
119
120static int lpbb_getscl(device_t dev)
121{
122	return ((ppb_rstr(device_get_parent(dev)) & SCL_in) == SCL_in);
123}
124
125static int lpbb_getsda(device_t dev)
126{
127	return ((ppb_rstr(device_get_parent(dev)) & SDA_in) == SDA_in);
128}
129
130static void lpbb_setsda(device_t dev, char val)
131{
132	device_t ppbus = device_get_parent(dev);
133
134	if(val==0)
135		ppb_wdtr(ppbus, (u_char)SDA_out);
136	else
137		ppb_wdtr(ppbus, (u_char)~SDA_out);
138}
139
140static void lpbb_setscl(device_t dev, unsigned char val)
141{
142	device_t ppbus = device_get_parent(dev);
143
144	if(val==0)
145		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)&~SCL_out));
146	else
147		ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)|SCL_out));
148}
149
150static int lpbb_detect(device_t dev)
151{
152	device_t ppbus = device_get_parent(dev);
153
154	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
155		device_printf(dev, "can't allocate ppbus\n");
156		return (0);
157	}
158
159	/* reset bus */
160	lpbb_setsda(dev, 1);
161	lpbb_setscl(dev, 1);
162
163	if ((ppb_rstr(ppbus) & I2CKEY) ||
164		((ppb_rstr(ppbus) & ALIM) != ALIM)) {
165
166		ppb_release_bus(ppbus, dev);
167		return (0);
168	}
169
170	ppb_release_bus(ppbus, dev);
171
172	return (1);
173}
174
175static int
176lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
177{
178	device_t ppbus = device_get_parent(dev);
179
180	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
181		device_printf(dev, "can't allocate ppbus\n");
182		return (0);
183	}
184
185	/* reset bus */
186	lpbb_setsda(dev, 1);
187	lpbb_setscl(dev, 1);
188
189	ppb_release_bus(ppbus, dev);
190
191	return (IIC_ENOADDR);
192}
193
194static devclass_t lpbb_devclass;
195
196static device_method_t lpbb_methods[] = {
197	/* device interface */
198	DEVMETHOD(device_identify,	lpbb_identify),
199	DEVMETHOD(device_probe,		lpbb_probe),
200	DEVMETHOD(device_attach,	lpbb_attach),
201
202	/* bus interface */
203	DEVMETHOD(bus_print_child,	bus_generic_print_child),
204
205	/* iicbb interface */
206	DEVMETHOD(iicbb_callback,	lpbb_callback),
207	DEVMETHOD(iicbb_setsda,		lpbb_setsda),
208	DEVMETHOD(iicbb_setscl,		lpbb_setscl),
209	DEVMETHOD(iicbb_getsda,		lpbb_getsda),
210	DEVMETHOD(iicbb_getscl,		lpbb_getscl),
211	DEVMETHOD(iicbb_reset,		lpbb_reset),
212
213	{ 0, 0 }
214};
215
216static driver_t lpbb_driver = {
217	"lpbb",
218	lpbb_methods,
219	1,
220};
221
222DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0);
223MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
224MODULE_VERSION(lpbb, 1);
225