3 * Keyboard support for the h4350 ipaq
5 * (c) Shawn Anderson, March, 2006
6 * This code is released under the GNU General Public License
8 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10 #include <linux/platform_device.h>
11 #include <linux/delay.h>
12 #include <linux/input.h>
13 #include <linux/input_pda.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <asm/arch/h4000-gpio.h>
17 #include <asm/arch/h4000-asic.h>
18 #include <linux/soc/asic3_base.h>
19 #include <asm/arch/pxa-regs.h>
20 #include <asm/hardware.h>
21 #include <asm/arch/bitfield.h>
23 int h4000_machine_is_h4300(void);
24 static struct input_dev
*h4300_kbd
;
25 #define a3 &h4000_asic3.dev
28 static unsigned int h4300_keycode
[0x80] = {
29 /*0*/ 0, 0, 0, 0, 0, 0, 0, 0,
30 0, 0, _KEY_CALENDAR
, _KEY_CONTACTS
,
31 0, 0, _KEY_MAIL
, _KEY_HOMEPAGE
,
32 /*1*/ KEY_R
, KEY_RIGHT
, KEY_W
, KEY_E
,
33 KEY_SELECT
, KEY_U
, KEY_I
, KEY_P
,
34 KEY_F
, KEY_Y
, KEY_Q
, KEY_D
,
35 KEY_DOWN
, KEY_K
, KEY_O
, KEY_BACKSPACE
,
36 /*2*/ KEY_X
, KEY_H
, KEY_A
, KEY_S
,
37 KEY_T
, KEY_J
, KEY_L
, KEY_ENTER
,
38 KEY_C
, KEY_B
, KEY_LEFTSHIFT
, KEY_Z
,
39 KEY_G
, KEY_M
, KEY_1
, KEY_APOSTROPHE
,
40 /*3*/ KEY_ESC
, KEY_SPACE
, 0/*KEY_FUNC*/, KEY_TAB
,
41 KEY_V
, KEY_N
, KEY_COMMA
, KEY_SLASH
,
42 KEY_LEFT
, KEY_UP
, 0, 0,
43 KEY_LEFTCTRL
, KEY_DOT
, 0, 0,
44 /*4*/ 0, 0, 0, 0, 0, 0, 0, 0,
45 0, 0, KEY_PROG1
, KEY_PROG2
,
46 0, 0, KEY_PROG3
, KEY_PROG4
,
47 /*5*/ KEY_5
, KEY_RIGHT
, KEY_2
, KEY_4
,
48 KEY_SELECT
, KEY_1
, KEY_2
, KEY_SLASH
,
49 KEY_EQUAL
, KEY_MINUS
, KEY_GRAVE
, KEY_SEMICOLON
,
50 KEY_DOWN
, KEY_5
, KEY_3
, KEY_DELETE
,
51 /*6*/ KEY_SEMICOLON
, KEY_KPPLUS
, KEY_9
, KEY_0
,
52 KEY_MINUS
, KEY_4
, KEY_6
, KEY_ENTER
,
53 KEY_LEFT
, KEY_RIGHT
, KEY_CAPSLOCK
, KEY_7
,
54 KEY_UP
, KEY_8
, KEY_9
, KEY_APOSTROPHE
,
55 /*7*/ KEY_ESC
, KEY_COMPOSE
, 0/*KEY_FUNC*/, KEY_TAB
,
56 KEY_DOWN
, KEY_7
, KEY_0
, KEY_3
,
57 KEY_LEFT
, KEY_UP
, 0, 0,
58 KEY_BACKSLASH
, KEY_KPASTERISK
, 0, 0
61 static int asic3_spi_process_byte(unsigned char data
)
63 #define ASIC3_SPI_CTRL (_IPAQ_ASIC3_SPI_Base + _IPAQ_ASIC3_SPI_Control)
64 #define ASIC3_SPI_TXDATA (_IPAQ_ASIC3_SPI_Base + _IPAQ_ASIC3_SPI_TxData)
65 #define ASIC3_SPI_RXDATA (_IPAQ_ASIC3_SPI_Base + _IPAQ_ASIC3_SPI_RxData)
66 #define SPI_CTRL_SEL (1 << 6)
67 #define SPI_CTRL_SPIE (1 << 5)
68 #define SPI_CTRL_SPE (1 << 4)
74 local_irq_save(flags
);
76 /* Enable interrupts */
77 ctrl
= asic3_read_register(a3
, ASIC3_SPI_CTRL
) | SPI_CTRL_SPIE
;
78 asic3_write_register(a3
, ASIC3_SPI_CTRL
, ctrl
);
80 /* Set data to send */
81 asic3_write_register(a3
, ASIC3_SPI_TXDATA
, data
);
82 /* Start the transfer (duplex) */
83 asic3_write_register(a3
, ASIC3_SPI_CTRL
, (ctrl
| SPI_CTRL_SPE
));
85 /* Wait for transfer completion and receive data being ready */
86 for (timeout
= 255; timeout
> 0; timeout
--) {
87 if (!(asic3_read_register(a3
, ASIC3_SPI_CTRL
) & SPI_CTRL_SPE
)){
88 udelay(20); /* wait for a while, or we'll miss it */
89 result
= asic3_read_register(a3
, ASIC3_SPI_RXDATA
);
94 /* Disable interrupts */
95 ctrl
= asic3_read_register(a3
, ASIC3_SPI_CTRL
) & ~SPI_CTRL_SPIE
;
96 asic3_write_register(a3
, ASIC3_SPI_CTRL
, ctrl
);
98 local_irq_restore(flags
);
102 void kbd_task(unsigned long na
)
104 #define FNC_PRESS (1<<0)
105 #define FNC_REL (1<<1)
106 #define KEY_REL (1<<2)
107 #define FAKE_SHIFT (1<<3)
109 unsigned char scancode
= 0;
110 static unsigned char flags
= 0;
112 scancode
= asic3_spi_process_byte(0);
113 pressed
= (scancode
& 0x80) ? 0 : 1;
116 if ((scancode
!= KEY_FUNC
) && (flags
& FNC_PRESS
)) {
119 flags
&= ~(FNC_PRESS
| FNC_REL
| KEY_REL
);
123 scancode
+= 0x40; // the keys alternate function
130 else if (flags
& KEY_REL
)
131 flags
&= ~(FNC_PRESS
| FNC_REL
| KEY_REL
);
136 case 0x2e:case 0x37:case 0x50:case 0x52:case 0x53:case 0x5a: case 0x5b:
137 case 0x62:case 0x63:case 0x64:case 0x6b:case 0x6f:case 0x77: case 0x7c:
139 input_report_key(h4300_kbd
, KEY_LEFTSHIFT
, pressed
);
143 if (flags
& FAKE_SHIFT
) {
144 input_report_key(h4300_kbd
, KEY_LEFTSHIFT
, 0);
145 flags
&= ~FAKE_SHIFT
;
150 input_report_key(h4300_kbd
, h4300_keycode
[scancode
], pressed
);
151 input_sync(h4300_kbd
);
153 DECLARE_TASKLET(task
, kbd_task
, 0);
155 static irqreturn_t
h4300_keyboard(int irq
, void *dev_id
, struct pt_regs
*regs
)
157 tasklet_schedule(&task
);
161 static irqreturn_t
h4300_rec_btn(int irq
, void *data
, struct pt_regs
*regs
)
164 pressed
= !(asic3_get_gpio_status_d(a3
) & GPIOD_RECORD_BUTTON_N
);
166 printk("%s: pressed = %d\n", __FUNCTION__
, pressed
);
169 set_irq_type(irq
, IRQT_RISING
);
171 set_irq_type(irq
, IRQT_FALLING
);
173 input_report_key(h4300_kbd
, _KEY_RECORD
, pressed
);
174 input_sync(h4300_kbd
);
179 static irqreturn_t
h4300_pwr_btn(int irq
, void* data
, struct pt_regs
*regs
)
182 pressed
= !GET_H4000_GPIO(POWER_BUTTON_N
);
184 input_report_key(h4300_kbd
, _KEY_POWER
, pressed
);
185 input_sync(h4300_kbd
);
190 static void setup_h4300kbd(void)
192 asic3_set_clock_cdex(a3
, CLOCK_CDEX_SPI
, CLOCK_CDEX_SPI
);
193 asic3_set_clock_cdex(a3
, CLOCK_CDEX_EX1
, CLOCK_CDEX_EX1
);
196 asic3_set_gpio_dir_b(a3
, GPIOB_KEYBOARD_IRQ
, 0);
197 asic3_set_gpio_out_b(a3
,
198 (GPIOB_MICRO_3V3_EN
| GPIOB_KEYBOARD_WAKE_UP
),
199 (GPIOB_MICRO_3V3_EN
| GPIOB_KEYBOARD_WAKE_UP
) );
201 asic3_set_gpio_dir_c(a3
, GPIOC_KEY_RXD
, 0);
203 asic3_set_gpio_alt_fn_c(a3
,
204 GPIOC_KEY_RXD
| GPIOC_KEY_TXD
| GPIOC_KEY_CLK
,
205 GPIOC_KEY_RXD
| GPIOC_KEY_TXD
| GPIOC_KEY_CLK
);
207 asic3_set_gpio_dir_d(a3
, // disable h4100 buttons
208 (GPIOD_TASK_BUTTON_N
| GPIOD_MAIL_BUTTON_N
|
209 GPIOD_CONTACTS_BUTTON_N
| GPIOD_CALENDAR_BUTTON_N
),
210 (GPIOD_TASK_BUTTON_N
| GPIOD_MAIL_BUTTON_N
|
211 GPIOD_CONTACTS_BUTTON_N
| GPIOD_CALENDAR_BUTTON_N
));
212 asic3_write_register(a3
, 0x400, 0xa104);
215 static int __init
h4300_kbd_probe(struct platform_device
* pdev
)
217 int i
, base_irq
= asic3_irq_base(a3
);
219 if (!h4000_machine_is_h4300())
222 if (!(h4300_kbd
= input_allocate_device()))
227 h4300_kbd
->name
= "HP iPAQ h4300 keyboard driver";
228 h4300_kbd
->evbit
[0] = BIT(EV_KEY
) | BIT(EV_REP
);
229 h4300_kbd
->keycode
= h4300_keycode
;
230 h4300_kbd
->keycodesize
= sizeof(unsigned char);
231 h4300_kbd
->keycodemax
= ARRAY_SIZE(h4300_keycode
);
233 for (i
= 0; i
< h4300_kbd
->keycodemax
; i
++)
234 if (h4300_keycode
[i
])
235 set_bit(h4300_keycode
[i
], h4300_kbd
->keybit
);
236 request_irq(base_irq
+ H4000_KEYBOARD_IRQ
, &h4300_keyboard
,
237 SA_SAMPLE_RANDOM
, "Keyboard", NULL
);
240 set_bit(_KEY_RECORD
, h4300_kbd
->keybit
);
241 request_irq(base_irq
+ H4000_RECORD_BTN_IRQ
, h4300_rec_btn
,
242 SA_SAMPLE_RANDOM
, "Record button", NULL
);
243 set_irq_type(base_irq
+ H4000_RECORD_BTN_IRQ
, IRQT_FALLING
);
245 set_bit(_KEY_POWER
, h4300_kbd
->keybit
);
246 request_irq(IRQ_GPIO(GPIO_NR_H4000_POWER_BUTTON_N
), h4300_pwr_btn
,
247 SA_SAMPLE_RANDOM
, "Power button", NULL
);
248 set_irq_type(IRQ_GPIO(GPIO_NR_H4000_POWER_BUTTON_N
), IRQT_BOTHEDGE
);
250 input_register_device(h4300_kbd
);
255 static int h4300_kbd_remove(struct platform_device
* pdev
)
257 int irq_base
= asic3_irq_base(a3
);
259 input_unregister_device(h4300_kbd
);
260 free_irq(IRQ_GPIO(GPIO_NR_H4000_POWER_BUTTON_N
), NULL
);
261 free_irq(irq_base
+ H4000_RECORD_BTN_IRQ
, &h4300_rec_btn
);
262 free_irq(irq_base
+ H4000_KEYBOARD_IRQ
, &h4300_keyboard
);
267 void h4300_kbd_shutdown(struct platform_device
* pdev
)
269 asic3_set_gpio_dir_b(a3
, GPIOB_KEYBOARD_IRQ
, GPIOB_KEYBOARD_IRQ
);
270 asic3_set_gpio_out_b(a3
,
271 (GPIOB_MICRO_3V3_EN
| GPIOB_KEYBOARD_WAKE_UP
), 0);
272 asic3_set_gpio_dir_c(a3
,GPIOC_KEY_RXD
,GPIOC_KEY_RXD
);
275 int h4300_kbd_suspend(struct platform_device
* pdev
, pm_message_t state
)
277 h4300_kbd_shutdown(pdev
);
280 int h4300_kbd_resume(struct platform_device
* pdev
)
286 static struct platform_driver h4300_kbd_driver
= {
287 .probe
= h4300_kbd_probe
,
288 .remove
= h4300_kbd_remove
,
289 .shutdown
= h4300_kbd_shutdown
,
291 .suspend
= h4300_kbd_suspend
,
292 .resume
= h4300_kbd_resume
,
299 static int __init
h4300_kbd_init(void)
301 return platform_driver_register(&h4300_kbd_driver
);
304 static void __exit
h4300_kbd_exit(void)
306 platform_driver_unregister(&h4300_kbd_driver
);
309 module_init(h4300_kbd_init
);
310 module_exit(h4300_kbd_exit
);
312 MODULE_AUTHOR("Shawn Anderson");
313 MODULE_DESCRIPTION("Keyboard support for the iPAQ h43xx");
314 MODULE_LICENSE("GPL");