* NEW SUN4i KEYPAD DRIVER @ 2015-09-15 14:05 yassinjaffer 2015-09-15 14:05 ` [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node yassinjaffer ` (3 more replies) 0 siblings, 4 replies; 12+ messages in thread From: yassinjaffer @ 2015-09-15 14:05 UTC (permalink / raw) To: linux-sunxi Cc: maxime.ripard, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel This series adds support for the sun4i keypad Controller I've tested the driver in a A20 custom board (4x4) only Allwinner keypad controller does not seem to handle more than two key press Please test and report bugs. Thank you in advance [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC [PATCH 3/4] input: Add new sun4i-keypad driver [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node 2015-09-15 14:05 NEW SUN4i KEYPAD DRIVER yassinjaffer @ 2015-09-15 14:05 ` yassinjaffer 2015-09-16 5:11 ` Maxime Ripard 2015-09-15 14:05 ` [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC yassinjaffer ` (2 subsequent siblings) 3 siblings, 1 reply; 12+ messages in thread From: yassinjaffer @ 2015-09-15 14:05 UTC (permalink / raw) To: linux-sunxi Cc: maxime.ripard, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel, Yassin Jaffer From: Yassin Jaffer <yassinjaffer@gmail.com> This patch add support to the keypad clock on sun7i Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> --- arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 2bebaa2..333604a 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -465,6 +465,14 @@ clock-output-names = "ir1"; }; + keypad_clk: clk@01c200c4 { + #clock-cells = <0>; + compatible = "allwinner,sun4i-a10-mod0-clk"; + reg = <0x01c200c4 0x4>; + clocks = <&osc24M>; + clock-output-names = "keypad"; + }; + usb_clk: clk@01c200cc { #clock-cells = <1>; #reset-cells = <1>; -- 1.9.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node 2015-09-15 14:05 ` [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node yassinjaffer @ 2015-09-16 5:11 ` Maxime Ripard 0 siblings, 0 replies; 12+ messages in thread From: Maxime Ripard @ 2015-09-16 5:11 UTC (permalink / raw) To: yassinjaffer Cc: linux-sunxi, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel [-- Attachment #1: Type: text/plain, Size: 384 bytes --] On Wed, Sep 16, 2015 at 12:05:54AM +1000, yassinjaffer@gmail.com wrote: > From: Yassin Jaffer <yassinjaffer@gmail.com> > > This patch add support to the keypad clock on sun7i > > Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> Applied, thanks! Maxime -- Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC 2015-09-15 14:05 NEW SUN4i KEYPAD DRIVER yassinjaffer 2015-09-15 14:05 ` [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node yassinjaffer @ 2015-09-15 14:05 ` yassinjaffer 2015-09-17 11:29 ` Maxime Ripard 2015-09-15 14:05 ` [PATCH 3/4] input: Add new sun4i-keypad driver yassinjaffer 2015-09-15 14:05 ` [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad yassinjaffer 3 siblings, 1 reply; 12+ messages in thread From: yassinjaffer @ 2015-09-15 14:05 UTC (permalink / raw) To: linux-sunxi Cc: maxime.ripard, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel, Yassin Jaffer From: Yassin Jaffer <yassinjaffer@gmail.com> Add Keypad controller node definition to the A20 SoC. Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> --- arch/arm/boot/dts/sun7i-a20.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 333604a..35cc8d0 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -1198,6 +1198,15 @@ status = "disabled"; }; + kp: kp@01c23000 { + compatible = "allwinner,sun4i-a10-keypad"; + reg = <0x01c23000 0x400>; + interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&apb0_gates 10>, <&keypad_clk>; + clock-names = "apb", "keypad"; + status = "disabled"; + }; + sid: eeprom@01c23800 { compatible = "allwinner,sun7i-a20-sid"; reg = <0x01c23800 0x200>; -- 1.9.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC 2015-09-15 14:05 ` [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC yassinjaffer @ 2015-09-17 11:29 ` Maxime Ripard 2015-09-17 14:20 ` [linux-sunxi] " Chen-Yu Tsai 0 siblings, 1 reply; 12+ messages in thread From: Maxime Ripard @ 2015-09-17 11:29 UTC (permalink / raw) To: yassinjaffer Cc: linux-sunxi, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel [-- Attachment #1: Type: text/plain, Size: 911 bytes --] Hi Yassin, On Wed, Sep 16, 2015 at 12:05:55AM +1000, yassinjaffer@gmail.com wrote: > From: Yassin Jaffer <yassinjaffer@gmail.com> > > Add Keypad controller node definition to the A20 SoC. > > Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> > --- > arch/arm/boot/dts/sun7i-a20.dtsi | 9 +++++++++ > 1 file changed, 9 insertions(+) > > diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi > index 333604a..35cc8d0 100644 > --- a/arch/arm/boot/dts/sun7i-a20.dtsi > +++ b/arch/arm/boot/dts/sun7i-a20.dtsi > @@ -1198,6 +1198,15 @@ > status = "disabled"; > }; > > + kp: kp@01c23000 { The node name should reflect the class of the device. keypad@01c23000 would be better for example. It looks good otherwise. Thanks! Maxime -- Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [linux-sunxi] Re: [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC 2015-09-17 11:29 ` Maxime Ripard @ 2015-09-17 14:20 ` Chen-Yu Tsai 0 siblings, 0 replies; 12+ messages in thread From: Chen-Yu Tsai @ 2015-09-17 14:20 UTC (permalink / raw) To: yassinjaffer Cc: linux-sunxi, Dmitry Torokhov, linux-input, linux-arm-kernel, linux-kernel, Maxime Ripard On Thu, Sep 17, 2015 at 7:29 PM, Maxime Ripard <maxime.ripard@free-electrons.com> wrote: > Hi Yassin, > > On Wed, Sep 16, 2015 at 12:05:55AM +1000, yassinjaffer@gmail.com wrote: >> From: Yassin Jaffer <yassinjaffer@gmail.com> >> >> Add Keypad controller node definition to the A20 SoC. >> >> Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> >> --- >> arch/arm/boot/dts/sun7i-a20.dtsi | 9 +++++++++ >> 1 file changed, 9 insertions(+) >> >> diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi >> index 333604a..35cc8d0 100644 >> --- a/arch/arm/boot/dts/sun7i-a20.dtsi >> +++ b/arch/arm/boot/dts/sun7i-a20.dtsi >> @@ -1198,6 +1198,15 @@ >> status = "disabled"; >> }; >> >> + kp: kp@01c23000 { > > The node name should reflect the class of the device. keypad@01c23000 > would be better for example. Expanding the label to "keypad" as well would be nice. "kp" could mean other things. Thanks. ChenYu > It looks good otherwise. > > Thanks! > Maxime > > -- > Maxime Ripard, Free Electrons > Embedded Linux, Kernel and Android engineering > http://free-electrons.com > > -- > You received this message because you are subscribed to the Google Groups "linux-sunxi" group. > To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/4] input: Add new sun4i-keypad driver 2015-09-15 14:05 NEW SUN4i KEYPAD DRIVER yassinjaffer 2015-09-15 14:05 ` [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node yassinjaffer 2015-09-15 14:05 ` [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC yassinjaffer @ 2015-09-15 14:05 ` yassinjaffer 2015-09-17 13:05 ` Maxime Ripard 2015-09-21 17:19 ` Dmitry Torokhov 2015-09-15 14:05 ` [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad yassinjaffer 3 siblings, 2 replies; 12+ messages in thread From: yassinjaffer @ 2015-09-15 14:05 UTC (permalink / raw) To: linux-sunxi Cc: maxime.ripard, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel, Yassin Jaffer From: Yassin Jaffer <yassinjaffer@gmail.com> Allwinnner SUN4i Keypad controller is used to interface a SoC with a matrix-typekeypad device. The keypad controller supports multiple row and column lines. A key can be placed at each intersection of a unique row and a unique column. The keypad controller can sense a key-press and key-release and report the event using a interrupt to the cpu. This patch adds a driver support to this. The keypad controller driver does not give proper information if more that two keys are selected. Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> --- drivers/input/keyboard/Kconfig | 11 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/sun4i-keypad.c | 361 ++++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 drivers/input/keyboard/sun4i-keypad.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2e80107..4f2f3f8 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -590,6 +590,17 @@ config KEYBOARD_SUN4I_LRADC To compile this driver as a module, choose M here: the module will be called sun4i-lradc-keys. +config KEYBOARD_SUN4I_KEYPAD + tristate "Allwinner sun4i keypad support" + depends on ARCH_SUNXI + select INPUT_MATRIXKMAP + help + This selects support for the Allwinner keypad + on Allwinner sunxi SoCs. + + To compile this driver as a module, choose M here: the + module will be called sun4i-keypad. + config KEYBOARD_DAVINCI tristate "TI DaVinci Key Scan" depends on ARCH_DAVINCI_DM365 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1d416dd..d9f54b4 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o +obj-$(CONFIG_KEYBOARD_SUN4I_KEYPAD) += sun4i-keypad.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/sun4i-keypad.c b/drivers/input/keyboard/sun4i-keypad.c new file mode 100644 index 0000000..995f9665 --- /dev/null +++ b/drivers/input/keyboard/sun4i-keypad.c @@ -0,0 +1,361 @@ +/* + * Allwinner sun4i keypad Controller driver + * + * Copyright (C) 2015 Yassin Jaffer <yassinjaffer@gmail.com> + * + * Parts of this software are based on (derived from): + * Copyright (C) 2013-2015 liming@allwinnertech.com, + * qys<qinyongshen@allwinnertech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> + +/* + * Keypad Controller registers + */ +#define KP_CTL 0x00 /* Keypad Control register */ +#define KP_TIMING 0x04 /* Keypad Timing register */ +#define KP_INT_CFG 0x08 /* Keypad interrupt Config register */ +#define KP_INT_STA 0x0c /* Keypad interrupt Status register */ + +#define KP_IN_OFFSET 0x10 /* Keypad Input Data register 0 */ +#define KP_INX_OFFSET(reg_n) (KP_IN_OFFSET + 4 * (reg_n)) + +/* KP_CTL bits */ +#define ENABLE(x) ((x) << 0) +#define COLMASK(x) ((~x & 0xff) << 8) +#define ROWMASK(x) ((~x & 0xff) << 16) + +/* KP_TIMING bits */ +#define SCAN_CYCLE(x) ((x) << 0) +#define DBC_CYCLE(x) ((x) << 16) + +/* KP_INT_CFG bits */ +#define KP_IRQ_FEDGE BIT(0) +#define KP_IRQ_REDGE BIT(1) + +/* KP_INT_STA bits */ +#define KP_STA_FEDGE BIT(0) +#define KP_STA_REDGE BIT(1) + +#define KP_MAX_ROWS 8 +#define KP_MAX_COLS 8 +#define N_ROWS_REG 2 +#define KP_ROW_SHIFT 3 +#define KP_BIT_SHIFT 32 + +#define MAX_MATRIX_KEY_NUM (KP_MAX_ROWS * KP_MAX_COLS) + +#define KP_BASE_CLK 1000000 +#define MIN_CYCLE 0x10 +#define MIN_SCAN_CYCLE 0x100 +#define MIN_DBC_CYCLE 0x200 + +/* + * keypad Controller structure: stores sunxi keypad controller information + * + * @dev: parent device + * @input: pointer to input device object + * @apb_clk: keypad Controller APB clock + * @clk: keypad Controller mod clock + * @base: keypad controller registers + * @irq: interrupt + * @rows_en_mask: Masks for enabled rows + * @cols_en_mask: Masks for enabled cols + * @keymap: matrix scan code table for keycodes + * @key_press_state: cached keys press state + * @debounce_cycl: keypad specific debounce cycle + * @scan_cycl: keypad specific scan cycle + * @autorepeat: flag for auto repetition + */ +struct sun4i_keypad_data { + struct device *dev; + struct input_dev *input; + + struct clk *apb_clk; + struct clk *clk; + void __iomem *base; + int irq; + + unsigned short rows_en_mask; + unsigned short cols_en_mask; + + unsigned short keymap[MAX_MATRIX_KEY_NUM]; + + unsigned int press_state[N_ROWS_REG]; + + unsigned int debounce_cycl; + unsigned int scan_cycl; + bool autorepeat; +}; + +static void sun4i_keypad_scan(struct sun4i_keypad_data *keypad, bool edge) +{ + struct input_dev *input_dev = keypad->input; + u32 key_scan[N_ROWS_REG]; + unsigned long change; + unsigned short code; + int reg_nr, bit_nr, key_up; + + for (reg_nr = 0; reg_nr < N_ROWS_REG; reg_nr++) { + key_scan[reg_nr] = ~(readl(keypad->base + + KP_INX_OFFSET(reg_nr))); + + change = edge ? key_scan[reg_nr] : + keypad->press_state[reg_nr] ^ key_scan[reg_nr]; + + key_up = edge || (change & keypad->press_state[reg_nr]); + + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { + code = keypad->keymap[KP_BIT_SHIFT * reg_nr + bit_nr]; + input_report_key(input_dev, code, key_up); + input_sync(input_dev); + } + + keypad->press_state[reg_nr] = edge ? 0 : key_scan[reg_nr]; + } +} + +static irqreturn_t sun4i_keypad_irq(int irq, void *dev_id) +{ + struct sun4i_keypad_data *keypad = dev_id; + u32 intr_status; + + intr_status = readl(keypad->base + KP_INT_STA); + + /* release only gives a valid information for a single key release */ + if (intr_status & KP_STA_REDGE) + sun4i_keypad_scan(keypad, true); + + /* press does not give valid information when + * multiple rows are selected + */ + if (intr_status & KP_STA_FEDGE) + sun4i_keypad_scan(keypad, false); + + writel(intr_status, keypad->base + KP_INT_STA); + + return IRQ_HANDLED; +} + +static int sun4i_keypad_open(struct input_dev *dev) +{ + struct sun4i_keypad_data *keypad = input_get_drvdata(dev); + int reg_nr; + + if (clk_prepare_enable(keypad->apb_clk)) + return -EINVAL; + + if (clk_prepare_enable(keypad->clk)) { + clk_disable_unprepare(keypad->apb_clk); + return -EINVAL; + } + + for (reg_nr = 0; reg_nr < N_ROWS_REG; reg_nr++) + keypad->press_state[reg_nr] = ~(readl(keypad->base + + KP_INX_OFFSET(reg_nr))); + + writel(KP_IRQ_FEDGE | KP_IRQ_REDGE, keypad->base + KP_INT_CFG); + + writel(SCAN_CYCLE(keypad->scan_cycl) | + DBC_CYCLE(keypad->debounce_cycl), keypad->base + KP_TIMING); + + writel(ENABLE(1) | ROWMASK(keypad->rows_en_mask) | + COLMASK(keypad->cols_en_mask), keypad->base + KP_CTL); + + return 0; +} + +static void sun4i_keypad_close(struct input_dev *dev) +{ + struct sun4i_keypad_data *keypad = input_get_drvdata(dev); + + writel(ENABLE(0) | ROWMASK(keypad->rows_en_mask) | + COLMASK(keypad->cols_en_mask), keypad->base + KP_CTL); + + clk_disable_unprepare(keypad->clk); + clk_disable_unprepare(keypad->apb_clk); +} + +static int sun4i_keypad_parse_dt(struct device *dev, + struct sun4i_keypad_data *keypad) +{ + struct device_node *np; + + np = dev->of_node; + if (!np) + return -EINVAL; + + of_property_read_u32(np, "debounce-cycle", &keypad->debounce_cycl); + if (keypad->debounce_cycl < MIN_CYCLE) + keypad->debounce_cycl = MIN_DBC_CYCLE; + + of_property_read_u32(np, "scan-cycle", &keypad->scan_cycl); + if (keypad->scan_cycl < MIN_CYCLE) + keypad->scan_cycl = MIN_SCAN_CYCLE; + + if (of_property_read_bool(np, "autorepeat")) + keypad->autorepeat = true; + + return 0; +} + +static int sun4i_keypad_probe(struct platform_device *pdev) +{ + struct sun4i_keypad_data *keypad; + struct device *dev = &pdev->dev; + int row, col, code; + int ret = 0; + + keypad = devm_kzalloc(dev, sizeof(struct sun4i_keypad_data), + GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + ret = sun4i_keypad_parse_dt(dev, keypad); + if (ret) + return ret; + + keypad->base = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (IS_ERR(keypad->base)) + return PTR_ERR(keypad->base); + + keypad->dev = dev; + keypad->input = devm_input_allocate_device(dev); + if (!keypad->input) + return -ENOMEM; + + keypad->input->name = pdev->name; + keypad->input->phys = "sun4i_keypad/input0"; + + keypad->input->id.bustype = BUS_HOST; + keypad->input->id.vendor = 0x0001; + keypad->input->id.product = 0x0001; + keypad->input->id.version = 0x0100; + + keypad->input->open = sun4i_keypad_open; + keypad->input->close = sun4i_keypad_close; + + /* matrix keypad keymap as: + * row << 24 | column << 16 | key-code + */ + ret = matrix_keypad_build_keymap(NULL, NULL, + KP_MAX_ROWS, + KP_MAX_COLS, + keypad->keymap, + keypad->input); + if (ret) { + dev_err(dev, "failed to build keymap\n"); + return ret; + } + + /* Search for enabled rows and cols */ + for (row = 0; row < KP_MAX_ROWS; row++) { + for (col = 0; col < KP_MAX_COLS; col++) { + code = MATRIX_SCAN_CODE(row, col, KP_ROW_SHIFT); + if (keypad->keymap[code] != KEY_RESERVED) { + keypad->rows_en_mask |= 1 << row; + keypad->cols_en_mask |= 1 << col; + } + } + } + + if (keypad->autorepeat) + __set_bit(EV_REP, keypad->input->evbit); + + input_set_capability(keypad->input, EV_MSC, MSC_SCAN); + input_set_drvdata(keypad->input, keypad); + + keypad->irq = platform_get_irq(pdev, 0); + if (keypad->irq < 0) { + dev_err(dev, "failed to get keypad IRQ\n"); + return -ENXIO; + } + + ret = devm_request_irq(dev, keypad->irq, + sun4i_keypad_irq, 0, + "sun4i-a10-keypad", keypad); + if (ret) { + dev_err(dev, "failed to request IRQ\n"); + return ret; + } + + keypad->apb_clk = devm_clk_get(dev, "apb"); + if (IS_ERR(keypad->apb_clk)) { + dev_err(dev, "failed to get a apb clock.\n"); + return PTR_ERR(keypad->apb_clk); + } + + keypad->clk = devm_clk_get(dev, "keypad"); + if (IS_ERR(keypad->clk)) { + dev_err(dev, "failed to get a keypad clock.\n"); + return PTR_ERR(keypad->clk); + } + + ret = clk_set_rate(keypad->clk, KP_BASE_CLK); + if (ret) { + dev_err(dev, "set keypad base clock failed!\n"); + return ret; + } + + ret = input_register_device(keypad->input); + if (ret) + return ret; + + platform_set_drvdata(pdev, keypad); + + return 0; +} + +static int sun4i_keypad_remove(struct platform_device *pdev) +{ + struct sun4i_keypad_data *keypad = platform_get_drvdata(pdev); + + free_irq(keypad->irq, keypad); + input_unregister_device(keypad->input); + kfree(keypad); + + return 0; +} + +static const struct of_device_id sun4i_keypad_of_match[] = { + { .compatible = "allwinner,sun4i-a10-keypad", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_keypad_of_match); + +static struct platform_driver sun4i_keypad_driver = { + .driver = { + .name = "sun4i-a10-keypad", + .of_match_table = of_match_ptr(sun4i_keypad_of_match), + }, + .probe = sun4i_keypad_probe, + .remove = sun4i_keypad_remove, +}; + +module_platform_driver(sun4i_keypad_driver); + +MODULE_DESCRIPTION("Allwinner sun4i keypad controller driver"); +MODULE_AUTHOR("Yassin Jaffer <yassinjaffer@gmail.com>"); +MODULE_LICENSE("GPL"); + -- 1.9.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 3/4] input: Add new sun4i-keypad driver 2015-09-15 14:05 ` [PATCH 3/4] input: Add new sun4i-keypad driver yassinjaffer @ 2015-09-17 13:05 ` Maxime Ripard [not found] ` <CAJzetvvUscEgTQ0Sr-BF7D6rzN_bfWT1KikRqz+BvSV-2+TWrw@mail.gmail.com> 2015-09-21 17:19 ` Dmitry Torokhov 1 sibling, 1 reply; 12+ messages in thread From: Maxime Ripard @ 2015-09-17 13:05 UTC (permalink / raw) To: yassinjaffer Cc: linux-sunxi, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel [-- Attachment #1: Type: text/plain, Size: 14464 bytes --] Hi, On Wed, Sep 16, 2015 at 12:05:56AM +1000, yassinjaffer@gmail.com wrote: > From: Yassin Jaffer <yassinjaffer@gmail.com> > > Allwinnner SUN4i Keypad controller is used to interface a SoC > with a matrix-typekeypad device. > The keypad controller supports multiple row and column lines. > A key can be placed at each intersection of a unique > row and a unique column. > The keypad controller can sense a key-press and key-release and report the > event using a interrupt to the cpu. > This patch adds a driver support to this. > The keypad controller driver does not give proper information > if more that two keys are selected. > > Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> > --- > drivers/input/keyboard/Kconfig | 11 ++ > drivers/input/keyboard/Makefile | 1 + > drivers/input/keyboard/sun4i-keypad.c | 361 ++++++++++++++++++++++++++++++++++ > 3 files changed, 373 insertions(+) > create mode 100644 drivers/input/keyboard/sun4i-keypad.c > > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig > index 2e80107..4f2f3f8 100644 > --- a/drivers/input/keyboard/Kconfig > +++ b/drivers/input/keyboard/Kconfig > @@ -590,6 +590,17 @@ config KEYBOARD_SUN4I_LRADC > To compile this driver as a module, choose M here: the > module will be called sun4i-lradc-keys. > > +config KEYBOARD_SUN4I_KEYPAD > + tristate "Allwinner sun4i keypad support" > + depends on ARCH_SUNXI Is this IP found on all the know SoCs, or just a subset of them? You probably want to add || COMPILE_TEST in that depends on too. > + select INPUT_MATRIXKMAP > + help > + This selects support for the Allwinner keypad > + on Allwinner sunxi SoCs. > + > + To compile this driver as a module, choose M here: the > + module will be called sun4i-keypad. > + > config KEYBOARD_DAVINCI > tristate "TI DaVinci Key Scan" > depends on ARCH_DAVINCI_DM365 > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile > index 1d416dd..d9f54b4 100644 > --- a/drivers/input/keyboard/Makefile > +++ b/drivers/input/keyboard/Makefile > @@ -57,6 +57,7 @@ obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o > obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o > obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o > obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o > +obj-$(CONFIG_KEYBOARD_SUN4I_KEYPAD) += sun4i-keypad.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/sun4i-keypad.c b/drivers/input/keyboard/sun4i-keypad.c > new file mode 100644 > index 0000000..995f9665 > --- /dev/null > +++ b/drivers/input/keyboard/sun4i-keypad.c > @@ -0,0 +1,361 @@ > +/* > + * Allwinner sun4i keypad Controller driver > + * > + * Copyright (C) 2015 Yassin Jaffer <yassinjaffer@gmail.com> > + * > + * Parts of this software are based on (derived from): > + * Copyright (C) 2013-2015 liming@allwinnertech.com, > + * qys<qinyongshen@allwinnertech.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/of_platform.h> > +#include <linux/slab.h> > +#include <linux/input.h> > +#include <linux/input/matrix_keypad.h> > + > +/* > + * Keypad Controller registers > + */ > +#define KP_CTL 0x00 /* Keypad Control register */ > +#define KP_TIMING 0x04 /* Keypad Timing register */ > +#define KP_INT_CFG 0x08 /* Keypad interrupt Config register */ > +#define KP_INT_STA 0x0c /* Keypad interrupt Status register */ > + > +#define KP_IN_OFFSET 0x10 /* Keypad Input Data register 0 */ > +#define KP_INX_OFFSET(reg_n) (KP_IN_OFFSET + 4 * (reg_n)) > + > +/* KP_CTL bits */ > +#define ENABLE(x) ((x) << 0) > +#define COLMASK(x) ((~x & 0xff) << 8) > +#define ROWMASK(x) ((~x & 0xff) << 16) Having the name of the register in that define would be great, to spot more easily issues (writing a value to another register, for example). Something like KP_CTL_ENABLE(x) in this case. > +/* KP_TIMING bits */ > +#define SCAN_CYCLE(x) ((x) << 0) > +#define DBC_CYCLE(x) ((x) << 16) > + > +/* KP_INT_CFG bits */ > +#define KP_IRQ_FEDGE BIT(0) > +#define KP_IRQ_REDGE BIT(1) > + > +/* KP_INT_STA bits */ > +#define KP_STA_FEDGE BIT(0) > +#define KP_STA_REDGE BIT(1) > + > +#define KP_MAX_ROWS 8 > +#define KP_MAX_COLS 8 > +#define N_ROWS_REG 2 > +#define KP_ROW_SHIFT 3 > +#define KP_BIT_SHIFT 32 > + > +#define MAX_MATRIX_KEY_NUM (KP_MAX_ROWS * KP_MAX_COLS) > + > +#define KP_BASE_CLK 1000000 > +#define MIN_CYCLE 0x10 > +#define MIN_SCAN_CYCLE 0x100 > +#define MIN_DBC_CYCLE 0x200 > + > +/* > + * keypad Controller structure: stores sunxi keypad controller information > + * > + * @dev: parent device > + * @input: pointer to input device object > + * @apb_clk: keypad Controller APB clock > + * @clk: keypad Controller mod clock > + * @base: keypad controller registers > + * @irq: interrupt > + * @rows_en_mask: Masks for enabled rows > + * @cols_en_mask: Masks for enabled cols > + * @keymap: matrix scan code table for keycodes > + * @key_press_state: cached keys press state > + * @debounce_cycl: keypad specific debounce cycle > + * @scan_cycl: keypad specific scan cycle > + * @autorepeat: flag for auto repetition > + */ > +struct sun4i_keypad_data { > + struct device *dev; > + struct input_dev *input; > + > + struct clk *apb_clk; > + struct clk *clk; > + void __iomem *base; > + int irq; > + > + unsigned short rows_en_mask; > + unsigned short cols_en_mask; > + > + unsigned short keymap[MAX_MATRIX_KEY_NUM]; > + > + unsigned int press_state[N_ROWS_REG]; > + > + unsigned int debounce_cycl; > + unsigned int scan_cycl; > + bool autorepeat; > +}; > + > +static void sun4i_keypad_scan(struct sun4i_keypad_data *keypad, bool edge) > +{ > + struct input_dev *input_dev = keypad->input; > + u32 key_scan[N_ROWS_REG]; > + unsigned long change; > + unsigned short code; > + int reg_nr, bit_nr, key_up; > + > + for (reg_nr = 0; reg_nr < N_ROWS_REG; reg_nr++) { > + key_scan[reg_nr] = ~(readl(keypad->base + > + KP_INX_OFFSET(reg_nr))); > + > + change = edge ? key_scan[reg_nr] : > + keypad->press_state[reg_nr] ^ key_scan[reg_nr]; > + > + key_up = edge || (change & keypad->press_state[reg_nr]); > + > + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { > + code = keypad->keymap[KP_BIT_SHIFT * reg_nr + bit_nr]; > + input_report_key(input_dev, code, key_up); > + input_sync(input_dev); I don't think you need to do an input_sync call each time for eah input_report_key. > + } > + > + keypad->press_state[reg_nr] = edge ? 0 : key_scan[reg_nr]; > + } > +} > + > +static irqreturn_t sun4i_keypad_irq(int irq, void *dev_id) > +{ > + struct sun4i_keypad_data *keypad = dev_id; > + u32 intr_status; > + > + intr_status = readl(keypad->base + KP_INT_STA); > + > + /* release only gives a valid information for a single key release */ > + if (intr_status & KP_STA_REDGE) > + sun4i_keypad_scan(keypad, true); > + > + /* press does not give valid information when > + * multiple rows are selected > + */ /* * This is the multi-line comment style */ > + if (intr_status & KP_STA_FEDGE) > + sun4i_keypad_scan(keypad, false); > + > + writel(intr_status, keypad->base + KP_INT_STA); > + > + return IRQ_HANDLED; > +} > + > +static int sun4i_keypad_open(struct input_dev *dev) > +{ > + struct sun4i_keypad_data *keypad = input_get_drvdata(dev); > + int reg_nr; > + > + if (clk_prepare_enable(keypad->apb_clk)) > + return -EINVAL; > + > + if (clk_prepare_enable(keypad->clk)) { > + clk_disable_unprepare(keypad->apb_clk); > + return -EINVAL; > + } > + > + for (reg_nr = 0; reg_nr < N_ROWS_REG; reg_nr++) > + keypad->press_state[reg_nr] = ~(readl(keypad->base + > + KP_INX_OFFSET(reg_nr))); > + > + writel(KP_IRQ_FEDGE | KP_IRQ_REDGE, keypad->base + KP_INT_CFG); > + > + writel(SCAN_CYCLE(keypad->scan_cycl) | > + DBC_CYCLE(keypad->debounce_cycl), keypad->base + KP_TIMING); > + > + writel(ENABLE(1) | ROWMASK(keypad->rows_en_mask) | > + COLMASK(keypad->cols_en_mask), keypad->base + KP_CTL); > + > + return 0; > +} > + > +static void sun4i_keypad_close(struct input_dev *dev) > +{ > + struct sun4i_keypad_data *keypad = input_get_drvdata(dev); > + > + writel(ENABLE(0) | ROWMASK(keypad->rows_en_mask) | > + COLMASK(keypad->cols_en_mask), keypad->base + KP_CTL); > + > + clk_disable_unprepare(keypad->clk); > + clk_disable_unprepare(keypad->apb_clk); > +} > + > +static int sun4i_keypad_parse_dt(struct device *dev, > + struct sun4i_keypad_data *keypad) > +{ > + struct device_node *np; > + > + np = dev->of_node; > + if (!np) > + return -EINVAL; > + > + of_property_read_u32(np, "debounce-cycle", &keypad->debounce_cycl); > + if (keypad->debounce_cycl < MIN_CYCLE) > + keypad->debounce_cycl = MIN_DBC_CYCLE; > + > + of_property_read_u32(np, "scan-cycle", &keypad->scan_cycl); > + if (keypad->scan_cycl < MIN_CYCLE) > + keypad->scan_cycl = MIN_SCAN_CYCLE; > + > + if (of_property_read_bool(np, "autorepeat")) > + keypad->autorepeat = true; > + > + return 0; > +} > + > +static int sun4i_keypad_probe(struct platform_device *pdev) > +{ > + struct sun4i_keypad_data *keypad; > + struct device *dev = &pdev->dev; > + int row, col, code; > + int ret = 0; > + > + keypad = devm_kzalloc(dev, sizeof(struct sun4i_keypad_data), > + GFP_KERNEL); > + if (!keypad) > + return -ENOMEM; > + > + ret = sun4i_keypad_parse_dt(dev, keypad); > + if (ret) > + return ret; > + > + keypad->base = devm_ioremap_resource(dev, > + platform_get_resource(pdev, IORESOURCE_MEM, 0)); Usually, it gets called on two lines to make it clearer. > + if (IS_ERR(keypad->base)) > + return PTR_ERR(keypad->base); > + > + keypad->dev = dev; > + keypad->input = devm_input_allocate_device(dev); > + if (!keypad->input) > + return -ENOMEM; > + > + keypad->input->name = pdev->name; > + keypad->input->phys = "sun4i_keypad/input0"; > + > + keypad->input->id.bustype = BUS_HOST; > + keypad->input->id.vendor = 0x0001; > + keypad->input->id.product = 0x0001; > + keypad->input->id.version = 0x0100; > + > + keypad->input->open = sun4i_keypad_open; > + keypad->input->close = sun4i_keypad_close; > + > + /* matrix keypad keymap as: > + * row << 24 | column << 16 | key-code > + */ > + ret = matrix_keypad_build_keymap(NULL, NULL, > + KP_MAX_ROWS, > + KP_MAX_COLS, > + keypad->keymap, > + keypad->input); > + if (ret) { > + dev_err(dev, "failed to build keymap\n"); > + return ret; > + } > + > + /* Search for enabled rows and cols */ > + for (row = 0; row < KP_MAX_ROWS; row++) { > + for (col = 0; col < KP_MAX_COLS; col++) { > + code = MATRIX_SCAN_CODE(row, col, KP_ROW_SHIFT); > + if (keypad->keymap[code] != KEY_RESERVED) { > + keypad->rows_en_mask |= 1 << row; > + keypad->cols_en_mask |= 1 << col; > + } > + } > + } > + > + if (keypad->autorepeat) > + __set_bit(EV_REP, keypad->input->evbit); > + > + input_set_capability(keypad->input, EV_MSC, MSC_SCAN); > + input_set_drvdata(keypad->input, keypad); > + > + keypad->irq = platform_get_irq(pdev, 0); > + if (keypad->irq < 0) { > + dev_err(dev, "failed to get keypad IRQ\n"); > + return -ENXIO; > + } > + > + ret = devm_request_irq(dev, keypad->irq, > + sun4i_keypad_irq, 0, > + "sun4i-a10-keypad", keypad); > + if (ret) { > + dev_err(dev, "failed to request IRQ\n"); > + return ret; > + } > + > + keypad->apb_clk = devm_clk_get(dev, "apb"); > + if (IS_ERR(keypad->apb_clk)) { > + dev_err(dev, "failed to get a apb clock.\n"); > + return PTR_ERR(keypad->apb_clk); > + } > + > + keypad->clk = devm_clk_get(dev, "keypad"); > + if (IS_ERR(keypad->clk)) { > + dev_err(dev, "failed to get a keypad clock.\n"); > + return PTR_ERR(keypad->clk); > + } > + > + ret = clk_set_rate(keypad->clk, KP_BASE_CLK); > + if (ret) { > + dev_err(dev, "set keypad base clock failed!\n"); > + return ret; > + } Do you need that rate to be enforced, or is it some leftover from the allwinner BSP? > + ret = input_register_device(keypad->input); > + if (ret) > + return ret; > + > + platform_set_drvdata(pdev, keypad); > + > + return 0; > +} > + > +static int sun4i_keypad_remove(struct platform_device *pdev) > +{ > + struct sun4i_keypad_data *keypad = platform_get_drvdata(pdev); > + > + free_irq(keypad->irq, keypad); > + input_unregister_device(keypad->input); > + kfree(keypad); The point of using devm_ functions is precisely to remove the removal code from both the probe error path (and you did so there), and from the remove. > + > + return 0; > +} > + > +static const struct of_device_id sun4i_keypad_of_match[] = { > + { .compatible = "allwinner,sun4i-a10-keypad", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, sun4i_keypad_of_match); > + > +static struct platform_driver sun4i_keypad_driver = { > + .driver = { > + .name = "sun4i-a10-keypad", > + .of_match_table = of_match_ptr(sun4i_keypad_of_match), > + }, > + .probe = sun4i_keypad_probe, > + .remove = sun4i_keypad_remove, > +}; > + > +module_platform_driver(sun4i_keypad_driver); > + > +MODULE_DESCRIPTION("Allwinner sun4i keypad controller driver"); > +MODULE_AUTHOR("Yassin Jaffer <yassinjaffer@gmail.com>"); > +MODULE_LICENSE("GPL"); > + It looks good otherwise, thanks! Maxime -- Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
[parent not found: <CAJzetvvUscEgTQ0Sr-BF7D6rzN_bfWT1KikRqz+BvSV-2+TWrw@mail.gmail.com>]
* Re: [PATCH 3/4] input: Add new sun4i-keypad driver [not found] ` <CAJzetvvUscEgTQ0Sr-BF7D6rzN_bfWT1KikRqz+BvSV-2+TWrw@mail.gmail.com> @ 2015-09-18 9:44 ` Maxime Ripard 0 siblings, 0 replies; 12+ messages in thread From: Maxime Ripard @ 2015-09-18 9:44 UTC (permalink / raw) To: Yassin Jaffer Cc: linux-sunxi, Dmitry Torokhov, linux-input, linux-arm-kernel, linux-kernel [-- Attachment #1: Type: text/plain, Size: 780 bytes --] Hi Yassin, On Fri, Sep 18, 2015 at 10:19:55AM +1000, Yassin Jaffer wrote: > Hi Maxime > > I appreciate your time and efforts . > > Do you need that rate to be enforced, or is it some leftover from the > > allwinner BSP? > > > I've found that clock rate works fine with the default denounce and scan > cycle. It was not really my point. My point was do you *need* it to operate. And so your properties were in clock cycles? Please use a unit indenpendant of the clock rate, like seconds or Hz (or any multiple of them). > By the way do you have any dev board which expose the keypad pins? I don't think I have any. Thanks, Maxime -- Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/4] input: Add new sun4i-keypad driver 2015-09-15 14:05 ` [PATCH 3/4] input: Add new sun4i-keypad driver yassinjaffer 2015-09-17 13:05 ` Maxime Ripard @ 2015-09-21 17:19 ` Dmitry Torokhov 1 sibling, 0 replies; 12+ messages in thread From: Dmitry Torokhov @ 2015-09-21 17:19 UTC (permalink / raw) To: yassinjaffer Cc: linux-sunxi, maxime.ripard, linux-input, linux-arm-kernel, linux-kernel Hi Yassin, On Wed, Sep 16, 2015 at 12:05:56AM +1000, yassinjaffer@gmail.com wrote: > From: Yassin Jaffer <yassinjaffer@gmail.com> > > Allwinnner SUN4i Keypad controller is used to interface a SoC > with a matrix-typekeypad device. > The keypad controller supports multiple row and column lines. > A key can be placed at each intersection of a unique > row and a unique column. > The keypad controller can sense a key-press and key-release and report the > event using a interrupt to the cpu. > This patch adds a driver support to this. > The keypad controller driver does not give proper information > if more that two keys are selected. > > Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> > --- > drivers/input/keyboard/Kconfig | 11 ++ > drivers/input/keyboard/Makefile | 1 + > drivers/input/keyboard/sun4i-keypad.c | 361 ++++++++++++++++++++++++++++++++++ > 3 files changed, 373 insertions(+) > create mode 100644 drivers/input/keyboard/sun4i-keypad.c > > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig > index 2e80107..4f2f3f8 100644 > --- a/drivers/input/keyboard/Kconfig > +++ b/drivers/input/keyboard/Kconfig > @@ -590,6 +590,17 @@ config KEYBOARD_SUN4I_LRADC > To compile this driver as a module, choose M here: the > module will be called sun4i-lradc-keys. > > +config KEYBOARD_SUN4I_KEYPAD > + tristate "Allwinner sun4i keypad support" > + depends on ARCH_SUNXI > + select INPUT_MATRIXKMAP > + help > + This selects support for the Allwinner keypad > + on Allwinner sunxi SoCs. > + > + To compile this driver as a module, choose M here: the > + module will be called sun4i-keypad. > + > config KEYBOARD_DAVINCI > tristate "TI DaVinci Key Scan" > depends on ARCH_DAVINCI_DM365 > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile > index 1d416dd..d9f54b4 100644 > --- a/drivers/input/keyboard/Makefile > +++ b/drivers/input/keyboard/Makefile > @@ -57,6 +57,7 @@ obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o > obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o > obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o > obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o > +obj-$(CONFIG_KEYBOARD_SUN4I_KEYPAD) += sun4i-keypad.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/sun4i-keypad.c b/drivers/input/keyboard/sun4i-keypad.c > new file mode 100644 > index 0000000..995f9665 > --- /dev/null > +++ b/drivers/input/keyboard/sun4i-keypad.c > @@ -0,0 +1,361 @@ > +/* > + * Allwinner sun4i keypad Controller driver > + * > + * Copyright (C) 2015 Yassin Jaffer <yassinjaffer@gmail.com> > + * > + * Parts of this software are based on (derived from): > + * Copyright (C) 2013-2015 liming@allwinnertech.com, > + * qys<qinyongshen@allwinnertech.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/of_platform.h> > +#include <linux/slab.h> > +#include <linux/input.h> > +#include <linux/input/matrix_keypad.h> > + > +/* > + * Keypad Controller registers > + */ > +#define KP_CTL 0x00 /* Keypad Control register */ > +#define KP_TIMING 0x04 /* Keypad Timing register */ > +#define KP_INT_CFG 0x08 /* Keypad interrupt Config register */ > +#define KP_INT_STA 0x0c /* Keypad interrupt Status register */ > + > +#define KP_IN_OFFSET 0x10 /* Keypad Input Data register 0 */ > +#define KP_INX_OFFSET(reg_n) (KP_IN_OFFSET + 4 * (reg_n)) > + > +/* KP_CTL bits */ > +#define ENABLE(x) ((x) << 0) > +#define COLMASK(x) ((~x & 0xff) << 8) > +#define ROWMASK(x) ((~x & 0xff) << 16) > + > +/* KP_TIMING bits */ > +#define SCAN_CYCLE(x) ((x) << 0) > +#define DBC_CYCLE(x) ((x) << 16) > + > +/* KP_INT_CFG bits */ > +#define KP_IRQ_FEDGE BIT(0) > +#define KP_IRQ_REDGE BIT(1) > + > +/* KP_INT_STA bits */ > +#define KP_STA_FEDGE BIT(0) > +#define KP_STA_REDGE BIT(1) > + > +#define KP_MAX_ROWS 8 > +#define KP_MAX_COLS 8 > +#define N_ROWS_REG 2 > +#define KP_ROW_SHIFT 3 > +#define KP_BIT_SHIFT 32 > + > +#define MAX_MATRIX_KEY_NUM (KP_MAX_ROWS * KP_MAX_COLS) > + > +#define KP_BASE_CLK 1000000 > +#define MIN_CYCLE 0x10 > +#define MIN_SCAN_CYCLE 0x100 > +#define MIN_DBC_CYCLE 0x200 > + > +/* > + * keypad Controller structure: stores sunxi keypad controller information > + * > + * @dev: parent device > + * @input: pointer to input device object > + * @apb_clk: keypad Controller APB clock > + * @clk: keypad Controller mod clock > + * @base: keypad controller registers > + * @irq: interrupt > + * @rows_en_mask: Masks for enabled rows > + * @cols_en_mask: Masks for enabled cols > + * @keymap: matrix scan code table for keycodes > + * @key_press_state: cached keys press state > + * @debounce_cycl: keypad specific debounce cycle > + * @scan_cycl: keypad specific scan cycle > + * @autorepeat: flag for auto repetition > + */ > +struct sun4i_keypad_data { > + struct device *dev; > + struct input_dev *input; > + > + struct clk *apb_clk; > + struct clk *clk; > + void __iomem *base; > + int irq; > + > + unsigned short rows_en_mask; > + unsigned short cols_en_mask; > + > + unsigned short keymap[MAX_MATRIX_KEY_NUM]; > + > + unsigned int press_state[N_ROWS_REG]; > + > + unsigned int debounce_cycl; > + unsigned int scan_cycl; > + bool autorepeat; > +}; > + > +static void sun4i_keypad_scan(struct sun4i_keypad_data *keypad, bool edge) > +{ > + struct input_dev *input_dev = keypad->input; > + u32 key_scan[N_ROWS_REG]; > + unsigned long change; > + unsigned short code; > + int reg_nr, bit_nr, key_up; > + > + for (reg_nr = 0; reg_nr < N_ROWS_REG; reg_nr++) { > + key_scan[reg_nr] = ~(readl(keypad->base + > + KP_INX_OFFSET(reg_nr))); > + > + change = edge ? key_scan[reg_nr] : > + keypad->press_state[reg_nr] ^ key_scan[reg_nr]; > + > + key_up = edge || (change & keypad->press_state[reg_nr]); > + > + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { > + code = keypad->keymap[KP_BIT_SHIFT * reg_nr + bit_nr]; > + input_report_key(input_dev, code, key_up); > + input_sync(input_dev); Maybe send sync only after you send all keys? > + } > + > + keypad->press_state[reg_nr] = edge ? 0 : key_scan[reg_nr]; > + } > +} > + > +static irqreturn_t sun4i_keypad_irq(int irq, void *dev_id) > +{ > + struct sun4i_keypad_data *keypad = dev_id; > + u32 intr_status; > + > + intr_status = readl(keypad->base + KP_INT_STA); > + > + /* release only gives a valid information for a single key release */ > + if (intr_status & KP_STA_REDGE) > + sun4i_keypad_scan(keypad, true); > + > + /* press does not give valid information when > + * multiple rows are selected > + */ > + if (intr_status & KP_STA_FEDGE) > + sun4i_keypad_scan(keypad, false); > + > + writel(intr_status, keypad->base + KP_INT_STA); > + > + return IRQ_HANDLED; > +} > + > +static int sun4i_keypad_open(struct input_dev *dev) > +{ > + struct sun4i_keypad_data *keypad = input_get_drvdata(dev); > + int reg_nr; > + > + if (clk_prepare_enable(keypad->apb_clk)) > + return -EINVAL; > + > + if (clk_prepare_enable(keypad->clk)) { > + clk_disable_unprepare(keypad->apb_clk); > + return -EINVAL; > + } > + > + for (reg_nr = 0; reg_nr < N_ROWS_REG; reg_nr++) > + keypad->press_state[reg_nr] = ~(readl(keypad->base + > + KP_INX_OFFSET(reg_nr))); > + > + writel(KP_IRQ_FEDGE | KP_IRQ_REDGE, keypad->base + KP_INT_CFG); > + > + writel(SCAN_CYCLE(keypad->scan_cycl) | > + DBC_CYCLE(keypad->debounce_cycl), keypad->base + KP_TIMING); > + > + writel(ENABLE(1) | ROWMASK(keypad->rows_en_mask) | > + COLMASK(keypad->cols_en_mask), keypad->base + KP_CTL); > + > + return 0; > +} > + > +static void sun4i_keypad_close(struct input_dev *dev) > +{ > + struct sun4i_keypad_data *keypad = input_get_drvdata(dev); > + > + writel(ENABLE(0) | ROWMASK(keypad->rows_en_mask) | > + COLMASK(keypad->cols_en_mask), keypad->base + KP_CTL); > + > + clk_disable_unprepare(keypad->clk); > + clk_disable_unprepare(keypad->apb_clk); > +} > + > +static int sun4i_keypad_parse_dt(struct device *dev, > + struct sun4i_keypad_data *keypad) > +{ > + struct device_node *np; > + > + np = dev->of_node; > + if (!np) > + return -EINVAL; > + > + of_property_read_u32(np, "debounce-cycle", &keypad->debounce_cycl); > + if (keypad->debounce_cycl < MIN_CYCLE) > + keypad->debounce_cycl = MIN_DBC_CYCLE; > + > + of_property_read_u32(np, "scan-cycle", &keypad->scan_cycl); > + if (keypad->scan_cycl < MIN_CYCLE) > + keypad->scan_cycl = MIN_SCAN_CYCLE; > + > + if (of_property_read_bool(np, "autorepeat")) > + keypad->autorepeat = true; > + > + return 0; > +} > + > +static int sun4i_keypad_probe(struct platform_device *pdev) > +{ > + struct sun4i_keypad_data *keypad; > + struct device *dev = &pdev->dev; > + int row, col, code; > + int ret = 0; Why do you need to initialize this variable? Also, my personal preference to name variables that hold erorr codes "error". > + > + keypad = devm_kzalloc(dev, sizeof(struct sun4i_keypad_data), > + GFP_KERNEL); > + if (!keypad) > + return -ENOMEM; > + > + ret = sun4i_keypad_parse_dt(dev, keypad); > + if (ret) > + return ret; > + > + keypad->base = devm_ioremap_resource(dev, > + platform_get_resource(pdev, IORESOURCE_MEM, 0)); > + if (IS_ERR(keypad->base)) > + return PTR_ERR(keypad->base); > + > + keypad->dev = dev; > + keypad->input = devm_input_allocate_device(dev); > + if (!keypad->input) > + return -ENOMEM; > + > + keypad->input->name = pdev->name; > + keypad->input->phys = "sun4i_keypad/input0"; > + > + keypad->input->id.bustype = BUS_HOST; > + keypad->input->id.vendor = 0x0001; > + keypad->input->id.product = 0x0001; > + keypad->input->id.version = 0x0100; > + > + keypad->input->open = sun4i_keypad_open; > + keypad->input->close = sun4i_keypad_close; > + > + /* matrix keypad keymap as: > + * row << 24 | column << 16 | key-code > + */ > + ret = matrix_keypad_build_keymap(NULL, NULL, > + KP_MAX_ROWS, > + KP_MAX_COLS, > + keypad->keymap, > + keypad->input); > + if (ret) { > + dev_err(dev, "failed to build keymap\n"); > + return ret; > + } > + > + /* Search for enabled rows and cols */ > + for (row = 0; row < KP_MAX_ROWS; row++) { > + for (col = 0; col < KP_MAX_COLS; col++) { > + code = MATRIX_SCAN_CODE(row, col, KP_ROW_SHIFT); > + if (keypad->keymap[code] != KEY_RESERVED) { > + keypad->rows_en_mask |= 1 << row; > + keypad->cols_en_mask |= 1 << col; > + } > + } > + } > + > + if (keypad->autorepeat) > + __set_bit(EV_REP, keypad->input->evbit); > + > + input_set_capability(keypad->input, EV_MSC, MSC_SCAN); > + input_set_drvdata(keypad->input, keypad); > + > + keypad->irq = platform_get_irq(pdev, 0); > + if (keypad->irq < 0) { > + dev_err(dev, "failed to get keypad IRQ\n"); > + return -ENXIO; Why not return keypad->irq; ? > + } > + > + ret = devm_request_irq(dev, keypad->irq, > + sun4i_keypad_irq, 0, > + "sun4i-a10-keypad", keypad); > + if (ret) { > + dev_err(dev, "failed to request IRQ\n"); > + return ret; > + } > + > + keypad->apb_clk = devm_clk_get(dev, "apb"); > + if (IS_ERR(keypad->apb_clk)) { > + dev_err(dev, "failed to get a apb clock.\n"); > + return PTR_ERR(keypad->apb_clk); > + } > + > + keypad->clk = devm_clk_get(dev, "keypad"); > + if (IS_ERR(keypad->clk)) { > + dev_err(dev, "failed to get a keypad clock.\n"); > + return PTR_ERR(keypad->clk); > + } > + > + ret = clk_set_rate(keypad->clk, KP_BASE_CLK); > + if (ret) { > + dev_err(dev, "set keypad base clock failed!\n"); > + return ret; > + } > + > + ret = input_register_device(keypad->input); > + if (ret) > + return ret; > + > + platform_set_drvdata(pdev, keypad); > + > + return 0; > +} > + > +static int sun4i_keypad_remove(struct platform_device *pdev) > +{ > + struct sun4i_keypad_data *keypad = platform_get_drvdata(pdev); > + > + free_irq(keypad->irq, keypad); > + input_unregister_device(keypad->input); > + kfree(keypad); You have never tried unlocking the module or unbinding the driver, haven't you? free_irq() and kfree() can't be used with devm-alloctaed resources. The good news is that you can simply remove sun4i_keypad_remove() altogether, devm will take care of destroying all resources, including input device, in right order. > + > + return 0; > +} > + > +static const struct of_device_id sun4i_keypad_of_match[] = { > + { .compatible = "allwinner,sun4i-a10-keypad", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, sun4i_keypad_of_match); > + > +static struct platform_driver sun4i_keypad_driver = { > + .driver = { > + .name = "sun4i-a10-keypad", > + .of_match_table = of_match_ptr(sun4i_keypad_of_match), > + }, > + .probe = sun4i_keypad_probe, > + .remove = sun4i_keypad_remove, > +}; > + > +module_platform_driver(sun4i_keypad_driver); > + > +MODULE_DESCRIPTION("Allwinner sun4i keypad controller driver"); > +MODULE_AUTHOR("Yassin Jaffer <yassinjaffer@gmail.com>"); > +MODULE_LICENSE("GPL"); > + > -- > 1.9.1 > -- Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad 2015-09-15 14:05 NEW SUN4i KEYPAD DRIVER yassinjaffer ` (2 preceding siblings ...) 2015-09-15 14:05 ` [PATCH 3/4] input: Add new sun4i-keypad driver yassinjaffer @ 2015-09-15 14:05 ` yassinjaffer 2015-09-15 15:30 ` Maxime Ripard 3 siblings, 1 reply; 12+ messages in thread From: yassinjaffer @ 2015-09-15 14:05 UTC (permalink / raw) To: linux-sunxi Cc: maxime.ripard, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel, Yassin Jaffer From: Yassin Jaffer <yassinjaffer@gmail.com> Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> --- .../devicetree/bindings/input/sun4i-keypad.txt | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/sun4i-keypad.txt diff --git a/Documentation/devicetree/bindings/input/sun4i-keypad.txt b/Documentation/devicetree/bindings/input/sun4i-keypad.txt new file mode 100644 index 0000000..60ed0f2 --- /dev/null +++ b/Documentation/devicetree/bindings/input/sun4i-keypad.txt @@ -0,0 +1,56 @@ +Allwinner sun4i keypad +------------------------------------------------ + +Required properties: + - compatible: "allwinner,sun4i-a10-keypad" + - reg: mmio address range of the chip + - interrupts: interrupt to which the chip is connected + - clocks : shall reference keypad controller clocks. + - clock-names : keypad controller internal clock names. Shall contain : + * "apb" : APB gating clock + * "keypad" : keypad controller clock + +Required Board Specific Properties: +- pinctrl-names: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- pinctrl-0: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- linux,keymap: The definition can be found at +bindings/input/matrix-keymap.txt. + +Optional properties: + - scan-cycle: device specific scan cycle + - debounce-cycle: device specific debounce cycle + - autorepeat: If specified device will autorepeat + +Example: + +#include <dt-bindings/input/input.h> + + kp: kp@01c23000 { + compatible = "allwinner,sun4i-a10-keypad"; + reg = <0x01c23000 0x400>; + interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&apb0_gates 10>, <&keypad_clk>; + clock-names = "apb", "keypad"; + pinctrl-names = "default"; + pinctrl-0 = <&keypad_rows>, <&keypad_cols>; + linux,keymap = <0x00000067 /* KEY_UP */ + 0x0001006c /* KEY_DOWN */ + 0x00020072 /* KEY_VOLUMEDOWN */ + 0x00030066 /* KEY_HOME */ + 0x0100006a /* KEY_RIGHT */ + 0x01010069 /* KEY_LEFT */ + 0x0102001c /* KEY_ENTER */ + 0x01030073 /* KEY_VOLUMEUP */ + 0x02000040 /* KEY_F6 */ + 0x02010042 /* KEY_F8 */ + 0x02020043 /* KEY_F9 */ + 0x02030044 /* KEY_F10 */ + 0x0300003b /* KEY_F1 */ + 0x0301003c /* KEY_F2 */ + 0x0302003d /* KEY_F3 */ + 0x03030074>; /* KEY_POWER */ + }; -- 1.9.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad 2015-09-15 14:05 ` [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad yassinjaffer @ 2015-09-15 15:30 ` Maxime Ripard 0 siblings, 0 replies; 12+ messages in thread From: Maxime Ripard @ 2015-09-15 15:30 UTC (permalink / raw) To: yassinjaffer Cc: linux-sunxi, dmitry.torokhov, linux-input, linux-arm-kernel, linux-kernel [-- Attachment #1: Type: text/plain, Size: 2914 bytes --] On Wed, Sep 16, 2015 at 12:05:57AM +1000, yassinjaffer@gmail.com wrote: > From: Yassin Jaffer <yassinjaffer@gmail.com> > > Signed-off-by: Yassin Jaffer <yassinjaffer@gmail.com> > --- > .../devicetree/bindings/input/sun4i-keypad.txt | 56 ++++++++++++++++++++++ > 1 file changed, 56 insertions(+) > create mode 100644 Documentation/devicetree/bindings/input/sun4i-keypad.txt > > diff --git a/Documentation/devicetree/bindings/input/sun4i-keypad.txt b/Documentation/devicetree/bindings/input/sun4i-keypad.txt > new file mode 100644 > index 0000000..60ed0f2 > --- /dev/null > +++ b/Documentation/devicetree/bindings/input/sun4i-keypad.txt > @@ -0,0 +1,56 @@ > +Allwinner sun4i keypad > +------------------------------------------------ > + > +Required properties: > + - compatible: "allwinner,sun4i-a10-keypad" > + - reg: mmio address range of the chip > + - interrupts: interrupt to which the chip is connected > + - clocks : shall reference keypad controller clocks. > + - clock-names : keypad controller internal clock names. Shall contain : > + * "apb" : APB gating clock > + * "keypad" : keypad controller clock > + > +Required Board Specific Properties: > +- pinctrl-names: The definition can be found at > +pinctrl/pinctrl-bindings.txt. > + > +- pinctrl-0: The definition can be found at > +pinctrl/pinctrl-bindings.txt. > + > +- linux,keymap: The definition can be found at > +bindings/input/matrix-keymap.txt. > + > +Optional properties: > + - scan-cycle: device specific scan cycle > + - debounce-cycle: device specific debounce cycle > + - autorepeat: If specified device will autorepeat Are those properties generic? I couldn't find them defined anywhere. In which units are those properties? > + > +Example: > + > +#include <dt-bindings/input/input.h> > + > + kp: kp@01c23000 { > + compatible = "allwinner,sun4i-a10-keypad"; > + reg = <0x01c23000 0x400>; > + interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>; > + clocks = <&apb0_gates 10>, <&keypad_clk>; > + clock-names = "apb", "keypad"; > + pinctrl-names = "default"; > + pinctrl-0 = <&keypad_rows>, <&keypad_cols>; > + linux,keymap = <0x00000067 /* KEY_UP */ > + 0x0001006c /* KEY_DOWN */ > + 0x00020072 /* KEY_VOLUMEDOWN */ > + 0x00030066 /* KEY_HOME */ > + 0x0100006a /* KEY_RIGHT */ > + 0x01010069 /* KEY_LEFT */ > + 0x0102001c /* KEY_ENTER */ > + 0x01030073 /* KEY_VOLUMEUP */ > + 0x02000040 /* KEY_F6 */ > + 0x02010042 /* KEY_F8 */ > + 0x02020043 /* KEY_F9 */ > + 0x02030044 /* KEY_F10 */ > + 0x0300003b /* KEY_F1 */ > + 0x0301003c /* KEY_F2 */ > + 0x0302003d /* KEY_F3 */ > + 0x03030074>; /* KEY_POWER */ You don't seem to use the header you just told us to include. Thanks! Maxime -- Maxime Ripard, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2015-09-21 17:19 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-09-15 14:05 NEW SUN4i KEYPAD DRIVER yassinjaffer 2015-09-15 14:05 ` [PATCH 1/4] ARM:dts:sun7i: Add keypad clk node yassinjaffer 2015-09-16 5:11 ` Maxime Ripard 2015-09-15 14:05 ` [PATCH 2/4] ARM: dts: sun7i: Add keypad node to Allwinner A20 SoC yassinjaffer 2015-09-17 11:29 ` Maxime Ripard 2015-09-17 14:20 ` [linux-sunxi] " Chen-Yu Tsai 2015-09-15 14:05 ` [PATCH 3/4] input: Add new sun4i-keypad driver yassinjaffer 2015-09-17 13:05 ` Maxime Ripard [not found] ` <CAJzetvvUscEgTQ0Sr-BF7D6rzN_bfWT1KikRqz+BvSV-2+TWrw@mail.gmail.com> 2015-09-18 9:44 ` Maxime Ripard 2015-09-21 17:19 ` Dmitry Torokhov 2015-09-15 14:05 ` [PATCH 4/4] devicetree: bindings:Allwinner sun4i keypad yassinjaffer 2015-09-15 15:30 ` Maxime Ripard
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).