[PATCH v3 0/5] dm: input: driver for buttons with linux, code declaration

Bootmenu requires an input device with arrows and enter key. A common smartphone luckily has power, volume up/down buttons, which may be used for controlling bootmenu.
Button keyboard driver relies on button driver - iterates over all button with linux,code, checks state and sends events. Add support for linux,code in button driver. Fix qcom pwr-key gpio driver to work with button driver.
Dzmitry Sankouski (5): gpio: qcom: add direction functions for pwrkey dts: add missing linux,code in gpio-keys test: create dedicated fdt node for ofnode_for_each_prop test dm: button: add support for linux_code in button-gpio.c driver dm: input: add button_kbd driver
arch/arm/dts/am3517-evm-ui.dtsi | 2 +- .../dts/imx6ul-phytec-segin-peb-eval-01.dtsi | 2 +- arch/arm/dts/rk3288-popmetal.dtsi | 2 + arch/arm/dts/rk3288-tinker.dtsi | 2 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 16 +++ drivers/button/button-gpio.c | 17 ++- drivers/button/button-uclass.c | 10 ++ drivers/gpio/qcom_pmic_gpio.c | 16 +++ drivers/input/Kconfig | 9 ++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 126 ++++++++++++++++++ include/button.h | 16 +++ test/dm/button.c | 13 ++ test/dm/ofnode.c | 2 +- 15 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 drivers/input/button_kbd.c

GPIO button driver requires direction functions to probe button gpio. Those functions are blank, since pwrkey is not really gpio, and don't support direction settings.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Sumit Garg sumit.garg@linaro.org --- Changes for v2: - none Changes for v3: - KDPWR and RESIN not gpio: fix comment and commit message
drivers/gpio/qcom_pmic_gpio.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c index 3be1be8692..65feb453eb 100644 --- a/drivers/gpio/qcom_pmic_gpio.c +++ b/drivers/gpio/qcom_pmic_gpio.c @@ -303,9 +303,25 @@ static int qcom_pwrkey_get_value(struct udevice *dev, unsigned offset) } }
+/* + * Since pmic buttons modelled as GPIO, we need empty direction functions + * to trick u-boot button driver + */ +static int qcom_pwrkey_direction_input(struct udevice *dev, unsigned int offset) +{ + return 0; +} + +static int qcom_pwrkey_direction_output(struct udevice *dev, unsigned int offset, int value) +{ + return -EOPNOTSUPP; +} + static const struct dm_gpio_ops qcom_pwrkey_ops = { .get_value = qcom_pwrkey_get_value, .get_function = qcom_pwrkey_get_function, + .direction_input = qcom_pwrkey_direction_input, + .direction_output = qcom_pwrkey_direction_output, };
static int qcom_pwrkey_probe(struct udevice *dev)

On Sun, Jan 22, 2023 at 06:21:21PM +0300, Dzmitry Sankouski wrote:
GPIO button driver requires direction functions to probe button gpio. Those functions are blank, since pwrkey is not really gpio, and don't support direction settings.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Sumit Garg sumit.garg@linaro.org
Applied to u-boot/master, thanks!

gpio-keys linux driver enforces user to specify linux,code. Add missing linux,code before implementing button input support.
- arch/arm/dts/rk3288-popmetal.dtsi -> KEY_POWER - arch/arm/dts/rk3288-tinker.dtsi -> KEY_POWER - arch/arm/dts/am3517-evm-ui.dtsi -> KEY_RECORD - sandbox/dts/sandbox.dtsi -> BTN_1 - sandbox/dts/sandbox.dts -> BTN_1
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- Changes for v2: - N/A Changes for v3: - import input.h in dts to provide event constants
arch/arm/dts/am3517-evm-ui.dtsi | 2 +- arch/arm/dts/imx6ul-phytec-segin-peb-eval-01.dtsi | 2 +- arch/arm/dts/rk3288-popmetal.dtsi | 2 ++ arch/arm/dts/rk3288-tinker.dtsi | 2 ++ arch/sandbox/dts/sandbox.dtsi | 4 ++++ 5 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/arm/dts/am3517-evm-ui.dtsi b/arch/arm/dts/am3517-evm-ui.dtsi index 7d8f32bf70..340e68178c 100644 --- a/arch/arm/dts/am3517-evm-ui.dtsi +++ b/arch/arm/dts/am3517-evm-ui.dtsi @@ -72,7 +72,7 @@
record { label = "Record"; - /* linux,code = <BTN_0>; */ + linux,code = <KEY_RECORD>; gpios = <&tca6416_2 15 GPIO_ACTIVE_LOW>; };
diff --git a/arch/arm/dts/imx6ul-phytec-segin-peb-eval-01.dtsi b/arch/arm/dts/imx6ul-phytec-segin-peb-eval-01.dtsi index 2f3fd32a11..5f760ed698 100644 --- a/arch/arm/dts/imx6ul-phytec-segin-peb-eval-01.dtsi +++ b/arch/arm/dts/imx6ul-phytec-segin-peb-eval-01.dtsi @@ -8,7 +8,7 @@
/ { gpio_keys: gpio-keys { - compatible = "gpio-key"; + compatible = "gpio-keys"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio_keys>; status = "disabled"; diff --git a/arch/arm/dts/rk3288-popmetal.dtsi b/arch/arm/dts/rk3288-popmetal.dtsi index 63785eb55e..0253933a11 100644 --- a/arch/arm/dts/rk3288-popmetal.dtsi +++ b/arch/arm/dts/rk3288-popmetal.dtsi @@ -38,6 +38,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */
+#include <dt-bindings/input/input.h> #include "rk3288.dtsi"
/ { @@ -63,6 +64,7 @@ power { gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; label = "GPIO Key Power"; + linux,code = <KEY_POWER>; linux,input-type = <1>; wakeup-source; debounce-interval = <100>; diff --git a/arch/arm/dts/rk3288-tinker.dtsi b/arch/arm/dts/rk3288-tinker.dtsi index 2f816af47f..46460ae455 100644 --- a/arch/arm/dts/rk3288-tinker.dtsi +++ b/arch/arm/dts/rk3288-tinker.dtsi @@ -38,6 +38,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */
+#include <dt-bindings/input/input.h> #include "rk3288.dtsi"
/ { @@ -63,6 +64,7 @@ button@0 { gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; label = "GPIO Key Power"; + linux,code = <KEY_POWER>; linux,input-type = <1>; gpio-key,wakeup = <1>; debounce-interval = <100>; diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index afe598a4f5..c2c71505ce 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -4,6 +4,8 @@ * and sandbox64 builds. */
+#include <dt-bindings/input/input.h> + #define USB_CLASS_HUB 9
/ { @@ -49,11 +51,13 @@ btn1 { gpios = <&gpio_a 3 0>; label = "button1"; + linux,code = <BTN_1>; };
btn2 { gpios = <&gpio_a 4 0>; label = "button2"; + linux,code = <BTN_2>; }; };

On Sun, Jan 22, 2023 at 06:21:22PM +0300, Dzmitry Sankouski wrote:
gpio-keys linux driver enforces user to specify linux,code. Add missing linux,code before implementing button input support.
- arch/arm/dts/rk3288-popmetal.dtsi -> KEY_POWER
- arch/arm/dts/rk3288-tinker.dtsi -> KEY_POWER
- arch/arm/dts/am3517-evm-ui.dtsi -> KEY_RECORD
- sandbox/dts/sandbox.dtsi -> BTN_1
- sandbox/dts/sandbox.dts -> BTN_1
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!

Property count may change in /buttons node, if more button tests added, and this will break ofnode_for_each_prop. Add separate node for mentioned test.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com --- Changes for v2: N/A Changes for v3: N/A
arch/sandbox/dts/test.dts | 14 ++++++++++++++ test/dm/ofnode.c | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2e580f980f..06ad027638 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1517,6 +1517,20 @@ }; };
+ ofnode-foreach { + compatible = "foreach"; + + first { + prop1 = <1>; + prop2 = <2>; + }; + + second { + prop1 = <1>; + prop2 = <2>; + }; + }; + osd { compatible = "sandbox,sandbox_osd"; }; diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 8077affabb..473a8cef57 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -1046,7 +1046,7 @@ static int dm_test_ofnode_for_each_prop(struct unit_test_state *uts) struct ofprop prop; int count;
- node = ofnode_path("/buttons"); + node = ofnode_path("/ofnode-foreach"); count = 0;
/* we expect "compatible" for each node */

On Sun, 22 Jan 2023 at 08:21, Dzmitry Sankouski dsankouski@gmail.com wrote:
Property count may change in /buttons node, if more button tests added, and this will break ofnode_for_each_prop. Add separate node for mentioned test.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
Changes for v2: N/A Changes for v3: N/A
arch/sandbox/dts/test.dts | 14 ++++++++++++++ test/dm/ofnode.c | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-)
Reviewed-by: Simon Glass sjg@chromium.org

On Sun, Jan 22, 2023 at 06:21:23PM +0300, Dzmitry Sankouski wrote:
Property count may change in /buttons node, if more button tests added, and this will break ofnode_for_each_prop. Add separate node for mentioned test.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!

Linux event code must be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com --- Changes for v2: - fail, if linux,code not found Changes for v3: - add test for linux,code - change linux,code type to int - new line after return - add specific error code in function docs
arch/sandbox/dts/test.dts | 2 ++ drivers/button/button-gpio.c | 17 ++++++++++++++++- drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ test/dm/button.c | 13 +++++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 06ad027638..f8d59f1ce1 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -172,11 +172,13 @@ btn1 { gpios = <&gpio_a 3 0>; label = "button1"; + linux,code = <BTN_1>; };
btn2 { gpios = <&gpio_a 4 0>; label = "button2"; + linux,code = <BTN_2>; }; };
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..7b5b3affe2 100644 --- a/drivers/button/button-gpio.c +++ b/drivers/button/button-gpio.c @@ -13,6 +13,7 @@
struct button_gpio_priv { struct gpio_desc gpio; + int linux_code; };
static enum button_state_t button_gpio_get_state(struct udevice *dev) @@ -29,6 +30,17 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static int button_gpio_get_code(struct udevice *dev) +{ + struct button_gpio_priv *priv = dev_get_priv(dev); + int code = priv->linux_code; + + if (!code) + return -ENODATA; + + return code; +} + static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); @@ -43,7 +55,9 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
- return 0; + ret = dev_read_u32(dev, "linux,code", &priv->linux_code); + + return ret; }
static int button_gpio_remove(struct udevice *dev) @@ -92,6 +106,7 @@ static int button_gpio_bind(struct udevice *parent)
static const struct button_ops button_gpio_ops = { .get_state = button_gpio_get_state, + .get_code = button_gpio_get_code, };
static const struct udevice_id button_gpio_ids[] = { diff --git a/drivers/button/button-uclass.c b/drivers/button/button-uclass.c index e33ed7d01d..032191d61a 100644 --- a/drivers/button/button-uclass.c +++ b/drivers/button/button-uclass.c @@ -38,6 +38,16 @@ enum button_state_t button_get_state(struct udevice *dev) return ops->get_state(dev); }
+int button_get_code(struct udevice *dev) +{ + struct button_ops *ops = button_get_ops(dev); + + if (!ops->get_code) + return -ENOSYS; + + return ops->get_code(dev); +} + UCLASS_DRIVER(button) = { .id = UCLASS_BUTTON, .name = "button", diff --git a/include/button.h b/include/button.h index 96e6b1901f..207f4a0f4d 100644 --- a/include/button.h +++ b/include/button.h @@ -37,6 +37,14 @@ struct button_ops { * @return button state button_state_t, or -ve on error */ enum button_state_t (*get_state)(struct udevice *dev); + + /** + * get_code() - get linux event code of a button + * + * @dev: button device to change + * @return button code, or -ENODATA on error + */ + int (*get_code)(struct udevice *dev); };
#define button_get_ops(dev) ((struct button_ops *)(dev)->driver->ops) @@ -58,4 +66,12 @@ int button_get_by_label(const char *label, struct udevice **devp); */ enum button_state_t button_get_state(struct udevice *dev);
+/** + * button_get_code() - get linux event code of a button + * + * @dev: button device to change + * @return button code, or -ve on error + */ +int button_get_code(struct udevice *dev); + #endif diff --git a/test/dm/button.c b/test/dm/button.c index e76c1ad030..3318668df2 100644 --- a/test/dm/button.c +++ b/test/dm/button.c @@ -13,6 +13,7 @@ #include <power/sandbox_pmic.h> #include <asm/gpio.h> #include <dm/test.h> +#include <dt-bindings/input/input.h> #include <test/ut.h>
/* Base test of the button uclass */ @@ -85,6 +86,18 @@ static int dm_test_button_label(struct unit_test_state *uts) } DM_TEST(dm_test_button_label, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+/* Test button has linux,code */ +static int dm_test_button_linux_code(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &dev)); + ut_asserteq(BTN_1, button_get_code(dev)); + + return 0; +} +DM_TEST(dm_test_button_linux_code, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + /* Test adc-keys driver */ static int dm_test_button_keys_adc(struct unit_test_state *uts) {

On Sun, 22 Jan 2023 at 08:22, Dzmitry Sankouski dsankouski@gmail.com wrote:
Linux event code must be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
Changes for v2:
- fail, if linux,code not found
Changes for v3:
- add test for linux,code
- change linux,code type to int
- new line after return
- add specific error code in function docs
arch/sandbox/dts/test.dts | 2 ++ drivers/button/button-gpio.c | 17 ++++++++++++++++- drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ test/dm/button.c | 13 +++++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-)
Reviewed-by: Simon Glass sjg@chromium.org

On Sun, Jan 22, 2023 at 06:21:24PM +0300, Dzmitry Sankouski wrote:
Linux event code must be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!

Bootmenu requires an input device with arrows and enter key. A common smartphone luckily has power, volume up/down buttons, which may be used for controlling bootmenu. To use driver, add 'button-kbd' to stdin.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com --- Changes for v2: - add doc on driver private data struct - use calloc instead of malloc Changes for v3: - none
drivers/input/Kconfig | 9 +++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 126 +++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 drivers/input/button_kbd.c
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 1c534be005..b9a4e443a3 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -46,6 +46,15 @@ config APPLE_SPI_KEYB laptops based on Apple SoCs. These keyboards use an Apple-specific HID-over-SPI protocol.
+config BUTTON_KEYBOARD + bool "Buttons as keyboard" + depends on BUTTON_GPIO + depends on DM_KEYBOARD + help + Enable support for mapping buttons to keycode events. Use linux,code button driver + dt node to define button-event mapping. + For example, an arrows and enter may be implemented to navigate boot menu. + config CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support" depends on INPUT diff --git a/drivers/input/Makefile b/drivers/input/Makefile index ded76bddb2..14c0ea7325 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)CROS_EC_KEYB) += cros_ec_keyb.o obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += key_matrix.o obj-$(CONFIG_$(SPL_TPL_)DM_KEYBOARD) += input.o keyboard-uclass.o +obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o
ifndef CONFIG_SPL_BUILD
diff --git a/drivers/input/button_kbd.c b/drivers/input/button_kbd.c new file mode 100644 index 0000000000..99e65f12f0 --- /dev/null +++ b/drivers/input/button_kbd.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 Dzmitry Sankouski dsankouski@gmail.com + */ + +#include <stdlib.h> +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <input.h> +#include <keyboard.h> +#include <button.h> +#include <dm/device-internal.h> +#include <log.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <linux/delay.h> +#include <linux/input.h> + +/** + * struct button_kbd_priv - driver private data + * + * @input: input configuration + * @button_size: number of buttons found + * @old_state: a pointer to old button states array. Used to determine button state change. + */ +struct button_kbd_priv { + struct input_config *input; + u32 button_size; + u32 *old_state; +}; + +static int button_kbd_start(struct udevice *dev) +{ + struct button_kbd_priv *priv = dev_get_priv(dev); + int i = 0; + struct udevice *button_gpio_devp; + + uclass_foreach_dev_probe(UCLASS_BUTTON, button_gpio_devp) { + struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp); + /* Ignore the top-level button node */ + if (!uc_plat->label) + continue; + debug("Found button %s #%d - %s, probing...\n", + uc_plat->label, i, button_gpio_devp->name); + i++; + } + + priv->button_size = i; + priv->old_state = calloc(i, sizeof(int)); + + return 0; +} + +int button_read_keys(struct input_config *input) +{ + struct button_kbd_priv *priv = dev_get_priv(input->dev); + struct udevice *button_gpio_devp; + struct uclass *uc; + int i = 0; + u32 code, state, state_changed = 0; + + uclass_id_foreach_dev(UCLASS_BUTTON, button_gpio_devp, uc) { + struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp); + /* Ignore the top-level button node */ + if (!uc_plat->label) + continue; + code = button_get_code(button_gpio_devp); + if (!code) + continue; + + state = button_get_state(button_gpio_devp); + state_changed = state != priv->old_state[i]; + + if (state_changed) { + debug("%s: %d\n", uc_plat->label, code); + priv->old_state[i] = state; + input_add_keycode(input, code, state); + } + i++; + } + return 0; +} + +static const struct keyboard_ops button_kbd_ops = { + .start = button_kbd_start, +}; + +static int button_kbd_probe(struct udevice *dev) +{ + struct button_kbd_priv *priv = dev_get_priv(dev); + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret = 0; + + input_init(input, false); + input_add_tables(input, false); + + /* Register the device. */ + priv->input = input; + input->dev = dev; + input->read_keys = button_read_keys; + strcpy(sdev->name, "button-kbd"); + ret = input_stdio_register(sdev); + if (ret) { + debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct udevice_id button_kbd_ids[] = { + { .compatible = "button-kbd" }, + { } +}; + +U_BOOT_DRIVER(button_kbd) = { + .name = "button_kbd", + .id = UCLASS_KEYBOARD, + .of_match = button_kbd_ids, + .ops = &button_kbd_ops, + .priv_auto = sizeof(struct button_kbd_priv), + .probe = button_kbd_probe, +};

On Sun, 22 Jan 2023 at 08:21, Dzmitry Sankouski dsankouski@gmail.com wrote:
Bootmenu requires an input device with arrows and enter key. A common smartphone luckily has power, volume up/down buttons, which may be used for controlling bootmenu. To use driver, add 'button-kbd' to stdin.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
Changes for v2:
- add doc on driver private data struct
- use calloc instead of malloc
Changes for v3:
- none
drivers/input/Kconfig | 9 +++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 126 +++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 drivers/input/button_kbd.c
Reviewed-by: Simon Glass sjg@chromium.org

On Sun, Jan 22, 2023 at 06:21:25PM +0300, Dzmitry Sankouski wrote:
Bootmenu requires an input device with arrows and enter key. A common smartphone luckily has power, volume up/down buttons, which may be used for controlling bootmenu. To use driver, add 'button-kbd' to stdin.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!
participants (3)
-
Dzmitry Sankouski
-
Simon Glass
-
Tom Rini