Message ID | 20231130-b4-qcom-dt-compat-v5-2-41500e237ad0@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series | Qualcomm PMIC fixes | expand |
On 30/11/2023 21:22, Caleb Connolly wrote: > Qualcomm PMICs include a "pon" function which handles two buttons, the > power button and "resin" button (usually volume down). Introduce a new > driver following upstream Linux DT to enable these and map them to Enter > and Down respectively to enable use in boot menus. > > Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org> > --- > MAINTAINERS | 1 + > drivers/button/Kconfig | 9 +++ > drivers/button/Makefile | 1 + > drivers/button/button-qcom-pmic.c | 165 ++++++++++++++++++++++++++++++++++++++ > 4 files changed, 176 insertions(+) > > diff --git a/MAINTAINERS b/MAINTAINERS > index f6d63c8ab563..8cd102eaa070 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -572,6 +572,7 @@ M: Neil Armstrong <neil.armstrong@linaro.org> > R: Sumit Garg <sumit.garg@linaro.org> > S: Maintained > F: arch/arm/mach-snapdragon/ > +F: drivers/button/button-qcom-pmic.c > F: drivers/clk/qcom/ > F: drivers/gpio/msm_gpio.c > F: drivers/mmc/msm_sdhci.c > diff --git a/drivers/button/Kconfig b/drivers/button/Kconfig > index 8ce2de37d62a..097b05f822e7 100644 > --- a/drivers/button/Kconfig > +++ b/drivers/button/Kconfig > @@ -27,4 +27,13 @@ config BUTTON_GPIO > The GPIO driver must used driver model. Buttons are configured using > the device tree. > > +config BUTTON_QCOM_PMIC > + bool "Qualcomm power button" > + depends on BUTTON > + depends on PMIC_QCOM > + help > + Enable support for the power and "resin" (usually volume down) buttons > + on Qualcomm SoCs. These will be configured as the Enter and Down keys > + respectively, allowing navigation of bootmenu with buttons on device. > + > endmenu > diff --git a/drivers/button/Makefile b/drivers/button/Makefile > index bbd18af14940..68555081a47a 100644 > --- a/drivers/button/Makefile > +++ b/drivers/button/Makefile > @@ -5,3 +5,4 @@ > obj-$(CONFIG_BUTTON) += button-uclass.o > obj-$(CONFIG_BUTTON_ADC) += button-adc.o > obj-$(CONFIG_BUTTON_GPIO) += button-gpio.o > +obj-$(CONFIG_BUTTON_QCOM_PMIC) += button-qcom-pmic.o > \ No newline at end of file > diff --git a/drivers/button/button-qcom-pmic.c b/drivers/button/button-qcom-pmic.c > new file mode 100644 > index 000000000000..34a976d1e6c6 > --- /dev/null > +++ b/drivers/button/button-qcom-pmic.c > @@ -0,0 +1,165 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Qualcomm generic pmic gpio driver > + * > + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> > + * (C) Copyright 2023 Linaro Ltd. > + */ > + > +#include <button.h> > +#include <dt-bindings/input/linux-event-codes.h> > +#include <dm.h> > +#include <dm/device-internal.h> > +#include <dm/lists.h> > +#include <log.h> > +#include <power/pmic.h> > +#include <spmi/spmi.h> > +#include <linux/bitops.h> > + > +#define REG_TYPE 0x4 > +#define REG_SUBTYPE 0x5 > + > +struct qcom_pmic_btn_priv { > + u32 base; > + u32 status_bit; > + int code; > + struct udevice *pmic; > +}; > + > +#define PON_INT_RT_STS 0x10 > +#define KPDPWR_ON_INT_BIT 0 > +#define RESIN_ON_INT_BIT 1 > + > +#define NODE_IS_PWRKEY(node) (!strncmp(ofnode_get_name(node), "pwrkey", strlen("pwrkey"))) > +#define NODE_IS_RESIN(node) (!strncmp(ofnode_get_name(node), "resin", strlen("resin"))) This is not very pretty, but I don't see better alternative except perhaps defining a struct with the node name and the properties and simply match over it, but since there's only 2 buttons it makes it generic for nothing. > + > +static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev) > +{ > + struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); > + > + int reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS); > + > + if (reg < 0) > + return 0; > + > + return (reg & BIT(priv->status_bit)) != 0; > +} > + > +static int qcom_pwrkey_get_code(struct udevice *dev) > +{ > + struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); > + > + return priv->code; > +} > + > +static int qcom_pwrkey_probe(struct udevice *dev) > +{ > + struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); > + struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); > + ofnode node = dev_ofnode(dev); > + int ret; > + u64 base; > + > + /* Ignore the top-level pon node */ > + if (!uc_plat->label) > + return 0; > + > + /* the pwrkey and resin nodes are children of the "pon" node, get the > + * PMIC device to use in pmic_reg_* calls. > + */ > + priv->pmic = dev->parent->parent; > + > + /* Get the address of the parent pon node */ > + base = dev_read_addr(dev->parent); > + if (base == FDT_ADDR_T_NONE) { > + printf("%s: Can't find address\n", dev->name); > + return -EINVAL; > + } > + > + priv->base = base; > + > + /* Do a sanity check */ > + ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE); > + if (ret != 0x1 && ret != 0xb) { > + printf("%s: unexpected PMIC function type %d\n", dev->name, ret); > + return -ENXIO; > + } > + > + ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE); > + if ((ret & 0x7) == 0) { > + printf("%s: unexpected PMCI function subtype %d\n", dev->name, ret); > + return -ENXIO; > + } > + > + if (NODE_IS_PWRKEY(node)) { > + priv->status_bit = 0; > + priv->code = KEY_ENTER; > + } else if (NODE_IS_RESIN(node)) { > + priv->status_bit = 1; > + priv->code = KEY_DOWN; > + } else { > + /* Should not get here! */ > + printf("Invalid pon node '%s' should be 'pwrkey' or 'resin'\n", > + ofnode_get_name(node)); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int button_qcom_pmic_bind(struct udevice *parent) > +{ > + struct udevice *dev; > + ofnode node; > + int ret; > + > + dev_for_each_subnode(node, parent) { > + struct button_uc_plat *uc_plat; > + const char *label; > + > + if (!ofnode_is_enabled(node)) > + continue; > + > + ret = device_bind_driver_to_node(parent, "qcom_pwrkey", > + ofnode_get_name(node), > + node, &dev); > + if (ret) { > + printf("Failed to bind %s! %d\n", label, ret); > + return ret; > + } > + uc_plat = dev_get_uclass_plat(dev); > + if (NODE_IS_PWRKEY(node)) { > + uc_plat->label = "pwrkey"; > + } else if (NODE_IS_RESIN(node)) { > + uc_plat->label = "vol_down"; > + } else { > + printf("Unknown button node '%s' should be 'pwrkey' or 'resin'\n", > + ofnode_get_name(node)); > + device_unbind(dev); > + } > + } > + > + return 0; > +} > + > +static const struct button_ops button_qcom_pmic_ops = { > + .get_state = qcom_pwrkey_get_state, > + .get_code = qcom_pwrkey_get_code, > +}; > + > +static const struct udevice_id qcom_pwrkey_ids[] = { > + { .compatible = "qcom,pm8916-pon" }, > + { .compatible = "qcom,pm8941-pon" }, > + { .compatible = "qcom,pm8998-pon" }, > + { } > +}; > + > +U_BOOT_DRIVER(qcom_pwrkey) = { > + .name = "qcom_pwrkey", > + .id = UCLASS_BUTTON, > + .of_match = qcom_pwrkey_ids, > + .bind = button_qcom_pmic_bind, > + .probe = qcom_pwrkey_probe, > + .ops = &button_qcom_pmic_ops, > + .priv_auto = sizeof(struct qcom_pmic_btn_priv), > +}; > Apart that it looks good: Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS index f6d63c8ab563..8cd102eaa070 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -572,6 +572,7 @@ M: Neil Armstrong <neil.armstrong@linaro.org> R: Sumit Garg <sumit.garg@linaro.org> S: Maintained F: arch/arm/mach-snapdragon/ +F: drivers/button/button-qcom-pmic.c F: drivers/clk/qcom/ F: drivers/gpio/msm_gpio.c F: drivers/mmc/msm_sdhci.c diff --git a/drivers/button/Kconfig b/drivers/button/Kconfig index 8ce2de37d62a..097b05f822e7 100644 --- a/drivers/button/Kconfig +++ b/drivers/button/Kconfig @@ -27,4 +27,13 @@ config BUTTON_GPIO The GPIO driver must used driver model. Buttons are configured using the device tree. +config BUTTON_QCOM_PMIC + bool "Qualcomm power button" + depends on BUTTON + depends on PMIC_QCOM + help + Enable support for the power and "resin" (usually volume down) buttons + on Qualcomm SoCs. These will be configured as the Enter and Down keys + respectively, allowing navigation of bootmenu with buttons on device. + endmenu diff --git a/drivers/button/Makefile b/drivers/button/Makefile index bbd18af14940..68555081a47a 100644 --- a/drivers/button/Makefile +++ b/drivers/button/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_BUTTON) += button-uclass.o obj-$(CONFIG_BUTTON_ADC) += button-adc.o obj-$(CONFIG_BUTTON_GPIO) += button-gpio.o +obj-$(CONFIG_BUTTON_QCOM_PMIC) += button-qcom-pmic.o \ No newline at end of file diff --git a/drivers/button/button-qcom-pmic.c b/drivers/button/button-qcom-pmic.c new file mode 100644 index 000000000000..34a976d1e6c6 --- /dev/null +++ b/drivers/button/button-qcom-pmic.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm generic pmic gpio driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * (C) Copyright 2023 Linaro Ltd. + */ + +#include <button.h> +#include <dt-bindings/input/linux-event-codes.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <log.h> +#include <power/pmic.h> +#include <spmi/spmi.h> +#include <linux/bitops.h> + +#define REG_TYPE 0x4 +#define REG_SUBTYPE 0x5 + +struct qcom_pmic_btn_priv { + u32 base; + u32 status_bit; + int code; + struct udevice *pmic; +}; + +#define PON_INT_RT_STS 0x10 +#define KPDPWR_ON_INT_BIT 0 +#define RESIN_ON_INT_BIT 1 + +#define NODE_IS_PWRKEY(node) (!strncmp(ofnode_get_name(node), "pwrkey", strlen("pwrkey"))) +#define NODE_IS_RESIN(node) (!strncmp(ofnode_get_name(node), "resin", strlen("resin"))) + +static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev) +{ + struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); + + int reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS); + + if (reg < 0) + return 0; + + return (reg & BIT(priv->status_bit)) != 0; +} + +static int qcom_pwrkey_get_code(struct udevice *dev) +{ + struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); + + return priv->code; +} + +static int qcom_pwrkey_probe(struct udevice *dev) +{ + struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct qcom_pmic_btn_priv *priv = dev_get_priv(dev); + ofnode node = dev_ofnode(dev); + int ret; + u64 base; + + /* Ignore the top-level pon node */ + if (!uc_plat->label) + return 0; + + /* the pwrkey and resin nodes are children of the "pon" node, get the + * PMIC device to use in pmic_reg_* calls. + */ + priv->pmic = dev->parent->parent; + + /* Get the address of the parent pon node */ + base = dev_read_addr(dev->parent); + if (base == FDT_ADDR_T_NONE) { + printf("%s: Can't find address\n", dev->name); + return -EINVAL; + } + + priv->base = base; + + /* Do a sanity check */ + ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE); + if (ret != 0x1 && ret != 0xb) { + printf("%s: unexpected PMIC function type %d\n", dev->name, ret); + return -ENXIO; + } + + ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE); + if ((ret & 0x7) == 0) { + printf("%s: unexpected PMCI function subtype %d\n", dev->name, ret); + return -ENXIO; + } + + if (NODE_IS_PWRKEY(node)) { + priv->status_bit = 0; + priv->code = KEY_ENTER; + } else if (NODE_IS_RESIN(node)) { + priv->status_bit = 1; + priv->code = KEY_DOWN; + } else { + /* Should not get here! */ + printf("Invalid pon node '%s' should be 'pwrkey' or 'resin'\n", + ofnode_get_name(node)); + return -EINVAL; + } + + return 0; +} + +static int button_qcom_pmic_bind(struct udevice *parent) +{ + struct udevice *dev; + ofnode node; + int ret; + + dev_for_each_subnode(node, parent) { + struct button_uc_plat *uc_plat; + const char *label; + + if (!ofnode_is_enabled(node)) + continue; + + ret = device_bind_driver_to_node(parent, "qcom_pwrkey", + ofnode_get_name(node), + node, &dev); + if (ret) { + printf("Failed to bind %s! %d\n", label, ret); + return ret; + } + uc_plat = dev_get_uclass_plat(dev); + if (NODE_IS_PWRKEY(node)) { + uc_plat->label = "pwrkey"; + } else if (NODE_IS_RESIN(node)) { + uc_plat->label = "vol_down"; + } else { + printf("Unknown button node '%s' should be 'pwrkey' or 'resin'\n", + ofnode_get_name(node)); + device_unbind(dev); + } + } + + return 0; +} + +static const struct button_ops button_qcom_pmic_ops = { + .get_state = qcom_pwrkey_get_state, + .get_code = qcom_pwrkey_get_code, +}; + +static const struct udevice_id qcom_pwrkey_ids[] = { + { .compatible = "qcom,pm8916-pon" }, + { .compatible = "qcom,pm8941-pon" }, + { .compatible = "qcom,pm8998-pon" }, + { } +}; + +U_BOOT_DRIVER(qcom_pwrkey) = { + .name = "qcom_pwrkey", + .id = UCLASS_BUTTON, + .of_match = qcom_pwrkey_ids, + .bind = button_qcom_pmic_bind, + .probe = qcom_pwrkey_probe, + .ops = &button_qcom_pmic_ops, + .priv_auto = sizeof(struct qcom_pmic_btn_priv), +};
Qualcomm PMICs include a "pon" function which handles two buttons, the power button and "resin" button (usually volume down). Introduce a new driver following upstream Linux DT to enable these and map them to Enter and Down respectively to enable use in boot menus. Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org> --- MAINTAINERS | 1 + drivers/button/Kconfig | 9 +++ drivers/button/Makefile | 1 + drivers/button/button-qcom-pmic.c | 165 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+)