1 /* $NetBSD: pfckbd.c,v 1.26 2009/03/18 10:22:30 cegger Exp $ */
4 * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Matrix scan keyboard connected to SH7709, SH7709A PFC module.
34 * currently, HP Jornada 680/690, HITACHI PERSONA HPW-50PAD only.
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: pfckbd.c,v 1.26 2009/03/18 10:22:30 cegger Exp $");
39 #include "debug_hpcsh.h"
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/callout.h>
46 #include <machine/bus.h>
47 #include <machine/platid.h>
48 #include <machine/platid_mask.h>
50 #include <dev/hpc/hpckbdvar.h>
52 #include <sh3/pfcreg.h>
54 #include <hpcsh/dev/pfckbdvar.h>
57 #define DPRINTF_ENABLE
58 #define DPRINTF_DEBUG pfckbd_debug
60 #include <machine/debug.h>
62 static struct pfckbd_core
{
65 struct callout pc_soft_ch
;
66 struct hpckbd_ic_if pc_if
;
67 struct hpckbd_if
*pc_hpckbd
;
68 uint16_t pc_column
[8];
69 void (*pc_callout
)(void *);
72 static int pfckbd_match(device_t
, cfdata_t
, void *);
73 static void pfckbd_attach(device_t
, device_t
, void *);
75 CFATTACH_DECL_NEW(pfckbd
, 0,
76 pfckbd_match
, pfckbd_attach
, NULL
, NULL
);
78 static void pfckbd_ifsetup(struct pfckbd_core
*);
80 /* callbacks for hpckbd */
81 static int pfckbd_input_establish(void *, struct hpckbd_if
*);
82 static int pfckbd_poll(void *);
84 static void pfckbd_input(struct pfckbd_core
*, int, uint16_t);
86 static void (*pfckbd_callout_lookup(void))(void *);
87 static void pfckbd_callout_unknown(void *);
88 static void pfckbd_callout_hp(void *);
89 static void pfckbd_callout_hitachi(void *);
90 void pfckbd_poll_hitachi_power(void);
93 /* callout function table. this function is platfrom specific. */
95 platid_mask_t
*platform
;
97 } pfckbd_calloutfunc_table
[] = {
98 { &platid_mask_MACH_HP
, pfckbd_callout_hp
},
99 { &platid_mask_MACH_HITACHI
, pfckbd_callout_hitachi
}
104 pfckbd_cnattach(void)
106 struct pfckbd_core
*pc
= &pfckbd_core
;
108 if ((cpu_product
!= CPU_PRODUCT_7709
)
109 && (cpu_product
!= CPU_PRODUCT_7709A
))
112 /* initialize interface */
115 /* attach descendants */
116 hpckbd_cnattach(&pc
->pc_if
);
120 pfckbd_match(device_t parent
, cfdata_t cf
, void *aux
)
123 if ((cpu_product
!= CPU_PRODUCT_7709
)
124 && (cpu_product
!= CPU_PRODUCT_7709A
))
127 return !pfckbd_core
.pc_attached
; /* attach only once */
131 pfckbd_attach(device_t parent
, device_t self
, void *aux
)
133 struct hpckbd_attach_args haa
;
138 pfckbd_core
.pc_attached
= 1;
140 pfckbd_ifsetup(&pfckbd_core
);
143 haa
.haa_ic
= &pfckbd_core
.pc_if
; /* tell hpckbd our interface */
144 config_found(self
, &haa
, hpckbd_print
);
146 /* install callout handler */
147 callout_init(&pfckbd_core
.pc_soft_ch
, 0);
148 callout_reset(&pfckbd_core
.pc_soft_ch
, 1,
149 pfckbd_core
.pc_callout
, &pfckbd_core
);
151 if (!pmf_device_register(self
, NULL
, NULL
))
152 aprint_error_dev(self
, "unable to establish power handler\n");
156 pfckbd_ifsetup(struct pfckbd_core
*pc
)
160 pc
->pc_if
.hii_ctx
= pc
;
161 pc
->pc_if
.hii_establish
= pfckbd_input_establish
;
162 pc
->pc_if
.hii_poll
= pfckbd_poll
;
164 for (i
= 0; i
< 8; i
++)
165 pc
->pc_column
[i
] = 0xdfff;
167 /* select PFC access method */
168 pc
->pc_callout
= pfckbd_callout_lookup();
173 * Callback for hpckbd_initif
176 pfckbd_input_establish(void *ic
, struct hpckbd_if
*kbdif
)
178 struct pfckbd_core
*pc
= ic
;
180 pc
->pc_hpckbd
= kbdif
; /* save hpckbd interface */
181 pc
->pc_enabled
= 1; /* ok to talk to hpckbd */
187 * Callback for hpckbd_cngetc
190 pfckbd_poll(void *ic
)
192 struct pfckbd_core
*pc
= ic
;
195 (*pc
->pc_callout
)(pc
);
201 * Called by platform specific scan routines to report key events to hpckbd
204 pfckbd_input(struct pfckbd_core
*pc
, int column
, uint16_t data
)
207 unsigned int edge
, mask
;
209 edge
= data
^ pc
->pc_column
[column
];
211 return; /* no changes in this column */
213 pc
->pc_column
[column
] = data
;
215 for (row
= 0, mask
= 1; row
< 16; ++row
, mask
<<= 1) {
217 type
= mask
& data
? /* up */ 0 : /* down */ 1;
218 DPRINTF("(%2d, %2d) %d \n", row
, column
, type
);
220 val
= row
* 8 + column
;
221 hpckbd_input(pc
->pc_hpckbd
, type
, val
);
228 * Platform dependent scan routines.
231 /* Look up appropriate callback handler */
233 (*pfckbd_callout_lookup())(void *)
237 n
= sizeof(pfckbd_calloutfunc_table
)
238 / sizeof(pfckbd_calloutfunc_table
[0]);
240 for (i
= 0; i
< n
; i
++)
241 if (platid_match(&platid
,
242 pfckbd_calloutfunc_table
[i
].platform
))
243 return pfckbd_calloutfunc_table
[i
].func
;
245 return pfckbd_callout_unknown
;
248 /* Placeholder for unknown platform */
250 pfckbd_callout_unknown(void *arg
)
253 printf("%s: unknown keyboard switch\n", __func__
);
257 * HP Jornada680/690, HP620LX
260 pfckbd_callout_hp(void *arg
)
262 #define PFCKBD_HP_PDCR_MASK 0xcc0c
263 #define PFCKBD_HP_PECR_MASK 0xf0cf
266 * Disable output on all lines but the n'th line in D.
267 * Pull the n'th scan line in D low.
270 { (uint16_t)(PFCKBD_HP_PDCR_MASK & (~(1 << (2*(n)+1)))), \
271 (uint16_t)(PFCKBD_HP_PECR_MASK & 0xffff), \
272 (uint8_t)~(1 << (n)), \
277 { (uint16_t)(PFCKBD_HP_PDCR_MASK & 0xffff), \
278 (uint16_t)(PFCKBD_HP_PECR_MASK & (~(1 << (2*(n)+1)))), \
280 (uint8_t)~(1 << (n)) }
282 static const struct {
283 uint16_t dc
, ec
; uint8_t d
, e
;
285 PD(1), PD(5), PE(1), PE(6), PE(7), PE(3), PE(0), PD(7)
291 struct pfckbd_core
*pc
= arg
;
299 /* bits in D/E control regs we do not touch (XXX: can they change?) */
300 dc
= _reg_read_2(SH7709_PDCR
) & ~PFCKBD_HP_PDCR_MASK
;
301 ec
= _reg_read_2(SH7709_PECR
) & ~PFCKBD_HP_PECR_MASK
;
303 for (column
= 0; column
< 8; column
++) {
304 /* disable output to all lines except the one we scan */
305 _reg_write_2(SH7709_PDCR
, dc
| scan
[column
].dc
);
306 _reg_write_2(SH7709_PECR
, ec
| scan
[column
].ec
);
309 /* pull the scan line low */
310 _reg_write_1(SH7709_PDDR
, scan
[column
].d
);
311 _reg_write_1(SH7709_PEDR
, scan
[column
].e
);
315 data
= _reg_read_1(SH7709_PFDR
)
316 | (_reg_read_1(SH7709_PCDR
) << 8);
318 pfckbd_input(pc
, column
, data
);
322 _reg_write_1(SH7709_PDDR
, 0xff);
323 _reg_write_1(SH7709_PEDR
, 0xff);
325 /* enable all scan lines */
326 _reg_write_2(SH7709_PDCR
, dc
| (0x5555 & PFCKBD_HP_PDCR_MASK
));
327 _reg_write_2(SH7709_PECR
, ec
| (0x5555 & PFCKBD_HP_PECR_MASK
));
330 /* (ignore) extra keys/events (recorder buttons, lid, cable &c) */
331 data
= _reg_read_1(SH7709_PGDR
) | (_reg_read_1(SH7709_PHDR
) << 8);
335 callout_schedule(&pc
->pc_soft_ch
, 1);
339 * HITACH PERSONA (HPW-50PAD)
342 pfckbd_callout_hitachi(void *arg
)
344 #define PFCKBD_HITACHI_PCCR_MASK 0xfff3
345 #define PFCKBD_HITACHI_PDCR_MASK 0x000c
346 #define PFCKBD_HITACHI_PECR_MASK 0x30cf
348 #define PFCKBD_HITACHI_PCDR_SCN_MASK 0xfd
349 #define PFCKBD_HITACHI_PDDR_SCN_MASK 0x02
350 #define PFCKBD_HITACHI_PEDR_SCN_MASK 0x4b
352 #define PFCKBD_HITACHI_PCDR_SNS_MASK 0x01
353 #define PFCKBD_HITACHI_PFDR_SNS_MASK 0xfe
356 * Disable output on all lines but the n'th line in C.
357 * Pull the n'th scan line in C low.
360 { (uint16_t)(PFCKBD_HITACHI_PCCR_MASK & (~(1 << (2*(n)+1)))), \
361 (uint16_t)(PFCKBD_HITACHI_PDCR_MASK & 0xffff), \
362 (uint16_t)(PFCKBD_HITACHI_PECR_MASK & 0xffff), \
363 (uint8_t)(PFCKBD_HITACHI_PCDR_SCN_MASK & ~(1 << (n))), \
364 PFCKBD_HITACHI_PDDR_SCN_MASK, \
365 PFCKBD_HITACHI_PEDR_SCN_MASK }
369 { (uint16_t)(PFCKBD_HITACHI_PCCR_MASK & 0xffff), \
370 (uint16_t)(PFCKBD_HITACHI_PDCR_MASK & (~(1 << (2*(n)+1)))), \
371 (uint16_t)(PFCKBD_HITACHI_PECR_MASK & 0xffff), \
372 PFCKBD_HITACHI_PCDR_SCN_MASK, \
373 (uint8_t)(PFCKBD_HITACHI_PDDR_SCN_MASK & ~(1 << (n))), \
374 PFCKBD_HITACHI_PEDR_SCN_MASK }
378 { (uint16_t)(PFCKBD_HITACHI_PCCR_MASK & 0xffff), \
379 (uint16_t)(PFCKBD_HITACHI_PDCR_MASK & 0xffff), \
380 (uint16_t)(PFCKBD_HITACHI_PECR_MASK & (~(1 << (2*(n)+1)))), \
381 PFCKBD_HITACHI_PCDR_SCN_MASK, \
382 PFCKBD_HITACHI_PDDR_SCN_MASK, \
383 (uint8_t)(PFCKBD_HITACHI_PEDR_SCN_MASK & ~(1 << (n))) }
385 static const struct {
386 uint16_t cc
, dc
, ec
; uint8_t c
, d
, e
;
388 PE(6), PE(3), PE(1), PE(0), PC(7), PC(6), PC(5), PC(4),
389 PC(3), PC(2), PD(1), PC(0)
392 struct pfckbd_core
*pc
= arg
;
394 uint8_t data
[2], cd
, dd
, ed
;
400 /* bits in C/D/E control regs we do not touch (XXX: can they change?) */
401 cc
= _reg_read_2(SH7709_PCCR
) & ~PFCKBD_HITACHI_PCCR_MASK
;
402 dc
= _reg_read_2(SH7709_PDCR
) & ~PFCKBD_HITACHI_PDCR_MASK
;
403 ec
= _reg_read_2(SH7709_PECR
) & ~PFCKBD_HITACHI_PECR_MASK
;
405 for (i
= 0; i
< 12; i
++) {
406 /* disable output to all lines except the one we scan */
407 _reg_write_2(SH7709_PCCR
, cc
| scan
[i
].cc
);
408 _reg_write_2(SH7709_PDCR
, dc
| scan
[i
].dc
);
409 _reg_write_2(SH7709_PECR
, ec
| scan
[i
].ec
);
412 cd
= _reg_read_1(SH7709_PCDR
) & ~PFCKBD_HITACHI_PCDR_SCN_MASK
;
413 dd
= _reg_read_1(SH7709_PDDR
) & ~PFCKBD_HITACHI_PDDR_SCN_MASK
;
414 ed
= _reg_read_1(SH7709_PEDR
) & ~PFCKBD_HITACHI_PEDR_SCN_MASK
;
416 /* pull the scan line low */
417 _reg_write_1(SH7709_PCDR
, cd
| scan
[i
].c
);
418 _reg_write_1(SH7709_PDDR
, dd
| scan
[i
].d
);
419 _reg_write_1(SH7709_PEDR
, ed
| scan
[i
].e
);
424 (_reg_read_1(SH7709_PCDR
) & PFCKBD_HITACHI_PCDR_SNS_MASK
)
425 | (_reg_read_1(SH7709_PFDR
) & PFCKBD_HITACHI_PFDR_SNS_MASK
);
428 pfckbd_input(pc
, (i
>> 1), (data
[0] | (data
[1] << 8)));
431 /* enable all scan lines */
432 _reg_write_2(SH7709_PCCR
, cc
| (0x5555 & PFCKBD_HITACHI_PCCR_MASK
));
433 _reg_write_2(SH7709_PDCR
, dc
| (0x5555 & PFCKBD_HITACHI_PDCR_MASK
));
434 _reg_write_2(SH7709_PECR
, ec
| (0x5555 & PFCKBD_HITACHI_PECR_MASK
));
437 callout_schedule(&pc
->pc_soft_ch
, 1);
441 pfckbd_poll_hitachi_power(void)
443 static const struct {
444 uint16_t cc
, dc
, ec
; uint8_t c
, d
, e
;
454 /* bits in C/D/E control regs we do not touch (XXX: can they change?) */
455 cc
= _reg_read_2(SH7709_PCCR
) & ~PFCKBD_HITACHI_PCCR_MASK
;
456 dc
= _reg_read_2(SH7709_PDCR
) & ~PFCKBD_HITACHI_PDCR_MASK
;
457 ec
= _reg_read_2(SH7709_PECR
) & ~PFCKBD_HITACHI_PECR_MASK
;
459 /* disable output to all lines except the one we scan */
460 _reg_write_2(SH7709_PCCR
, cc
| poll
.cc
);
461 _reg_write_2(SH7709_PDCR
, dc
| poll
.dc
);
462 _reg_write_2(SH7709_PECR
, ec
| poll
.ec
);
465 cd
= _reg_read_1(SH7709_PCDR
) & ~PFCKBD_HITACHI_PCDR_SCN_MASK
;
466 dd
= _reg_read_1(SH7709_PDDR
) & ~PFCKBD_HITACHI_PDDR_SCN_MASK
;
467 ed
= _reg_read_1(SH7709_PEDR
) & ~PFCKBD_HITACHI_PEDR_SCN_MASK
;
469 /* pull the scan line low */
470 _reg_write_1(SH7709_PCDR
, cd
| poll
.c
);
471 _reg_write_1(SH7709_PDDR
, dd
| poll
.d
);
472 _reg_write_1(SH7709_PEDR
, ed
| poll
.e
);
476 while (_reg_read_1(SH7709_PCDR
) & PFCKBD_HITACHI_PCDR_SNS_MASK
& 0x01);
478 /* enable all scan lines */
479 _reg_write_2(SH7709_PCCR
, cc
| (0x5555 & PFCKBD_HITACHI_PCCR_MASK
));
480 _reg_write_2(SH7709_PDCR
, dc
| (0x5555 & PFCKBD_HITACHI_PDCR_MASK
));
481 _reg_write_2(SH7709_PECR
, ec
| (0x5555 & PFCKBD_HITACHI_PECR_MASK
));