MAXIM Semiconductor's PMIC, MAX77663 has 8 GPIO pins and 3 GPIO-like
pins. It also supports interrupts from these pins.

Add GPIO driver for these pins to control via GPIO APIs.

Signed-off-by: Svyatoslav Ryhel <clamo...@gmail.com>
---
 drivers/gpio/Kconfig                |   9 ++
 drivers/gpio/Makefile               |   1 +
 drivers/gpio/max77663_gpio.c        | 179 ++++++++++++++++++++++++++++
 drivers/power/pmic/max77663.c       |   9 ++
 include/dt-bindings/pmic/max77663.h |  18 +++
 include/power/max77663.h            |   1 +
 6 files changed, 217 insertions(+)
 create mode 100644 drivers/gpio/max77663_gpio.c
 create mode 100644 include/dt-bindings/pmic/max77663.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 74baa98d3c..3c701eff5c 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -238,6 +238,15 @@ config MAX7320_GPIO
         original maxim device has 8 push/pull outputs,
         some clones offers 16bit.
 
+config MAX77663_GPIO
+       bool "MAX77663 GPIO cell of PMIC driver"
+       depends on DM_GPIO && DM_PMIC_MAX77663
+       help
+         GPIO driver for MAX77663 PMIC from Maxim Semiconductor.
+         MAX77663 PMIC has 8 pins that can be configured as GPIOs
+         and 3 GPIO-like pins dedicated for power/reset buttons
+         and LID sensor.
+
 config MCP230XX_GPIO
        bool "MCP230XX GPIO driver"
        depends on DM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c8b3fd7814..dab3eb93a3 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_NX_GPIO)         += nx_gpio.o
 obj-$(CONFIG_SIFIVE_GPIO)      += sifive-gpio.o
 obj-$(CONFIG_NOMADIK_GPIO)     += nmk_gpio.o
 obj-$(CONFIG_MAX7320_GPIO)     += max7320_gpio.o
+obj-$(CONFIG_$(SPL_)MAX77663_GPIO)     += max77663_gpio.o
 obj-$(CONFIG_SL28CPLD_GPIO)    += sl28cpld-gpio.o
 obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN)      += zynqmp_gpio_modepin.o
 obj-$(CONFIG_SLG7XL45106_I2C_GPO)      += gpio_slg7xl45106.o
diff --git a/drivers/gpio/max77663_gpio.c b/drivers/gpio/max77663_gpio.c
new file mode 100644
index 0000000000..a377d01739
--- /dev/null
+++ b/drivers/gpio/max77663_gpio.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Copyright(C) 2023 Svyatoslav Ryhel <clamo...@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <power/max77663.h>
+#include <power/pmic.h>
+
+#define NUM_ENTRIES                            11 /* 8 GPIOs + 3 KEYs  */
+#define NUM_GPIOS                              8
+
+#define MAX77663_CNFG1_GPIO                    0x36
+#define GPIO_REG_ADDR(offset)                  (MAX77663_CNFG1_GPIO + (offset))
+
+#define MAX77663_CNFG_GPIO_DIR_MASK            BIT(1)
+#define MAX77663_CNFG_GPIO_DIR_INPUT           BIT(1)
+#define MAX77663_CNFG_GPIO_DIR_OUTPUT          0
+#define MAX77663_CNFG_GPIO_INPUT_VAL_MASK      BIT(2)
+#define MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK     BIT(3)
+#define MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH     BIT(3)
+#define MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW      0
+#define MAX77663_CNFG_IRQ                      GENMASK(5, 4)
+
+#define MAX77663_ONOFFSTAT_REG                 0x15
+#define   EN0                                  BIT(2) /* KEY 2 */
+#define   ACOK                                 BIT(1) /* KEY 1 */
+#define   LID                                  BIT(0) /* KEY 0 */
+
+static int max77663_gpio_direction_input(struct udevice *dev, unsigned int 
offset)
+{
+       int ret;
+
+       if (offset >= NUM_GPIOS)
+               return 0;
+
+       ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+                             MAX77663_CNFG_GPIO_DIR_MASK,
+                             MAX77663_CNFG_GPIO_DIR_INPUT);
+       if (ret < 0)
+               log_err("CNFG_GPIOx dir update failed: %d\n", ret);
+
+       return ret;
+}
+
+static int max77663_gpio_direction_output(struct udevice *dev, unsigned int 
offset,
+                                         int value)
+{
+       u8 val;
+       int ret;
+
+       if (offset >= NUM_GPIOS)
+               return -EINVAL;
+
+       val = (value) ? MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH :
+                               MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+       ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+                             MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+       if (ret < 0) {
+               log_err("CNFG_GPIOx val update failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+                             MAX77663_CNFG_GPIO_DIR_MASK,
+                             MAX77663_CNFG_GPIO_DIR_OUTPUT);
+       if (ret < 0)
+               log_err("CNFG_GPIOx dir update failed: %d\n", ret);
+
+       return ret;
+}
+
+static int max77663_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+       int ret;
+
+       if (offset >= NUM_GPIOS) {
+               ret = pmic_reg_read(dev->parent, MAX77663_ONOFFSTAT_REG);
+               if (ret < 0) {
+                       log_err("ONOFFSTAT_REG read failed: %d\n", ret);
+                       return ret;
+               }
+
+               return !!(ret & BIT(offset - NUM_GPIOS));
+       }
+
+       ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset));
+       if (ret < 0) {
+               log_err("CNFG_GPIOx read failed: %d\n", ret);
+               return ret;
+       }
+
+       if (ret & MAX77663_CNFG_GPIO_DIR_MASK)
+               return !!(ret & MAX77663_CNFG_GPIO_INPUT_VAL_MASK);
+       else
+               return !!(ret & MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK);
+}
+
+static int max77663_gpio_set_value(struct udevice *dev, unsigned int offset,
+                                  int value)
+{
+       u8 val;
+       int ret;
+
+       if (offset >= NUM_GPIOS)
+               return -EINVAL;
+
+       val = (value) ? MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH :
+                               MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+       ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+                             MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+       if (ret < 0)
+               log_err("CNFG_GPIO_OUT update failed: %d\n", ret);
+
+       return ret;
+}
+
+static int max77663_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+       int ret;
+
+       if (offset >= NUM_GPIOS)
+               return GPIOF_INPUT;
+
+       ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset));
+       if (ret < 0) {
+               log_err("CNFG_GPIOx read failed: %d\n", ret);
+               return ret;
+       }
+
+       if (ret & MAX77663_CNFG_GPIO_DIR_MASK)
+               return GPIOF_INPUT;
+       else
+               return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops max77663_gpio_ops = {
+       .direction_input        = max77663_gpio_direction_input,
+       .direction_output       = max77663_gpio_direction_output,
+       .get_value              = max77663_gpio_get_value,
+       .set_value              = max77663_gpio_set_value,
+       .get_function           = max77663_gpio_get_function,
+};
+
+static int max77663_gpio_probe(struct udevice *dev)
+{
+       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+       int i, ret;
+
+       uc_priv->gpio_count = NUM_ENTRIES;
+       uc_priv->bank_name = "GPIO";
+
+       /*
+        * GPIO interrupts may be left ON after bootloader, hence let's
+        * pre-initialize hardware to the expected state by disabling all
+        * the interrupts.
+        */
+       for (i = 0; i < NUM_GPIOS; i++) {
+               ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(i),
+                                     MAX77663_CNFG_IRQ, 0);
+               if (ret < 0) {
+                       log_err("failed to disable interrupt: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+U_BOOT_DRIVER(max77663_gpio) = {
+       .name   = MAX77663_GPIO_DRIVER,
+       .id     = UCLASS_GPIO,
+       .probe  = max77663_gpio_probe,
+       .ops    = &max77663_gpio_ops,
+};
diff --git a/drivers/power/pmic/max77663.c b/drivers/power/pmic/max77663.c
index e333c8dcf5..187171bc52 100644
--- a/drivers/power/pmic/max77663.c
+++ b/drivers/power/pmic/max77663.c
@@ -61,6 +61,15 @@ static int max77663_bind(struct udevice *dev)
                }
        }
 
+       if (IS_ENABLED(CONFIG_MAX77663_GPIO)) {
+               ret = device_bind_driver(dev, MAX77663_GPIO_DRIVER,
+                                        "gpio", NULL);
+               if (ret) {
+                       log_err("cannot bind GPIOs (ret = %d)\n", ret);
+                       return ret;
+               }
+       }
+
        regulators_node = dev_read_subnode(dev, "regulators");
        if (!ofnode_valid(regulators_node)) {
                log_err("%s regulators subnode not found!\n", dev->name);
diff --git a/include/dt-bindings/pmic/max77663.h 
b/include/dt-bindings/pmic/max77663.h
new file mode 100644
index 0000000000..ee169a83e3
--- /dev/null
+++ b/include/dt-bindings/pmic/max77663.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Copyright(C) 2023 Svyatoslav Ryhel <clamo...@gmail.com>
+ */
+
+#ifndef _DT_BINDINGS_MAX77663_H_
+#define _DT_BINDINGS_MAX77663_H_
+
+/*
+ * MAX77663 has 8 GPIO (0 to 7) and 3 KEYS
+ * KEYS are appended after GPIOs
+ */
+
+#define EN0    10
+#define ACOK   9
+#define LID    8
+
+#endif
diff --git a/include/power/max77663.h b/include/power/max77663.h
index b3ae3dabf4..fcb5916f27 100644
--- a/include/power/max77663.h
+++ b/include/power/max77663.h
@@ -13,6 +13,7 @@
 #define MAX77663_LDO_DRIVER            "max77663_ldo"
 #define MAX77663_SD_DRIVER             "max77663_sd"
 #define MAX77663_RST_DRIVER            "max77663_rst"
+#define MAX77663_GPIO_DRIVER           "max77663_gpio"
 
 /* Step-Down (SD) Regulator calculations */
 #define SD_STATUS_MASK                 0x30
-- 
2.39.2

Reply via email to