aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorTom Rini2022-10-03 15:39:46 -0400
committerTom Rini2022-10-03 15:39:46 -0400
commit2d4591353452638132d711551fec3495b7644731 (patch)
treee12058de7f553e84f8d13e545f130c7a48973589 /drivers/gpio
parent4debc57a3da6c3f4d3f89a637e99206f4cea0a96 (diff)
parent6ee6e15975cad3c99fad3a66223f3fd9287a369b (diff)
Merge branch 'next'
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig10
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/atmel_pio4.c2
-rw-r--r--drivers/gpio/gpio-uclass.c18
-rw-r--r--drivers/gpio/msm_gpio.c10
-rw-r--r--drivers/gpio/pm8916_gpio.c303
-rw-r--r--drivers/gpio/qcom_pmic_gpio.c359
-rw-r--r--drivers/gpio/sandbox.c5
8 files changed, 383 insertions, 326 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7e4c3577b36..c949f9d2f7c 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -303,14 +303,14 @@ config CMD_PCA953X
legacy GPIO interface. Several subcommands are provided which mirror
the standard 'gpio' command. It should use that instead.
-config PM8916_GPIO
- bool "Qualcomm PM8916 PMIC GPIO/keypad driver"
- depends on DM_GPIO && PMIC_PM8916
+config QCOM_PMIC_GPIO
+ bool "Qualcomm generic PMIC GPIO/keypad driver"
+ depends on DM_GPIO && PMIC_QCOM
help
Support for GPIO pins and power/reset buttons found on
- Qualcomm PM8916 PMIC.
+ Qualcomm SoCs PMIC.
Default name for GPIO bank is "pm8916".
- Power and reset buttons are placed in "pm8916_key" bank and
+ Power and reset buttons are placed in "pwkey_qcom" bank and
have gpio numbers 0 and 1 respectively.
config PCF8575_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 39762fa06ce..9d718a554e5 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -63,7 +63,7 @@ obj-$(CONFIG_OCTEON_GPIO) += octeon_gpio.o
obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o
obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
obj-$(CONFIG_$(SPL_)PCF8575_GPIO) += pcf8575_gpio.o
-obj-$(CONFIG_$(SPL_TPL_)PM8916_GPIO) += pm8916_gpio.o
+obj-$(CONFIG_$(SPL_TPL_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o
obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o
obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o
obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o
diff --git a/drivers/gpio/atmel_pio4.c b/drivers/gpio/atmel_pio4.c
index 77a76c1d505..47ed2979814 100644
--- a/drivers/gpio/atmel_pio4.c
+++ b/drivers/gpio/atmel_pio4.c
@@ -350,10 +350,8 @@ static const struct atmel_pioctrl_data microchip_sama7g5_pioctrl_data = {
static const struct udevice_id atmel_pio4_ids[] = {
{
- .compatible = "atmel,sama5d2-gpio",
.data = (ulong)&atmel_sama5d2_pioctrl_data,
}, {
- .compatible = "microchip,sama7g5-gpio",
.data = (ulong)&microchip_sama7g5_pioctrl_data,
},
{}
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 0ed32b72170..a00880e446c 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -884,26 +884,31 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
const struct dm_gpio_ops *ops = gpio_get_ops(dev);
struct gpio_dev_priv *priv;
char *str = buf;
+ const char *label;
int func;
int ret;
int len;
+ bool used;
BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
*buf = 0;
priv = dev_get_uclass_priv(dev);
- ret = gpio_get_raw_function(dev, offset, NULL);
+ ret = gpio_get_raw_function(dev, offset, &label);
if (ret < 0)
return ret;
func = ret;
len = snprintf(str, buffsize, "%s%d: %s",
priv->bank_name ? priv->bank_name : "",
offset, gpio_function[func]);
- if (func == GPIOF_INPUT || func == GPIOF_OUTPUT ||
- func == GPIOF_UNUSED) {
- const char *label;
- bool used;
+ switch (func) {
+ case GPIOF_FUNC:
+ snprintf(str + len, buffsize - len, " %s", label ? label : "");
+ break;
+ case GPIOF_INPUT:
+ case GPIOF_OUTPUT:
+ case GPIOF_UNUSED:
ret = ops->get_value(dev, offset);
if (ret < 0)
return ret;
@@ -911,8 +916,9 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
snprintf(str + len, buffsize - len, ": %d [%c]%s%s",
ret,
used ? 'x' : ' ',
- used ? " " : "",
+ label ? " " : "",
label ? label : "");
+ break;
}
return 0;
diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c
index a3c3cd7824c..51670f26371 100644
--- a/drivers/gpio/msm_gpio.c
+++ b/drivers/gpio/msm_gpio.c
@@ -116,20 +116,12 @@ static int msm_gpio_of_to_plat(struct udevice *dev)
return 0;
}
-static const struct udevice_id msm_gpio_ids[] = {
- { .compatible = "qcom,msm8916-pinctrl" },
- { .compatible = "qcom,apq8016-pinctrl" },
- { .compatible = "qcom,ipq4019-pinctrl" },
- { .compatible = "qcom,sdm845-pinctrl" },
- { }
-};
-
U_BOOT_DRIVER(gpio_msm) = {
.name = "gpio_msm",
.id = UCLASS_GPIO,
- .of_match = msm_gpio_ids,
.of_to_plat = msm_gpio_of_to_plat,
.probe = msm_gpio_probe,
.ops = &gpio_msm_ops,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
.priv_auto = sizeof(struct msm_gpio_bank),
};
diff --git a/drivers/gpio/pm8916_gpio.c b/drivers/gpio/pm8916_gpio.c
deleted file mode 100644
index 7ad95784a89..00000000000
--- a/drivers/gpio/pm8916_gpio.c
+++ /dev/null
@@ -1,303 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Qualcomm pm8916 pmic gpio driver - part of Qualcomm PM8916 PMIC
- *
- * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
- */
-
-#include <common.h>
-#include <dm.h>
-#include <log.h>
-#include <power/pmic.h>
-#include <spmi/spmi.h>
-#include <asm/io.h>
-#include <asm/gpio.h>
-#include <linux/bitops.h>
-
-/* Register offset for each gpio */
-#define REG_OFFSET(x) ((x) * 0x100)
-
-/* Register maps */
-
-/* Type and subtype are shared for all pm8916 peripherals */
-#define REG_TYPE 0x4
-#define REG_SUBTYPE 0x5
-
-#define REG_STATUS 0x08
-#define REG_STATUS_VAL_MASK 0x1
-
-/* MODE_CTL */
-#define REG_CTL 0x40
-#define REG_CTL_MODE_MASK 0x70
-#define REG_CTL_MODE_INPUT 0x00
-#define REG_CTL_MODE_INOUT 0x20
-#define REG_CTL_MODE_OUTPUT 0x10
-#define REG_CTL_OUTPUT_MASK 0x0F
-
-#define REG_DIG_VIN_CTL 0x41
-#define REG_DIG_VIN_VIN0 0
-
-#define REG_DIG_PULL_CTL 0x42
-#define REG_DIG_PULL_NO_PU 0x5
-
-#define REG_DIG_OUT_CTL 0x45
-#define REG_DIG_OUT_CTL_CMOS (0x0 << 4)
-#define REG_DIG_OUT_CTL_DRIVE_L 0x1
-
-#define REG_EN_CTL 0x46
-#define REG_EN_CTL_ENABLE (1 << 7)
-
-struct pm8916_gpio_bank {
- uint32_t pid; /* Peripheral ID on SPMI bus */
-};
-
-static int pm8916_gpio_set_direction(struct udevice *dev, unsigned offset,
- bool input, int value)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
- uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
- int ret;
-
- /* Disable the GPIO */
- ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL,
- REG_EN_CTL_ENABLE, 0);
- if (ret < 0)
- return ret;
-
- /* Select the mode */
- if (input)
- ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL,
- REG_CTL_MODE_INPUT);
- else
- ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL,
- REG_CTL_MODE_INOUT | (value ? 1 : 0));
- if (ret < 0)
- return ret;
-
- /* Set the right pull (no pull) */
- ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_PULL_CTL,
- REG_DIG_PULL_NO_PU);
- if (ret < 0)
- return ret;
-
- /* Configure output pin drivers if needed */
- if (!input) {
- /* Select the VIN - VIN0, pin is input so it doesn't matter */
- ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_VIN_CTL,
- REG_DIG_VIN_VIN0);
- if (ret < 0)
- return ret;
-
- /* Set the right dig out control */
- ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_OUT_CTL,
- REG_DIG_OUT_CTL_CMOS |
- REG_DIG_OUT_CTL_DRIVE_L);
- if (ret < 0)
- return ret;
- }
-
- /* Enable the GPIO */
- return pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, 0,
- REG_EN_CTL_ENABLE);
-}
-
-static int pm8916_gpio_direction_input(struct udevice *dev, unsigned offset)
-{
- return pm8916_gpio_set_direction(dev, offset, true, 0);
-}
-
-static int pm8916_gpio_direction_output(struct udevice *dev, unsigned offset,
- int value)
-{
- return pm8916_gpio_set_direction(dev, offset, false, value);
-}
-
-static int pm8916_gpio_get_function(struct udevice *dev, unsigned offset)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
- uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
- int reg;
-
- /* Set the output value of the gpio */
- reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL);
- if (reg < 0)
- return reg;
-
- switch (reg & REG_CTL_MODE_MASK) {
- case REG_CTL_MODE_INPUT:
- return GPIOF_INPUT;
- case REG_CTL_MODE_INOUT: /* Fallthrough */
- case REG_CTL_MODE_OUTPUT:
- return GPIOF_OUTPUT;
- default:
- return GPIOF_UNKNOWN;
- }
-}
-
-static int pm8916_gpio_get_value(struct udevice *dev, unsigned offset)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
- uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
- int reg;
-
- reg = pmic_reg_read(dev->parent, gpio_base + REG_STATUS);
- if (reg < 0)
- return reg;
-
- return !!(reg & REG_STATUS_VAL_MASK);
-}
-
-static int pm8916_gpio_set_value(struct udevice *dev, unsigned offset,
- int value)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
- uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
-
- /* Set the output value of the gpio */
- return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL,
- REG_CTL_OUTPUT_MASK, !!value);
-}
-
-static const struct dm_gpio_ops pm8916_gpio_ops = {
- .direction_input = pm8916_gpio_direction_input,
- .direction_output = pm8916_gpio_direction_output,
- .get_value = pm8916_gpio_get_value,
- .set_value = pm8916_gpio_set_value,
- .get_function = pm8916_gpio_get_function,
-};
-
-static int pm8916_gpio_probe(struct udevice *dev)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
- int reg;
-
- priv->pid = dev_read_addr(dev);
- if (priv->pid == FDT_ADDR_T_NONE)
- return log_msg_ret("bad address", -EINVAL);
-
- /* Do a sanity check */
- reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
- if (reg != 0x10)
- return log_msg_ret("bad type", -ENXIO);
-
- reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
- if (reg != 0x5 && reg != 0x1)
- return log_msg_ret("bad subtype", -ENXIO);
-
- return 0;
-}
-
-static int pm8916_gpio_of_to_plat(struct udevice *dev)
-{
- struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
-
- uc_priv->gpio_count = dev_read_u32_default(dev, "gpio-count", 0);
- uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
- if (uc_priv->bank_name == NULL)
- uc_priv->bank_name = "pm8916";
-
- return 0;
-}
-
-static const struct udevice_id pm8916_gpio_ids[] = {
- { .compatible = "qcom,pm8916-gpio" },
- { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */
- { .compatible = "qcom,pm8998-gpio" },
- { }
-};
-
-U_BOOT_DRIVER(gpio_pm8916) = {
- .name = "gpio_pm8916",
- .id = UCLASS_GPIO,
- .of_match = pm8916_gpio_ids,
- .of_to_plat = pm8916_gpio_of_to_plat,
- .probe = pm8916_gpio_probe,
- .ops = &pm8916_gpio_ops,
- .priv_auto = sizeof(struct pm8916_gpio_bank),
-};
-
-
-/* Add pmic buttons as GPIO as well - there is no generic way for now */
-#define PON_INT_RT_STS 0x10
-#define KPDPWR_ON_INT_BIT 0
-#define RESIN_ON_INT_BIT 1
-
-static int pm8941_pwrkey_get_function(struct udevice *dev, unsigned offset)
-{
- return GPIOF_INPUT;
-}
-
-static int pm8941_pwrkey_get_value(struct udevice *dev, unsigned offset)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
-
- int reg = pmic_reg_read(dev->parent, priv->pid + PON_INT_RT_STS);
-
- if (reg < 0)
- return 0;
-
- switch (offset) {
- case 0: /* Power button */
- return (reg & BIT(KPDPWR_ON_INT_BIT)) != 0;
- break;
- case 1: /* Reset button */
- default:
- return (reg & BIT(RESIN_ON_INT_BIT)) != 0;
- break;
- }
-}
-
-static const struct dm_gpio_ops pm8941_pwrkey_ops = {
- .get_value = pm8941_pwrkey_get_value,
- .get_function = pm8941_pwrkey_get_function,
-};
-
-static int pm8941_pwrkey_probe(struct udevice *dev)
-{
- struct pm8916_gpio_bank *priv = dev_get_priv(dev);
- int reg;
-
- priv->pid = dev_read_addr(dev);
- if (priv->pid == FDT_ADDR_T_NONE)
- return log_msg_ret("bad address", -EINVAL);
-
- /* Do a sanity check */
- reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
- if (reg != 0x1)
- return log_msg_ret("bad type", -ENXIO);
-
- reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
- if ((reg & 0x5) == 0)
- return log_msg_ret("bad subtype", -ENXIO);
-
- return 0;
-}
-
-static int pm8941_pwrkey_of_to_plat(struct udevice *dev)
-{
- struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
-
- uc_priv->gpio_count = 2;
- uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
- if (uc_priv->bank_name == NULL)
- uc_priv->bank_name = "pm8916_key";
-
- return 0;
-}
-
-static const struct udevice_id pm8941_pwrkey_ids[] = {
- { .compatible = "qcom,pm8916-pwrkey" },
- { .compatible = "qcom,pm8994-pwrkey" },
- { .compatible = "qcom,pm8998-pwrkey" },
- { }
-};
-
-U_BOOT_DRIVER(pwrkey_pm89xx) = {
- .name = "pwrkey_pm89xx",
- .id = UCLASS_GPIO,
- .of_match = pm8941_pwrkey_ids,
- .of_to_plat = pm8941_pwrkey_of_to_plat,
- .probe = pm8941_pwrkey_probe,
- .ops = &pm8941_pwrkey_ops,
- .priv_auto = sizeof(struct pm8916_gpio_bank),
-};
diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c
new file mode 100644
index 00000000000..3be1be8692c
--- /dev/null
+++ b/drivers/gpio/qcom_pmic_gpio.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm generic pmic gpio driver
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <spmi/spmi.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+
+/* Register offset for each gpio */
+#define REG_OFFSET(x) ((x) * 0x100)
+
+/* Register maps */
+
+/* Type and subtype are shared for all PMIC peripherals */
+#define REG_TYPE 0x4
+#define REG_SUBTYPE 0x5
+
+/* GPIO peripheral type and subtype out_values */
+#define REG_TYPE_VAL 0x10
+#define REG_SUBTYPE_GPIO_4CH 0x1
+#define REG_SUBTYPE_GPIOC_4CH 0x5
+#define REG_SUBTYPE_GPIO_8CH 0x9
+#define REG_SUBTYPE_GPIOC_8CH 0xd
+#define REG_SUBTYPE_GPIO_LV 0x10
+#define REG_SUBTYPE_GPIO_MV 0x11
+
+#define REG_STATUS 0x08
+#define REG_STATUS_VAL_MASK 0x1
+
+/* MODE_CTL */
+#define REG_CTL 0x40
+#define REG_CTL_MODE_MASK 0x70
+#define REG_CTL_MODE_INPUT 0x00
+#define REG_CTL_MODE_INOUT 0x20
+#define REG_CTL_MODE_OUTPUT 0x10
+#define REG_CTL_OUTPUT_MASK 0x0F
+#define REG_CTL_LV_MV_MODE_MASK 0x3
+#define REG_CTL_LV_MV_MODE_INPUT 0x0
+#define REG_CTL_LV_MV_MODE_INOUT 0x2
+#define REG_CTL_LV_MV_MODE_OUTPUT 0x1
+
+#define REG_DIG_VIN_CTL 0x41
+#define REG_DIG_VIN_VIN0 0
+
+#define REG_DIG_PULL_CTL 0x42
+#define REG_DIG_PULL_NO_PU 0x5
+
+#define REG_LV_MV_OUTPUT_CTL 0x44
+#define REG_LV_MV_OUTPUT_CTL_MASK 0x80
+#define REG_LV_MV_OUTPUT_CTL_SHIFT 7
+
+#define REG_DIG_OUT_CTL 0x45
+#define REG_DIG_OUT_CTL_CMOS (0x0 << 4)
+#define REG_DIG_OUT_CTL_DRIVE_L 0x1
+
+#define REG_EN_CTL 0x46
+#define REG_EN_CTL_ENABLE (1 << 7)
+
+struct qcom_gpio_bank {
+ uint32_t pid; /* Peripheral ID on SPMI bus */
+ bool lv_mv_type; /* If subtype is GPIO_LV(0x10) or GPIO_MV(0x11) */
+};
+
+static int qcom_gpio_set_direction(struct udevice *dev, unsigned offset,
+ bool input, int value)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+ uint32_t reg_ctl_val;
+ int ret;
+
+ /* Disable the GPIO */
+ ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL,
+ REG_EN_CTL_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Select the mode and output */
+ if (priv->lv_mv_type) {
+ if (input)
+ reg_ctl_val = REG_CTL_LV_MV_MODE_INPUT;
+ else
+ reg_ctl_val = REG_CTL_LV_MV_MODE_INOUT;
+ } else {
+ if (input)
+ reg_ctl_val = REG_CTL_MODE_INPUT;
+ else
+ reg_ctl_val = REG_CTL_MODE_INOUT | !!value;
+ }
+
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL, reg_ctl_val);
+ if (ret < 0)
+ return ret;
+
+ if (priv->lv_mv_type && !input) {
+ ret = pmic_reg_write(dev->parent,
+ gpio_base + REG_LV_MV_OUTPUT_CTL,
+ !!value << REG_LV_MV_OUTPUT_CTL_SHIFT);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set the right pull (no pull) */
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_PULL_CTL,
+ REG_DIG_PULL_NO_PU);
+ if (ret < 0)
+ return ret;
+
+ /* Configure output pin drivers if needed */
+ if (!input) {
+ /* Select the VIN - VIN0, pin is input so it doesn't matter */
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_VIN_CTL,
+ REG_DIG_VIN_VIN0);
+ if (ret < 0)
+ return ret;
+
+ /* Set the right dig out control */
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_OUT_CTL,
+ REG_DIG_OUT_CTL_CMOS |
+ REG_DIG_OUT_CTL_DRIVE_L);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Enable the GPIO */
+ return pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, 0,
+ REG_EN_CTL_ENABLE);
+}
+
+static int qcom_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ return qcom_gpio_set_direction(dev, offset, true, 0);
+}
+
+static int qcom_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ return qcom_gpio_set_direction(dev, offset, false, value);
+}
+
+static int qcom_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+ int reg;
+
+ reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL);
+ if (reg < 0)
+ return reg;
+
+ if (priv->lv_mv_type) {
+ switch (reg & REG_CTL_LV_MV_MODE_MASK) {
+ case REG_CTL_LV_MV_MODE_INPUT:
+ return GPIOF_INPUT;
+ case REG_CTL_LV_MV_MODE_INOUT: /* Fallthrough */
+ case REG_CTL_LV_MV_MODE_OUTPUT:
+ return GPIOF_OUTPUT;
+ default:
+ return GPIOF_UNKNOWN;
+ }
+ } else {
+ switch (reg & REG_CTL_MODE_MASK) {
+ case REG_CTL_MODE_INPUT:
+ return GPIOF_INPUT;
+ case REG_CTL_MODE_INOUT: /* Fallthrough */
+ case REG_CTL_MODE_OUTPUT:
+ return GPIOF_OUTPUT;
+ default:
+ return GPIOF_UNKNOWN;
+ }
+ }
+}
+
+static int qcom_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+ int reg;
+
+ reg = pmic_reg_read(dev->parent, gpio_base + REG_STATUS);
+ if (reg < 0)
+ return reg;
+
+ return !!(reg & REG_STATUS_VAL_MASK);
+}
+
+static int qcom_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+
+ /* Set the output value of the gpio */
+ if (priv->lv_mv_type)
+ return pmic_clrsetbits(dev->parent,
+ gpio_base + REG_LV_MV_OUTPUT_CTL,
+ REG_LV_MV_OUTPUT_CTL_MASK,
+ !!value << REG_LV_MV_OUTPUT_CTL_SHIFT);
+ else
+ return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL,
+ REG_CTL_OUTPUT_MASK, !!value);
+}
+
+static const struct dm_gpio_ops qcom_gpio_ops = {
+ .direction_input = qcom_gpio_direction_input,
+ .direction_output = qcom_gpio_direction_output,
+ .get_value = qcom_gpio_get_value,
+ .set_value = qcom_gpio_set_value,
+ .get_function = qcom_gpio_get_function,
+};
+
+static int qcom_gpio_probe(struct udevice *dev)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ int reg;
+
+ priv->pid = dev_read_addr(dev);
+ if (priv->pid == FDT_ADDR_T_NONE)
+ return log_msg_ret("bad address", -EINVAL);
+
+ /* Do a sanity check */
+ reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
+ if (reg != REG_TYPE_VAL)
+ return log_msg_ret("bad type", -ENXIO);
+
+ reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
+ if (reg != REG_SUBTYPE_GPIO_4CH && reg != REG_SUBTYPE_GPIOC_4CH &&
+ reg != REG_SUBTYPE_GPIO_LV && reg != REG_SUBTYPE_GPIO_MV)
+ return log_msg_ret("bad subtype", -ENXIO);
+
+ priv->lv_mv_type = reg == REG_SUBTYPE_GPIO_LV ||
+ reg == REG_SUBTYPE_GPIO_MV;
+
+ return 0;
+}
+
+static int qcom_gpio_of_to_plat(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count = dev_read_u32_default(dev, "gpio-count", 0);
+ uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
+ if (uc_priv->bank_name == NULL)
+ uc_priv->bank_name = "qcom_pmic";
+
+ return 0;
+}
+
+static const struct udevice_id qcom_gpio_ids[] = {
+ { .compatible = "qcom,pm8916-gpio" },
+ { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */
+ { .compatible = "qcom,pm8998-gpio" },
+ { .compatible = "qcom,pms405-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(qcom_pmic_gpio) = {
+ .name = "qcom_pmic_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = qcom_gpio_ids,
+ .of_to_plat = qcom_gpio_of_to_plat,
+ .probe = qcom_gpio_probe,
+ .ops = &qcom_gpio_ops,
+ .priv_auto = sizeof(struct qcom_gpio_bank),
+};
+
+
+/* Add pmic buttons as GPIO as well - there is no generic way for now */
+#define PON_INT_RT_STS 0x10
+#define KPDPWR_ON_INT_BIT 0
+#define RESIN_ON_INT_BIT 1
+
+static int qcom_pwrkey_get_function(struct udevice *dev, unsigned offset)
+{
+ return GPIOF_INPUT;
+}
+
+static int qcom_pwrkey_get_value(struct udevice *dev, unsigned offset)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+
+ int reg = pmic_reg_read(dev->parent, priv->pid + PON_INT_RT_STS);
+
+ if (reg < 0)
+ return 0;
+
+ switch (offset) {
+ case 0: /* Power button */
+ return (reg & BIT(KPDPWR_ON_INT_BIT)) != 0;
+ break;
+ case 1: /* Reset button */
+ default:
+ return (reg & BIT(RESIN_ON_INT_BIT)) != 0;
+ break;
+ }
+}
+
+static const struct dm_gpio_ops qcom_pwrkey_ops = {
+ .get_value = qcom_pwrkey_get_value,
+ .get_function = qcom_pwrkey_get_function,
+};
+
+static int qcom_pwrkey_probe(struct udevice *dev)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ int reg;
+
+ priv->pid = dev_read_addr(dev);
+ if (priv->pid == FDT_ADDR_T_NONE)
+ return log_msg_ret("bad address", -EINVAL);
+
+ /* Do a sanity check */
+ reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
+ if (reg != 0x1)
+ return log_msg_ret("bad type", -ENXIO);
+
+ reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
+ if ((reg & 0x5) == 0)
+ return log_msg_ret("bad subtype", -ENXIO);
+
+ return 0;
+}
+
+static int qcom_pwrkey_of_to_plat(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count = 2;
+ uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
+ if (uc_priv->bank_name == NULL)
+ uc_priv->bank_name = "pwkey_qcom";
+
+ return 0;
+}
+
+static const struct udevice_id qcom_pwrkey_ids[] = {
+ { .compatible = "qcom,pm8916-pwrkey" },
+ { .compatible = "qcom,pm8994-pwrkey" },
+ { .compatible = "qcom,pm8998-pwrkey" },
+ { }
+};
+
+U_BOOT_DRIVER(pwrkey_qcom) = {
+ .name = "pwrkey_qcom",
+ .id = UCLASS_GPIO,
+ .of_match = qcom_pwrkey_ids,
+ .of_to_plat = qcom_pwrkey_of_to_plat,
+ .probe = qcom_pwrkey_probe,
+ .ops = &qcom_pwrkey_ops,
+ .priv_auto = sizeof(struct qcom_gpio_bank),
+};
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index 106b2a7b27c..305f9a6ff62 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -196,6 +196,8 @@ static int sb_gpio_get_function(struct udevice *dev, unsigned offset)
return GPIOF_OUTPUT;
if (get_gpio_flag(dev, offset, GPIOD_IS_IN))
return GPIOF_INPUT;
+ if (get_gpio_flag(dev, offset, GPIOD_IS_AF))
+ return GPIOF_FUNC;
return GPIOF_INPUT; /*GPIO is not configurated */
}
@@ -219,6 +221,9 @@ static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
if (args->args[1] & GPIO_OUT_ACTIVE)
desc->flags |= GPIOD_IS_OUT_ACTIVE;
+ if (args->args[1] & GPIO_AF)
+ desc->flags |= GPIOD_IS_AF;
+
return 0;
}