1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // Copyright (C) 2014 Broadcom Corporation |
3 | |
4 | #include <linux/bitops.h> |
5 | #include <linux/clk.h> |
6 | #include <linux/gfp.h> |
7 | #include <linux/io.h> |
8 | #include <linux/input.h> |
9 | #include <linux/input/matrix_keypad.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/stddef.h> |
15 | #include <linux/types.h> |
16 | |
17 | #define DEFAULT_CLK_HZ 31250 |
18 | #define MAX_ROWS 8 |
19 | #define MAX_COLS 8 |
20 | |
21 | /* Register/field definitions */ |
22 | #define KPCR_OFFSET 0x00000080 |
23 | #define KPCR_MODE 0x00000002 |
24 | #define KPCR_MODE_SHIFT 1 |
25 | #define KPCR_MODE_MASK 1 |
26 | #define KPCR_ENABLE 0x00000001 |
27 | #define KPCR_STATUSFILTERENABLE 0x00008000 |
28 | #define KPCR_STATUSFILTERTYPE_SHIFT 12 |
29 | #define KPCR_COLFILTERENABLE 0x00000800 |
30 | #define KPCR_COLFILTERTYPE_SHIFT 8 |
31 | #define KPCR_ROWWIDTH_SHIFT 20 |
32 | #define KPCR_COLUMNWIDTH_SHIFT 16 |
33 | |
34 | #define KPIOR_OFFSET 0x00000084 |
35 | #define KPIOR_ROWOCONTRL_SHIFT 24 |
36 | #define KPIOR_ROWOCONTRL_MASK 0xFF000000 |
37 | #define KPIOR_COLUMNOCONTRL_SHIFT 16 |
38 | #define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000 |
39 | #define KPIOR_COLUMN_IO_DATA_SHIFT 0 |
40 | |
41 | #define KPEMR0_OFFSET 0x00000090 |
42 | #define KPEMR1_OFFSET 0x00000094 |
43 | #define KPEMR2_OFFSET 0x00000098 |
44 | #define KPEMR3_OFFSET 0x0000009C |
45 | #define KPEMR_EDGETYPE_BOTH 3 |
46 | |
47 | #define KPSSR0_OFFSET 0x000000A0 |
48 | #define KPSSR1_OFFSET 0x000000A4 |
49 | #define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n)) |
50 | #define KPIMR0_OFFSET 0x000000B0 |
51 | #define KPIMR1_OFFSET 0x000000B4 |
52 | #define KPICR0_OFFSET 0x000000B8 |
53 | #define KPICR1_OFFSET 0x000000BC |
54 | #define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n)) |
55 | #define KPISR0_OFFSET 0x000000C0 |
56 | #define KPISR1_OFFSET 0x000000C4 |
57 | |
58 | #define KPCR_STATUSFILTERTYPE_MAX 7 |
59 | #define KPCR_COLFILTERTYPE_MAX 7 |
60 | |
61 | /* Macros to determine the row/column from a bit that is set in SSR0/1. */ |
62 | #define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n)) |
63 | #define BIT_TO_COL(bit_nr) ((bit_nr) % 8) |
64 | |
65 | /* Structure representing various run-time entities */ |
66 | struct bcm_kp { |
67 | void __iomem *base; |
68 | int irq; |
69 | struct clk *clk; |
70 | struct input_dev *input_dev; |
71 | unsigned long last_state[2]; |
72 | unsigned int n_rows; |
73 | unsigned int n_cols; |
74 | u32 kpcr; |
75 | u32 kpior; |
76 | u32 kpemr; |
77 | u32 imr0_val; |
78 | u32 imr1_val; |
79 | }; |
80 | |
81 | /* |
82 | * Returns the keycode from the input device keymap given the row and |
83 | * column. |
84 | */ |
85 | static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col) |
86 | { |
87 | unsigned int row_shift = get_count_order(count: kp->n_cols); |
88 | unsigned short *keymap = kp->input_dev->keycode; |
89 | |
90 | return keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; |
91 | } |
92 | |
93 | static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode) |
94 | { |
95 | unsigned long state, change; |
96 | int bit_nr; |
97 | int key_press; |
98 | int row, col; |
99 | unsigned int keycode; |
100 | |
101 | /* Clear interrupts */ |
102 | writel(val: 0xFFFFFFFF, addr: kp->base + KPICRN_OFFSET(reg_num)); |
103 | |
104 | state = readl(addr: kp->base + KPSSRN_OFFSET(reg_num)); |
105 | change = kp->last_state[reg_num] ^ state; |
106 | kp->last_state[reg_num] = state; |
107 | |
108 | for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { |
109 | key_press = state & BIT(bit_nr); |
110 | /* The meaning of SSR register depends on pull mode. */ |
111 | key_press = pull_mode ? !key_press : key_press; |
112 | row = BIT_TO_ROW_SSRN(bit_nr, reg_num); |
113 | col = BIT_TO_COL(bit_nr); |
114 | keycode = bcm_kp_get_keycode(kp, row, col); |
115 | input_report_key(dev: kp->input_dev, code: keycode, value: key_press); |
116 | } |
117 | } |
118 | |
119 | static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id) |
120 | { |
121 | struct bcm_kp *kp = dev_id; |
122 | int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK; |
123 | int reg_num; |
124 | |
125 | for (reg_num = 0; reg_num <= 1; reg_num++) |
126 | bcm_kp_report_keys(kp, reg_num, pull_mode); |
127 | |
128 | input_sync(dev: kp->input_dev); |
129 | |
130 | return IRQ_HANDLED; |
131 | } |
132 | |
133 | static int bcm_kp_start(struct bcm_kp *kp) |
134 | { |
135 | int error; |
136 | |
137 | if (kp->clk) { |
138 | error = clk_prepare_enable(clk: kp->clk); |
139 | if (error) |
140 | return error; |
141 | } |
142 | |
143 | writel(val: kp->kpior, addr: kp->base + KPIOR_OFFSET); |
144 | |
145 | writel(val: kp->imr0_val, addr: kp->base + KPIMR0_OFFSET); |
146 | writel(val: kp->imr1_val, addr: kp->base + KPIMR1_OFFSET); |
147 | |
148 | writel(val: kp->kpemr, addr: kp->base + KPEMR0_OFFSET); |
149 | writel(val: kp->kpemr, addr: kp->base + KPEMR1_OFFSET); |
150 | writel(val: kp->kpemr, addr: kp->base + KPEMR2_OFFSET); |
151 | writel(val: kp->kpemr, addr: kp->base + KPEMR3_OFFSET); |
152 | |
153 | writel(val: 0xFFFFFFFF, addr: kp->base + KPICR0_OFFSET); |
154 | writel(val: 0xFFFFFFFF, addr: kp->base + KPICR1_OFFSET); |
155 | |
156 | kp->last_state[0] = readl(addr: kp->base + KPSSR0_OFFSET); |
157 | kp->last_state[0] = readl(addr: kp->base + KPSSR1_OFFSET); |
158 | |
159 | writel(val: kp->kpcr | KPCR_ENABLE, addr: kp->base + KPCR_OFFSET); |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static void bcm_kp_stop(const struct bcm_kp *kp) |
165 | { |
166 | u32 val; |
167 | |
168 | val = readl(addr: kp->base + KPCR_OFFSET); |
169 | val &= ~KPCR_ENABLE; |
170 | writel(val: 0, addr: kp->base + KPCR_OFFSET); |
171 | writel(val: 0, addr: kp->base + KPIMR0_OFFSET); |
172 | writel(val: 0, addr: kp->base + KPIMR1_OFFSET); |
173 | writel(val: 0xFFFFFFFF, addr: kp->base + KPICR0_OFFSET); |
174 | writel(val: 0xFFFFFFFF, addr: kp->base + KPICR1_OFFSET); |
175 | |
176 | clk_disable_unprepare(clk: kp->clk); |
177 | } |
178 | |
179 | static int bcm_kp_open(struct input_dev *dev) |
180 | { |
181 | struct bcm_kp *kp = input_get_drvdata(dev); |
182 | |
183 | return bcm_kp_start(kp); |
184 | } |
185 | |
186 | static void bcm_kp_close(struct input_dev *dev) |
187 | { |
188 | struct bcm_kp *kp = input_get_drvdata(dev); |
189 | |
190 | bcm_kp_stop(kp); |
191 | } |
192 | |
193 | static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp) |
194 | { |
195 | struct device *dev = kp->input_dev->dev.parent; |
196 | struct device_node *np = dev->of_node; |
197 | int error; |
198 | unsigned int dt_val; |
199 | unsigned int i; |
200 | unsigned int num_rows, col_mask, rows_set; |
201 | |
202 | /* Initialize the KPCR Keypad Configuration Register */ |
203 | kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE; |
204 | |
205 | error = matrix_keypad_parse_properties(dev, rows: &kp->n_rows, cols: &kp->n_cols); |
206 | if (error) { |
207 | dev_err(dev, "failed to parse kp params\n" ); |
208 | return error; |
209 | } |
210 | |
211 | /* Set row width for the ASIC block. */ |
212 | kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT; |
213 | |
214 | /* Set column width for the ASIC block. */ |
215 | kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT; |
216 | |
217 | /* Configure the IMR registers */ |
218 | |
219 | /* |
220 | * IMR registers contain interrupt enable bits for 8x8 matrix |
221 | * IMR0 register format: <row3> <row2> <row1> <row0> |
222 | * IMR1 register format: <row7> <row6> <row5> <row4> |
223 | */ |
224 | col_mask = (1 << (kp->n_cols)) - 1; |
225 | num_rows = kp->n_rows; |
226 | |
227 | /* Set column bits in rows 0 to 3 in IMR0 */ |
228 | kp->imr0_val = col_mask; |
229 | |
230 | rows_set = 1; |
231 | while (--num_rows && rows_set++ < 4) |
232 | kp->imr0_val |= kp->imr0_val << MAX_COLS; |
233 | |
234 | /* Set column bits in rows 4 to 7 in IMR1 */ |
235 | kp->imr1_val = 0; |
236 | if (num_rows) { |
237 | kp->imr1_val = col_mask; |
238 | while (--num_rows) |
239 | kp->imr1_val |= kp->imr1_val << MAX_COLS; |
240 | } |
241 | |
242 | /* Initialize the KPEMR Keypress Edge Mode Registers */ |
243 | /* Trigger on both edges */ |
244 | kp->kpemr = 0; |
245 | for (i = 0; i <= 30; i += 2) |
246 | kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i); |
247 | |
248 | /* |
249 | * Obtain the Status filter debounce value and verify against the |
250 | * possible values specified in the DT binding. |
251 | */ |
252 | of_property_read_u32(np, propname: "status-debounce-filter-period" , out_value: &dt_val); |
253 | |
254 | if (dt_val > KPCR_STATUSFILTERTYPE_MAX) { |
255 | dev_err(dev, "Invalid Status filter debounce value %d\n" , |
256 | dt_val); |
257 | return -EINVAL; |
258 | } |
259 | |
260 | kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT; |
261 | |
262 | /* |
263 | * Obtain the Column filter debounce value and verify against the |
264 | * possible values specified in the DT binding. |
265 | */ |
266 | of_property_read_u32(np, propname: "col-debounce-filter-period" , out_value: &dt_val); |
267 | |
268 | if (dt_val > KPCR_COLFILTERTYPE_MAX) { |
269 | dev_err(dev, "Invalid Column filter debounce value %d\n" , |
270 | dt_val); |
271 | return -EINVAL; |
272 | } |
273 | |
274 | kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT; |
275 | |
276 | /* |
277 | * Determine between the row and column, |
278 | * which should be configured as output. |
279 | */ |
280 | if (of_property_read_bool(np, propname: "row-output-enabled" )) { |
281 | /* |
282 | * Set RowOContrl or ColumnOContrl in KPIOR |
283 | * to the number of pins to drive as outputs |
284 | */ |
285 | kp->kpior = ((1 << kp->n_rows) - 1) << |
286 | KPIOR_ROWOCONTRL_SHIFT; |
287 | } else { |
288 | kp->kpior = ((1 << kp->n_cols) - 1) << |
289 | KPIOR_COLUMNOCONTRL_SHIFT; |
290 | } |
291 | |
292 | /* |
293 | * Determine if the scan pull up needs to be enabled |
294 | */ |
295 | if (of_property_read_bool(np, propname: "pull-up-enabled" )) |
296 | kp->kpcr |= KPCR_MODE; |
297 | |
298 | dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n" , |
299 | kp->n_rows, kp->n_cols, |
300 | kp->kpcr, kp->kpior, kp->kpemr); |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | |
306 | static int bcm_kp_probe(struct platform_device *pdev) |
307 | { |
308 | struct bcm_kp *kp; |
309 | struct input_dev *input_dev; |
310 | int error; |
311 | |
312 | kp = devm_kzalloc(dev: &pdev->dev, size: sizeof(*kp), GFP_KERNEL); |
313 | if (!kp) |
314 | return -ENOMEM; |
315 | |
316 | input_dev = devm_input_allocate_device(&pdev->dev); |
317 | if (!input_dev) { |
318 | dev_err(&pdev->dev, "failed to allocate the input device\n" ); |
319 | return -ENOMEM; |
320 | } |
321 | |
322 | __set_bit(EV_KEY, input_dev->evbit); |
323 | |
324 | /* Enable auto repeat feature of Linux input subsystem */ |
325 | if (of_property_read_bool(np: pdev->dev.of_node, propname: "autorepeat" )) |
326 | __set_bit(EV_REP, input_dev->evbit); |
327 | |
328 | input_dev->name = pdev->name; |
329 | input_dev->phys = "keypad/input0" ; |
330 | input_dev->dev.parent = &pdev->dev; |
331 | input_dev->open = bcm_kp_open; |
332 | input_dev->close = bcm_kp_close; |
333 | |
334 | input_dev->id.bustype = BUS_HOST; |
335 | input_dev->id.vendor = 0x0001; |
336 | input_dev->id.product = 0x0001; |
337 | input_dev->id.version = 0x0100; |
338 | |
339 | input_set_drvdata(dev: input_dev, data: kp); |
340 | |
341 | kp->input_dev = input_dev; |
342 | |
343 | error = bcm_kp_matrix_key_parse_dt(kp); |
344 | if (error) |
345 | return error; |
346 | |
347 | error = matrix_keypad_build_keymap(NULL, NULL, |
348 | rows: kp->n_rows, cols: kp->n_cols, |
349 | NULL, input_dev); |
350 | if (error) { |
351 | dev_err(&pdev->dev, "failed to build keymap\n" ); |
352 | return error; |
353 | } |
354 | |
355 | kp->base = devm_platform_ioremap_resource(pdev, index: 0); |
356 | if (IS_ERR(ptr: kp->base)) |
357 | return PTR_ERR(ptr: kp->base); |
358 | |
359 | /* Enable clock */ |
360 | kp->clk = devm_clk_get_optional(dev: &pdev->dev, id: "peri_clk" ); |
361 | if (IS_ERR(ptr: kp->clk)) { |
362 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: kp->clk), fmt: "Failed to get clock\n" ); |
363 | } else if (!kp->clk) { |
364 | dev_dbg(&pdev->dev, "No clock specified. Assuming it's enabled\n" ); |
365 | } else { |
366 | unsigned int desired_rate; |
367 | long actual_rate; |
368 | |
369 | error = of_property_read_u32(np: pdev->dev.of_node, |
370 | propname: "clock-frequency" , out_value: &desired_rate); |
371 | if (error < 0) |
372 | desired_rate = DEFAULT_CLK_HZ; |
373 | |
374 | actual_rate = clk_round_rate(clk: kp->clk, rate: desired_rate); |
375 | if (actual_rate <= 0) |
376 | return -EINVAL; |
377 | |
378 | error = clk_set_rate(clk: kp->clk, rate: actual_rate); |
379 | if (error) |
380 | return error; |
381 | |
382 | error = clk_prepare_enable(clk: kp->clk); |
383 | if (error) |
384 | return error; |
385 | } |
386 | |
387 | /* Put the kp into a known sane state */ |
388 | bcm_kp_stop(kp); |
389 | |
390 | kp->irq = platform_get_irq(pdev, 0); |
391 | if (kp->irq < 0) |
392 | return -EINVAL; |
393 | |
394 | error = devm_request_threaded_irq(dev: &pdev->dev, irq: kp->irq, |
395 | NULL, thread_fn: bcm_kp_isr_thread, |
396 | IRQF_ONESHOT, devname: pdev->name, dev_id: kp); |
397 | if (error) { |
398 | dev_err(&pdev->dev, "failed to request IRQ\n" ); |
399 | return error; |
400 | } |
401 | |
402 | error = input_register_device(input_dev); |
403 | if (error) { |
404 | dev_err(&pdev->dev, "failed to register input device\n" ); |
405 | return error; |
406 | } |
407 | |
408 | return 0; |
409 | } |
410 | |
411 | static const struct of_device_id bcm_kp_of_match[] = { |
412 | { .compatible = "brcm,bcm-keypad" }, |
413 | { }, |
414 | }; |
415 | MODULE_DEVICE_TABLE(of, bcm_kp_of_match); |
416 | |
417 | static struct platform_driver bcm_kp_device_driver = { |
418 | .probe = bcm_kp_probe, |
419 | .driver = { |
420 | .name = "bcm-keypad" , |
421 | .of_match_table = of_match_ptr(bcm_kp_of_match), |
422 | } |
423 | }; |
424 | |
425 | module_platform_driver(bcm_kp_device_driver); |
426 | |
427 | MODULE_AUTHOR("Broadcom Corporation" ); |
428 | MODULE_DESCRIPTION("BCM Keypad Driver" ); |
429 | MODULE_LICENSE("GPL v2" ); |
430 | |