1 /* $NetBSD: spic.c,v 1.15 2008/05/04 16:13:35 xtraeme Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
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
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
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
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.)
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>
58 #include <sys/kernel.h>
59 #include <sys/callout.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 */
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
= {
95 #define SPIC_COMMAND(quiet, command) do { \
96 unsigned int n = 10000; \
97 while (--n && (command)) \
99 if (n == 0 && !(quiet)) { \
100 printf("spic0: command failed at line %d\n", __LINE__); \
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)
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)
114 spic_call1(struct spic_softc
*sc
, u_int8_t dev
)
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
);
126 spic_call2(struct spic_softc
*sc
, u_int8_t dev
, u_int8_t fn
)
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
);
138 /* Interrupt handler: some event is available */
141 struct spic_softc
*sc
= v
;
145 v1
= INB(sc
, SPIC_PORT1
);
146 v2
= INB(sc
, SPIC_PORT2
);
148 /* Handle lid switch */
151 case 0x50: /* opened */
152 sysmon_pswitch_event(&sc
->sc_smpsw
[SPIC_PSWITCH_LID
],
153 PSWITCH_EVENT_RELEASED
);
156 case 0x51: /* closed */
157 sysmon_pswitch_event(&sc
->sc_smpsw
[SPIC_PSWITCH_LID
],
158 PSWITCH_EVENT_PRESSED
);
162 aprint_debug_dev(sc
->sc_dev
, "unknown lid event 0x%02x\n", v1
);
168 /* Handle suspend/hibernate buttons */
171 case 0x10: /* suspend */
172 sysmon_pswitch_event(
173 &sc
->sc_smpsw
[SPIC_PSWITCH_SUSPEND
],
174 PSWITCH_EVENT_PRESSED
);
177 case 0x1c: /* hibernate */
178 sysmon_pswitch_event(
179 &sc
->sc_smpsw
[SPIC_PSWITCH_HIBERNATE
],
180 PSWITCH_EVENT_PRESSED
);
203 case SPIC_EVENT_BRIGHTNESS_UP
:
204 pmf_event_inject(sc
->sc_dev
, PMFE_DISPLAY_BRIGHTNESS_UP
);
206 case SPIC_EVENT_BRIGHTNESS_DOWN
:
207 pmf_event_inject(sc
->sc_dev
, PMFE_DISPLAY_BRIGHTNESS_DOWN
);
210 printf("spic0: v1=0x%02x v2=0x%02x\n", v1
, v2
);
214 if (!sc
->sc_enabled
) {
215 /*printf("spic: not enabled\n");*/
219 if (dz
!= 0 || buttons
!= sc
->sc_buttons
) {
222 printf("spic: but=0x%x dz=%d v1=0x%02x v2=0x%02x\n",
223 buttons
, dz
, v1
, v2
);
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
);
233 spic_call2(sc
, 0x81, 0xff); /* Clear event */
240 struct spic_softc
*sc
= v
;
249 callout_reset(&sc
->sc_poll
, POLLRATE
, spictimeout
, sc
);
253 spic_attach(struct spic_softc
*sc
)
255 struct wsmousedev_attach_args a
;
260 printf("spic_attach %x %x\n", sc
->sc_iot
, (uint
)sc
->sc_ioh
);
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
;
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
]);
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
);
293 spic_suspend(device_t dev
, pmf_qual_t qual
)
295 struct spic_softc
*sc
= device_private(dev
);
297 callout_stop(&sc
->sc_poll
);
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
);
318 struct spic_softc
*sc
= v
;
328 printf("spic_enable\n");
335 spic_disable(void *v
)
337 struct spic_softc
*sc
= v
;
340 if (!sc
->sc_enabled
) {
341 printf("spic_disable: not enabled\n");
350 printf("spic_disable\n");
355 spic_ioctl(void *v
, u_long cmd
, void *data
,
356 int flag
, struct lwp
*l
)
359 case WSMOUSEIO_GTYPE
:
360 /* XXX this is not really correct */
361 *(u_int
*)data
= WSMOUSE_TYPE_PS2
;