input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
Subsystems (3)
PATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
The MXC family of Application Processors is shipped with a Keypad Port supported now by this driver.
The peripheral can control up to an 8x8 matrix key pad where all the scanning procedure is done via software.
The hardware provide two interrupts: one for a key pressed (KDI) and one for all key releases (KRI). There is also a simple circuit for glitch reduction (said for synchronization) made by two series of 3 D-latches clocked by the keypad-clock that stabilize the interrupts sources.
KDI and KRI are fired only if the respective conditions are maintained for at last 4 keypad-clock cycle.
Since those circuits are poor for a correct debounce process (the keypad-clock frequency is 32K and bounces longer than 94us are not masked) the driver, when an interrupt arrive, sample the matrix with a period of 2ms until for MXC_KEYPAD_SCANS_FOR_STABILITY (3) times the matrix result stable. After reached the stability proper events are fired comparing the new and previous stable state.
If any key are maintained pressed for a longer time the driver continue to probe the matrix with a greater period (60ms) to catch possible multiple key pressures without overloading the cpu.
This process ends when all keys are released.
This driver is tested to build in kernel or as a module and follow the specification of Freescale Application processors:
i.MX25 i.MX27 i.MX31 i.MX35 i.MX51 especially tested on i.MX31.
---
Changes in v2:
-This driver completely apply to matrix_keypad interface. -Rearranged remove procedure.
-Automated hardware configuration, based on matrix_keypad_data. -Removed useless macro.
Changes in v3:
-Make use of timer API for interrupt management instead of threaded irq. -Little fixes.
Changes in v4:
-Better error handling.
-Coding style fixes.
-Fix parameters of clk_get.
Changes in v5:
-Rework of debouncing process. Now it is the driver that decide when the
matrix is stable to check for events.
-No needs of particular platform data.
drivers/input/keyboard/Kconfig | 9 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/mxc_keypad.c | 569 +++++++++++++++++++++++++++++++++++ 3 files changed, 579 insertions(+), 0 deletions(-) create mode 100644 drivers/input/keyboard/mxc_keypad.c
drivers/input/keyboard/Kconfig +9 -0
@@ -292,6 +292,15 @@ config KEYBOARD_MAX7359 To compile this driver as a module, choose M here: the module will be called max7359_keypad. +config KEYBOARD_MXC + tristate "MXC keypad support" + depends on ARCH_MXC + help + Enable support for MXC keypad port. + + To compile this driver as a module, choose M here: the + module will be called mxc_keypad. + config KEYBOARD_NEWTON tristate "Newton keyboard" select SERIO
drivers/input/keyboard/Makefile +1 -0
@@ -25,6 +25,7 @@ obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o +obj-$(CONFIG_KEYBOARD_MXC) += mxc_keypad.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
drivers/input/keyboard/mxc_keypad.c new file +569 -0
569 lines not on this page · raw message
Signature · 1 line
1.6.3.3
PATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
Hello Alberto,
Quoted 1 line · Alberto Panizzo
The MXC family of Application Processors is shipped with a Keypad Port
would you mind to do s/MXC/IMX/ in your patch? Currently both are used but we (=Sascha and me) will try to deprecate MXC.
Quoted 1 line
+ * >>Power management need to be implemented<<.
s/need/needs/ (or alternatively do it :-) and maybe s/<<\./.<</ ?
drivers/input/keyboard/mxc_keypad.c line 59
Quoted 4 lines
[...]
drivers/input/keyboard/mxc_keypad.c new file +3
-0+ +/* The matrix is stable only if no changes are detected after +* MXC_KEYPAD_SCANS_FOR_STABILITY scans */
unusual format of the comment.
/* * ... * ... */
is the most found way in the kernel for multi-line comments.
Quoted 2 lines
[...]
+MODULE_LICENSE("GPL v2");
There is no space expected in the string passed to MODULE_LICENSE.
Best regards
Uwe
Signature · 2 lines
Pengutronix e.K. | Uwe Kleine-K?nig | Industrial Linux Solutions | http://www.pengutronix.de/ |
PATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
Quoted 6 lines · Uwe Kleine-K?nig
Hello Alberto,
Quoted 1 line · Alberto Panizzo
The MXC family of Application Processors is shipped with a Keypad Port
would you mind to do s/MXC/IMX/ in your patch? Currently both are used but we (=Sascha and me) will try to deprecate MXC.
For sure! and for what I can say I approve this plan.
Quoted 3 lines
Quoted 2 lines
+ * >>Power management need to be implemented<<.
s/need/needs/ (or alternatively do it :-) and maybe s/<<\./.<</ ?
hehe, power management is the next part. What I wont is to put the device running before the next merge window if it is possible and then present all my work on mx31_3ds.
Quoted 7 lines
drivers/input/keyboard/mxc_keypad.c line 59
Quoted 5 lines
[...]
drivers/input/keyboard/mxc_keypad.c new file +3
-0+ +/* The matrix is stable only if no changes are detected after +* MXC_KEYPAD_SCANS_FOR_STABILITY scans */ unusual format of the comment.
Quoted 6 lines
/* * ... * ... */is the most found way in the kernel for multi-line comments.
Only a way to compress them..
Quoted 4 lines
Quoted 3 lines
[...]
+MODULE_LICENSE("GPL v2");There is no space expected in the string passed to
MODULE_LICENSE.
Thanks.
Alberto!
PATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
In reply to Uwe Kleine-KönigQuoted 3 lines · Uwe Kleine-K?nig
Quoted 2 lines
[...]
+MODULE_LICENSE("GPL v2");There is no space expected in the string passed to
MODULE_LICENSE.
in include/linux/module.h:
/*
* The following license idents are currently accepted as indicating free
* software modules
*
* "GPL" [GNU Public License v2 or later]
* "GPL v2" [GNU Public License v2]
...
PATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
Quoted 7 lines · Alberto Panizzo
Quoted 3 lines · Uwe Kleine-K?nig
Quoted 2 lines
[...]
+MODULE_LICENSE("GPL v2");There is no space expected in the string passed to
MODULE_LICENSE.in
include/linux/module.h:
Quoted 7 lines
/*
* The following license idents are currently accepted as indicating free
* software modules
*
* "GPL" [GNU Public License v2 or later]
* "GPL v2" [GNU Public License v2]
...
Hmmmm, then sorry.
Best regards
Uwe
Signature · 2 lines
Pengutronix e.K. | Uwe Kleine-K?nig | Industrial Linux Solutions | http://www.pengutronix.de/ |
Dmitry Torokhov
maintainerPATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
In reply to Alberto PanizzoHi Alberto,
Quoted 8 lines · Alberto Panizzo
drivers/input/keyboard/mxc_keypad.c new file +8
-0+ +/* mxc_keypad_check_for_events is the timer handler. +* It is executed in a non interruptible area of the kernel (Soft interrupt) */ +static void mxc_keypad_check_for_events (unsigned long data) +{ +struct mxc_keypad *keypad = (struct mxc_keypad *) data; +struct input_dev *input_dev = keypad->input_dev; +unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS ];
drivers/input/keyboard/mxc_keypad.c line 188
Quoted 10 lines
drivers/input/keyboard/mxc_keypad.c new file +10
-0+unsigned short reg_val; +bool state_changed, is_zero_matrix; +int i; + +memset (matrix_volatile_state,0 ,sizeof (matrix_volatile_state)); + +/* Before exit fire key release events. */ +if (keypad->exit_flag) { +dev_dbg (&input_dev->dev,"%s: exiting.\n" , __func__); +mxc_keypad_fire_events (keypad, matrix_volatile_state);
Input core already does it no device disconnect, no need to do this here I think.
Quoted 22 lines
drivers/input/keyboard/mxc_keypad.c new file +22
-0+return ; + } + +mxc_keypad_scan_matrix (keypad, matrix_volatile_state); + + state_changed = false; +for (i =0 ; (i <MAX_MATRIX_KEY_COLS ) && !state_changed; i++) { +if ((keypad->cols_en_mask & (1 << i)) ==0 ) +continue ; + +if (keypad->matrix_unstable_state[i] ^ + matrix_volatile_state[i]) + state_changed = true; + } + +/* If the matrix state is changed from the previous scan +* (Re)Begin the debouncing process, saving the new state in +* keypad->matrix_unstable_state. +* else +* Increase the count of number of scans with a stable state.*/ +if (state_changed) { + memcpy(keypad->matrix_unstable_state, matrix_volatile_state,
drivers/input/keyboard/mxc_keypad.c line 220
Quoted 10 lines
drivers/input/keyboard/mxc_keypad.c new file +10
-0+ sizeof(matrix_volatile_state)); + keypad->stable_count =0 ; + }else + keypad->stable_count++; + +/* If the matrix is not as stable as we want reschedule a matrix scan +* near in the future. */ +if (keypad->stable_count <MXC_KEYPAD_SCANS_FOR_STABILITY ) { +mod_timer (&keypad->check_matrix_timer, + jiffies +msecs_to_jiffies (2 ));
2ms is too fast IMO; 10 ms is more realistic.
Quoted 118 lines
drivers/input/keyboard/mxc_keypad.c new file +118
-0+return ; + } + +/* If the matrix is stable as we need, fire the events and save the new +* stable state. +* Note, if the matrix is more stable (keypad->stable_count > +* MXC_KEYPAD_SCANS_FOR_STABILITY)all events are already fired.We are in +* the loop of multiple key pressure detection waiting for a change. */ +if (keypad->stable_count ==MXC_KEYPAD_SCANS_FOR_STABILITY ) { +mxc_keypad_fire_events (keypad, matrix_volatile_state); + +memcpy (keypad->matrix_stable_state, matrix_volatile_state, +sizeof (matrix_volatile_state)); + } + + is_zero_matrix = true; +for (i =0 ; (i <MAX_MATRIX_KEY_COLS ) && is_zero_matrix; i++) +if (matrix_volatile_state[i] !=0 ) + is_zero_matrix = false; + + +if (is_zero_matrix) { +/* No keys are still pressed. +* Enable only the KDI interrupt for future key pressures. +* (clear the KDI status bit and his sync chain before) */ + reg_val =readw (keypad->mmio_base +KPSR ); + reg_val |=KBD_STAT_KPKD |KBD_STAT_KDSC ; +writew (reg_val, keypad->mmio_base +KPSR ); + + reg_val =readw (keypad->mmio_base +KPSR ); + reg_val |=KBD_STAT_KDIE ; + reg_val &= ~KBD_STAT_KRIE ; +writew (reg_val, keypad->mmio_base +KPSR ); + }else { +/* Still there are keys pressed. Schedule a rescan for multiple +* key pressure detection and enable the KRI interrupt for fast +* reaction to an all key release event. */ +mod_timer (&keypad->check_matrix_timer, + jiffies +msecs_to_jiffies (60 )); + + reg_val =readw (keypad->mmio_base +KPSR ); + reg_val |=KBD_STAT_KPKR |KBD_STAT_KRSS ; +writew (reg_val, keypad->mmio_base +KPSR ); + + reg_val =readw (keypad->mmio_base +KPSR ); + reg_val |=KBD_STAT_KRIE ; + reg_val &= ~KBD_STAT_KDIE ; +writew (reg_val, keypad->mmio_base +KPSR ); + } +} + +static irqreturn_t mxc_keypad_irq_handler (int irq,void *dev_id) +{ +struct mxc_keypad *keypad = dev_id; +unsigned short reg_val; + + reg_val =readw (keypad->mmio_base +KPSR ); + +/* Disable every keypad interrupt */ + reg_val &= ~(KBD_STAT_KRIE |KBD_STAT_KDIE ); +/* Clear interrupts status bits */ + reg_val |=KBD_STAT_KPKR |KBD_STAT_KPKD ; +writew (reg_val, keypad->mmio_base +KPSR ); + +/* The matrix is supposed to be changed */ + keypad->stable_count =0 ; + +/* Schedule the scanning procedure near in the future */ +mod_timer (&keypad->check_matrix_timer, jiffies +msecs_to_jiffies (2 )); + +return IRQ_HANDLED ; +} + +static void mxc_keypad_config (struct mxc_keypad *keypad) +{ +unsigned short reg_val; + +/* Include enabled rows in interrupt generation (KPCR[7:0]) +* Configure keypad columns as open-drain (KPCR[15:8]) */ + reg_val =readw (keypad->mmio_base +KPCR ); + reg_val |= keypad->rows_en_mask &0xff ;/* rows */ + reg_val |= (keypad->cols_en_mask &0xff ) <<8 ;/* cols */ +writew (reg_val, keypad->mmio_base +KPCR ); + +/* Write 0's to KPDR[15:8] (Colums)*/ + reg_val =readw (keypad->mmio_base +KPDR ); + reg_val &=0x00ff ; +writew (reg_val, keypad->mmio_base +KPDR ); + +/* Configure columns as output, rows as input (KDDR[15:0]) */ +writew (0xff00 , keypad->mmio_base +KDDR ); + +/* Clear Key Depress and Key Release status bit. +* Clear both synchronizer chain. */ + reg_val =readw (keypad->mmio_base +KPSR ); + reg_val |=KBD_STAT_KPKR |KBD_STAT_KPKD | +KBD_STAT_KDSC |KBD_STAT_KRSS ; +writew (reg_val, keypad->mmio_base +KPSR ); + +/* Enable KDI and disable KRI (avoid false release events). */ + reg_val |=KBD_STAT_KDIE ; + reg_val &= ~KBD_STAT_KRIE ; +writew (reg_val, keypad->mmio_base +KPSR ); +} + +static void mxc_keypad_inhibit (struct mxc_keypad *keypad) +{ +unsigned short reg_val; + +/* Inhibit KDI and KRI interrupts. */ + reg_val =readw (keypad->mmio_base +KPSR ); + reg_val &= ~(KBD_STAT_KRIE |KBD_STAT_KDIE ); +writew (reg_val, keypad->mmio_base +KPSR ); + +/* Colums as open drain and disable all rows */ +writew (0xff00 , keypad->mmio_base +KPCR ); +} +
drivers/input/keyboard/mxc_keypad.c line 348
Quoted 10 lines
drivers/input/keyboard/mxc_keypad.c new file +10
-0+static int mxc_keypad_open (struct input_dev *dev) +{ +struct mxc_keypad *keypad =input_get_drvdata (dev); + +dev_dbg (&dev->dev,">%s\n" , __func__); + +/* Init Keypad timer */ +init_timer (&keypad->check_matrix_timer); + keypad->check_matrix_timer.function = mxc_keypad_check_for_events; + keypad->check_matrix_timer.data = (unsigned long ) keypad;
setup_timer(). You also need to do "keypad->exit_flag = false;" here, otherwise you will not be able to open the device again.
Quoted 21 lines
drivers/input/keyboard/mxc_keypad.c new file +21
-0+ +/* Enable the kpp clock */ +clk_enable (keypad->clk); +mxc_keypad_config (keypad); + +/* Sanity control, not all the rows must be actived now. */ +if ((readw (keypad->mmio_base +KPDR ) & keypad->rows_en_mask) ==0 ) { +dev_err (&dev->dev, +"too much keys pressed for now, control pins initialisation\n" ); + goto open_err; + } + +return 0 ; + +open_err: + keypad->exit_flag = true; +del_timer_sync (&keypad->check_matrix_timer); +mxc_keypad_inhibit (keypad); +return -EIO ; +} +
drivers/input/keyboard/mxc_keypad.c line 379
Quoted 10 lines
drivers/input/keyboard/mxc_keypad.c new file +10
-0+static void mxc_keypad_close (struct input_dev *dev) +{ +struct mxc_keypad *keypad =input_get_drvdata (dev); + +dev_dbg (&dev->dev,">%s\n" , __func__); + +/* Make sure no checks are pending and release any events.*/ + keypad->exit_flag = true; +del_timer_sync (&keypad->check_matrix_timer); +mxc_keypad_check_for_events ((unsigned long ) keypad);
Why bother with checking for events? The device is being closed - nobody is in terested in the events since nobody is listening.
Quoted 93 lines
drivers/input/keyboard/mxc_keypad.c new file +93
-0+ +mxc_keypad_inhibit (keypad); + +/* Disable clock unit */ +clk_disable (keypad->clk); +} + +static int __devinit mxc_keypad_probe (struct platform_device *pdev) +{ +const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; +struct mxc_keypad *keypad; +struct input_dev *input_dev; +struct resource *res; +int irq, error, i; + +if (keymap_data ==NULL ) { +dev_err (&pdev->dev,"no keymap defined\n" ); +return -EINVAL ; + } + + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) { +dev_err (&pdev->dev,"no irq defined in platform data\n" ); +return -EINVAL ; + } + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (res ==NULL ) { +dev_err (&pdev->dev,"no I/O memory defined in platform data\n" ); +return -EINVAL ; + } + + res =request_mem_region (res->start,resource_size (res), pdev->name); +if (res ==NULL ) { +dev_err (&pdev->dev,"failed to request I/O memory\n" ); +return -EBUSY ; + } + + input_dev =input_allocate_device (); +if (!input_dev) { +dev_err (&pdev->dev,"failed to allocate the input device\n" ); + error = -ENOMEM ; + goto failed_rel_mem; + } + + keypad =kzalloc (sizeof (struct mxc_keypad ),GFP_KERNEL ); +if (!keypad) { +dev_err (&pdev->dev,"not enough memory for driver data\n" ); + error = -ENOMEM ; + goto failed_free_input; + } + + keypad->input_dev = input_dev; + keypad->irq = irq; + keypad->stable_count =0 ; + keypad->exit_flag = false; + + keypad->mmio_base =ioremap (res->start,resource_size (res)); +if (keypad->mmio_base ==NULL ) { +dev_err (&pdev->dev,"failed to remap I/O memory\n" ); + error = -ENOMEM ; + goto failed_free_priv; + } + + keypad->clk =clk_get (&pdev->dev,"kpp" ); +if (IS_ERR (keypad->clk)) { +dev_err (&pdev->dev,"failed to get keypad clock\n" ); + error =PTR_ERR (keypad->clk); + goto failed_unmap; + } + +/* Search for rows and cols enabled */ +for (i =0 ; i < keymap_data->keymap_size; i++) { + keypad->rows_en_mask |=1 <<KEY_ROW (keymap_data->keymap[i]); + keypad->cols_en_mask |=1 <<KEY_COL (keymap_data->keymap[i]); + } + +if (keypad->rows_en_mask > ((1 <<MAX_MATRIX_KEY_ROWS ) -1 ) || + keypad->cols_en_mask > ((1 <<MAX_MATRIX_KEY_COLS ) -1 )) { +dev_err (&pdev->dev,"invalid key data (too rows or colums)\n" ); + error = -EINVAL ; + goto failed_clock_put; + } +dev_dbg (&pdev->dev,"enabled rows mask: %x\n" , keypad->rows_en_mask); +dev_dbg (&pdev->dev,"enabled cols mask: %x\n" , keypad->cols_en_mask); + +/* Init the Input device */ + input_dev->name = pdev->name; + input_dev->id.bustype =BUS_HOST ; + input_dev->dev.parent = &pdev->dev; + input_dev->open = mxc_keypad_open; + input_dev->close = mxc_keypad_close; + input_dev->evbit[0 ] =BIT_MASK (EV_KEY ) |BIT_MASK (EV_REP );
drivers/input/keyboard/mxc_keypad.c line 482
Quoted 10 lines
drivers/input/keyboard/mxc_keypad.c new file +10
-0+ input_dev->keycode = keypad->keycodes; + input_dev->keycodesize =sizeof (keypad->keycodes[0 ]); + input_dev->keycodemax =ARRAY_SIZE (keypad->keycodes); + +matrix_keypad_build_keymap (keymap_data,MATRIX_ROW_SHIFT , + keypad->keycodes, input_dev->keybit); + +input_set_capability (input_dev,EV_MSC ,MSC_SCAN ); +input_set_drvdata (input_dev, keypad); +
I would feell more safe if we also called inhibit here. I there any chance that matrix can gerenrate interrupts at this point? If it can and we never call open/close then in remove there is a chace of freeing input device before freeing IRQ.
Quoted 92 lines
drivers/input/keyboard/mxc_keypad.c new file +87
-0+ error =request_irq (irq, mxc_keypad_irq_handler,IRQF_DISABLED , + pdev->name, keypad); +if (error) { +dev_err (&pdev->dev,"failed to request IRQ\n" ); + goto failed_clock_put; + } + +/* Register the input device */ + error =input_register_device (input_dev); +if (error) { +dev_err (&pdev->dev,"failed to register input device\n" ); + goto failed_free_irq; + } + +platform_set_drvdata (pdev, keypad); +device_init_wakeup (&pdev->dev,1 ); + +dev_info (&pdev->dev,"device probed\n" ); + +return 0 ; + +failed_free_irq: +free_irq (irq, pdev); +failed_clock_put: +clk_put (keypad->clk); +failed_unmap: +iounmap (keypad->mmio_base); +failed_free_priv: +kfree (keypad); +failed_free_input: +input_free_device (input_dev); +failed_rel_mem: +release_mem_region (res->start,resource_size (res)); +return error; +} + +static int __devexit mxc_keypad_remove(struct platform_device *pdev) +{ +struct mxc_keypad *keypad =platform_get_drvdata (pdev); +struct resource *res; + +dev_dbg (&pdev->dev,">%s\n" , __func__); + +platform_set_drvdata (pdev,NULL ); + +input_unregister_device (keypad->input_dev); + +free_irq (keypad->irq, keypad); +clk_put (keypad->clk); + +iounmap (keypad->mmio_base); + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +release_mem_region (res->start,resource_size (res)); + +kfree (keypad); + +dev_info (&pdev->dev,"device removed\n" ); + +return 0 ; +} + +static struct platform_driver mxc_keypad_driver = { + .driver = { + .name ="mxc-keypad" , + .owner =THIS_MODULE , + }, + .probe = mxc_keypad_probe, + .remove =__devexit_p (mxc_keypad_remove), +}; + +static int __init mxc_keypad_init (void ) +{ +return platform_driver_register (&mxc_keypad_driver); +} + +static void __exit mxc_keypad_exit(void) +{ +platform_driver_unregister (&mxc_keypad_driver); +} + +module_init (mxc_keypad_init); +module_exit (mxc_keypad_exit); + +MODULE_AUTHOR ("Alberto Panizzo <[email protected]>" ); +MODULE_DESCRIPTION ("MXC Keypad Port Driver" ); +MODULE_LICENSE ("GPL v2" ); +MODULE_ALIAS ("platform:mxc-keypad" );Signature · 1 line
1.6.3.3
Signature · 1 line
Dmitry
PATCH v5 1/1 input: MXC: add mxc-keypad driver to support the Keypad Port present in the mxc application processors family.
One question.. out of the patch
I am working at home, and for me the clock say 23:12
I am working on this because I am fresh graduated and I am continuing a University project adding row on my resume ;) and I am proud to work on linux kernel.
I suppose you respond me now for work.. true?
I wonder, where are your keyboards pressed? :)
Where do you live?