1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
6 #include <linux/input/matrix_keypad.h>
7 #include <linux/platform_device.h>
8 #include <linux/interrupt.h>
10 #include <linux/delay.h>
11 #include <linux/input.h>
12 #include <linux/slab.h>
13 #include <linux/clk.h>
14 #include <linux/module.h>
17 #define KEYPAD_SCAN_MODE 0x00
18 #define KEYPAD_CNTL 0x04
19 #define KEYPAD_INT 0x08
20 #define KEYPAD_INTMSK 0x0C
22 #define KEYPAD_DATA 0x10
23 #define KEYPAD_GPIO 0x30
25 #define KEYPAD_UNKNOWN_INT 0x40
26 #define KEYPAD_UNKNOWN_INT_STS 0x44
28 #define KEYPAD_BITMASK_COLS 11
29 #define KEYPAD_BITMASK_ROWS 8
31 struct nspire_keypad
{
32 void __iomem
*reg_base
;
35 struct input_dev
*input
;
38 struct matrix_keymap_data
*keymap
;
41 /* Maximum delay estimated assuming 33MHz APB */
42 u32 scan_interval
; /* In microseconds (~2000us max) */
43 u32 row_delay
; /* In microseconds (~500us max) */
45 u16 state
[KEYPAD_BITMASK_ROWS
];
50 static irqreturn_t
nspire_keypad_irq(int irq
, void *dev_id
)
52 struct nspire_keypad
*keypad
= dev_id
;
53 struct input_dev
*input
= keypad
->input
;
54 unsigned short *keymap
= input
->keycode
;
61 int_sts
= readl(keypad
->reg_base
+ KEYPAD_INT
) & keypad
->int_mask
;
65 memcpy_fromio(state
, keypad
->reg_base
+ KEYPAD_DATA
, sizeof(state
));
67 for (row
= 0; row
< KEYPAD_BITMASK_ROWS
; row
++) {
69 if (keypad
->active_low
)
72 changed
= bits
^ keypad
->state
[row
];
76 keypad
->state
[row
] = bits
;
78 for (col
= 0; col
< KEYPAD_BITMASK_COLS
; col
++) {
79 if (!(changed
& (1U << col
)))
82 code
= MATRIX_SCAN_CODE(row
, col
, keypad
->row_shift
);
83 input_event(input
, EV_MSC
, MSC_SCAN
, code
);
84 input_report_key(input
, keymap
[code
],
91 writel(0x3, keypad
->reg_base
+ KEYPAD_INT
);
96 static int nspire_keypad_chip_init(struct nspire_keypad
*keypad
)
98 unsigned long val
= 0, cycles_per_us
, delay_cycles
, row_delay_cycles
;
100 cycles_per_us
= (clk_get_rate(keypad
->clk
) / 1000000);
101 if (cycles_per_us
== 0)
104 delay_cycles
= cycles_per_us
* keypad
->scan_interval
;
105 WARN_ON(delay_cycles
>= (1 << 16)); /* Overflow */
106 delay_cycles
&= 0xffff;
108 row_delay_cycles
= cycles_per_us
* keypad
->row_delay
;
109 WARN_ON(row_delay_cycles
>= (1 << 14)); /* Overflow */
110 row_delay_cycles
&= 0x3fff;
112 val
|= 3 << 0; /* Set scan mode to 3 (continuous scan) */
113 val
|= row_delay_cycles
<< 2; /* Delay between scanning each row */
114 val
|= delay_cycles
<< 16; /* Delay between scans */
115 writel(val
, keypad
->reg_base
+ KEYPAD_SCAN_MODE
);
117 val
= (KEYPAD_BITMASK_ROWS
& 0xff) | (KEYPAD_BITMASK_COLS
& 0xff)<<8;
118 writel(val
, keypad
->reg_base
+ KEYPAD_CNTL
);
120 /* Enable interrupts */
121 keypad
->int_mask
= 1 << 1;
122 writel(keypad
->int_mask
, keypad
->reg_base
+ KEYPAD_INTMSK
);
124 /* Disable GPIO interrupts to prevent hanging on touchpad */
125 /* Possibly used to detect touchpad events */
126 writel(0, keypad
->reg_base
+ KEYPAD_UNKNOWN_INT
);
127 /* Acknowledge existing interrupts */
128 writel(~0, keypad
->reg_base
+ KEYPAD_UNKNOWN_INT_STS
);
133 static int nspire_keypad_open(struct input_dev
*input
)
135 struct nspire_keypad
*keypad
= input_get_drvdata(input
);
138 error
= clk_prepare_enable(keypad
->clk
);
142 error
= nspire_keypad_chip_init(keypad
);
144 clk_disable_unprepare(keypad
->clk
);
151 static void nspire_keypad_close(struct input_dev
*input
)
153 struct nspire_keypad
*keypad
= input_get_drvdata(input
);
155 clk_disable_unprepare(keypad
->clk
);
158 static int nspire_keypad_probe(struct platform_device
*pdev
)
160 const struct device_node
*of_node
= pdev
->dev
.of_node
;
161 struct nspire_keypad
*keypad
;
162 struct input_dev
*input
;
163 struct resource
*res
;
167 irq
= platform_get_irq(pdev
, 0);
171 keypad
= devm_kzalloc(&pdev
->dev
, sizeof(struct nspire_keypad
),
174 dev_err(&pdev
->dev
, "failed to allocate keypad memory\n");
178 keypad
->row_shift
= get_count_order(KEYPAD_BITMASK_COLS
);
180 error
= of_property_read_u32(of_node
, "scan-interval",
181 &keypad
->scan_interval
);
183 dev_err(&pdev
->dev
, "failed to get scan-interval\n");
187 error
= of_property_read_u32(of_node
, "row-delay",
190 dev_err(&pdev
->dev
, "failed to get row-delay\n");
194 keypad
->active_low
= of_property_read_bool(of_node
, "active-low");
196 keypad
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
197 if (IS_ERR(keypad
->clk
)) {
198 dev_err(&pdev
->dev
, "unable to get clock\n");
199 return PTR_ERR(keypad
->clk
);
202 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
203 keypad
->reg_base
= devm_ioremap_resource(&pdev
->dev
, res
);
204 if (IS_ERR(keypad
->reg_base
))
205 return PTR_ERR(keypad
->reg_base
);
207 keypad
->input
= input
= devm_input_allocate_device(&pdev
->dev
);
209 dev_err(&pdev
->dev
, "failed to allocate input device\n");
213 input_set_drvdata(input
, keypad
);
215 input
->id
.bustype
= BUS_HOST
;
216 input
->name
= "nspire-keypad";
217 input
->open
= nspire_keypad_open
;
218 input
->close
= nspire_keypad_close
;
220 __set_bit(EV_KEY
, input
->evbit
);
221 __set_bit(EV_REP
, input
->evbit
);
222 input_set_capability(input
, EV_MSC
, MSC_SCAN
);
224 error
= matrix_keypad_build_keymap(NULL
, NULL
,
229 dev_err(&pdev
->dev
, "building keymap failed\n");
233 error
= devm_request_irq(&pdev
->dev
, irq
, nspire_keypad_irq
, 0,
234 "nspire_keypad", keypad
);
236 dev_err(&pdev
->dev
, "allocate irq %d failed\n", irq
);
240 error
= input_register_device(input
);
243 "unable to register input device: %d\n", error
);
248 "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n",
249 res
, keypad
->row_delay
, keypad
->scan_interval
,
250 keypad
->active_low
? ", active_low" : "");
255 static const struct of_device_id nspire_keypad_dt_match
[] = {
256 { .compatible
= "ti,nspire-keypad" },
259 MODULE_DEVICE_TABLE(of
, nspire_keypad_dt_match
);
261 static struct platform_driver nspire_keypad_driver
= {
263 .name
= "nspire-keypad",
264 .of_match_table
= nspire_keypad_dt_match
,
266 .probe
= nspire_keypad_probe
,
269 module_platform_driver(nspire_keypad_driver
);
271 MODULE_LICENSE("GPL");
272 MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver");