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