Deleted Added
sdiff udiff text old ( 46743 ) new ( 49195 )
full compact
1/*-
2 * Copyright (c) 1998 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 * $Id: lpbb.c,v 1.5 1999/05/08 21:59:06 dfr Exp $
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/conf.h>
42#include <sys/buf.h>
43#include <sys/uio.h>
44#include <sys/malloc.h>
45
46#include <machine/clock.h>
47
48#include <dev/ppbus/ppbconf.h>
49
50#include <dev/iicbus/iiconf.h>
51#include <dev/iicbus/iicbus.h>
52
53#include "iicbb_if.h"
54
55/* iicbus softc */
56struct lpbb_softc {
57
58 struct ppb_device lpbb_dev;
59};
60
61static int lpbb_detect(struct lpbb_softc *);
62
63static int lpbb_probe(device_t);
64static int lpbb_attach(device_t);
65
66static int lpbb_callback(device_t, int, caddr_t *);
67static void lpbb_setlines(device_t, int, int);
68static int lpbb_getdataline(device_t);
69static int lpbb_reset(device_t, u_char, u_char, u_char *);
70
71static devclass_t lpbb_devclass;
72
73static device_method_t lpbb_methods[] = {
74 /* device interface */
75 DEVMETHOD(device_probe, lpbb_probe),
76 DEVMETHOD(device_attach, lpbb_attach),
77
78 /* bus interface */
79 DEVMETHOD(bus_print_child, bus_generic_print_child),
80
81 /* iicbb interface */
82 DEVMETHOD(iicbb_callback, lpbb_callback),
83 DEVMETHOD(iicbb_setlines, lpbb_setlines),
84 DEVMETHOD(iicbb_getdataline, lpbb_getdataline),
85 DEVMETHOD(iicbb_reset, lpbb_reset),
86
87 { 0, 0 }
88};
89
90static driver_t lpbb_driver = {
91 "lpbb",
92 lpbb_methods,
93 sizeof(struct lpbb_softc),
94};
95
96/*
97 * Make ourselves visible as a ppbus driver
98 */
99static struct ppb_device *lpbb_ppb_probe(struct ppb_data *ppb);
100static int lpbb_ppb_attach(struct ppb_device *dev);
101
102#define MAXLPBB 8 /* XXX not much better! */
103static struct lpbb_softc *lpbbdata[MAXLPBB];
104static int nlpbb = 0;
105
106#ifdef KERNEL
107
108static struct ppb_driver lpbbdriver = {
109 lpbb_ppb_probe, lpbb_ppb_attach, "lpbb"
110};
111DATA_SET(ppbdriver_set, lpbbdriver);
112
113#endif /* KERNEL */
114
115static int
116lpbb_probe(device_t dev)
117{
118 struct lpbb_softc *sc = lpbbdata[device_get_unit(dev)];
119 struct lpbb_softc *scdst = (struct lpbb_softc *)device_get_softc(dev);
120
121 /* XXX copy softc. Yet, ppbus device is sc->lpbb_dev, but will be
122 * dev->parent when ppbus will be ported to the new bus architecture */
123 bcopy(sc, scdst, sizeof(struct lpbb_softc));
124
125 device_set_desc(dev, "parallel I2C bit-banging interface");
126
127 /* probe done by ppbus initialization */
128 return (0);
129}
130
131static int
132lpbb_attach(device_t dev)
133{
134 device_t bitbang, iicbus;
135
136 /* add generic bit-banging code */
137 bitbang = device_add_child(dev, "iicbb", -1, NULL);
138
139 /* add the iicbus to the tree */
140 iicbus = iicbus_alloc_bus(bitbang);
141
142 device_probe_and_attach(bitbang);
143
144 /* XXX should be in iicbb_attach! */
145 device_probe_and_attach(iicbus);
146
147 return (0);
148}
149
150/*
151 * lppbb_ppb_probe()
152 */
153static struct ppb_device *
154lpbb_ppb_probe(struct ppb_data *ppb)
155{
156 struct lpbb_softc *sc;
157
158 sc = (struct lpbb_softc *) malloc(sizeof(struct lpbb_softc),
159 M_TEMP, M_NOWAIT);
160 if (!sc) {
161 printf("lpbb: cannot malloc!\n");
162 return (0);
163 }
164 bzero(sc, sizeof(struct lpbb_softc));
165
166 lpbbdata[nlpbb] = sc;
167
168 /*
169 * ppbus dependent initialisation.
170 */
171 sc->lpbb_dev.id_unit = nlpbb;
172 sc->lpbb_dev.name = lpbbdriver.name;
173 sc->lpbb_dev.ppb = ppb;
174 sc->lpbb_dev.intr = 0;
175
176 if (!lpbb_detect(sc)) {
177 free(sc, M_TEMP);
178 return (NULL);
179 }
180
181 /* Ok, go to next device on next probe */
182 nlpbb ++;
183
184 /* XXX wrong according to new bus architecture. ppbus needs to be
185 * ported
186 */
187 return (&sc->lpbb_dev);
188}
189
190static int
191lpbb_ppb_attach(struct ppb_device *dev)
192{
193 /* add the parallel port I2C interface to the bus tree */
194 if (!device_add_child(root_bus, "lpbb", dev->id_unit, NULL))
195 return (0);
196
197 return (1);
198}
199
200static int
201lpbb_callback(device_t dev, int index, caddr_t *data)
202{
203 struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev);
204 int error = 0;
205 int how;
206
207 switch (index) {
208 case IIC_REQUEST_BUS:
209 /* request the ppbus */
210 how = *(int *)data;
211 error = ppb_request_bus(&sc->lpbb_dev, how);
212 break;
213
214 case IIC_RELEASE_BUS:
215 /* release the ppbus */
216 error = ppb_release_bus(&sc->lpbb_dev);
217 break;
218
219 default:
220 error = EINVAL;
221 }
222
223 return (error);
224}
225
226#define SDA_out 0x80
227#define SCL_out 0x08
228#define SDA_in 0x80
229#define SCL_in 0x08
230#define ALIM 0x20
231#define I2CKEY 0x50
232
233static int getSDA(struct lpbb_softc *sc)
234{
235if((ppb_rstr(&sc->lpbb_dev)&SDA_in)==SDA_in)
236 return 1;
237else
238 return 0;
239}
240
241static void setSDA(struct lpbb_softc *sc, char val)
242{
243if(val==0)
244 ppb_wdtr(&sc->lpbb_dev, (u_char)SDA_out);
245else
246 ppb_wdtr(&sc->lpbb_dev, (u_char)~SDA_out);
247}
248
249static void setSCL(struct lpbb_softc *sc, unsigned char val)
250{
251if(val==0)
252 ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)&~SCL_out));
253else
254 ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)|SCL_out));
255}
256
257static int lpbb_detect(struct lpbb_softc *sc)
258{
259 if (ppb_request_bus(&sc->lpbb_dev, PPB_DONTWAIT)) {
260 printf("lpbb: can't allocate ppbus\n");
261 return (0);
262 }
263
264 /* reset bus */
265 setSDA(sc, 1);
266 setSCL(sc, 1);
267
268 if ((ppb_rstr(&sc->lpbb_dev) & I2CKEY) ||
269 ((ppb_rstr(&sc->lpbb_dev) & ALIM) != ALIM)) {
270
271 ppb_release_bus(&sc->lpbb_dev);
272 return (0);
273 }
274
275 ppb_release_bus(&sc->lpbb_dev);
276
277 return (1);
278}
279
280static int
281lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
282{
283 struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev);
284
285 /* reset bus */
286 setSDA(sc, 1);
287 setSCL(sc, 1);
288
289 return (IIC_ENOADDR);
290}
291
292static void
293lpbb_setlines(device_t dev, int ctrl, int data)
294{
295 struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev);
296
297 setSCL(sc, ctrl);
298 setSDA(sc, data);
299}
300
301static int
302lpbb_getdataline(device_t dev)
303{
304 struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev);
305
306 return (getSDA(sc));
307}
308
309DRIVER_MODULE(lpbb, root, lpbb_driver, lpbb_devclass, 0, 0);