FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/spic.c
1 /* $NetBSD: spic.c,v 1.6 2006/11/16 01:32:52 christos Exp $ */
2
3 /*
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 /*
41 * The SPIC is used on some Sony Vaios to handle the jog dial and other
42 * peripherals.
43 * The protocol used by the SPIC seems to vary wildly among the different
44 * models, and I've found no documentation.
45 * This file handles the jog dial on the SRX77 model, and perhaps nothing
46 * else.
47 *
48 * The general way of talking to the SPIC was gleaned from the Linux and
49 * FreeBSD drivers. The hex numbers were taken from these drivers (they
50 * come from reverese engineering.)
51 *
52 * TODO:
53 * Make it handle more models.
54 * Figure out why the interrupt mode doesn't work.
55 */
56
57
58 #include <sys/cdefs.h>
59 __KERNEL_RCSID(0, "$NetBSD: spic.c,v 1.6 2006/11/16 01:32:52 christos Exp $");
60
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/device.h>
64 #include <sys/proc.h>
65 #include <sys/kernel.h>
66 #include <sys/callout.h>
67
68 #include <machine/bus.h>
69
70 #include <dev/sysmon/sysmonvar.h>
71
72 #include <dev/ic/spicvar.h>
73
74 #include <dev/wscons/wsconsio.h>
75 #include <dev/wscons/wsmousevar.h>
76
77 #define POLLRATE (hz/30)
78
79 /* Some hardware constants */
80 #define SPIC_PORT1 0
81 #define SPIC_PORT2 4
82
83 #ifdef SPIC_DEBUG
84 int spicdebug = 0;
85 #endif
86
87 static int spicerror = 0;
88
89 static int spic_enable(void *);
90 static void spic_disable(void *);
91 static int spic_ioctl(void *, u_long, caddr_t, int, struct lwp *);
92
93 static const struct wsmouse_accessops spic_accessops = {
94 spic_enable,
95 spic_ioctl,
96 spic_disable,
97 };
98
99 #define SPIC_COMMAND(quiet, command) do { \
100 unsigned int n = 10000; \
101 while (--n && (command)) \
102 delay(1); \
103 if (n == 0 && !(quiet)) { \
104 printf("spic0: command failed at line %d\n", __LINE__); \
105 spicerror++; \
106 } \
107 } while (0)
108
109 #if 0
110 #define INB(sc, p) (delay(100), printf("inb(%x)=%x\n", (uint)sc->sc_ioh+p, bus_space_read_1(sc->sc_iot, sc->sc_ioh, p)), delay(100), bus_space_read_1(sc->sc_iot, sc->sc_ioh, (p)))
111 #define OUTB(sc, v, p) do { delay(100); bus_space_write_1(sc->sc_iot, sc->sc_ioh, (p), (v)); printf("outb(%x, %x)\n", (uint)sc->sc_ioh+p, v); } while(0)
112 #else
113 #define INB(sc, p) (delay(100), bus_space_read_1(sc->sc_iot, sc->sc_ioh, (p)))
114 #define OUTB(sc, v, p) do { delay(100); bus_space_write_1(sc->sc_iot, sc->sc_ioh, (p), (v)); } while(0)
115 #endif
116
117 static u_int8_t
118 spic_call1(struct spic_softc *sc, u_int8_t dev)
119 {
120 u_int8_t v1, v2;
121
122 SPIC_COMMAND(0, INB(sc, SPIC_PORT2) & 2);
123 OUTB(sc, dev, SPIC_PORT2);
124 v1 = INB(sc, SPIC_PORT2);
125 v2 = INB(sc, SPIC_PORT1);
126 return v2;
127 }
128
129 static u_int8_t
130 spic_call2(struct spic_softc *sc, u_int8_t dev, u_int8_t fn)
131 {
132 u_int8_t v1;
133
134 SPIC_COMMAND(0, INB(sc, SPIC_PORT2) & 2);
135 OUTB(sc, dev, SPIC_PORT2);
136 SPIC_COMMAND(0, INB(sc, SPIC_PORT2) & 2);
137 OUTB(sc, fn, SPIC_PORT1);
138 v1 = INB(sc, SPIC_PORT1);
139 return v1;
140 }
141
142 /* Interrupt handler: some event is available */
143 int
144 spic_intr(void *v) {
145 struct spic_softc *sc = v;
146 u_int8_t v1, v2;
147 int dz, buttons;
148
149 v1 = INB(sc, SPIC_PORT1);
150 v2 = INB(sc, SPIC_PORT2);
151
152 /* Handle lid switch */
153 if (v2 == 0x30) {
154 switch (v1) {
155 case 0x50: /* opened */
156 sysmon_pswitch_event(&sc->sc_smpsw[SPIC_PSWITCH_LID],
157 PSWITCH_EVENT_RELEASED);
158 goto skip;
159 break;
160 case 0x51: /* closed */
161 sysmon_pswitch_event(&sc->sc_smpsw[SPIC_PSWITCH_LID],
162 PSWITCH_EVENT_PRESSED);
163 goto skip;
164 break;
165 default:
166 aprint_debug("%s: unknown lid event 0x%02x\n",
167 sc->sc_dev.dv_xname, v1);
168 goto skip;
169 break;
170 }
171 }
172
173 /* Handle suspend/hibernate buttons */
174 if (v2 == 0x20) {
175 switch (v1) {
176 case 0x10: /* suspend */
177 sysmon_pswitch_event(
178 &sc->sc_smpsw[SPIC_PSWITCH_SUSPEND],
179 PSWITCH_EVENT_PRESSED);
180 goto skip;
181 break;
182 case 0x1c: /* hibernate */
183 sysmon_pswitch_event(
184 &sc->sc_smpsw[SPIC_PSWITCH_HIBERNATE],
185 PSWITCH_EVENT_PRESSED);
186 goto skip;
187 break;
188 }
189 }
190
191 buttons = 0;
192 if (v1 & 0x40)
193 buttons |= 1 << 1;
194 if (v1 & 0x20)
195 buttons |= 1 << 5;
196 dz = v1 & 0x1f;
197 switch (dz) {
198 case 0:
199 case 1:
200 case 2:
201 case 3:
202 break;
203 case 0x1f:
204 case 0x1e:
205 case 0x1d:
206 dz -= 0x20;
207 break;
208 default:
209 printf("spic0: v1=0x%02x v2=0x%02x\n", v1, v2);
210 goto skip;
211 }
212
213 if (!sc->sc_enabled) {
214 /*printf("spic: not enabled\n");*/
215 goto skip;
216 }
217
218 if (dz != 0 || buttons != sc->sc_buttons) {
219 #ifdef SPIC_DEBUG
220 if (spicdebug)
221 printf("spic: but=0x%x dz=%d v1=0x%02x v2=0x%02x\n",
222 buttons, dz, v1, v2);
223 #endif
224 sc->sc_buttons = buttons;
225 if (sc->sc_wsmousedev != NULL) {
226 wsmouse_input(sc->sc_wsmousedev, buttons, 0, 0, dz, 0,
227 WSMOUSE_INPUT_DELTA);
228 }
229 }
230
231 skip:
232 spic_call2(sc, 0x81, 0xff); /* Clear event */
233 return (1);
234 }
235
236 static void
237 spictimeout(void *v)
238 {
239 struct spic_softc *sc = v;
240 int s;
241
242 if (spicerror >= 3)
243 return;
244
245 s = spltty();
246 spic_intr(v);
247 splx(s);
248 callout_reset(&sc->sc_poll, POLLRATE, spictimeout, sc);
249 }
250
251 void
252 spic_attach(struct spic_softc *sc)
253 {
254 struct wsmousedev_attach_args a;
255 int i, rv;
256
257 #ifdef SPIC_DEBUG
258 if (spicdebug)
259 printf("spic_attach %x %x\n", sc->sc_iot, (uint)sc->sc_ioh);
260 #endif
261
262 callout_init(&sc->sc_poll);
263
264 spic_call1(sc, 0x82);
265 spic_call2(sc, 0x81, 0xff);
266 spic_call1(sc, 0x92); /* or 0x82 */
267
268 a.accessops = &spic_accessops;
269 a.accesscookie = sc;
270 sc->sc_wsmousedev = config_found(&sc->sc_dev, &a, wsmousedevprint);
271
272 sc->sc_smpsw[SPIC_PSWITCH_LID].smpsw_name = "spiclid0";
273 sc->sc_smpsw[SPIC_PSWITCH_LID].smpsw_type = PSWITCH_TYPE_LID;
274 sc->sc_smpsw[SPIC_PSWITCH_SUSPEND].smpsw_name = "spicsuspend0";
275 sc->sc_smpsw[SPIC_PSWITCH_SUSPEND].smpsw_type = PSWITCH_TYPE_SLEEP;
276 sc->sc_smpsw[SPIC_PSWITCH_HIBERNATE].smpsw_name = "spichibernate0";
277 sc->sc_smpsw[SPIC_PSWITCH_HIBERNATE].smpsw_type = PSWITCH_TYPE_SLEEP;
278
279 for (i = 0; i < SPIC_NPSWITCH; i++) {
280 rv = sysmon_pswitch_register(&sc->sc_smpsw[i]);
281 if (rv != 0)
282 aprint_error("%s: unable to register %s with sysmon\n",
283 sc->sc_dev.dv_xname,
284 sc->sc_smpsw[i].smpsw_name);
285 }
286
287 callout_reset(&sc->sc_poll, POLLRATE, spictimeout, sc);
288
289 return;
290 }
291
292
293 static int
294 spic_enable(void *v)
295 {
296 struct spic_softc *sc = v;
297
298 if (sc->sc_enabled)
299 return (EBUSY);
300
301 sc->sc_enabled = 1;
302 sc->sc_buttons = 0;
303
304 #ifdef SPIC_DEBUG
305 if (spicdebug)
306 printf("spic_enable\n");
307 #endif
308
309 return (0);
310 }
311
312 static void
313 spic_disable(void *v)
314 {
315 struct spic_softc *sc = v;
316
317 #ifdef DIAGNOSTIC
318 if (!sc->sc_enabled) {
319 printf("spic_disable: not enabled\n");
320 return;
321 }
322 #endif
323
324 sc->sc_enabled = 0;
325
326 #ifdef SPIC_DEBUG
327 if (spicdebug)
328 printf("spic_disable\n");
329 #endif
330 }
331
332 static int
333 spic_ioctl(void *v, u_long cmd, caddr_t data,
334 int flag, struct lwp *l)
335 {
336 switch (cmd) {
337 case WSMOUSEIO_GTYPE:
338 /* XXX this is not really correct */
339 *(u_int *)data = WSMOUSE_TYPE_PS2;
340 return (0);
341 }
342
343 return (-1);
344 }
Cache object: 82472a51114472cebb4bb0c34a2d9c17
|