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