Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / dev / ic / spic.c
blob1d06ddfdbbbdff3acf4e5359458cf45946ee89e6
1 /* $NetBSD: spic.c,v 1.15 2008/05/04 16:13:35 xtraeme Exp $ */
3 /*
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology.
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.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 * The SPIC is used on some Sony Vaios to handle the jog dial and other
35 * peripherals.
36 * The protocol used by the SPIC seems to vary wildly among the different
37 * models, and I've found no documentation.
38 * This file handles the jog dial on the SRX77 model, and perhaps nothing
39 * else.
41 * The general way of talking to the SPIC was gleaned from the Linux and
42 * FreeBSD drivers. The hex numbers were taken from these drivers (they
43 * come from reverese engineering.)
45 * TODO:
46 * Make it handle more models.
47 * Figure out why the interrupt mode doesn't work.
51 #include <sys/cdefs.h>
52 __KERNEL_RCSID(0, "$NetBSD: spic.c,v 1.15 2008/05/04 16:13:35 xtraeme Exp $");
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/device.h>
57 #include <sys/proc.h>
58 #include <sys/kernel.h>
59 #include <sys/callout.h>
61 #include <sys/bus.h>
63 #include <dev/sysmon/sysmonvar.h>
65 #include <dev/ic/spicvar.h>
67 #include <dev/wscons/wsconsio.h>
68 #include <dev/wscons/wsmousevar.h>
70 #define SPIC_EVENT_BRIGHTNESS_DOWN 0x15
71 #define SPIC_EVENT_BRIGHTNESS_UP 0x16
73 #define POLLRATE (hz/30)
75 /* Some hardware constants */
76 #define SPIC_PORT1 0
77 #define SPIC_PORT2 4
79 #ifdef SPIC_DEBUG
80 int spicdebug = 0;
81 #endif
83 static int spicerror = 0;
85 static int spic_enable(void *);
86 static void spic_disable(void *);
87 static int spic_ioctl(void *, u_long, void *, int, struct lwp *);
89 static const struct wsmouse_accessops spic_accessops = {
90 spic_enable,
91 spic_ioctl,
92 spic_disable,
95 #define SPIC_COMMAND(quiet, command) do { \
96 unsigned int n = 10000; \
97 while (--n && (command)) \
98 delay(1); \
99 if (n == 0 && !(quiet)) { \
100 printf("spic0: command failed at line %d\n", __LINE__); \
101 spicerror++; \
103 } while (0)
105 #if 0
106 #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)))
107 #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)
108 #else
109 #define INB(sc, p) (delay(100), bus_space_read_1(sc->sc_iot, sc->sc_ioh, (p)))
110 #define OUTB(sc, v, p) do { delay(100); bus_space_write_1(sc->sc_iot, sc->sc_ioh, (p), (v)); } while(0)
111 #endif
113 static u_int8_t
114 spic_call1(struct spic_softc *sc, u_int8_t dev)
116 u_int8_t v1, v2;
118 SPIC_COMMAND(0, INB(sc, SPIC_PORT2) & 2);
119 OUTB(sc, dev, SPIC_PORT2);
120 v1 = INB(sc, SPIC_PORT2);
121 v2 = INB(sc, SPIC_PORT1);
122 return v2;
125 static u_int8_t
126 spic_call2(struct spic_softc *sc, u_int8_t dev, u_int8_t fn)
128 u_int8_t v1;
130 SPIC_COMMAND(0, INB(sc, SPIC_PORT2) & 2);
131 OUTB(sc, dev, SPIC_PORT2);
132 SPIC_COMMAND(0, INB(sc, SPIC_PORT2) & 2);
133 OUTB(sc, fn, SPIC_PORT1);
134 v1 = INB(sc, SPIC_PORT1);
135 return v1;
138 /* Interrupt handler: some event is available */
140 spic_intr(void *v) {
141 struct spic_softc *sc = v;
142 u_int8_t v1, v2;
143 int dz, buttons;
145 v1 = INB(sc, SPIC_PORT1);
146 v2 = INB(sc, SPIC_PORT2);
148 /* Handle lid switch */
149 if (v2 == 0x30) {
150 switch (v1) {
151 case 0x50: /* opened */
152 sysmon_pswitch_event(&sc->sc_smpsw[SPIC_PSWITCH_LID],
153 PSWITCH_EVENT_RELEASED);
154 goto skip;
155 break;
156 case 0x51: /* closed */
157 sysmon_pswitch_event(&sc->sc_smpsw[SPIC_PSWITCH_LID],
158 PSWITCH_EVENT_PRESSED);
159 goto skip;
160 break;
161 default:
162 aprint_debug_dev(sc->sc_dev, "unknown lid event 0x%02x\n", v1);
163 goto skip;
164 break;
168 /* Handle suspend/hibernate buttons */
169 if (v2 == 0x20) {
170 switch (v1) {
171 case 0x10: /* suspend */
172 sysmon_pswitch_event(
173 &sc->sc_smpsw[SPIC_PSWITCH_SUSPEND],
174 PSWITCH_EVENT_PRESSED);
175 goto skip;
176 break;
177 case 0x1c: /* hibernate */
178 sysmon_pswitch_event(
179 &sc->sc_smpsw[SPIC_PSWITCH_HIBERNATE],
180 PSWITCH_EVENT_PRESSED);
181 goto skip;
182 break;
186 buttons = 0;
187 if (v1 & 0x40)
188 buttons |= 1 << 1;
189 if (v1 & 0x20)
190 buttons |= 1 << 5;
191 dz = v1 & 0x1f;
192 switch (dz) {
193 case 0:
194 case 1:
195 case 2:
196 case 3:
197 break;
198 case 0x1f:
199 case 0x1e:
200 case 0x1d:
201 dz -= 0x20;
202 break;
203 case SPIC_EVENT_BRIGHTNESS_UP:
204 pmf_event_inject(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP);
205 break;
206 case SPIC_EVENT_BRIGHTNESS_DOWN:
207 pmf_event_inject(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN);
208 break;
209 default:
210 printf("spic0: v1=0x%02x v2=0x%02x\n", v1, v2);
211 goto skip;
214 if (!sc->sc_enabled) {
215 /*printf("spic: not enabled\n");*/
216 goto skip;
219 if (dz != 0 || buttons != sc->sc_buttons) {
220 #ifdef SPIC_DEBUG
221 if (spicdebug)
222 printf("spic: but=0x%x dz=%d v1=0x%02x v2=0x%02x\n",
223 buttons, dz, v1, v2);
224 #endif
225 sc->sc_buttons = buttons;
226 if (sc->sc_wsmousedev != NULL) {
227 wsmouse_input(sc->sc_wsmousedev, buttons, 0, 0, dz, 0,
228 WSMOUSE_INPUT_DELTA);
232 skip:
233 spic_call2(sc, 0x81, 0xff); /* Clear event */
234 return (1);
237 static void
238 spictimeout(void *v)
240 struct spic_softc *sc = v;
241 int s;
243 if (spicerror >= 3)
244 return;
246 s = spltty();
247 spic_intr(v);
248 splx(s);
249 callout_reset(&sc->sc_poll, POLLRATE, spictimeout, sc);
252 void
253 spic_attach(struct spic_softc *sc)
255 struct wsmousedev_attach_args a;
256 int i, rv;
258 #ifdef SPIC_DEBUG
259 if (spicdebug)
260 printf("spic_attach %x %x\n", sc->sc_iot, (uint)sc->sc_ioh);
261 #endif
263 callout_init(&sc->sc_poll, 0);
265 spic_call1(sc, 0x82);
266 spic_call2(sc, 0x81, 0xff);
267 spic_call1(sc, 0x92); /* or 0x82 */
269 a.accessops = &spic_accessops;
270 a.accesscookie = sc;
271 sc->sc_wsmousedev = config_found(sc->sc_dev, &a, wsmousedevprint);
273 sc->sc_smpsw[SPIC_PSWITCH_LID].smpsw_name = "spiclid0";
274 sc->sc_smpsw[SPIC_PSWITCH_LID].smpsw_type = PSWITCH_TYPE_LID;
275 sc->sc_smpsw[SPIC_PSWITCH_SUSPEND].smpsw_name = "spicsuspend0";
276 sc->sc_smpsw[SPIC_PSWITCH_SUSPEND].smpsw_type = PSWITCH_TYPE_SLEEP;
277 sc->sc_smpsw[SPIC_PSWITCH_HIBERNATE].smpsw_name = "spichibernate0";
278 sc->sc_smpsw[SPIC_PSWITCH_HIBERNATE].smpsw_type = PSWITCH_TYPE_SLEEP;
280 for (i = 0; i < SPIC_NPSWITCH; i++) {
281 rv = sysmon_pswitch_register(&sc->sc_smpsw[i]);
282 if (rv != 0)
283 aprint_error_dev(sc->sc_dev, "unable to register %s with sysmon\n",
284 sc->sc_smpsw[i].smpsw_name);
287 callout_reset(&sc->sc_poll, POLLRATE, spictimeout, sc);
289 return;
292 bool
293 spic_suspend(device_t dev, pmf_qual_t qual)
295 struct spic_softc *sc = device_private(dev);
297 callout_stop(&sc->sc_poll);
299 return true;
302 bool
303 spic_resume(device_t dev, pmf_qual_t qual)
305 struct spic_softc *sc = device_private(dev);
307 spic_call1(sc, 0x82);
308 spic_call2(sc, 0x81, 0xff);
309 spic_call1(sc, 0x92); /* or 0x82 */
311 callout_reset(&sc->sc_poll, POLLRATE, spictimeout, sc);
312 return true;
315 static int
316 spic_enable(void *v)
318 struct spic_softc *sc = v;
320 if (sc->sc_enabled)
321 return (EBUSY);
323 sc->sc_enabled = 1;
324 sc->sc_buttons = 0;
326 #ifdef SPIC_DEBUG
327 if (spicdebug)
328 printf("spic_enable\n");
329 #endif
331 return (0);
334 static void
335 spic_disable(void *v)
337 struct spic_softc *sc = v;
339 #ifdef DIAGNOSTIC
340 if (!sc->sc_enabled) {
341 printf("spic_disable: not enabled\n");
342 return;
344 #endif
346 sc->sc_enabled = 0;
348 #ifdef SPIC_DEBUG
349 if (spicdebug)
350 printf("spic_disable\n");
351 #endif
354 static int
355 spic_ioctl(void *v, u_long cmd, void *data,
356 int flag, struct lwp *l)
358 switch (cmd) {
359 case WSMOUSEIO_GTYPE:
360 /* XXX this is not really correct */
361 *(u_int *)data = WSMOUSE_TYPE_PS2;
362 return (0);
365 return (-1);