[PATCH 0/3] 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 (3): gpio: qcom: add direction functions for pwrkey dm: button: add support for linux_code in button-gpio.c driver dm: input: add button_kbd driver
drivers/button/button-gpio.c | 20 ++++++ drivers/button/button-uclass.c | 10 +++ drivers/gpio/qcom_pmic_gpio.c | 15 ++++ drivers/input/Kconfig | 9 +++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 123 +++++++++++++++++++++++++++++++++ include/button.h | 16 +++++ 7 files changed, 194 insertions(+) create mode 100644 drivers/input/button_kbd.c

GPIO button driver requires direction functions to probe button gpio. Those functions are blank, since pwrkey gpio configured earlier not by u-boot.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com --- drivers/gpio/qcom_pmic_gpio.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c index 3be1be8692..e46377ce3b 100644 --- a/drivers/gpio/qcom_pmic_gpio.c +++ b/drivers/gpio/qcom_pmic_gpio.c @@ -303,9 +303,24 @@ static int qcom_pwrkey_get_value(struct udevice *dev, unsigned offset) } }
+/* + * Power button already configured as input by previous bootloader. + */ +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)

Linux event code may be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com --- drivers/button/button-gpio.c | 20 ++++++++++++++++++++ drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..e6eff5c1da 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; + u32 linux_code; };
static enum button_state_t button_gpio_get_state(struct udevice *dev) @@ -29,10 +30,22 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static u32 button_gpio_get_code(struct udevice *dev) +{ + struct button_gpio_priv *priv = dev_get_priv(dev); + u32 code = priv->linux_code; + + if (!code) + return 0; + + return code; +} + static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct button_gpio_priv *priv = dev_get_priv(dev); + u32 linux_code; int ret;
/* Ignore the top-level button node */ @@ -43,6 +56,12 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
+ linux_code = dev_read_u32_default(dev, "linux,code", -ENODATA); + debug("linux code value: %d, ret: %d", linux_code, ret); + if (ret) + return ret; + priv->linux_code = linux_code; + return 0; }
@@ -92,6 +111,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..6d0c6f69c5 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); }
+u32 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..27af4a6a1a 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 -ve on error + */ + u32 (*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 + */ +u32 button_get_code(struct udevice *dev); + #endif

Hi Dzmitry,
On 1/11/23 11:19, Dzmitry Sankouski wrote:
Linux event code may be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
drivers/button/button-gpio.c | 20 ++++++++++++++++++++ drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..e6eff5c1da 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;
u32 linux_code; };
static enum button_state_t button_gpio_get_state(struct udevice *dev)
@@ -29,10 +30,22 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static u32 button_gpio_get_code(struct udevice *dev) +{
- struct button_gpio_priv *priv = dev_get_priv(dev);
- u32 code = priv->linux_code;
- if (!code)
return 0;
- return code;
+}
static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct button_gpio_priv *priv = dev_get_priv(dev);
u32 linux_code; int ret;
/* Ignore the top-level button node */
@@ -43,6 +56,12 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
- linux_code = dev_read_u32_default(dev, "linux,code", -ENODATA);
- debug("linux code value: %d, ret: %d", linux_code, ret);
- if (ret)
return ret;
ret is not modified here so it'll always pass even if it fails to parse dev_read_u32_default.
I believe dev_read_u32_default incorrectly requests an int as last argument while it's going to fill it with u32 data on success. dev_read_u8/u16 get this correctly so that might just be an oversight.
Please use dev_read_u32 instead:
ret = dev_read_u32(dev, "linux,code", &priv->linux_code); debug("linux code value: %d, ret: %d", linux_code, ret); return ret;
(no need to check the value of ret since it's the same as if (ret) return ret;
return 0; )
Cheers, Quentin

dev_read_u32 will fail, if linux,code is not found. We shouldn't fail here, as linux,code is optional, so maybe dev_read_u32_default with 0 default value, instead of negative error code?
ср, 11 янв. 2023 г. в 18:48, Quentin Schulz quentin.schulz@theobroma-systems.com:
Hi Dzmitry,
On 1/11/23 11:19, Dzmitry Sankouski wrote:
Linux event code may be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
drivers/button/button-gpio.c | 20 ++++++++++++++++++++ drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..e6eff5c1da 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;
u32 linux_code;
};
static enum button_state_t button_gpio_get_state(struct udevice *dev)
@@ -29,10 +30,22 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static u32 button_gpio_get_code(struct udevice *dev) +{
struct button_gpio_priv *priv = dev_get_priv(dev);
u32 code = priv->linux_code;
if (!code)
return 0;
return code;
+}
static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct button_gpio_priv *priv = dev_get_priv(dev);
u32 linux_code; int ret; /* Ignore the top-level button node */
@@ -43,6 +56,12 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
linux_code = dev_read_u32_default(dev, "linux,code", -ENODATA);
debug("linux code value: %d, ret: %d", linux_code, ret);
if (ret)
return ret;
ret is not modified here so it'll always pass even if it fails to parse dev_read_u32_default.
I believe dev_read_u32_default incorrectly requests an int as last argument while it's going to fill it with u32 data on success. dev_read_u8/u16 get this correctly so that might just be an oversight.
Please use dev_read_u32 instead:
ret = dev_read_u32(dev, "linux,code", &priv->linux_code); debug("linux code value: %d, ret: %d", linux_code, ret); return ret;
(no need to check the value of ret since it's the same as if (ret) return ret;
return 0; )
Cheers, Quentin

Hi Dzmitry,
On 1/14/23 20:42, Dzmitry Sankouski wrote:
dev_read_u32 will fail, if linux,code is not found. We shouldn't fail here, as linux,code is optional, so maybe dev_read_u32_default with 0 default value, instead of negative error code?
No, 0 is an existing and valid code.
FYI, linux,code is not optional for GPIO buttons in the Linux kernel and fails the device probing. What would be the usecase for GPIO buttons without a code?
In the event we really want to allow no linux,cpde for GPIO button: It's fine if dev_read_u32 fails, you can use the return code as logic on what to do next.
I believe you need to have a signed variable to store one invalid value (e.g. -EINVAL or -ENOTSUPP or something like that). You could also have a boolean stating whether a linux,code was found for this device. You could also use some pointer to an u32 which would be NULL if not present, but then you'd have to do memory allocation/freeing.
ср, 11 янв. 2023 г. в 18:48, Quentin Schulz quentin.schulz@theobroma-systems.com:
Hi Dzmitry,
On 1/11/23 11:19, Dzmitry Sankouski wrote:
Linux event code may be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
drivers/button/button-gpio.c | 20 ++++++++++++++++++++ drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..e6eff5c1da 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;
u32 linux_code;
};
static enum button_state_t button_gpio_get_state(struct udevice *dev)
@@ -29,10 +30,22 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static u32 button_gpio_get_code(struct udevice *dev) +{
struct button_gpio_priv *priv = dev_get_priv(dev);
u32 code = priv->linux_code;
if (!code)
return 0;
I think we need something better than returning 0 in case there's no linux,code, since 0 is technically a valid linux,code.
There are multiple ways of doing this, here are the two that comes to my mind right now.
1) have int (*get_code)(struct udevice *dev, u32 *code); instead of u32 (*get_code)(struct udevice *dev);
and have the function return 0 on success or -EINVAL/-ENOTSUPP/whatever if linux,code was not set.
2) use a struct struct res { int ret; u32 code; }; struct res (*get_code)(struct udevice *dev); instead of u32 (*get_code)(struct udevice *dev);
and check on res.ret before reading res.code in your framework.
Cheers, Quentin
return code;
+}
static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct button_gpio_priv *priv = dev_get_priv(dev);
u32 linux_code; int ret; /* Ignore the top-level button node */
@@ -43,6 +56,12 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
linux_code = dev_read_u32_default(dev, "linux,code", -ENODATA);
debug("linux code value: %d, ret: %d", linux_code, ret);
if (ret)
return ret;
ret is not modified here so it'll always pass even if it fails to parse dev_read_u32_default.
I believe dev_read_u32_default incorrectly requests an int as last argument while it's going to fill it with u32 data on success. dev_read_u8/u16 get this correctly so that might just be an oversight.
Please use dev_read_u32 instead:
ret = dev_read_u32(dev, "linux,code", &priv->linux_code); debug("linux code value: %d, ret: %d", linux_code, ret); return ret;
(no need to check the value of ret since it's the same as if (ret) return ret;
return 0; )
Cheers, Quentin

I guess, u-boot button driver was created as minimal as possible intentionally, to keep the size small. It may be used in scripts without event code with button command. Some dts has no event code, for example sandbox.dtsi.
пн, 16 янв. 2023 г. в 12:53, Quentin Schulz quentin.schulz@theobroma-systems.com:
Hi Dzmitry,
On 1/14/23 20:42, Dzmitry Sankouski wrote:
dev_read_u32 will fail, if linux,code is not found. We shouldn't fail here, as linux,code is optional, so maybe dev_read_u32_default with 0 default value, instead of negative error code?
No, 0 is an existing and valid code.
FYI, linux,code is not optional for GPIO buttons in the Linux kernel and fails the device probing. What would be the usecase for GPIO buttons without a code?
In the event we really want to allow no linux,cpde for GPIO button: It's fine if dev_read_u32 fails, you can use the return code as logic on what to do next.
I believe you need to have a signed variable to store one invalid value (e.g. -EINVAL or -ENOTSUPP or something like that). You could also have a boolean stating whether a linux,code was found for this device. You could also use some pointer to an u32 which would be NULL if not present, but then you'd have to do memory allocation/freeing.
ср, 11 янв. 2023 г. в 18:48, Quentin Schulz quentin.schulz@theobroma-systems.com:
Hi Dzmitry,
On 1/11/23 11:19, Dzmitry Sankouski wrote:
Linux event code may be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
drivers/button/button-gpio.c | 20 ++++++++++++++++++++ drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..e6eff5c1da 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;
u32 linux_code;
};
static enum button_state_t button_gpio_get_state(struct udevice *dev)
@@ -29,10 +30,22 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static u32 button_gpio_get_code(struct udevice *dev) +{
struct button_gpio_priv *priv = dev_get_priv(dev);
u32 code = priv->linux_code;
if (!code)
return 0;
I think we need something better than returning 0 in case there's no linux,code, since 0 is technically a valid linux,code.
There are multiple ways of doing this, here are the two that comes to my mind right now.
- have
int (*get_code)(struct udevice *dev, u32 *code); instead of u32 (*get_code)(struct udevice *dev);
and have the function return 0 on success or -EINVAL/-ENOTSUPP/whatever if linux,code was not set.
- use a struct
struct res { int ret; u32 code; }; struct res (*get_code)(struct udevice *dev); instead of u32 (*get_code)(struct udevice *dev);
and check on res.ret before reading res.code in your framework.
Cheers, Quentin
return code;
+}
static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct button_gpio_priv *priv = dev_get_priv(dev);
u32 linux_code; int ret; /* Ignore the top-level button node */
@@ -43,6 +56,12 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
linux_code = dev_read_u32_default(dev, "linux,code", -ENODATA);
debug("linux code value: %d, ret: %d", linux_code, ret);
if (ret)
return ret;
ret is not modified here so it'll always pass even if it fails to parse dev_read_u32_default.
I believe dev_read_u32_default incorrectly requests an int as last argument while it's going to fill it with u32 data on success. dev_read_u8/u16 get this correctly so that might just be an oversight.
Please use dev_read_u32 instead:
ret = dev_read_u32(dev, "linux,code", &priv->linux_code); debug("linux code value: %d, ret: %d", linux_code, ret); return ret;
(no need to check the value of ret since it's the same as if (ret) return ret;
return 0; )
Cheers, Quentin

Hi Dzmitry,
On 1/16/23 12:19, Dzmitry Sankouski wrote:
I guess, u-boot button driver was created as minimal as possible intentionally, to keep the size small.
I'm not sure that we're *that* size constrained in U-Boot proper.
It may be used in scripts without event code with button command. Some dts has no event code, for example sandbox.dtsi.
sandbox.dtsi is only for testing purposes, it's fine to add one there. I believe we can have linux,code = <BTN_1>; for btn1 and linux,code = <BTN_2>; for the btn2. You can do the same to test.dtsi probably.
I found three other dts without a linux,code entry: - arch/arm/dts/rk3288-popmetal.dtsi gpio-keys>power. Upstream linux has one though, c.f. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch.... Just needs to be synchronized - arch/arm/dts/rk3288-tinker.dtsi gpio-keys>button0. Upstream linux has one though, c.f. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch.... Just needs to be synced - arch/arm/dts/am3517-evm-ui.dtsi expander-keys>record. Nothing in upstream kernel either. I suspect, linux,code = <KEY_RECORD>; should be enough.
Note that linux,code is enforced in Linux both by the driver and also by the DT binding, c.f. https://www.kernel.org/doc/Documentation/devicetree/bindings/input/gpio-keys....
Cheers, Quentin
пн, 16 янв. 2023 г. в 12:53, Quentin Schulz quentin.schulz@theobroma-systems.com:
Hi Dzmitry,
On 1/14/23 20:42, Dzmitry Sankouski wrote:
dev_read_u32 will fail, if linux,code is not found. We shouldn't fail here, as linux,code is optional, so maybe dev_read_u32_default with 0 default value, instead of negative error code?
No, 0 is an existing and valid code.
FYI, linux,code is not optional for GPIO buttons in the Linux kernel and fails the device probing. What would be the usecase for GPIO buttons without a code?
In the event we really want to allow no linux,cpde for GPIO button: It's fine if dev_read_u32 fails, you can use the return code as logic on what to do next.
I believe you need to have a signed variable to store one invalid value (e.g. -EINVAL or -ENOTSUPP or something like that). You could also have a boolean stating whether a linux,code was found for this device. You could also use some pointer to an u32 which would be NULL if not present, but then you'd have to do memory allocation/freeing.
ср, 11 янв. 2023 г. в 18:48, Quentin Schulz quentin.schulz@theobroma-systems.com:
Hi Dzmitry,
On 1/11/23 11:19, Dzmitry Sankouski wrote:
Linux event code may be used in input devices, using buttons.
Signed-off-by: Dzmitry Sankouski dsankouski@gmail.com
drivers/button/button-gpio.c | 20 ++++++++++++++++++++ drivers/button/button-uclass.c | 10 ++++++++++ include/button.h | 16 ++++++++++++++++ 3 files changed, 46 insertions(+)
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c..e6eff5c1da 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;
u32 linux_code;
};
static enum button_state_t button_gpio_get_state(struct udevice *dev)
@@ -29,10 +30,22 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; }
+static u32 button_gpio_get_code(struct udevice *dev) +{
struct button_gpio_priv *priv = dev_get_priv(dev);
u32 code = priv->linux_code;
if (!code)
return 0;
I think we need something better than returning 0 in case there's no linux,code, since 0 is technically a valid linux,code.
There are multiple ways of doing this, here are the two that comes to my mind right now.
- have
int (*get_code)(struct udevice *dev, u32 *code); instead of u32 (*get_code)(struct udevice *dev);
and have the function return 0 on success or -EINVAL/-ENOTSUPP/whatever if linux,code was not set.
- use a struct
struct res { int ret; u32 code; }; struct res (*get_code)(struct udevice *dev); instead of u32 (*get_code)(struct udevice *dev);
and check on res.ret before reading res.code in your framework.
Cheers, Quentin
return code;
+}
static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct button_gpio_priv *priv = dev_get_priv(dev);
u32 linux_code; int ret; /* Ignore the top-level button node */
@@ -43,6 +56,12 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret;
linux_code = dev_read_u32_default(dev, "linux,code", -ENODATA);
debug("linux code value: %d, ret: %d", linux_code, ret);
if (ret)
return ret;
ret is not modified here so it'll always pass even if it fails to parse dev_read_u32_default.
I believe dev_read_u32_default incorrectly requests an int as last argument while it's going to fill it with u32 data on success. dev_read_u8/u16 get this correctly so that might just be an oversight.
Please use dev_read_u32 instead:
ret = dev_read_u32(dev, "linux,code", &priv->linux_code); debug("linux code value: %d, ret: %d", linux_code, ret); return ret;
(no need to check the value of ret since it's the same as if (ret) return ret;
return 0; )
Cheers, Quentin

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 --- drivers/input/Kconfig | 9 +++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 123 +++++++++++++++++++++++++++++++++++++ 3 files changed, 133 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..cacec5f699 --- /dev/null +++ b/drivers/input/button_kbd.c @@ -0,0 +1,123 @@ +// 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 { + struct input_config *input; + u32 *keys; + u32 keysize; + 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 = malloc(sizeof(int) * i); + priv->keysize = 0; + priv->keys = malloc(sizeof(int) * i); + + 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 == -ENODATA) + 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, +};

Hi Dzmitry,
On Wed, 11 Jan 2023 at 03:19, 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
drivers/input/Kconfig | 9 +++ drivers/input/Makefile | 1 + drivers/input/button_kbd.c | 123 +++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 drivers/input/button_kbd.c
Reviewed-by: Simon Glass sjg@chromium.org
nit below
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..cacec5f699 --- /dev/null +++ b/drivers/input/button_kbd.c @@ -0,0 +1,123 @@ +// 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 {
struct input_config *input;
u32 *keys;
u32 keysize;
u32 button_size;
u32 *old_state;
Please add a comment for this struct
+};
+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 = malloc(sizeof(int) * i);
You can use calloc() if you like
priv->keysize = 0;
priv->keys = malloc(sizeof(int) * i);
return 0;
+}
Regards, Simon
participants (3)
-
Dzmitry Sankouski
-
Quentin Schulz
-
Simon Glass