2014-04-11 15:09:29

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 0/7] Add ST Keyscan driver


Changes in v4:
- add reset controller management
- rename 'st,debounce_us' into 'st,debounce-us' in dt binding

Changes in v3: (bad manipulation)

Changes in v2:
- use standard format for matrix keymap
- suppress __exit mark for keyscan_remove()
- Call to keyscan_stop() shoudl go into keyscan_close() implementation
- use of SIMPLE_DEV_PM_OPS()
- rename compatibility name into "st,sti-keyscan"
- suppress platform data management
- omit vendor information
- cosmetic change and renaming

The goal of this series is to add ST Keyscan support to ST SoCs.
The DT definition is added for STiH415 and STiH416 SoCs on
B2000 board.


Gabriel Fernandez (7):
drivers: input: keyboard: st-keyscan: add keyscan driver
driver: reset: sti: add keyscan for stih415
driver: reset: sti: add keyscan for stih416
ARM: STi: DT: add keyscan for stih415
ARM: STi: DT: add keyscan for stih416
ARM: STi: DT: add keyscan for stih41x-b2000
ARM: multi_v7_defconfig: add ST Keyscan driver

.../devicetree/bindings/input/st-keyscan.txt | 60 +++++
arch/arm/boot/dts/stih415-pinctrl.dtsi | 16 ++
arch/arm/boot/dts/stih415.dtsi | 12 +
arch/arm/boot/dts/stih416-pinctrl.dtsi | 16 ++
arch/arm/boot/dts/stih416.dtsi | 12 +
arch/arm/boot/dts/stih41x-b2000.dtsi | 23 ++
arch/arm/configs/multi_v7_defconfig | 1 +
drivers/input/keyboard/Kconfig | 12 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/st-keyscan.c | 260 +++++++++++++++++++++
drivers/reset/sti/reset-stih415.c | 1 +
drivers/reset/sti/reset-stih416.c | 1 +
.../dt-bindings/reset-controller/stih415-resets.h | 1 +
.../dt-bindings/reset-controller/stih416-resets.h | 1 +
14 files changed, 417 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/st-keyscan.txt
create mode 100644 drivers/input/keyboard/st-keyscan.c

--
1.9.1


2014-04-11 15:09:04

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 4/7] ARM: STi: DT: add keyscan for stih415

Add keyscan support for stih415.
It is put disabled by default because it is not enabled on all boards
Also there are PIOs conflict with already claimed lines.

Signed-off-by: Giuseppe Condorelli <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
arch/arm/boot/dts/stih415-pinctrl.dtsi | 16 ++++++++++++++++
arch/arm/boot/dts/stih415.dtsi | 12 ++++++++++++
2 files changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/stih415-pinctrl.dtsi b/arch/arm/boot/dts/stih415-pinctrl.dtsi
index f09fb10..caeac7e 100644
--- a/arch/arm/boot/dts/stih415-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih415-pinctrl.dtsi
@@ -102,6 +102,22 @@
};
};

+ keyscan {
+ pinctrl_keyscan: keyscan {
+ st,pins {
+ keyin0 = <&PIO0 2 ALT2 IN>;
+ keyin1 = <&PIO0 3 ALT2 IN>;
+ keyin2 = <&PIO0 4 ALT2 IN>;
+ keyin3 = <&PIO2 6 ALT2 IN>;
+
+ keyout0 = <&PIO1 6 ALT2 OUT>;
+ keyout1 = <&PIO1 7 ALT2 OUT>;
+ keyout2 = <&PIO0 6 ALT2 OUT>;
+ keyout3 = <&PIO2 7 ALT2 OUT>;
+ };
+ };
+ };
+
sbc_i2c0 {
pinctrl_sbc_i2c0_default: sbc_i2c0-default {
st,pins {
diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi
index d89064c..ba0905c 100644
--- a/arch/arm/boot/dts/stih415.dtsi
+++ b/arch/arm/boot/dts/stih415.dtsi
@@ -206,5 +206,17 @@
pinctrl-0 = <&pinctrl_ir>;
resets = <&softreset STIH415_IRB_SOFTRESET>;
};
+
+ keyscan: keyscan@fe4b0000 {
+ compatible = "st,sti-keyscan";
+ status = "disabled";
+ reg = <0xfe4b0000 0x2000>;
+ interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+ clocks = <&CLK_SYSIN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_keyscan>;
+ resets = <&powerdown STIH415_KEYSCAN_POWERDOWN>,
+ <&softreset STIH415_KEYSCAN_SOFTRESET>;
+ };
};
};
--
1.9.1

2014-04-11 15:08:57

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 1/7] drivers: input: keyboard: st-keyscan: add keyscan driver

This patch adds ST Keyscan driver to use the keypad hw a subset
of ST boards provide. Specific board setup will be put in the
given dt.

Signed-off-by: Gabriel Fernandez <[email protected]>
Signed-off-by: Giuseppe Condorelli <[email protected]>
---
.../devicetree/bindings/input/st-keyscan.txt | 60 +++++
drivers/input/keyboard/Kconfig | 12 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/st-keyscan.c | 260 +++++++++++++++++++++
4 files changed, 333 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/st-keyscan.txt
create mode 100644 drivers/input/keyboard/st-keyscan.c

diff --git a/Documentation/devicetree/bindings/input/st-keyscan.txt b/Documentation/devicetree/bindings/input/st-keyscan.txt
new file mode 100644
index 0000000..51eb428
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/st-keyscan.txt
@@ -0,0 +1,60 @@
+* ST Keyscan controller Device Tree bindings
+
+The ST keyscan controller Device Tree binding is based on the
+matrix-keymap.
+
+Required properties:
+- compatible: "st,sti-keyscan"
+
+- reg: Register base address and size of st-keyscan controller.
+
+- interrupts: Interrupt number for the st-keyscan controller.
+
+- clocks: Must contain one entry, for the module clock.
+ See ../clocks/clock-bindings.txt for details.
+
+- pinctrl: Should specify pin control groups used for this controller.
+ See ../pinctrl/pinctrl-bindings.txt for details.
+
+- linux,keymap: The keymap for keys as described in the binding document
+ devicetree/bindings/input/matrix-keymap.txt.
+
+- keypad,num-rows: Number of row lines connected to the keypad controller.
+
+- keypad,num-columns: Number of column lines connected to the keypad
+ controller.
+
+Optional property:
+- st,debounce_us: Debouncing interval time in microseconds
+
+Example:
+
+keyscan: keyscan@fe4b0000 {
+ compatible = "st,sti-keyscan";
+ reg = <0xfe4b0000 0x2000>;
+ interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+ clocks = <&CLK_SYSIN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_keyscan>;
+
+ keypad,num-rows = <4>;
+ keypad,num-columns = <4>;
+ st,debounce_us = <5000>;
+
+ linux,keymap = < MATRIX_KEY(0x00, 0x00, KEY_F13)
+ MATRIX_KEY(0x00, 0x01, KEY_F9)
+ MATRIX_KEY(0x00, 0x02, KEY_F5)
+ MATRIX_KEY(0x00, 0x03, KEY_F1)
+ MATRIX_KEY(0x01, 0x00, KEY_F14)
+ MATRIX_KEY(0x01, 0x01, KEY_F10)
+ MATRIX_KEY(0x01, 0x02, KEY_F6)
+ MATRIX_KEY(0x01, 0x03, KEY_F2)
+ MATRIX_KEY(0x02, 0x00, KEY_F15)
+ MATRIX_KEY(0x02, 0x01, KEY_F11)
+ MATRIX_KEY(0x02, 0x02, KEY_F7)
+ MATRIX_KEY(0x02, 0x03, KEY_F3)
+ MATRIX_KEY(0x03, 0x00, KEY_F16)
+ MATRIX_KEY(0x03, 0x01, KEY_F12)
+ MATRIX_KEY(0x03, 0x02, KEY_F8)
+ MATRIX_KEY(0x03, 0x03, KEY_F4) >;
+ };
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 76842d7..40a377c 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -524,6 +524,18 @@ config KEYBOARD_STOWAWAY
To compile this driver as a module, choose M here: the
module will be called stowaway.

+config KEYBOARD_ST_KEYSCAN
+ tristate "STMicroelectronics keyscan support"
+ depends on ARCH_STI
+ select INPUT_EVDEV
+ select INPUT_MATRIXKMAP
+ help
+ Say Y here if you want to use a keypad attached to the keyscan block
+ on some STMicroelectronics SoC devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st-keyscan.
+
config KEYBOARD_SUNKBD
tristate "Sun Type 4 and Type 5 keyboard"
select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 11cff7b..7504ae1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
new file mode 100644
index 0000000..7ed3b3e
--- /dev/null
+++ b/drivers/input/keyboard/st-keyscan.c
@@ -0,0 +1,260 @@
+/*
+ * STMicroelectronics Key Scanning driver
+ *
+ * Copyright (c) 2014 STMicroelectonics Ltd.
+ * Author: Stuart Menefy <[email protected]>
+ *
+ * Based on sh_keysc.c, copyright 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input/matrix_keypad.h>
+
+#define ST_KEYSCAN_MAXKEYS 16
+
+#define KEYSCAN_CONFIG_OFF 0x0
+#define KEYSCAN_CONFIG_ENABLE 0x1
+#define KEYSCAN_DEBOUNCE_TIME_OFF 0x4
+#define KEYSCAN_MATRIX_STATE_OFF 0x8
+#define KEYSCAN_MATRIX_DIM_OFF 0xc
+#define KEYSCAN_MATRIX_DIM_X_SHIFT 0x0
+#define KEYSCAN_MATRIX_DIM_Y_SHIFT 0x2
+
+struct st_keyscan {
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ struct input_dev *input_dev;
+ unsigned int last_state;
+ unsigned int n_rows;
+ unsigned int n_cols;
+ unsigned int debounce_us;
+};
+
+static irqreturn_t keyscan_isr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct st_keyscan *keypad = platform_get_drvdata(pdev);
+ unsigned short *keycode = keypad->input_dev->keycode;
+ int state;
+ int change;
+
+ state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
+ change = keypad->last_state ^ state;
+
+ while (change) {
+ int scancode = __ffs(change);
+ int down = state & BIT(scancode);
+
+ input_report_key(keypad->input_dev, keycode[scancode], down);
+
+ change ^= BIT(scancode);
+ };
+ input_sync(keypad->input_dev);
+
+ keypad->last_state = state;
+
+ return IRQ_HANDLED;
+}
+
+static int keyscan_start(struct st_keyscan *keypad)
+{
+ clk_enable(keypad->clk);
+
+ writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
+ keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
+
+ writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
+ ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
+ keypad->base + KEYSCAN_MATRIX_DIM_OFF);
+
+ writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
+
+ return 0;
+}
+
+static void keyscan_stop(struct st_keyscan *keypad)
+{
+ writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
+
+ clk_disable(keypad->clk);
+}
+
+static int keyscan_open(struct input_dev *dev)
+{
+ struct st_keyscan *keypad = input_get_drvdata(dev);
+
+ return keyscan_start(keypad);
+}
+
+static void keyscan_close(struct input_dev *dev)
+{
+ struct st_keyscan *keypad = input_get_drvdata(dev);
+
+ keyscan_stop(keypad);
+}
+
+static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
+{
+ struct device *dev = keypad_data->input_dev->dev.parent;
+ struct device_node *np = dev->of_node;
+ int error;
+
+ error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
+ &keypad_data->n_cols);
+ if (error) {
+ dev_err(dev, "failed to parse keypad params\n");
+ return error;
+ }
+
+ of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
+
+ dev_info(dev, "n_rows=%d n_col=%d debounce=%d\n",
+ keypad_data->n_rows,
+ keypad_data->n_cols,
+ keypad_data->debounce_us);
+
+ return 0;
+}
+
+static int __init keyscan_probe(struct platform_device *pdev)
+{
+ struct st_keyscan *keypad_data;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int error;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "no keymap defined\n");
+ return -EINVAL;
+ }
+
+ keypad_data = devm_kzalloc(&pdev->dev,
+ sizeof(*keypad_data), GFP_KERNEL);
+ if (!keypad_data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(keypad_data->base))
+ return PTR_ERR(keypad_data->base);
+
+ keypad_data->irq = platform_get_irq(pdev, 0);
+ if (keypad_data->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
+ pdev->name, pdev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return error;
+ }
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+ keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad_data->clk)) {
+ dev_err(&pdev->dev, "cannot get clock");
+ return PTR_ERR(keypad_data->clk);
+ }
+
+ keypad_data->input_dev = input_dev;
+
+ input_dev->name = pdev->name;
+ input_dev->phys = "keyscan-keys/input0";
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = keyscan_open;
+ input_dev->close = keyscan_close;
+
+ input_dev->id.bustype = BUS_HOST;
+
+ error = keypad_matrix_key_parse_dt(keypad_data);
+ if (error)
+ return error;
+
+ error = matrix_keypad_build_keymap(NULL, NULL,
+ keypad_data->n_rows, keypad_data->n_cols,
+ NULL, input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to build keymap\n");
+ return error;
+ }
+
+ input_set_drvdata(input_dev, keypad_data);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ return error;
+ }
+
+ platform_set_drvdata(pdev, keypad_data);
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+
+ return 0;
+}
+
+static int keyscan_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct st_keyscan *keypad = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(keypad->irq);
+ else
+ keyscan_stop(keypad);
+
+ return 0;
+}
+
+static int keyscan_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct st_keyscan *keypad = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(keypad->irq);
+ else
+ keyscan_start(keypad);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume);
+
+static const struct of_device_id keyscan_of_match[] = {
+ { .compatible = "st,sti-keyscan" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, keyscan_of_match);
+
+struct platform_driver keyscan_device_driver = {
+ .probe = keyscan_probe,
+ .driver = {
+ .name = "st-keyscan",
+ .pm = &keyscan_dev_pm_ops,
+ .of_match_table = of_match_ptr(keyscan_of_match),
+ }
+};
+
+module_platform_driver(keyscan_device_driver);
+
+MODULE_AUTHOR("Stuart Menefy <[email protected]>");
+MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
+MODULE_LICENSE("GPL");
--
1.9.1

2014-04-11 15:09:31

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 3/7] driver: reset: sti: add keyscan for stih416

Add keyscan reset on stih416 reset controller.

Signed-off-by: Giuseppe Condorelli <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
drivers/reset/sti/reset-stih416.c | 1 +
include/dt-bindings/reset-controller/stih416-resets.h | 1 +
2 files changed, 2 insertions(+)

diff --git a/drivers/reset/sti/reset-stih416.c b/drivers/reset/sti/reset-stih416.c
index fe3bf02..5fc9870 100644
--- a/drivers/reset/sti/reset-stih416.c
+++ b/drivers/reset/sti/reset-stih416.c
@@ -104,6 +104,7 @@ static const struct syscfg_reset_channel_data stih416_softresets[] = {
[STIH416_COMPO_A_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 4),
[STIH416_VP8_DEC_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 10),
[STIH416_VTG_MAIN_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 16),
+ [STIH416_KEYSCAN_SOFTRESET] = STIH416_SRST_LPM(LPM_SYSCFG_1, 8),
};

static struct syscfg_reset_controller_data stih416_powerdown_controller = {
diff --git a/include/dt-bindings/reset-controller/stih416-resets.h b/include/dt-bindings/reset-controller/stih416-resets.h
index 2127743..fcf9af1 100644
--- a/include/dt-bindings/reset-controller/stih416-resets.h
+++ b/include/dt-bindings/reset-controller/stih416-resets.h
@@ -46,5 +46,6 @@
#define STIH416_COMPO_A_SOFTRESET 25
#define STIH416_VP8_DEC_SOFTRESET 26
#define STIH416_VTG_MAIN_SOFTRESET 27
+#define STIH416_KEYSCAN_SOFTRESET 28

#endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH416 */
--
1.9.1

2014-04-11 15:10:42

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 6/7] ARM: STi: DT: add keyscan for stih41x-b2000

Add keyscan setup for stih415/h416 b2000.
Both have same raw/column lines number, debounce time and keymap.

Signed-off-by: Giuseppe Condorelli <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
arch/arm/boot/dts/stih41x-b2000.dtsi | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

diff --git a/arch/arm/boot/dts/stih41x-b2000.dtsi b/arch/arm/boot/dts/stih41x-b2000.dtsi
index bf65c49..403bf1b 100644
--- a/arch/arm/boot/dts/stih41x-b2000.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2000.dtsi
@@ -6,6 +6,7 @@
* it under the terms of the GNU General Public License version 2 as
* publishhed by the Free Software Foundation.
*/
+#include <dt-bindings/input/input.h>
/ {

memory{
@@ -68,5 +69,27 @@
snps,reset-active-low;
snps,reset-delays-us = <0 10000 10000>;
};
+
+ keyscan: keyscan@fe4b0000 {
+ keypad,num-rows = <4>;
+ keypad,num-columns = <4>;
+ st,debounce-us = <5000>;
+ linux,keymap = < MATRIX_KEY(0x00, 0x00, KEY_F13)
+ MATRIX_KEY(0x00, 0x01, KEY_F9)
+ MATRIX_KEY(0x00, 0x02, KEY_F5)
+ MATRIX_KEY(0x00, 0x03, KEY_F1)
+ MATRIX_KEY(0x01, 0x00, KEY_F14)
+ MATRIX_KEY(0x01, 0x01, KEY_F10)
+ MATRIX_KEY(0x01, 0x02, KEY_F6)
+ MATRIX_KEY(0x01, 0x03, KEY_F2)
+ MATRIX_KEY(0x02, 0x00, KEY_F15)
+ MATRIX_KEY(0x02, 0x01, KEY_F11)
+ MATRIX_KEY(0x02, 0x02, KEY_F7)
+ MATRIX_KEY(0x02, 0x03, KEY_F3)
+ MATRIX_KEY(0x03, 0x00, KEY_F16)
+ MATRIX_KEY(0x03, 0x01, KEY_F12)
+ MATRIX_KEY(0x03, 0x02, KEY_F8)
+ MATRIX_KEY(0x03, 0x03, KEY_F4) >;
+ };
};
};
--
1.9.1

2014-04-11 15:10:40

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 7/7] ARM: multi_v7_defconfig: add ST Keyscan driver

This patch adds KEYBOARD_ST_KEYSCAN config

Signed-off-by: Gabriel Fernandez <[email protected]>
Acked-by: Lee Jones <[email protected]>
---
arch/arm/configs/multi_v7_defconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index d4e8a47..4f0dce1 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -143,6 +143,7 @@ CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_TEGRA=y
CONFIG_KEYBOARD_SPEAR=y
+CONFIG_KEYBOARD_ST_KEYSCAN=y
CONFIG_KEYBOARD_CROS_EC=y
CONFIG_MOUSE_PS2_ELANTECH=y
CONFIG_INPUT_MISC=y
--
1.9.1

2014-04-11 15:11:38

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 2/7] driver: reset: sti: add keyscan for stih415

Add keyscan reset on stih415 reset controller.

Signed-off-by: Giuseppe Condorelli <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
drivers/reset/sti/reset-stih415.c | 1 +
include/dt-bindings/reset-controller/stih415-resets.h | 1 +
2 files changed, 2 insertions(+)

diff --git a/drivers/reset/sti/reset-stih415.c b/drivers/reset/sti/reset-stih415.c
index e6f6c41..c93fd26 100644
--- a/drivers/reset/sti/reset-stih415.c
+++ b/drivers/reset/sti/reset-stih415.c
@@ -73,6 +73,7 @@ static const struct syscfg_reset_channel_data stih415_softresets[] = {
[STIH415_USB0_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 9),
[STIH415_USB1_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 10),
[STIH415_USB2_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 11),
+ [STIH415_KEYSCAN_SOFTRESET] = STIH415_SRST_LPM(LPM_SYSCFG_1, 8),
};

static struct syscfg_reset_controller_data stih415_powerdown_controller = {
diff --git a/include/dt-bindings/reset-controller/stih415-resets.h b/include/dt-bindings/reset-controller/stih415-resets.h
index c2f8a66..c2329fe 100644
--- a/include/dt-bindings/reset-controller/stih415-resets.h
+++ b/include/dt-bindings/reset-controller/stih415-resets.h
@@ -22,5 +22,6 @@
#define STIH415_USB0_SOFTRESET 3
#define STIH415_USB1_SOFTRESET 4
#define STIH415_USB2_SOFTRESET 5
+#define STIH415_KEYSCAN_SOFTRESET 6

#endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH415 */
--
1.9.1

2014-04-11 15:11:37

by Gabriel FERNANDEZ

[permalink] [raw]
Subject: [PATCH v4 5/7] ARM: STi: DT: add keyscan for stih416

Add keyscan support for stih416.
It is disabled by default given that it is not enabled on all boards.
Also there are PIOs conflict with already claimed lines.

Signed-off-by: Giuseppe Condorelli <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
arch/arm/boot/dts/stih416-pinctrl.dtsi | 16 ++++++++++++++++
arch/arm/boot/dts/stih416.dtsi | 12 ++++++++++++
2 files changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/stih416-pinctrl.dtsi b/arch/arm/boot/dts/stih416-pinctrl.dtsi
index aeea304..6252188 100644
--- a/arch/arm/boot/dts/stih416-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih416-pinctrl.dtsi
@@ -122,6 +122,22 @@
};
};

+ keyscan {
+ pinctrl_keyscan: keyscan {
+ st,pins {
+ keyin0 = <&PIO0 2 ALT2 IN>;
+ keyin1 = <&PIO0 3 ALT2 IN>;
+ keyin2 = <&PIO0 4 ALT2 IN>;
+ keyin3 = <&PIO2 6 ALT2 IN>;
+
+ keyout0 = <&PIO1 6 ALT2 OUT>;
+ keyout1 = <&PIO1 7 ALT2 OUT>;
+ keyout2 = <&PIO0 6 ALT2 OUT>;
+ keyout3 = <&PIO2 7 ALT2 OUT>;
+ };
+ };
+ };
+
sbc_i2c0 {
pinctrl_sbc_i2c0_default: sbc_i2c0-default {
st,pins {
diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi
index 78746d2..d3bc263 100644
--- a/arch/arm/boot/dts/stih416.dtsi
+++ b/arch/arm/boot/dts/stih416.dtsi
@@ -224,5 +224,17 @@

status = "disabled";
};
+
+ keyscan: keyscan@fe4b0000 {
+ compatible = "st,sti-keyscan";
+ status = "disabled";
+ reg = <0xfe4b0000 0x2000>;
+ interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+ clocks = <&CLK_SYSIN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_keyscan>;
+ resets = <&powerdown STIH416_KEYSCAN_POWERDOWN>,
+ <&softreset STIH416_KEYSCAN_SOFTRESET>;
+ };
};
};
--
1.9.1

2014-04-13 05:10:59

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] drivers: input: keyboard: st-keyscan: add keyscan driver

Hi Gabriel,

On Fri, Apr 11, 2014 at 05:07:30PM +0200, Gabriel FERNANDEZ wrote:
> This patch adds ST Keyscan driver to use the keypad hw a subset
> of ST boards provide. Specific board setup will be put in the
> given dt.
>
> Signed-off-by: Gabriel Fernandez <[email protected]>
> Signed-off-by: Giuseppe Condorelli <[email protected]>
> ---
> .../devicetree/bindings/input/st-keyscan.txt | 60 +++++
> drivers/input/keyboard/Kconfig | 12 +
> drivers/input/keyboard/Makefile | 1 +
> drivers/input/keyboard/st-keyscan.c | 260 +++++++++++++++++++++
> 4 files changed, 333 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/input/st-keyscan.txt
> create mode 100644 drivers/input/keyboard/st-keyscan.c
>
> diff --git a/Documentation/devicetree/bindings/input/st-keyscan.txt b/Documentation/devicetree/bindings/input/st-keyscan.txt
> new file mode 100644
> index 0000000..51eb428
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/st-keyscan.txt
> @@ -0,0 +1,60 @@
> +* ST Keyscan controller Device Tree bindings
> +
> +The ST keyscan controller Device Tree binding is based on the
> +matrix-keymap.
> +
> +Required properties:
> +- compatible: "st,sti-keyscan"
> +
> +- reg: Register base address and size of st-keyscan controller.
> +
> +- interrupts: Interrupt number for the st-keyscan controller.
> +
> +- clocks: Must contain one entry, for the module clock.
> + See ../clocks/clock-bindings.txt for details.
> +
> +- pinctrl: Should specify pin control groups used for this controller.
> + See ../pinctrl/pinctrl-bindings.txt for details.
> +
> +- linux,keymap: The keymap for keys as described in the binding document
> + devicetree/bindings/input/matrix-keymap.txt.
> +
> +- keypad,num-rows: Number of row lines connected to the keypad controller.
> +
> +- keypad,num-columns: Number of column lines connected to the keypad
> + controller.
> +
> +Optional property:
> +- st,debounce_us: Debouncing interval time in microseconds
> +
> +Example:
> +
> +keyscan: keyscan@fe4b0000 {
> + compatible = "st,sti-keyscan";
> + reg = <0xfe4b0000 0x2000>;
> + interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
> + clocks = <&CLK_SYSIN>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_keyscan>;
> +
> + keypad,num-rows = <4>;
> + keypad,num-columns = <4>;
> + st,debounce_us = <5000>;
> +
> + linux,keymap = < MATRIX_KEY(0x00, 0x00, KEY_F13)
> + MATRIX_KEY(0x00, 0x01, KEY_F9)
> + MATRIX_KEY(0x00, 0x02, KEY_F5)
> + MATRIX_KEY(0x00, 0x03, KEY_F1)
> + MATRIX_KEY(0x01, 0x00, KEY_F14)
> + MATRIX_KEY(0x01, 0x01, KEY_F10)
> + MATRIX_KEY(0x01, 0x02, KEY_F6)
> + MATRIX_KEY(0x01, 0x03, KEY_F2)
> + MATRIX_KEY(0x02, 0x00, KEY_F15)
> + MATRIX_KEY(0x02, 0x01, KEY_F11)
> + MATRIX_KEY(0x02, 0x02, KEY_F7)
> + MATRIX_KEY(0x02, 0x03, KEY_F3)
> + MATRIX_KEY(0x03, 0x00, KEY_F16)
> + MATRIX_KEY(0x03, 0x01, KEY_F12)
> + MATRIX_KEY(0x03, 0x02, KEY_F8)
> + MATRIX_KEY(0x03, 0x03, KEY_F4) >;
> + };
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 76842d7..40a377c 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -524,6 +524,18 @@ config KEYBOARD_STOWAWAY
> To compile this driver as a module, choose M here: the
> module will be called stowaway.
>
> +config KEYBOARD_ST_KEYSCAN
> + tristate "STMicroelectronics keyscan support"
> + depends on ARCH_STI
> + select INPUT_EVDEV

Why are you selecting evdev? Its presence is not essential for the
driver.

> + select INPUT_MATRIXKMAP
> + help
> + Say Y here if you want to use a keypad attached to the keyscan block
> + on some STMicroelectronics SoC devices.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called st-keyscan.
> +
> config KEYBOARD_SUNKBD
> tristate "Sun Type 4 and Type 5 keyboard"
> select SERIO
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 11cff7b..7504ae1 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
> obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
> obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
> obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
> +obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
> obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
> obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
> obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
> diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
> new file mode 100644
> index 0000000..7ed3b3e
> --- /dev/null
> +++ b/drivers/input/keyboard/st-keyscan.c
> @@ -0,0 +1,260 @@
> +/*
> + * STMicroelectronics Key Scanning driver
> + *
> + * Copyright (c) 2014 STMicroelectonics Ltd.
> + * Author: Stuart Menefy <[email protected]>
> + *
> + * Based on sh_keysc.c, copyright 2008 Magnus Damm
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/input/matrix_keypad.h>
> +
> +#define ST_KEYSCAN_MAXKEYS 16
> +
> +#define KEYSCAN_CONFIG_OFF 0x0
> +#define KEYSCAN_CONFIG_ENABLE 0x1
> +#define KEYSCAN_DEBOUNCE_TIME_OFF 0x4
> +#define KEYSCAN_MATRIX_STATE_OFF 0x8
> +#define KEYSCAN_MATRIX_DIM_OFF 0xc
> +#define KEYSCAN_MATRIX_DIM_X_SHIFT 0x0
> +#define KEYSCAN_MATRIX_DIM_Y_SHIFT 0x2
> +
> +struct st_keyscan {
> + void __iomem *base;
> + int irq;
> + struct clk *clk;
> + struct input_dev *input_dev;
> + unsigned int last_state;
> + unsigned int n_rows;
> + unsigned int n_cols;
> + unsigned int debounce_us;
> +};
> +
> +static irqreturn_t keyscan_isr(int irq, void *dev_id)
> +{
> + struct platform_device *pdev = dev_id;
> + struct st_keyscan *keypad = platform_get_drvdata(pdev);

This is not safe, you are assigning platform data at the very end of
probe; input device may get opened before probe is completed and IRS
may come early causing OOPS.

> + unsigned short *keycode = keypad->input_dev->keycode;
> + int state;
> + int change;
> +
> + state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
> + change = keypad->last_state ^ state;
> +
> + while (change) {
> + int scancode = __ffs(change);
> + int down = state & BIT(scancode);
> +
> + input_report_key(keypad->input_dev, keycode[scancode], down);
> +
> + change ^= BIT(scancode);
> + };
> + input_sync(keypad->input_dev);
> +
> + keypad->last_state = state;
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int keyscan_start(struct st_keyscan *keypad)
> +{
> + clk_enable(keypad->clk);

Should handle clk_enable() failures.

> +
> + writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
> + keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
> +
> + writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
> + ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
> + keypad->base + KEYSCAN_MATRIX_DIM_OFF);
> +
> + writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
> +
> + return 0;
> +}
> +
> +static void keyscan_stop(struct st_keyscan *keypad)
> +{
> + writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
> +
> + clk_disable(keypad->clk);
> +}
> +
> +static int keyscan_open(struct input_dev *dev)
> +{
> + struct st_keyscan *keypad = input_get_drvdata(dev);
> +
> + return keyscan_start(keypad);
> +}
> +
> +static void keyscan_close(struct input_dev *dev)
> +{
> + struct st_keyscan *keypad = input_get_drvdata(dev);
> +
> + keyscan_stop(keypad);
> +}
> +
> +static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
> +{
> + struct device *dev = keypad_data->input_dev->dev.parent;
> + struct device_node *np = dev->of_node;
> + int error;
> +
> + error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
> + &keypad_data->n_cols);
> + if (error) {
> + dev_err(dev, "failed to parse keypad params\n");
> + return error;
> + }
> +
> + of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
> +
> + dev_info(dev, "n_rows=%d n_col=%d debounce=%d\n",
> + keypad_data->n_rows,
> + keypad_data->n_cols,
> + keypad_data->debounce_us);
> +
> + return 0;
> +}
> +
> +static int __init keyscan_probe(struct platform_device *pdev)

keyscan_probe() should not be marked __init as it may be needed again if
one were to unbind and rebind the driver to the device through sysfs.

> +{
> + struct st_keyscan *keypad_data;
> + struct input_dev *input_dev;
> + struct resource *res;
> + int error;
> +
> + if (!pdev->dev.of_node) {
> + dev_err(&pdev->dev, "no keymap defined\n");
> + return -EINVAL;
> + }
> +
> + keypad_data = devm_kzalloc(&pdev->dev,
> + sizeof(*keypad_data), GFP_KERNEL);
> + if (!keypad_data)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(keypad_data->base))
> + return PTR_ERR(keypad_data->base);
> +
> + keypad_data->irq = platform_get_irq(pdev, 0);
> + if (keypad_data->irq < 0) {
> + dev_err(&pdev->dev, "no IRQ specified\n");
> + return -EINVAL;
> + }
> +
> + error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
> + pdev->name, pdev);
> + if (error) {
> + dev_err(&pdev->dev, "failed to request IRQ\n");
> + return error;
> + }
> +
> + input_dev = devm_input_allocate_device(&pdev->dev);
> +
> + if (!input_dev) {
> + dev_err(&pdev->dev, "failed to allocate the input device\n");
> + return -ENOMEM;
> + }
> +
> + keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(keypad_data->clk)) {
> + dev_err(&pdev->dev, "cannot get clock");
> + return PTR_ERR(keypad_data->clk);
> + }
> +
> + keypad_data->input_dev = input_dev;
> +
> + input_dev->name = pdev->name;
> + input_dev->phys = "keyscan-keys/input0";
> + input_dev->dev.parent = &pdev->dev;
> + input_dev->open = keyscan_open;
> + input_dev->close = keyscan_close;
> +
> + input_dev->id.bustype = BUS_HOST;
> +
> + error = keypad_matrix_key_parse_dt(keypad_data);
> + if (error)
> + return error;
> +
> + error = matrix_keypad_build_keymap(NULL, NULL,
> + keypad_data->n_rows, keypad_data->n_cols,
> + NULL, input_dev);
> + if (error) {
> + dev_err(&pdev->dev, "failed to build keymap\n");
> + return error;
> + }
> +
> + input_set_drvdata(input_dev, keypad_data);
> +
> + error = input_register_device(input_dev);
> + if (error) {
> + dev_err(&pdev->dev, "failed to register input device\n");
> + return error;
> + }
> +
> + platform_set_drvdata(pdev, keypad_data);
> +
> + device_set_wakeup_capable(&pdev->dev, 1);
> +
> + return 0;
> +}
> +
> +static int keyscan_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
> +
> + if (device_may_wakeup(dev))
> + enable_irq_wake(keypad->irq);
> + else
> + keyscan_stop(keypad);
> +
> + return 0;
> +}
> +
> +static int keyscan_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(keypad->irq);
> + else
> + keyscan_start(keypad);

If devoice has not been opened you should not try to stop it otherwise
clk counter will be imbalanced.

> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume);
> +
> +static const struct of_device_id keyscan_of_match[] = {
> + { .compatible = "st,sti-keyscan" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, keyscan_of_match);
> +
> +struct platform_driver keyscan_device_driver = {
> + .probe = keyscan_probe,
> + .driver = {
> + .name = "st-keyscan",
> + .pm = &keyscan_dev_pm_ops,
> + .of_match_table = of_match_ptr(keyscan_of_match),
> + }
> +};
> +
> +module_platform_driver(keyscan_device_driver);
> +
> +MODULE_AUTHOR("Stuart Menefy <[email protected]>");
> +MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
> +MODULE_LICENSE("GPL");
> --
> 1.9.1
>

Does the version of the patch below still work for you?

Thanks.

--
Dmitry

Input: add st-keyscan driver

From: Gabriel FERNANDEZ <[email protected]>

This patch adds ST Keyscan driver to use the keypad hw a subset of ST
boards provide. Specific board setup will be put in the given dt.

Signed-off-by: Gabriel Fernandez <[email protected]>
Signed-off-by: Giuseppe Condorelli <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
---
.../devicetree/bindings/input/st-keyscan.txt | 60 ++++
drivers/input/keyboard/Kconfig | 11 +
drivers/input/keyboard/Makefile | 1
drivers/input/keyboard/st-keyscan.c | 274 ++++++++++++++++++++
4 files changed, 346 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/st-keyscan.txt
create mode 100644 drivers/input/keyboard/st-keyscan.c

diff --git a/Documentation/devicetree/bindings/input/st-keyscan.txt b/Documentation/devicetree/bindings/input/st-keyscan.txt
new file mode 100644
index 0000000..51eb428
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/st-keyscan.txt
@@ -0,0 +1,60 @@
+* ST Keyscan controller Device Tree bindings
+
+The ST keyscan controller Device Tree binding is based on the
+matrix-keymap.
+
+Required properties:
+- compatible: "st,sti-keyscan"
+
+- reg: Register base address and size of st-keyscan controller.
+
+- interrupts: Interrupt number for the st-keyscan controller.
+
+- clocks: Must contain one entry, for the module clock.
+ See ../clocks/clock-bindings.txt for details.
+
+- pinctrl: Should specify pin control groups used for this controller.
+ See ../pinctrl/pinctrl-bindings.txt for details.
+
+- linux,keymap: The keymap for keys as described in the binding document
+ devicetree/bindings/input/matrix-keymap.txt.
+
+- keypad,num-rows: Number of row lines connected to the keypad controller.
+
+- keypad,num-columns: Number of column lines connected to the keypad
+ controller.
+
+Optional property:
+- st,debounce_us: Debouncing interval time in microseconds
+
+Example:
+
+keyscan: keyscan@fe4b0000 {
+ compatible = "st,sti-keyscan";
+ reg = <0xfe4b0000 0x2000>;
+ interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
+ clocks = <&CLK_SYSIN>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_keyscan>;
+
+ keypad,num-rows = <4>;
+ keypad,num-columns = <4>;
+ st,debounce_us = <5000>;
+
+ linux,keymap = < MATRIX_KEY(0x00, 0x00, KEY_F13)
+ MATRIX_KEY(0x00, 0x01, KEY_F9)
+ MATRIX_KEY(0x00, 0x02, KEY_F5)
+ MATRIX_KEY(0x00, 0x03, KEY_F1)
+ MATRIX_KEY(0x01, 0x00, KEY_F14)
+ MATRIX_KEY(0x01, 0x01, KEY_F10)
+ MATRIX_KEY(0x01, 0x02, KEY_F6)
+ MATRIX_KEY(0x01, 0x03, KEY_F2)
+ MATRIX_KEY(0x02, 0x00, KEY_F15)
+ MATRIX_KEY(0x02, 0x01, KEY_F11)
+ MATRIX_KEY(0x02, 0x02, KEY_F7)
+ MATRIX_KEY(0x02, 0x03, KEY_F3)
+ MATRIX_KEY(0x03, 0x00, KEY_F16)
+ MATRIX_KEY(0x03, 0x01, KEY_F12)
+ MATRIX_KEY(0x03, 0x02, KEY_F8)
+ MATRIX_KEY(0x03, 0x03, KEY_F4) >;
+ };
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 76842d7..948a303 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -524,6 +524,17 @@ config KEYBOARD_STOWAWAY
To compile this driver as a module, choose M here: the
module will be called stowaway.

+config KEYBOARD_ST_KEYSCAN
+ tristate "STMicroelectronics keyscan support"
+ depends on ARCH_STI || COMPILE_TEST
+ select INPUT_MATRIXKMAP
+ help
+ Say Y here if you want to use a keypad attached to the keyscan block
+ on some STMicroelectronics SoC devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st-keyscan.
+
config KEYBOARD_SUNKBD
tristate "Sun Type 4 and Type 5 keyboard"
select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 11cff7b..7504ae1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
new file mode 100644
index 0000000..758b487
--- /dev/null
+++ b/drivers/input/keyboard/st-keyscan.c
@@ -0,0 +1,274 @@
+/*
+ * STMicroelectronics Key Scanning driver
+ *
+ * Copyright (c) 2014 STMicroelectonics Ltd.
+ * Author: Stuart Menefy <[email protected]>
+ *
+ * Based on sh_keysc.c, copyright 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input/matrix_keypad.h>
+
+#define ST_KEYSCAN_MAXKEYS 16
+
+#define KEYSCAN_CONFIG_OFF 0x0
+#define KEYSCAN_CONFIG_ENABLE 0x1
+#define KEYSCAN_DEBOUNCE_TIME_OFF 0x4
+#define KEYSCAN_MATRIX_STATE_OFF 0x8
+#define KEYSCAN_MATRIX_DIM_OFF 0xc
+#define KEYSCAN_MATRIX_DIM_X_SHIFT 0x0
+#define KEYSCAN_MATRIX_DIM_Y_SHIFT 0x2
+
+struct st_keyscan {
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ struct input_dev *input_dev;
+ unsigned long last_state;
+ unsigned int n_rows;
+ unsigned int n_cols;
+ unsigned int debounce_us;
+};
+
+static irqreturn_t keyscan_isr(int irq, void *dev_id)
+{
+ struct st_keyscan *keypad = dev_id;
+ unsigned short *keycode = keypad->input_dev->keycode;
+ unsigned long state, change;
+ int bit_nr;
+
+ state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
+ change = keypad->last_state ^ state;
+ keypad->last_state = state;
+
+ for_each_set_bit(bit_nr, &change, BITS_PER_LONG)
+ input_report_key(keypad->input_dev,
+ keycode[bit_nr], state & BIT(bit_nr));
+
+ input_sync(keypad->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int keyscan_start(struct st_keyscan *keypad)
+{
+ int error;
+
+ error = clk_enable(keypad->clk);
+ if (error)
+ return error;
+
+ writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
+ keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
+
+ writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
+ ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
+ keypad->base + KEYSCAN_MATRIX_DIM_OFF);
+
+ writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
+
+ return 0;
+}
+
+static void keyscan_stop(struct st_keyscan *keypad)
+{
+ writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
+
+ clk_disable(keypad->clk);
+}
+
+static int keyscan_open(struct input_dev *dev)
+{
+ struct st_keyscan *keypad = input_get_drvdata(dev);
+
+ return keyscan_start(keypad);
+}
+
+static void keyscan_close(struct input_dev *dev)
+{
+ struct st_keyscan *keypad = input_get_drvdata(dev);
+
+ keyscan_stop(keypad);
+}
+
+static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
+{
+ struct device *dev = keypad_data->input_dev->dev.parent;
+ struct device_node *np = dev->of_node;
+ int error;
+
+ error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
+ &keypad_data->n_cols);
+ if (error) {
+ dev_err(dev, "failed to parse keypad params\n");
+ return error;
+ }
+
+ of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
+
+ dev_dbg(dev, "n_rows=%d n_col=%d debounce=%d\n",
+ keypad_data->n_rows, keypad_data->n_cols,
+ keypad_data->debounce_us);
+
+ return 0;
+}
+
+static int keyscan_probe(struct platform_device *pdev)
+{
+ struct st_keyscan *keypad_data;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int error;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "no DT data present\n");
+ return -EINVAL;
+ }
+
+ keypad_data = devm_kzalloc(&pdev->dev, sizeof(*keypad_data),
+ GFP_KERNEL);
+ if (!keypad_data)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = pdev->name;
+ input_dev->phys = "keyscan-keys/input0";
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = keyscan_open;
+ input_dev->close = keyscan_close;
+
+ input_dev->id.bustype = BUS_HOST;
+
+ error = keypad_matrix_key_parse_dt(keypad_data);
+ if (error)
+ return error;
+
+ error = matrix_keypad_build_keymap(NULL, NULL,
+ keypad_data->n_rows,
+ keypad_data->n_cols,
+ NULL, input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to build keymap\n");
+ return error;
+ }
+
+ input_set_drvdata(input_dev, keypad_data);
+
+ keypad_data->input_dev = input_dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(keypad_data->base))
+ return PTR_ERR(keypad_data->base);
+
+ keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad_data->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ return PTR_ERR(keypad_data->clk);
+ }
+
+ error = clk_enable(keypad_data->clk);
+ if (error) {
+ dev_err(&pdev->dev, "failed to enable clock\n");
+ return error;
+ }
+
+ keyscan_stop(keypad_data);
+
+ keypad_data->irq = platform_get_irq(pdev, 0);
+ if (keypad_data->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
+ pdev->name, keypad_data);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return error;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ return error;
+ }
+
+ platform_set_drvdata(pdev, keypad_data);
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+
+ return 0;
+}
+
+static int keyscan_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct st_keyscan *keypad = platform_get_drvdata(pdev);
+ struct input_dev *input = keypad->input_dev;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(keypad->irq);
+ else if (input->users)
+ keyscan_stop(keypad);
+
+ mutex_unlock(&input->mutex);
+ return 0;
+}
+
+static int keyscan_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct st_keyscan *keypad = platform_get_drvdata(pdev);
+ struct input_dev *input = keypad->input_dev;
+ int retval = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(keypad->irq);
+ else if (input->users)
+ retval = keyscan_start(keypad);
+
+ mutex_unlock(&input->mutex);
+ return retval;
+}
+
+static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume);
+
+static const struct of_device_id keyscan_of_match[] = {
+ { .compatible = "st,sti-keyscan" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, keyscan_of_match);
+
+static struct platform_driver keyscan_device_driver = {
+ .probe = keyscan_probe,
+ .driver = {
+ .name = "st-keyscan",
+ .pm = &keyscan_dev_pm_ops,
+ .of_match_table = of_match_ptr(keyscan_of_match),
+ }
+};
+
+module_platform_driver(keyscan_device_driver);
+
+MODULE_AUTHOR("Stuart Menefy <[email protected]>");
+MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
+MODULE_LICENSE("GPL");

2014-04-16 08:49:37

by Gabriel Fernandez

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] drivers: input: keyboard: st-keyscan: add keyscan driver

On 13 April 2014 07:10, Dmitry Torokhov <[email protected]> wrote:
> Hi Gabriel,
>
> On Fri, Apr 11, 2014 at 05:07:30PM +0200, Gabriel FERNANDEZ wrote:
>> This patch adds ST Keyscan driver to use the keypad hw a subset
>> of ST boards provide. Specific board setup will be put in the
>> given dt.
>>
>> Signed-off-by: Gabriel Fernandez <[email protected]>
>> Signed-off-by: Giuseppe Condorelli <[email protected]>
>> ---
>> .../devicetree/bindings/input/st-keyscan.txt | 60 +++++
>> drivers/input/keyboard/Kconfig | 12 +
>> drivers/input/keyboard/Makefile | 1 +
>> drivers/input/keyboard/st-keyscan.c | 260 +++++++++++++++++++++
>> 4 files changed, 333 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/input/st-keyscan.txt
>> create mode 100644 drivers/input/keyboard/st-keyscan.c
>>
>> diff --git a/Documentation/devicetree/bindings/input/st-keyscan.txt b/Documentation/devicetree/bindings/input/st-keyscan.txt
>> new file mode 100644
>> index 0000000..51eb428
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/input/st-keyscan.txt
>> @@ -0,0 +1,60 @@
>> +* ST Keyscan controller Device Tree bindings
>> +
>> +The ST keyscan controller Device Tree binding is based on the
>> +matrix-keymap.
>> +
>> +Required properties:
>> +- compatible: "st,sti-keyscan"
>> +
>> +- reg: Register base address and size of st-keyscan controller.
>> +
>> +- interrupts: Interrupt number for the st-keyscan controller.
>> +
>> +- clocks: Must contain one entry, for the module clock.
>> + See ../clocks/clock-bindings.txt for details.
>> +
>> +- pinctrl: Should specify pin control groups used for this controller.
>> + See ../pinctrl/pinctrl-bindings.txt for details.
>> +
>> +- linux,keymap: The keymap for keys as described in the binding document
>> + devicetree/bindings/input/matrix-keymap.txt.
>> +
>> +- keypad,num-rows: Number of row lines connected to the keypad controller.
>> +
>> +- keypad,num-columns: Number of column lines connected to the keypad
>> + controller.
>> +
>> +Optional property:
>> +- st,debounce_us: Debouncing interval time in microseconds
>> +
>> +Example:
>> +
>> +keyscan: keyscan@fe4b0000 {
>> + compatible = "st,sti-keyscan";
>> + reg = <0xfe4b0000 0x2000>;
>> + interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
>> + clocks = <&CLK_SYSIN>;
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_keyscan>;
>> +
>> + keypad,num-rows = <4>;
>> + keypad,num-columns = <4>;
>> + st,debounce_us = <5000>;
>> +
>> + linux,keymap = < MATRIX_KEY(0x00, 0x00, KEY_F13)
>> + MATRIX_KEY(0x00, 0x01, KEY_F9)
>> + MATRIX_KEY(0x00, 0x02, KEY_F5)
>> + MATRIX_KEY(0x00, 0x03, KEY_F1)
>> + MATRIX_KEY(0x01, 0x00, KEY_F14)
>> + MATRIX_KEY(0x01, 0x01, KEY_F10)
>> + MATRIX_KEY(0x01, 0x02, KEY_F6)
>> + MATRIX_KEY(0x01, 0x03, KEY_F2)
>> + MATRIX_KEY(0x02, 0x00, KEY_F15)
>> + MATRIX_KEY(0x02, 0x01, KEY_F11)
>> + MATRIX_KEY(0x02, 0x02, KEY_F7)
>> + MATRIX_KEY(0x02, 0x03, KEY_F3)
>> + MATRIX_KEY(0x03, 0x00, KEY_F16)
>> + MATRIX_KEY(0x03, 0x01, KEY_F12)
>> + MATRIX_KEY(0x03, 0x02, KEY_F8)
>> + MATRIX_KEY(0x03, 0x03, KEY_F4) >;
>> + };
>> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
>> index 76842d7..40a377c 100644
>> --- a/drivers/input/keyboard/Kconfig
>> +++ b/drivers/input/keyboard/Kconfig
>> @@ -524,6 +524,18 @@ config KEYBOARD_STOWAWAY
>> To compile this driver as a module, choose M here: the
>> module will be called stowaway.
>>
>> +config KEYBOARD_ST_KEYSCAN
>> + tristate "STMicroelectronics keyscan support"
>> + depends on ARCH_STI
>> + select INPUT_EVDEV
>
> Why are you selecting evdev? Its presence is not essential for the
> driver.
it's historic..
i agree no reason to keep it.

>
>> + select INPUT_MATRIXKMAP
>> + help
>> + Say Y here if you want to use a keypad attached to the keyscan block
>> + on some STMicroelectronics SoC devices.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called st-keyscan.
>> +
>> config KEYBOARD_SUNKBD
>> tristate "Sun Type 4 and Type 5 keyboard"
>> select SERIO
>> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
>> index 11cff7b..7504ae1 100644
>> --- a/drivers/input/keyboard/Makefile
>> +++ b/drivers/input/keyboard/Makefile
>> @@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
>> obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
>> obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
>> obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
>> +obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
>> obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
>> obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
>> obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
>> diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
>> new file mode 100644
>> index 0000000..7ed3b3e
>> --- /dev/null
>> +++ b/drivers/input/keyboard/st-keyscan.c
>> @@ -0,0 +1,260 @@
>> +/*
>> + * STMicroelectronics Key Scanning driver
>> + *
>> + * Copyright (c) 2014 STMicroelectonics Ltd.
>> + * Author: Stuart Menefy <[email protected]>
>> + *
>> + * Based on sh_keysc.c, copyright 2008 Magnus Damm
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/input/matrix_keypad.h>
>> +
>> +#define ST_KEYSCAN_MAXKEYS 16
>> +
>> +#define KEYSCAN_CONFIG_OFF 0x0
>> +#define KEYSCAN_CONFIG_ENABLE 0x1
>> +#define KEYSCAN_DEBOUNCE_TIME_OFF 0x4
>> +#define KEYSCAN_MATRIX_STATE_OFF 0x8
>> +#define KEYSCAN_MATRIX_DIM_OFF 0xc
>> +#define KEYSCAN_MATRIX_DIM_X_SHIFT 0x0
>> +#define KEYSCAN_MATRIX_DIM_Y_SHIFT 0x2
>> +
>> +struct st_keyscan {
>> + void __iomem *base;
>> + int irq;
>> + struct clk *clk;
>> + struct input_dev *input_dev;
>> + unsigned int last_state;
>> + unsigned int n_rows;
>> + unsigned int n_cols;
>> + unsigned int debounce_us;
>> +};
>> +
>> +static irqreturn_t keyscan_isr(int irq, void *dev_id)
>> +{
>> + struct platform_device *pdev = dev_id;
>> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
>
> This is not safe, you are assigning platform data at the very end of
> probe; input device may get opened before probe is completed and IRS
> may come early causing OOPS.
>
>> + unsigned short *keycode = keypad->input_dev->keycode;
>> + int state;
>> + int change;
>> +
>> + state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
>> + change = keypad->last_state ^ state;
>> +
>> + while (change) {
>> + int scancode = __ffs(change);
>> + int down = state & BIT(scancode);
>> +
>> + input_report_key(keypad->input_dev, keycode[scancode], down);
>> +
>> + change ^= BIT(scancode);
>> + };
>> + input_sync(keypad->input_dev);
>> +
>> + keypad->last_state = state;
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int keyscan_start(struct st_keyscan *keypad)
>> +{
>> + clk_enable(keypad->clk);
>
> Should handle clk_enable() failures.
>
ok

>> +
>> + writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
>> + keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
>> +
>> + writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
>> + ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
>> + keypad->base + KEYSCAN_MATRIX_DIM_OFF);
>> +
>> + writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
>> +
>> + return 0;
>> +}
>> +
>> +static void keyscan_stop(struct st_keyscan *keypad)
>> +{
>> + writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
>> +
>> + clk_disable(keypad->clk);
>> +}
>> +
>> +static int keyscan_open(struct input_dev *dev)
>> +{
>> + struct st_keyscan *keypad = input_get_drvdata(dev);
>> +
>> + return keyscan_start(keypad);
>> +}
>> +
>> +static void keyscan_close(struct input_dev *dev)
>> +{
>> + struct st_keyscan *keypad = input_get_drvdata(dev);
>> +
>> + keyscan_stop(keypad);
>> +}
>> +
>> +static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
>> +{
>> + struct device *dev = keypad_data->input_dev->dev.parent;
>> + struct device_node *np = dev->of_node;
>> + int error;
>> +
>> + error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
>> + &keypad_data->n_cols);
>> + if (error) {
>> + dev_err(dev, "failed to parse keypad params\n");
>> + return error;
>> + }
>> +
>> + of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
>> +
>> + dev_info(dev, "n_rows=%d n_col=%d debounce=%d\n",
>> + keypad_data->n_rows,
>> + keypad_data->n_cols,
>> + keypad_data->debounce_us);
>> +
>> + return 0;
>> +}
>> +
>> +static int __init keyscan_probe(struct platform_device *pdev)
>
> keyscan_probe() should not be marked __init as it may be needed again if
> one were to unbind and rebind the driver to the device through sysfs.
>
ok

>> +{
>> + struct st_keyscan *keypad_data;
>> + struct input_dev *input_dev;
>> + struct resource *res;
>> + int error;
>> +
>> + if (!pdev->dev.of_node) {
>> + dev_err(&pdev->dev, "no keymap defined\n");
>> + return -EINVAL;
>> + }
>> +
>> + keypad_data = devm_kzalloc(&pdev->dev,
>> + sizeof(*keypad_data), GFP_KERNEL);
>> + if (!keypad_data)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(keypad_data->base))
>> + return PTR_ERR(keypad_data->base);
>> +
>> + keypad_data->irq = platform_get_irq(pdev, 0);
>> + if (keypad_data->irq < 0) {
>> + dev_err(&pdev->dev, "no IRQ specified\n");
>> + return -EINVAL;
>> + }
>> +
>> + error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
>> + pdev->name, pdev);
>> + if (error) {
>> + dev_err(&pdev->dev, "failed to request IRQ\n");
>> + return error;
>> + }
>> +
>> + input_dev = devm_input_allocate_device(&pdev->dev);
>> +
>> + if (!input_dev) {
>> + dev_err(&pdev->dev, "failed to allocate the input device\n");
>> + return -ENOMEM;
>> + }
>> +
>> + keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(keypad_data->clk)) {
>> + dev_err(&pdev->dev, "cannot get clock");
>> + return PTR_ERR(keypad_data->clk);
>> + }
>> +
>> + keypad_data->input_dev = input_dev;
>> +
>> + input_dev->name = pdev->name;
>> + input_dev->phys = "keyscan-keys/input0";
>> + input_dev->dev.parent = &pdev->dev;
>> + input_dev->open = keyscan_open;
>> + input_dev->close = keyscan_close;
>> +
>> + input_dev->id.bustype = BUS_HOST;
>> +
>> + error = keypad_matrix_key_parse_dt(keypad_data);
>> + if (error)
>> + return error;
>> +
>> + error = matrix_keypad_build_keymap(NULL, NULL,
>> + keypad_data->n_rows, keypad_data->n_cols,
>> + NULL, input_dev);
>> + if (error) {
>> + dev_err(&pdev->dev, "failed to build keymap\n");
>> + return error;
>> + }
>> +
>> + input_set_drvdata(input_dev, keypad_data);
>> +
>> + error = input_register_device(input_dev);
>> + if (error) {
>> + dev_err(&pdev->dev, "failed to register input device\n");
>> + return error;
>> + }
>> +
>> + platform_set_drvdata(pdev, keypad_data);
>> +
>> + device_set_wakeup_capable(&pdev->dev, 1);
>> +
>> + return 0;
>> +}
>> +
>> +static int keyscan_suspend(struct device *dev)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
>> +
>> + if (device_may_wakeup(dev))
>> + enable_irq_wake(keypad->irq);
>> + else
>> + keyscan_stop(keypad);
>> +
>> + return 0;
>> +}
>> +
>> +static int keyscan_resume(struct device *dev)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
>> +
>> + if (device_may_wakeup(dev))
>> + disable_irq_wake(keypad->irq);
>> + else
>> + keyscan_start(keypad);
>
> If devoice has not been opened you should not try to stop it otherwise
> clk counter will be imbalanced.
>
Right

>> +
>> + return 0;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume);
>> +
>> +static const struct of_device_id keyscan_of_match[] = {
>> + { .compatible = "st,sti-keyscan" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, keyscan_of_match);
>> +
>> +struct platform_driver keyscan_device_driver = {
>> + .probe = keyscan_probe,
>> + .driver = {
>> + .name = "st-keyscan",
>> + .pm = &keyscan_dev_pm_ops,
>> + .of_match_table = of_match_ptr(keyscan_of_match),
>> + }
>> +};
>> +
>> +module_platform_driver(keyscan_device_driver);
>> +
>> +MODULE_AUTHOR("Stuart Menefy <[email protected]>");
>> +MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 1.9.1
>>
>
> Does the version of the patch below still work for you?
>
Yes it's was tested on b2000 and b2089 sti boards.

> Thanks.
>
> --
> Dmitry
>
Thanks for yours remarks, i will prepare a v5 versions.
Regards

> Input: add st-keyscan driver
>
> From: Gabriel FERNANDEZ <[email protected]>
>
> This patch adds ST Keyscan driver to use the keypad hw a subset of ST
> boards provide. Specific board setup will be put in the given dt.
>
> Signed-off-by: Gabriel Fernandez <[email protected]>
> Signed-off-by: Giuseppe Condorelli <[email protected]>
> Signed-off-by: Dmitry Torokhov <[email protected]>
> ---
> .../devicetree/bindings/input/st-keyscan.txt | 60 ++++
> drivers/input/keyboard/Kconfig | 11 +
> drivers/input/keyboard/Makefile | 1
> drivers/input/keyboard/st-keyscan.c | 274 ++++++++++++++++++++
> 4 files changed, 346 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/input/st-keyscan.txt
> create mode 100644 drivers/input/keyboard/st-keyscan.c
>
> diff --git a/Documentation/devicetree/bindings/input/st-keyscan.txt b/Documentation/devicetree/bindings/input/st-keyscan.txt
> new file mode 100644
> index 0000000..51eb428
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/st-keyscan.txt
> @@ -0,0 +1,60 @@
> +* ST Keyscan controller Device Tree bindings
> +
> +The ST keyscan controller Device Tree binding is based on the
> +matrix-keymap.
> +
> +Required properties:
> +- compatible: "st,sti-keyscan"
> +
> +- reg: Register base address and size of st-keyscan controller.
> +
> +- interrupts: Interrupt number for the st-keyscan controller.
> +
> +- clocks: Must contain one entry, for the module clock.
> + See ../clocks/clock-bindings.txt for details.
> +
> +- pinctrl: Should specify pin control groups used for this controller.
> + See ../pinctrl/pinctrl-bindings.txt for details.
> +
> +- linux,keymap: The keymap for keys as described in the binding document
> + devicetree/bindings/input/matrix-keymap.txt.
> +
> +- keypad,num-rows: Number of row lines connected to the keypad controller.
> +
> +- keypad,num-columns: Number of column lines connected to the keypad
> + controller.
> +
> +Optional property:
> +- st,debounce_us: Debouncing interval time in microseconds
> +
> +Example:
> +
> +keyscan: keyscan@fe4b0000 {
> + compatible = "st,sti-keyscan";
> + reg = <0xfe4b0000 0x2000>;
> + interrupts = <GIC_SPI 212 IRQ_TYPE_NONE>;
> + clocks = <&CLK_SYSIN>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_keyscan>;
> +
> + keypad,num-rows = <4>;
> + keypad,num-columns = <4>;
> + st,debounce_us = <5000>;
> +
> + linux,keymap = < MATRIX_KEY(0x00, 0x00, KEY_F13)
> + MATRIX_KEY(0x00, 0x01, KEY_F9)
> + MATRIX_KEY(0x00, 0x02, KEY_F5)
> + MATRIX_KEY(0x00, 0x03, KEY_F1)
> + MATRIX_KEY(0x01, 0x00, KEY_F14)
> + MATRIX_KEY(0x01, 0x01, KEY_F10)
> + MATRIX_KEY(0x01, 0x02, KEY_F6)
> + MATRIX_KEY(0x01, 0x03, KEY_F2)
> + MATRIX_KEY(0x02, 0x00, KEY_F15)
> + MATRIX_KEY(0x02, 0x01, KEY_F11)
> + MATRIX_KEY(0x02, 0x02, KEY_F7)
> + MATRIX_KEY(0x02, 0x03, KEY_F3)
> + MATRIX_KEY(0x03, 0x00, KEY_F16)
> + MATRIX_KEY(0x03, 0x01, KEY_F12)
> + MATRIX_KEY(0x03, 0x02, KEY_F8)
> + MATRIX_KEY(0x03, 0x03, KEY_F4) >;
> + };
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 76842d7..948a303 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -524,6 +524,17 @@ config KEYBOARD_STOWAWAY
> To compile this driver as a module, choose M here: the
> module will be called stowaway.
>
> +config KEYBOARD_ST_KEYSCAN
> + tristate "STMicroelectronics keyscan support"
> + depends on ARCH_STI || COMPILE_TEST
> + select INPUT_MATRIXKMAP
> + help
> + Say Y here if you want to use a keypad attached to the keyscan block
> + on some STMicroelectronics SoC devices.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called st-keyscan.
> +
> config KEYBOARD_SUNKBD
> tristate "Sun Type 4 and Type 5 keyboard"
> select SERIO
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 11cff7b..7504ae1 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
> obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
> obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
> obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
> +obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
> obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
> obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
> obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
> diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
> new file mode 100644
> index 0000000..758b487
> --- /dev/null
> +++ b/drivers/input/keyboard/st-keyscan.c
> @@ -0,0 +1,274 @@
> +/*
> + * STMicroelectronics Key Scanning driver
> + *
> + * Copyright (c) 2014 STMicroelectonics Ltd.
> + * Author: Stuart Menefy <[email protected]>
> + *
> + * Based on sh_keysc.c, copyright 2008 Magnus Damm
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/input/matrix_keypad.h>
> +
> +#define ST_KEYSCAN_MAXKEYS 16
> +
> +#define KEYSCAN_CONFIG_OFF 0x0
> +#define KEYSCAN_CONFIG_ENABLE 0x1
> +#define KEYSCAN_DEBOUNCE_TIME_OFF 0x4
> +#define KEYSCAN_MATRIX_STATE_OFF 0x8
> +#define KEYSCAN_MATRIX_DIM_OFF 0xc
> +#define KEYSCAN_MATRIX_DIM_X_SHIFT 0x0
> +#define KEYSCAN_MATRIX_DIM_Y_SHIFT 0x2
> +
> +struct st_keyscan {
> + void __iomem *base;
> + int irq;
> + struct clk *clk;
> + struct input_dev *input_dev;
> + unsigned long last_state;
> + unsigned int n_rows;
> + unsigned int n_cols;
> + unsigned int debounce_us;
> +};
> +
> +static irqreturn_t keyscan_isr(int irq, void *dev_id)
> +{
> + struct st_keyscan *keypad = dev_id;
> + unsigned short *keycode = keypad->input_dev->keycode;
> + unsigned long state, change;
> + int bit_nr;
> +
> + state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
> + change = keypad->last_state ^ state;
> + keypad->last_state = state;
> +
> + for_each_set_bit(bit_nr, &change, BITS_PER_LONG)
> + input_report_key(keypad->input_dev,
> + keycode[bit_nr], state & BIT(bit_nr));
> +
> + input_sync(keypad->input_dev);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int keyscan_start(struct st_keyscan *keypad)
> +{
> + int error;
> +
> + error = clk_enable(keypad->clk);
> + if (error)
> + return error;
> +
> + writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
> + keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
> +
> + writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
> + ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
> + keypad->base + KEYSCAN_MATRIX_DIM_OFF);
> +
> + writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
> +
> + return 0;
> +}
> +
> +static void keyscan_stop(struct st_keyscan *keypad)
> +{
> + writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
> +
> + clk_disable(keypad->clk);
> +}
> +
> +static int keyscan_open(struct input_dev *dev)
> +{
> + struct st_keyscan *keypad = input_get_drvdata(dev);
> +
> + return keyscan_start(keypad);
> +}
> +
> +static void keyscan_close(struct input_dev *dev)
> +{
> + struct st_keyscan *keypad = input_get_drvdata(dev);
> +
> + keyscan_stop(keypad);
> +}
> +
> +static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
> +{
> + struct device *dev = keypad_data->input_dev->dev.parent;
> + struct device_node *np = dev->of_node;
> + int error;
> +
> + error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
> + &keypad_data->n_cols);
> + if (error) {
> + dev_err(dev, "failed to parse keypad params\n");
> + return error;
> + }
> +
> + of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
> +
> + dev_dbg(dev, "n_rows=%d n_col=%d debounce=%d\n",
> + keypad_data->n_rows, keypad_data->n_cols,
> + keypad_data->debounce_us);
> +
> + return 0;
> +}
> +
> +static int keyscan_probe(struct platform_device *pdev)
> +{
> + struct st_keyscan *keypad_data;
> + struct input_dev *input_dev;
> + struct resource *res;
> + int error;
> +
> + if (!pdev->dev.of_node) {
> + dev_err(&pdev->dev, "no DT data present\n");
> + return -EINVAL;
> + }
> +
> + keypad_data = devm_kzalloc(&pdev->dev, sizeof(*keypad_data),
> + GFP_KERNEL);
> + if (!keypad_data)
> + return -ENOMEM;
> +
> + input_dev = devm_input_allocate_device(&pdev->dev);
> + if (!input_dev) {
> + dev_err(&pdev->dev, "failed to allocate the input device\n");
> + return -ENOMEM;
> + }
> +
> + input_dev->name = pdev->name;
> + input_dev->phys = "keyscan-keys/input0";
> + input_dev->dev.parent = &pdev->dev;
> + input_dev->open = keyscan_open;
> + input_dev->close = keyscan_close;
> +
> + input_dev->id.bustype = BUS_HOST;
> +
> + error = keypad_matrix_key_parse_dt(keypad_data);
> + if (error)
> + return error;
> +
> + error = matrix_keypad_build_keymap(NULL, NULL,
> + keypad_data->n_rows,
> + keypad_data->n_cols,
> + NULL, input_dev);
> + if (error) {
> + dev_err(&pdev->dev, "failed to build keymap\n");
> + return error;
> + }
> +
> + input_set_drvdata(input_dev, keypad_data);
> +
> + keypad_data->input_dev = input_dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(keypad_data->base))
> + return PTR_ERR(keypad_data->base);
> +
> + keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(keypad_data->clk)) {
> + dev_err(&pdev->dev, "cannot get clock\n");
> + return PTR_ERR(keypad_data->clk);
> + }
> +
> + error = clk_enable(keypad_data->clk);
> + if (error) {
> + dev_err(&pdev->dev, "failed to enable clock\n");
> + return error;
> + }
> +
> + keyscan_stop(keypad_data);
> +
> + keypad_data->irq = platform_get_irq(pdev, 0);
> + if (keypad_data->irq < 0) {
> + dev_err(&pdev->dev, "no IRQ specified\n");
> + return -EINVAL;
> + }
> +
> + error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
> + pdev->name, keypad_data);
> + if (error) {
> + dev_err(&pdev->dev, "failed to request IRQ\n");
> + return error;
> + }
> +
> + error = input_register_device(input_dev);
> + if (error) {
> + dev_err(&pdev->dev, "failed to register input device\n");
> + return error;
> + }
> +
> + platform_set_drvdata(pdev, keypad_data);
> +
> + device_set_wakeup_capable(&pdev->dev, 1);
> +
> + return 0;
> +}
> +
> +static int keyscan_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
> + struct input_dev *input = keypad->input_dev;
> +
> + mutex_lock(&input->mutex);
> +
> + if (device_may_wakeup(dev))
> + enable_irq_wake(keypad->irq);
> + else if (input->users)
> + keyscan_stop(keypad);
> +
> + mutex_unlock(&input->mutex);
> + return 0;
> +}
> +
> +static int keyscan_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct st_keyscan *keypad = platform_get_drvdata(pdev);
> + struct input_dev *input = keypad->input_dev;
> + int retval = 0;
> +
> + mutex_lock(&input->mutex);
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(keypad->irq);
> + else if (input->users)
> + retval = keyscan_start(keypad);
> +
> + mutex_unlock(&input->mutex);
> + return retval;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume);
> +
> +static const struct of_device_id keyscan_of_match[] = {
> + { .compatible = "st,sti-keyscan" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, keyscan_of_match);
> +
> +static struct platform_driver keyscan_device_driver = {
> + .probe = keyscan_probe,
> + .driver = {
> + .name = "st-keyscan",
> + .pm = &keyscan_dev_pm_ops,
> + .of_match_table = of_match_ptr(keyscan_of_match),
> + }
> +};
> +
> +module_platform_driver(keyscan_device_driver);
> +
> +MODULE_AUTHOR("Stuart Menefy <[email protected]>");
> +MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
> +MODULE_LICENSE("GPL");

2014-04-19 21:09:40

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] drivers: input: keyboard: st-keyscan: add keyscan driver

On Wed, Apr 16, 2014 at 10:49:29AM +0200, Gabriel Fernandez wrote:
> On 13 April 2014 07:10, Dmitry Torokhov <[email protected]> wrote:
> >
> > Does the version of the patch below still work for you?
> >
> Yes it's was tested on b2000 and b2089 sti boards.
>
> > Thanks.
> >
> > --
> > Dmitry
> >
> Thanks for yours remarks, i will prepare a v5 versions.


If the version I sent to you works then you do not need to prepare v5,
I'll just apply what I have.

Thanks!

--
Dmitry