j6x0tp.c revision 1.2 1 /* $NetBSD: j6x0tp.c,v 1.2 2003/10/22 23:52:46 uwe Exp $ */
2
3 /*
4 * Copyright (c) 2003 Valeriy E. Ushakov
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 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: j6x0tp.c,v 1.2 2003/10/22 23:52:46 uwe Exp $");
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/malloc.h>
37 #include <sys/systm.h>
38 #include <sys/callout.h>
39 #ifdef GPROF
40 #include <sys/gmon.h>
41 #endif
42
43 #include <dev/wscons/wsconsio.h>
44 #include <dev/wscons/wsmousevar.h>
45 #include <dev/hpc/tpcalibvar.h>
46
47 #include <machine/platid.h>
48 #include <machine/platid_mask.h>
49
50 #include <machine/intr.h>
51
52 #include <sh3/exception.h>
53 #include <sh3/intcreg.h>
54 #include <sh3/pfcreg.h>
55 #include <sh3/adcreg.h>
56
57 #include <sh3/dev/adcvar.h>
58
59
60 #define J6X0TP_DEBUG
61 #if 0 /* XXX: disabled in favor of local version that uses printf_nolog */
62 #define DPRINTF_ENABLE
63 #define DPRINTF_DEBUG j6x0tp_debug
64 #define DPRINTF_LEVEL 0
65 #include <machine/debug.h>
66 #else
67 #ifdef J6X0TP_DEBUG
68 volatile int j6x0tp_debug = 0;
69 #define DPRINTF(arg) if (j6x0tp_debug) printf_nolog arg
70 #define DPRINTFN(n, arg) if (j6x0tp_debug > (n)) printf_nolog arg
71 #else
72 #define DPRINTF(arg) ((void)0)
73 #define DPRINTFN(n, arg) ((void)0)
74 #endif
75 #endif
76
77
78 /* PFC bits pertinent to Jornada 6x0 touchpanel */
79 #define PHDR_TP_PEN_DOWN 0x08
80
81 #define SCPDR_TP_SCAN_ENABLE 0x20
82 #define SCPDR_TP_SCAN_Y 0x02
83 #define SCPDR_TP_SCAN_X 0x01
84
85 /* A/D covnerter channels to get x/y from */
86 #define ADC_CHANNEL_TP_Y 1
87 #define ADC_CHANNEL_TP_X 2
88
89
90 struct j6x0tp_softc {
91 struct device sc_dev;
92
93 struct callout sc_touch_ch;
94 int sc_intrlevel;
95 int sc_enabled;
96
97 struct device *sc_wsmousedev;
98 struct tpcalib_softc sc_tpcalib;
99 };
100
101 static int j6x0tp_match(struct device *, struct cfdata *, void *);
102 static void j6x0tp_attach(struct device *, struct device *, void *);
103
104 CFATTACH_DECL(j6x0tp, sizeof(struct j6x0tp_softc),
105 j6x0tp_match, j6x0tp_attach, NULL, NULL);
106
107
108 static int j6x0tp_enable(void *);
109 static int j6x0tp_ioctl(void *, u_long, caddr_t, int, struct proc *);
110 static void j6x0tp_disable(void *);
111
112 const struct wsmouse_accessops j6x0tp_accessops = {
113 j6x0tp_enable,
114 j6x0tp_ioctl,
115 j6x0tp_disable,
116 };
117
118 static const struct wsmouse_calibcoords j6x0tp_default_calib = {
119 0, 0, 639, 239,
120 4,
121 {{ 38, 80, 0, 0 }, /* upper left */
122 { 950, 80, 639, 0 }, /* upper right */
123 { 38, 900, 0, 239 }, /* lower left */
124 { 950, 900, 639, 239 }} /* lower right */
125 };
126
127
128 static int j6x0tp_intr(void *);
129 static void j6x0tp_poll_callout(void *self);
130
131
132 static int
133 j6x0tp_match(struct device *parent, struct cfdata *cfp, void *aux)
134 {
135
136 /*
137 * XXX: does platid_mask_MACH_HP_LX matches _JORNADA_6XX too?
138 * Is 620 wired similarly?
139 */
140 if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX))
141 return (0);
142
143 if (strcmp(cfp->cf_name, "j6x0tp") != 0)
144 return (0);
145
146 return (1);
147 }
148
149
150 static void
151 j6x0tp_attach(struct device *parent, struct device *self, void *aux)
152 {
153 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
154 struct wsmousedev_attach_args wsma;
155
156 printf("\n");
157
158 sc->sc_enabled = 0;
159
160 wsma.accessops = &j6x0tp_accessops;
161 wsma.accesscookie = sc;
162
163 sc->sc_wsmousedev = config_found(self, &wsma, wsmousedevprint);
164 if (sc->sc_wsmousedev == NULL)
165 return;
166
167 tpcalib_init(&sc->sc_tpcalib);
168 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
169 (caddr_t)&j6x0tp_default_calib, 0, 0);
170
171 callout_init(&sc->sc_touch_ch);
172
173 intc_intr_establish(SH7709_INTEVT2_IRQ3, IST_EDGE, IPL_TTY,
174 j6x0tp_intr, sc);
175 sc->sc_intrlevel = intc_intr_disable(SH7709_INTEVT2_IRQ3);
176 }
177
178
179 static int
180 j6x0tp_enable(void *self)
181 {
182 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
183 int s;
184
185 DPRINTFN(1, ("%s: enable\n", sc->sc_dev.dv_xname));
186
187 s = spltty();
188
189 sc->sc_enabled = 1;
190 intc_intr_enable(SH7709_INTEVT2_IRQ3, sc->sc_intrlevel);
191
192 splx(s);
193 return (0);
194 }
195
196
197 static void
198 j6x0tp_disable(void *self)
199 {
200 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
201 int s;
202
203 s = spltty();
204
205 DPRINTFN(1, ("%s: disable\n", sc->sc_dev.dv_xname));
206
207 sc->sc_enabled = 0;
208 intc_intr_disable(SH7709_INTEVT2_IRQ3);
209 callout_stop(&sc->sc_touch_ch);
210
211 splx(s);
212 }
213
214
215 static int
216 j6x0tp_intr(void *self)
217 {
218 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
219
220 uint8_t irr0;
221 uint8_t phdr, touched;
222 unsigned int steady, tremor_timeout;
223
224 irr0 = _reg_read_1(SH7709_IRR0);
225 if ((irr0 & IRR0_IRQ3) == 0) {
226 #ifdef DIAGNOSTIC
227 printf("%s: irr0 %02x?\n", sc->sc_dev.dv_xname, irr0);
228 #endif
229 return (0);
230 }
231
232 /*
233 * Number of times the "touched" bit should be read
234 * consecutively.
235 */
236 # define TREMOR_THRESHOLD 0x300
237
238 steady = 0;
239 tremor_timeout = TREMOR_THRESHOLD * 16; /* XXX: arbitrary */
240 touched = PHDR_TP_PEN_DOWN; /* we start with "touched" state */
241
242 do {
243 phdr = _reg_read_1(SH7709_PHDR);
244
245 if ((phdr & PHDR_TP_PEN_DOWN) == touched)
246 ++steady;
247 else {
248 steady = 0;
249 touched = phdr & PHDR_TP_PEN_DOWN;
250 }
251
252 if (--tremor_timeout == 0) {
253 DPRINTF(("%s: tremor timeout!\n",
254 sc->sc_dev.dv_xname));
255 goto served;
256 }
257 } while (steady < TREMOR_THRESHOLD);
258
259 if (touched == 0) {
260 DPRINTFN(1, ("%s: tremor\n", sc->sc_dev.dv_xname));
261 goto served;
262 }
263
264 intc_intr_disable(SH7709_INTEVT2_IRQ3);
265
266 if (sc->sc_enabled)
267 callout_reset(&sc->sc_touch_ch, hz/32,
268 j6x0tp_poll_callout, sc);
269 else
270 DPRINTFN(1, ("%s: intr: !sc_enabled\n", sc->sc_dev.dv_xname));
271
272 served:
273 /* clear the interrupt */
274 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
275
276 return (1);
277 }
278
279
280 static void
281 j6x0tp_poll_callout(void *self)
282 {
283 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
284 uint8_t phdr, scpdr;
285 uint8_t irr0;
286 int rawx, rawy, x, y;
287 int s;
288
289 s = spltty();
290
291 if (!sc->sc_enabled) {
292 DPRINTFN(1, ("%s: callout: !sc_enabled\n",
293 sc->sc_dev.dv_xname));
294 splx(s);
295 return;
296 }
297
298 phdr = _reg_read_1(SH7709_PHDR);
299 if ((phdr & PHDR_TP_PEN_DOWN) == 0) {
300 wsmouse_input(sc->sc_wsmousedev, 0, 0, 0, 0, 0); /* mouse up */
301
302 intc_intr_enable(SH7709_INTEVT2_IRQ3, sc->sc_intrlevel);
303
304 irr0 = _reg_read_1(SH7709_IRR0);
305 if ((irr0 & IRR0_IRQ3) != 0)
306 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
307
308 splx(s);
309 return;
310 }
311
312 /* XXX: protect accesses to SCPDR? */
313
314 /* Y axis */
315 scpdr = _reg_read_1(SH7709_SCPDR);
316 scpdr |= SCPDR_TP_SCAN_ENABLE;
317 scpdr &= ~SCPDR_TP_SCAN_Y; /* pull low to scan */
318 _reg_write_1(SH7709_SCPDR, scpdr);
319 delay(10);
320 rawy = adc_sample_channel(ADC_CHANNEL_TP_Y);
321
322 /* X axis */
323 scpdr = _reg_read_1(SH7709_SCPDR);
324 scpdr |= SCPDR_TP_SCAN_Y;
325 scpdr &= ~SCPDR_TP_SCAN_X; /* pull low to scan */
326 _reg_write_1(SH7709_SCPDR, scpdr);
327 delay(10);
328 rawx = adc_sample_channel(ADC_CHANNEL_TP_X);
329
330 /* restore SCPDR */
331 scpdr = _reg_read_1(SH7709_SCPDR);
332 scpdr |= SCPDR_TP_SCAN_X;
333 scpdr &= ~SCPDR_TP_SCAN_ENABLE;
334 _reg_write_1(SH7709_SCPDR, scpdr);
335
336 tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
337
338 DPRINTFN(2, ("%s: %4d %4d -> %3d %3d\n",
339 sc->sc_dev.dv_xname, rawx, rawy, x, y));
340
341 wsmouse_input(sc->sc_wsmousedev, 1, x, y, 0,
342 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
343
344 callout_schedule(&sc->sc_touch_ch, hz/32);
345 splx(s);
346 }
347
348
349 static int
350 j6x0tp_ioctl(void *self, u_long cmd, caddr_t data, int flag,
351 struct proc *p)
352 {
353 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
354
355 switch (cmd) {
356 case WSMOUSEIO_GTYPE:
357 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
358 return (0);
359
360 case WSMOUSEIO_SCALIBCOORDS:
361 case WSMOUSEIO_GCALIBCOORDS:
362 case WSMOUSEIO_GETID:
363 return (tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, p));
364
365 default:
366 return (EPASSTHROUGH);
367 }
368 }
369