10eb70904SFabio Estevam // SPDX-License-Identifier: GPL-2.0
20eb70904SFabio Estevam //
30eb70904SFabio Estevam // Driver for the IMX keypad port.
40eb70904SFabio Estevam // Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
576cdc083SAlberto Panizzo
676cdc083SAlberto Panizzo #include <linux/clk.h>
776cdc083SAlberto Panizzo #include <linux/delay.h>
876cdc083SAlberto Panizzo #include <linux/device.h>
976cdc083SAlberto Panizzo #include <linux/err.h>
10*4e9cded6SDmitry Torokhov #include <linux/input.h>
1176cdc083SAlberto Panizzo #include <linux/input/matrix_keypad.h>
1276cdc083SAlberto Panizzo #include <linux/interrupt.h>
1376cdc083SAlberto Panizzo #include <linux/io.h>
1476cdc083SAlberto Panizzo #include <linux/jiffies.h>
1576cdc083SAlberto Panizzo #include <linux/kernel.h>
1676cdc083SAlberto Panizzo #include <linux/module.h>
170e14235eSLiu Ying #include <linux/of.h>
1876cdc083SAlberto Panizzo #include <linux/platform_device.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
2076cdc083SAlberto Panizzo #include <linux/timer.h>
2176cdc083SAlberto Panizzo
2276cdc083SAlberto Panizzo /*
2376cdc083SAlberto Panizzo * Keypad Controller registers (halfword)
2476cdc083SAlberto Panizzo */
2576cdc083SAlberto Panizzo #define KPCR 0x00 /* Keypad Control Register */
2676cdc083SAlberto Panizzo
2776cdc083SAlberto Panizzo #define KPSR 0x02 /* Keypad Status Register */
2876cdc083SAlberto Panizzo #define KBD_STAT_KPKD (0x1 << 0) /* Key Press Interrupt Status bit (w1c) */
2976cdc083SAlberto Panizzo #define KBD_STAT_KPKR (0x1 << 1) /* Key Release Interrupt Status bit (w1c) */
3076cdc083SAlberto Panizzo #define KBD_STAT_KDSC (0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/
3176cdc083SAlberto Panizzo #define KBD_STAT_KRSS (0x1 << 3) /* Key Release Synch Status bit (w1c)*/
3276cdc083SAlberto Panizzo #define KBD_STAT_KDIE (0x1 << 8) /* Key Depress Interrupt Enable Status bit */
3376cdc083SAlberto Panizzo #define KBD_STAT_KRIE (0x1 << 9) /* Key Release Interrupt Enable */
3476cdc083SAlberto Panizzo #define KBD_STAT_KPPEN (0x1 << 10) /* Keypad Clock Enable */
3576cdc083SAlberto Panizzo
3676cdc083SAlberto Panizzo #define KDDR 0x04 /* Keypad Data Direction Register */
3776cdc083SAlberto Panizzo #define KPDR 0x06 /* Keypad Data Register */
3876cdc083SAlberto Panizzo
3976cdc083SAlberto Panizzo #define MAX_MATRIX_KEY_ROWS 8
4076cdc083SAlberto Panizzo #define MAX_MATRIX_KEY_COLS 8
4176cdc083SAlberto Panizzo #define MATRIX_ROW_SHIFT 3
4276cdc083SAlberto Panizzo
4376cdc083SAlberto Panizzo #define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
4476cdc083SAlberto Panizzo
4576cdc083SAlberto Panizzo struct imx_keypad {
4676cdc083SAlberto Panizzo
4776cdc083SAlberto Panizzo struct clk *clk;
4876cdc083SAlberto Panizzo struct input_dev *input_dev;
4976cdc083SAlberto Panizzo void __iomem *mmio_base;
5076cdc083SAlberto Panizzo
5176cdc083SAlberto Panizzo int irq;
5276cdc083SAlberto Panizzo struct timer_list check_matrix_timer;
5376cdc083SAlberto Panizzo
5476cdc083SAlberto Panizzo /*
5576cdc083SAlberto Panizzo * The matrix is stable only if no changes are detected after
5676cdc083SAlberto Panizzo * IMX_KEYPAD_SCANS_FOR_STABILITY scans
5776cdc083SAlberto Panizzo */
5876cdc083SAlberto Panizzo #define IMX_KEYPAD_SCANS_FOR_STABILITY 3
5976cdc083SAlberto Panizzo int stable_count;
6076cdc083SAlberto Panizzo
6176cdc083SAlberto Panizzo bool enabled;
6276cdc083SAlberto Panizzo
6376cdc083SAlberto Panizzo /* Masks for enabled rows/cols */
6476cdc083SAlberto Panizzo unsigned short rows_en_mask;
6576cdc083SAlberto Panizzo unsigned short cols_en_mask;
6676cdc083SAlberto Panizzo
6776cdc083SAlberto Panizzo unsigned short keycodes[MAX_MATRIX_KEY_NUM];
6876cdc083SAlberto Panizzo
6976cdc083SAlberto Panizzo /*
7076cdc083SAlberto Panizzo * Matrix states:
7176cdc083SAlberto Panizzo * -stable: achieved after a complete debounce process.
7276cdc083SAlberto Panizzo * -unstable: used in the debouncing process.
7376cdc083SAlberto Panizzo */
7476cdc083SAlberto Panizzo unsigned short matrix_stable_state[MAX_MATRIX_KEY_COLS];
7576cdc083SAlberto Panizzo unsigned short matrix_unstable_state[MAX_MATRIX_KEY_COLS];
7676cdc083SAlberto Panizzo };
7776cdc083SAlberto Panizzo
7876cdc083SAlberto Panizzo /* Scan the matrix and return the new state in *matrix_volatile_state. */
imx_keypad_scan_matrix(struct imx_keypad * keypad,unsigned short * matrix_volatile_state)7976cdc083SAlberto Panizzo static void imx_keypad_scan_matrix(struct imx_keypad *keypad,
8076cdc083SAlberto Panizzo unsigned short *matrix_volatile_state)
8176cdc083SAlberto Panizzo {
8276cdc083SAlberto Panizzo int col;
8376cdc083SAlberto Panizzo unsigned short reg_val;
8476cdc083SAlberto Panizzo
8576cdc083SAlberto Panizzo for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
8676cdc083SAlberto Panizzo if ((keypad->cols_en_mask & (1 << col)) == 0)
8776cdc083SAlberto Panizzo continue;
8876cdc083SAlberto Panizzo /*
8976cdc083SAlberto Panizzo * Discharge keypad capacitance:
9076cdc083SAlberto Panizzo * 2. write 1s on column data.
9176cdc083SAlberto Panizzo * 3. configure columns as totem-pole to discharge capacitance.
9276cdc083SAlberto Panizzo * 4. configure columns as open-drain.
9376cdc083SAlberto Panizzo */
9476cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPDR);
9576cdc083SAlberto Panizzo reg_val |= 0xff00;
9676cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPDR);
9776cdc083SAlberto Panizzo
9876cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPCR);
9976cdc083SAlberto Panizzo reg_val &= ~((keypad->cols_en_mask & 0xff) << 8);
10076cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPCR);
10176cdc083SAlberto Panizzo
10276cdc083SAlberto Panizzo udelay(2);
10376cdc083SAlberto Panizzo
10476cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPCR);
10576cdc083SAlberto Panizzo reg_val |= (keypad->cols_en_mask & 0xff) << 8;
10676cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPCR);
10776cdc083SAlberto Panizzo
10876cdc083SAlberto Panizzo /*
10976cdc083SAlberto Panizzo * 5. Write a single column to 0, others to 1.
11076cdc083SAlberto Panizzo * 6. Sample row inputs and save data.
11176cdc083SAlberto Panizzo * 7. Repeat steps 2 - 6 for remaining columns.
11276cdc083SAlberto Panizzo */
11376cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPDR);
11476cdc083SAlberto Panizzo reg_val &= ~(1 << (8 + col));
11576cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPDR);
11676cdc083SAlberto Panizzo
11776cdc083SAlberto Panizzo /*
11876cdc083SAlberto Panizzo * Delay added to avoid propagating the 0 from column to row
11976cdc083SAlberto Panizzo * when scanning.
12076cdc083SAlberto Panizzo */
12176cdc083SAlberto Panizzo udelay(5);
12276cdc083SAlberto Panizzo
12376cdc083SAlberto Panizzo /*
12476cdc083SAlberto Panizzo * 1s in matrix_volatile_state[col] means key pressures
12576cdc083SAlberto Panizzo * throw data from non enabled rows.
12676cdc083SAlberto Panizzo */
12776cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPDR);
12876cdc083SAlberto Panizzo matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask;
12976cdc083SAlberto Panizzo }
13076cdc083SAlberto Panizzo
13176cdc083SAlberto Panizzo /*
13276cdc083SAlberto Panizzo * Return in standby mode:
13376cdc083SAlberto Panizzo * 9. write 0s to columns
13476cdc083SAlberto Panizzo */
13576cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPDR);
13676cdc083SAlberto Panizzo reg_val &= 0x00ff;
13776cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPDR);
13876cdc083SAlberto Panizzo }
13976cdc083SAlberto Panizzo
14076cdc083SAlberto Panizzo /*
14176cdc083SAlberto Panizzo * Compare the new matrix state (volatile) with the stable one stored in
14276cdc083SAlberto Panizzo * keypad->matrix_stable_state and fire events if changes are detected.
14376cdc083SAlberto Panizzo */
imx_keypad_fire_events(struct imx_keypad * keypad,unsigned short * matrix_volatile_state)14476cdc083SAlberto Panizzo static void imx_keypad_fire_events(struct imx_keypad *keypad,
14576cdc083SAlberto Panizzo unsigned short *matrix_volatile_state)
14676cdc083SAlberto Panizzo {
14776cdc083SAlberto Panizzo struct input_dev *input_dev = keypad->input_dev;
14876cdc083SAlberto Panizzo int row, col;
14976cdc083SAlberto Panizzo
15076cdc083SAlberto Panizzo for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
15176cdc083SAlberto Panizzo unsigned short bits_changed;
15276cdc083SAlberto Panizzo int code;
15376cdc083SAlberto Panizzo
15476cdc083SAlberto Panizzo if ((keypad->cols_en_mask & (1 << col)) == 0)
15576cdc083SAlberto Panizzo continue; /* Column is not enabled */
15676cdc083SAlberto Panizzo
15776cdc083SAlberto Panizzo bits_changed = keypad->matrix_stable_state[col] ^
15876cdc083SAlberto Panizzo matrix_volatile_state[col];
15976cdc083SAlberto Panizzo
16076cdc083SAlberto Panizzo if (bits_changed == 0)
16176cdc083SAlberto Panizzo continue; /* Column does not contain changes */
16276cdc083SAlberto Panizzo
16376cdc083SAlberto Panizzo for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
16476cdc083SAlberto Panizzo if ((keypad->rows_en_mask & (1 << row)) == 0)
16576cdc083SAlberto Panizzo continue; /* Row is not enabled */
16676cdc083SAlberto Panizzo if ((bits_changed & (1 << row)) == 0)
16776cdc083SAlberto Panizzo continue; /* Row does not contain changes */
16876cdc083SAlberto Panizzo
16976cdc083SAlberto Panizzo code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
17076cdc083SAlberto Panizzo input_event(input_dev, EV_MSC, MSC_SCAN, code);
17176cdc083SAlberto Panizzo input_report_key(input_dev, keypad->keycodes[code],
17276cdc083SAlberto Panizzo matrix_volatile_state[col] & (1 << row));
17376cdc083SAlberto Panizzo dev_dbg(&input_dev->dev, "Event code: %d, val: %d",
17476cdc083SAlberto Panizzo keypad->keycodes[code],
17576cdc083SAlberto Panizzo matrix_volatile_state[col] & (1 << row));
17676cdc083SAlberto Panizzo }
17776cdc083SAlberto Panizzo }
17876cdc083SAlberto Panizzo input_sync(input_dev);
17976cdc083SAlberto Panizzo }
18076cdc083SAlberto Panizzo
18176cdc083SAlberto Panizzo /*
18276cdc083SAlberto Panizzo * imx_keypad_check_for_events is the timer handler.
18376cdc083SAlberto Panizzo */
imx_keypad_check_for_events(struct timer_list * t)1844ea40278SKees Cook static void imx_keypad_check_for_events(struct timer_list *t)
18576cdc083SAlberto Panizzo {
1864ea40278SKees Cook struct imx_keypad *keypad = from_timer(keypad, t, check_matrix_timer);
18776cdc083SAlberto Panizzo unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS];
18876cdc083SAlberto Panizzo unsigned short reg_val;
18976cdc083SAlberto Panizzo bool state_changed, is_zero_matrix;
19076cdc083SAlberto Panizzo int i;
19176cdc083SAlberto Panizzo
19276cdc083SAlberto Panizzo memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state));
19376cdc083SAlberto Panizzo
19476cdc083SAlberto Panizzo imx_keypad_scan_matrix(keypad, matrix_volatile_state);
19576cdc083SAlberto Panizzo
19676cdc083SAlberto Panizzo state_changed = false;
19776cdc083SAlberto Panizzo for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {
19876cdc083SAlberto Panizzo if ((keypad->cols_en_mask & (1 << i)) == 0)
19976cdc083SAlberto Panizzo continue;
20076cdc083SAlberto Panizzo
20176cdc083SAlberto Panizzo if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) {
20276cdc083SAlberto Panizzo state_changed = true;
20376cdc083SAlberto Panizzo break;
20476cdc083SAlberto Panizzo }
20576cdc083SAlberto Panizzo }
20676cdc083SAlberto Panizzo
20776cdc083SAlberto Panizzo /*
20876cdc083SAlberto Panizzo * If the matrix state is changed from the previous scan
20976cdc083SAlberto Panizzo * (Re)Begin the debouncing process, saving the new state in
21076cdc083SAlberto Panizzo * keypad->matrix_unstable_state.
21176cdc083SAlberto Panizzo * else
21276cdc083SAlberto Panizzo * Increase the count of number of scans with a stable state.
21376cdc083SAlberto Panizzo */
21476cdc083SAlberto Panizzo if (state_changed) {
21576cdc083SAlberto Panizzo memcpy(keypad->matrix_unstable_state, matrix_volatile_state,
21676cdc083SAlberto Panizzo sizeof(matrix_volatile_state));
21776cdc083SAlberto Panizzo keypad->stable_count = 0;
21876cdc083SAlberto Panizzo } else
21976cdc083SAlberto Panizzo keypad->stable_count++;
22076cdc083SAlberto Panizzo
22176cdc083SAlberto Panizzo /*
22276cdc083SAlberto Panizzo * If the matrix is not as stable as we want reschedule scan
22376cdc083SAlberto Panizzo * in the near future.
22476cdc083SAlberto Panizzo */
22576cdc083SAlberto Panizzo if (keypad->stable_count < IMX_KEYPAD_SCANS_FOR_STABILITY) {
22676cdc083SAlberto Panizzo mod_timer(&keypad->check_matrix_timer,
22776cdc083SAlberto Panizzo jiffies + msecs_to_jiffies(10));
22876cdc083SAlberto Panizzo return;
22976cdc083SAlberto Panizzo }
23076cdc083SAlberto Panizzo
23176cdc083SAlberto Panizzo /*
23276cdc083SAlberto Panizzo * If the matrix state is stable, fire the events and save the new
23376cdc083SAlberto Panizzo * stable state. Note, if the matrix is kept stable for longer
23476cdc083SAlberto Panizzo * (keypad->stable_count > IMX_KEYPAD_SCANS_FOR_STABILITY) all
23576cdc083SAlberto Panizzo * events have already been generated.
23676cdc083SAlberto Panizzo */
23776cdc083SAlberto Panizzo if (keypad->stable_count == IMX_KEYPAD_SCANS_FOR_STABILITY) {
23876cdc083SAlberto Panizzo imx_keypad_fire_events(keypad, matrix_volatile_state);
23976cdc083SAlberto Panizzo
24076cdc083SAlberto Panizzo memcpy(keypad->matrix_stable_state, matrix_volatile_state,
24176cdc083SAlberto Panizzo sizeof(matrix_volatile_state));
24276cdc083SAlberto Panizzo }
24376cdc083SAlberto Panizzo
24476cdc083SAlberto Panizzo is_zero_matrix = true;
24576cdc083SAlberto Panizzo for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {
24676cdc083SAlberto Panizzo if (matrix_volatile_state[i] != 0) {
24776cdc083SAlberto Panizzo is_zero_matrix = false;
24876cdc083SAlberto Panizzo break;
24976cdc083SAlberto Panizzo }
25076cdc083SAlberto Panizzo }
25176cdc083SAlberto Panizzo
25276cdc083SAlberto Panizzo
25376cdc083SAlberto Panizzo if (is_zero_matrix) {
25476cdc083SAlberto Panizzo /*
25576cdc083SAlberto Panizzo * All keys have been released. Enable only the KDI
25676cdc083SAlberto Panizzo * interrupt for future key presses (clear the KDI
25776cdc083SAlberto Panizzo * status bit and its sync chain before that).
25876cdc083SAlberto Panizzo */
25976cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
26076cdc083SAlberto Panizzo reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC;
26176cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
26276cdc083SAlberto Panizzo
26376cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
26476cdc083SAlberto Panizzo reg_val |= KBD_STAT_KDIE;
26576cdc083SAlberto Panizzo reg_val &= ~KBD_STAT_KRIE;
26676cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
26776cdc083SAlberto Panizzo } else {
26876cdc083SAlberto Panizzo /*
26976cdc083SAlberto Panizzo * Some keys are still pressed. Schedule a rescan in
27076cdc083SAlberto Panizzo * attempt to detect multiple key presses and enable
27176cdc083SAlberto Panizzo * the KRI interrupt to react quickly to key release
27276cdc083SAlberto Panizzo * event.
27376cdc083SAlberto Panizzo */
27476cdc083SAlberto Panizzo mod_timer(&keypad->check_matrix_timer,
27576cdc083SAlberto Panizzo jiffies + msecs_to_jiffies(60));
27676cdc083SAlberto Panizzo
27776cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
27876cdc083SAlberto Panizzo reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS;
27976cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
28076cdc083SAlberto Panizzo
28176cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
28276cdc083SAlberto Panizzo reg_val |= KBD_STAT_KRIE;
28376cdc083SAlberto Panizzo reg_val &= ~KBD_STAT_KDIE;
28476cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
28576cdc083SAlberto Panizzo }
28676cdc083SAlberto Panizzo }
28776cdc083SAlberto Panizzo
imx_keypad_irq_handler(int irq,void * dev_id)28876cdc083SAlberto Panizzo static irqreturn_t imx_keypad_irq_handler(int irq, void *dev_id)
28976cdc083SAlberto Panizzo {
29076cdc083SAlberto Panizzo struct imx_keypad *keypad = dev_id;
29176cdc083SAlberto Panizzo unsigned short reg_val;
29276cdc083SAlberto Panizzo
29376cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
29476cdc083SAlberto Panizzo
29576cdc083SAlberto Panizzo /* Disable both interrupt types */
29676cdc083SAlberto Panizzo reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
29776cdc083SAlberto Panizzo /* Clear interrupts status bits */
29876cdc083SAlberto Panizzo reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
29976cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
30076cdc083SAlberto Panizzo
30176cdc083SAlberto Panizzo if (keypad->enabled) {
30276cdc083SAlberto Panizzo /* The matrix is supposed to be changed */
30376cdc083SAlberto Panizzo keypad->stable_count = 0;
30476cdc083SAlberto Panizzo
30576cdc083SAlberto Panizzo /* Schedule the scanning procedure near in the future */
30676cdc083SAlberto Panizzo mod_timer(&keypad->check_matrix_timer,
30776cdc083SAlberto Panizzo jiffies + msecs_to_jiffies(2));
30876cdc083SAlberto Panizzo }
30976cdc083SAlberto Panizzo
31076cdc083SAlberto Panizzo return IRQ_HANDLED;
31176cdc083SAlberto Panizzo }
31276cdc083SAlberto Panizzo
imx_keypad_config(struct imx_keypad * keypad)31376cdc083SAlberto Panizzo static void imx_keypad_config(struct imx_keypad *keypad)
31476cdc083SAlberto Panizzo {
31576cdc083SAlberto Panizzo unsigned short reg_val;
31676cdc083SAlberto Panizzo
31776cdc083SAlberto Panizzo /*
31876cdc083SAlberto Panizzo * Include enabled rows in interrupt generation (KPCR[7:0])
31976cdc083SAlberto Panizzo * Configure keypad columns as open-drain (KPCR[15:8])
32076cdc083SAlberto Panizzo */
32176cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPCR);
32276cdc083SAlberto Panizzo reg_val |= keypad->rows_en_mask & 0xff; /* rows */
32376cdc083SAlberto Panizzo reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */
32476cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPCR);
32576cdc083SAlberto Panizzo
32676cdc083SAlberto Panizzo /* Write 0's to KPDR[15:8] (Colums) */
32776cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPDR);
32876cdc083SAlberto Panizzo reg_val &= 0x00ff;
32976cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPDR);
33076cdc083SAlberto Panizzo
33176cdc083SAlberto Panizzo /* Configure columns as output, rows as input (KDDR[15:0]) */
33276cdc083SAlberto Panizzo writew(0xff00, keypad->mmio_base + KDDR);
33376cdc083SAlberto Panizzo
33476cdc083SAlberto Panizzo /*
33576cdc083SAlberto Panizzo * Clear Key Depress and Key Release status bit.
33676cdc083SAlberto Panizzo * Clear both synchronizer chain.
33776cdc083SAlberto Panizzo */
33876cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
33976cdc083SAlberto Panizzo reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD |
34076cdc083SAlberto Panizzo KBD_STAT_KDSC | KBD_STAT_KRSS;
34176cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
34276cdc083SAlberto Panizzo
34376cdc083SAlberto Panizzo /* Enable KDI and disable KRI (avoid false release events). */
34476cdc083SAlberto Panizzo reg_val |= KBD_STAT_KDIE;
34576cdc083SAlberto Panizzo reg_val &= ~KBD_STAT_KRIE;
34676cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
34776cdc083SAlberto Panizzo }
34876cdc083SAlberto Panizzo
imx_keypad_inhibit(struct imx_keypad * keypad)34976cdc083SAlberto Panizzo static void imx_keypad_inhibit(struct imx_keypad *keypad)
35076cdc083SAlberto Panizzo {
35176cdc083SAlberto Panizzo unsigned short reg_val;
35276cdc083SAlberto Panizzo
35376cdc083SAlberto Panizzo /* Inhibit KDI and KRI interrupts. */
35476cdc083SAlberto Panizzo reg_val = readw(keypad->mmio_base + KPSR);
35576cdc083SAlberto Panizzo reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
356f35dd69bSMichael Grzeschik reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
35776cdc083SAlberto Panizzo writew(reg_val, keypad->mmio_base + KPSR);
35876cdc083SAlberto Panizzo
35976cdc083SAlberto Panizzo /* Colums as open drain and disable all rows */
36092aab960SAndreas Pretzsch reg_val = (keypad->cols_en_mask & 0xff) << 8;
36192aab960SAndreas Pretzsch writew(reg_val, keypad->mmio_base + KPCR);
36276cdc083SAlberto Panizzo }
36376cdc083SAlberto Panizzo
imx_keypad_close(struct input_dev * dev)36476cdc083SAlberto Panizzo static void imx_keypad_close(struct input_dev *dev)
36576cdc083SAlberto Panizzo {
36676cdc083SAlberto Panizzo struct imx_keypad *keypad = input_get_drvdata(dev);
36776cdc083SAlberto Panizzo
36876cdc083SAlberto Panizzo dev_dbg(&dev->dev, ">%s\n", __func__);
36976cdc083SAlberto Panizzo
37076cdc083SAlberto Panizzo /* Mark keypad as being inactive */
37176cdc083SAlberto Panizzo keypad->enabled = false;
37276cdc083SAlberto Panizzo synchronize_irq(keypad->irq);
37376cdc083SAlberto Panizzo del_timer_sync(&keypad->check_matrix_timer);
37476cdc083SAlberto Panizzo
37576cdc083SAlberto Panizzo imx_keypad_inhibit(keypad);
37676cdc083SAlberto Panizzo
37776cdc083SAlberto Panizzo /* Disable clock unit */
378a1e636e6SFabio Estevam clk_disable_unprepare(keypad->clk);
37976cdc083SAlberto Panizzo }
38076cdc083SAlberto Panizzo
imx_keypad_open(struct input_dev * dev)38176cdc083SAlberto Panizzo static int imx_keypad_open(struct input_dev *dev)
38276cdc083SAlberto Panizzo {
38376cdc083SAlberto Panizzo struct imx_keypad *keypad = input_get_drvdata(dev);
384333fbe84SFabio Estevam int error;
38576cdc083SAlberto Panizzo
38676cdc083SAlberto Panizzo dev_dbg(&dev->dev, ">%s\n", __func__);
38776cdc083SAlberto Panizzo
388333fbe84SFabio Estevam /* Enable the kpp clock */
389333fbe84SFabio Estevam error = clk_prepare_enable(keypad->clk);
390333fbe84SFabio Estevam if (error)
391333fbe84SFabio Estevam return error;
392333fbe84SFabio Estevam
39376cdc083SAlberto Panizzo /* We became active from now */
39476cdc083SAlberto Panizzo keypad->enabled = true;
39576cdc083SAlberto Panizzo
39676cdc083SAlberto Panizzo imx_keypad_config(keypad);
39776cdc083SAlberto Panizzo
39876cdc083SAlberto Panizzo /* Sanity control, not all the rows must be actived now. */
39976cdc083SAlberto Panizzo if ((readw(keypad->mmio_base + KPDR) & keypad->rows_en_mask) == 0) {
40076cdc083SAlberto Panizzo dev_err(&dev->dev,
40176cdc083SAlberto Panizzo "too many keys pressed, control pins initialisation\n");
40276cdc083SAlberto Panizzo goto open_err;
40376cdc083SAlberto Panizzo }
40476cdc083SAlberto Panizzo
40576cdc083SAlberto Panizzo return 0;
40676cdc083SAlberto Panizzo
40776cdc083SAlberto Panizzo open_err:
40876cdc083SAlberto Panizzo imx_keypad_close(dev);
40976cdc083SAlberto Panizzo return -EIO;
41076cdc083SAlberto Panizzo }
41176cdc083SAlberto Panizzo
41227ec39c0SJingoo Han static const struct of_device_id imx_keypad_of_match[] = {
4130e14235eSLiu Ying { .compatible = "fsl,imx21-kpp", },
4140e14235eSLiu Ying { /* sentinel */ }
4150e14235eSLiu Ying };
4160e14235eSLiu Ying MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
4170e14235eSLiu Ying
imx_keypad_probe(struct platform_device * pdev)4185298cc4cSBill Pemberton static int imx_keypad_probe(struct platform_device *pdev)
41976cdc083SAlberto Panizzo {
42076cdc083SAlberto Panizzo struct imx_keypad *keypad;
42176cdc083SAlberto Panizzo struct input_dev *input_dev;
4220e14235eSLiu Ying int irq, error, i, row, col;
42376cdc083SAlberto Panizzo
42476cdc083SAlberto Panizzo irq = platform_get_irq(pdev, 0);
4250bec8b7eSStephen Boyd if (irq < 0)
4267fb45edbSFabio Estevam return irq;
42776cdc083SAlberto Panizzo
428da5bce19SFabio Estevam input_dev = devm_input_allocate_device(&pdev->dev);
42976cdc083SAlberto Panizzo if (!input_dev) {
43076cdc083SAlberto Panizzo dev_err(&pdev->dev, "failed to allocate the input device\n");
431da5bce19SFabio Estevam return -ENOMEM;
43276cdc083SAlberto Panizzo }
43376cdc083SAlberto Panizzo
434061a5ad7SFabio Estevam keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
43576cdc083SAlberto Panizzo if (!keypad) {
43676cdc083SAlberto Panizzo dev_err(&pdev->dev, "not enough memory for driver data\n");
437da5bce19SFabio Estevam return -ENOMEM;
43876cdc083SAlberto Panizzo }
43976cdc083SAlberto Panizzo
44076cdc083SAlberto Panizzo keypad->input_dev = input_dev;
44176cdc083SAlberto Panizzo keypad->irq = irq;
44276cdc083SAlberto Panizzo keypad->stable_count = 0;
44376cdc083SAlberto Panizzo
4444ea40278SKees Cook timer_setup(&keypad->check_matrix_timer,
4454ea40278SKees Cook imx_keypad_check_for_events, 0);
44676cdc083SAlberto Panizzo
447d8ea61c5SAnson Huang keypad->mmio_base = devm_platform_ioremap_resource(pdev, 0);
448da5bce19SFabio Estevam if (IS_ERR(keypad->mmio_base))
449da5bce19SFabio Estevam return PTR_ERR(keypad->mmio_base);
45076cdc083SAlberto Panizzo
451da5bce19SFabio Estevam keypad->clk = devm_clk_get(&pdev->dev, NULL);
45276cdc083SAlberto Panizzo if (IS_ERR(keypad->clk)) {
45376cdc083SAlberto Panizzo dev_err(&pdev->dev, "failed to get keypad clock\n");
454da5bce19SFabio Estevam return PTR_ERR(keypad->clk);
45576cdc083SAlberto Panizzo }
45676cdc083SAlberto Panizzo
45776cdc083SAlberto Panizzo /* Init the Input device */
45876cdc083SAlberto Panizzo input_dev->name = pdev->name;
45976cdc083SAlberto Panizzo input_dev->id.bustype = BUS_HOST;
46076cdc083SAlberto Panizzo input_dev->dev.parent = &pdev->dev;
46176cdc083SAlberto Panizzo input_dev->open = imx_keypad_open;
46276cdc083SAlberto Panizzo input_dev->close = imx_keypad_close;
46376cdc083SAlberto Panizzo
46473e7f173SFabio Estevam error = matrix_keypad_build_keymap(NULL, NULL,
4651932811fSDmitry Torokhov MAX_MATRIX_KEY_ROWS,
4661932811fSDmitry Torokhov MAX_MATRIX_KEY_COLS,
4671932811fSDmitry Torokhov keypad->keycodes, input_dev);
4681932811fSDmitry Torokhov if (error) {
4691932811fSDmitry Torokhov dev_err(&pdev->dev, "failed to build keymap\n");
470da5bce19SFabio Estevam return error;
4711932811fSDmitry Torokhov }
47276cdc083SAlberto Panizzo
4730e14235eSLiu Ying /* Search for rows and cols enabled */
4740e14235eSLiu Ying for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
4750e14235eSLiu Ying for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
4760e14235eSLiu Ying i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
4770e14235eSLiu Ying if (keypad->keycodes[i] != KEY_RESERVED) {
4780e14235eSLiu Ying keypad->rows_en_mask |= 1 << row;
4790e14235eSLiu Ying keypad->cols_en_mask |= 1 << col;
4800e14235eSLiu Ying }
4810e14235eSLiu Ying }
4820e14235eSLiu Ying }
4830e14235eSLiu Ying dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
4840e14235eSLiu Ying dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
4850e14235eSLiu Ying
4861932811fSDmitry Torokhov __set_bit(EV_REP, input_dev->evbit);
48776cdc083SAlberto Panizzo input_set_capability(input_dev, EV_MSC, MSC_SCAN);
48876cdc083SAlberto Panizzo input_set_drvdata(input_dev, keypad);
48976cdc083SAlberto Panizzo
49076cdc083SAlberto Panizzo /* Ensure that the keypad will stay dormant until opened */
491e998200cSFabio Estevam error = clk_prepare_enable(keypad->clk);
492e998200cSFabio Estevam if (error)
493e998200cSFabio Estevam return error;
49476cdc083SAlberto Panizzo imx_keypad_inhibit(keypad);
495609455f4SFabio Estevam clk_disable_unprepare(keypad->clk);
49676cdc083SAlberto Panizzo
497da5bce19SFabio Estevam error = devm_request_irq(&pdev->dev, irq, imx_keypad_irq_handler, 0,
49876cdc083SAlberto Panizzo pdev->name, keypad);
49976cdc083SAlberto Panizzo if (error) {
50076cdc083SAlberto Panizzo dev_err(&pdev->dev, "failed to request IRQ\n");
501da5bce19SFabio Estevam return error;
50276cdc083SAlberto Panizzo }
50376cdc083SAlberto Panizzo
50476cdc083SAlberto Panizzo /* Register the input device */
50576cdc083SAlberto Panizzo error = input_register_device(input_dev);
50676cdc083SAlberto Panizzo if (error) {
50776cdc083SAlberto Panizzo dev_err(&pdev->dev, "failed to register input device\n");
508da5bce19SFabio Estevam return error;
50976cdc083SAlberto Panizzo }
51076cdc083SAlberto Panizzo
51176cdc083SAlberto Panizzo platform_set_drvdata(pdev, keypad);
51276cdc083SAlberto Panizzo device_init_wakeup(&pdev->dev, 1);
51376cdc083SAlberto Panizzo
51476cdc083SAlberto Panizzo return 0;
51576cdc083SAlberto Panizzo }
51676cdc083SAlberto Panizzo
imx_kbd_noirq_suspend(struct device * dev)517ce9a53ebSAnson Huang static int __maybe_unused imx_kbd_noirq_suspend(struct device *dev)
51881e8f2bcSHui Wang {
51981e8f2bcSHui Wang struct platform_device *pdev = to_platform_device(dev);
52081e8f2bcSHui Wang struct imx_keypad *kbd = platform_get_drvdata(pdev);
52181e8f2bcSHui Wang struct input_dev *input_dev = kbd->input_dev;
522ce9a53ebSAnson Huang unsigned short reg_val = readw(kbd->mmio_base + KPSR);
52381e8f2bcSHui Wang
52481e8f2bcSHui Wang /* imx kbd can wake up system even clock is disabled */
52581e8f2bcSHui Wang mutex_lock(&input_dev->mutex);
52681e8f2bcSHui Wang
527d69f0a43SAndrzej Pietrasiewicz if (input_device_enabled(input_dev))
528a1e636e6SFabio Estevam clk_disable_unprepare(kbd->clk);
52981e8f2bcSHui Wang
53081e8f2bcSHui Wang mutex_unlock(&input_dev->mutex);
53181e8f2bcSHui Wang
532ce9a53ebSAnson Huang if (device_may_wakeup(&pdev->dev)) {
533ce9a53ebSAnson Huang if (reg_val & KBD_STAT_KPKD)
534ce9a53ebSAnson Huang reg_val |= KBD_STAT_KRIE;
535ce9a53ebSAnson Huang if (reg_val & KBD_STAT_KPKR)
536ce9a53ebSAnson Huang reg_val |= KBD_STAT_KDIE;
537ce9a53ebSAnson Huang writew(reg_val, kbd->mmio_base + KPSR);
538ce9a53ebSAnson Huang
53981e8f2bcSHui Wang enable_irq_wake(kbd->irq);
540ce9a53ebSAnson Huang }
54181e8f2bcSHui Wang
54281e8f2bcSHui Wang return 0;
54381e8f2bcSHui Wang }
54481e8f2bcSHui Wang
imx_kbd_noirq_resume(struct device * dev)545ce9a53ebSAnson Huang static int __maybe_unused imx_kbd_noirq_resume(struct device *dev)
54681e8f2bcSHui Wang {
54781e8f2bcSHui Wang struct platform_device *pdev = to_platform_device(dev);
54881e8f2bcSHui Wang struct imx_keypad *kbd = platform_get_drvdata(pdev);
54981e8f2bcSHui Wang struct input_dev *input_dev = kbd->input_dev;
550333fbe84SFabio Estevam int ret = 0;
55181e8f2bcSHui Wang
55281e8f2bcSHui Wang if (device_may_wakeup(&pdev->dev))
55381e8f2bcSHui Wang disable_irq_wake(kbd->irq);
55481e8f2bcSHui Wang
55581e8f2bcSHui Wang mutex_lock(&input_dev->mutex);
55681e8f2bcSHui Wang
557d69f0a43SAndrzej Pietrasiewicz if (input_device_enabled(input_dev)) {
558333fbe84SFabio Estevam ret = clk_prepare_enable(kbd->clk);
559333fbe84SFabio Estevam if (ret)
560333fbe84SFabio Estevam goto err_clk;
561333fbe84SFabio Estevam }
56281e8f2bcSHui Wang
563333fbe84SFabio Estevam err_clk:
56481e8f2bcSHui Wang mutex_unlock(&input_dev->mutex);
56581e8f2bcSHui Wang
566333fbe84SFabio Estevam return ret;
56781e8f2bcSHui Wang }
56881e8f2bcSHui Wang
569ce9a53ebSAnson Huang static const struct dev_pm_ops imx_kbd_pm_ops = {
570ce9a53ebSAnson Huang SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_kbd_noirq_suspend, imx_kbd_noirq_resume)
571ce9a53ebSAnson Huang };
57281e8f2bcSHui Wang
57376cdc083SAlberto Panizzo static struct platform_driver imx_keypad_driver = {
57476cdc083SAlberto Panizzo .driver = {
57576cdc083SAlberto Panizzo .name = "imx-keypad",
57681e8f2bcSHui Wang .pm = &imx_kbd_pm_ops,
57773e7f173SFabio Estevam .of_match_table = imx_keypad_of_match,
57876cdc083SAlberto Panizzo },
57976cdc083SAlberto Panizzo .probe = imx_keypad_probe,
58076cdc083SAlberto Panizzo };
5815146c84fSJJ Ding module_platform_driver(imx_keypad_driver);
58276cdc083SAlberto Panizzo
58376cdc083SAlberto Panizzo MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>");
58476cdc083SAlberto Panizzo MODULE_DESCRIPTION("IMX Keypad Port Driver");
58576cdc083SAlberto Panizzo MODULE_LICENSE("GPL v2");
58676cdc083SAlberto Panizzo MODULE_ALIAS("platform:imx-keypad");
587