[PATCH v1 0/3] Implement GPIO cells for PALMAS and MAX77663 PMICs

This patchset adds support for gpio-uclass to work with pmic gpio childrens properly and implements MAX77663 and PALMAS GPIO cells/children as a reference and to be further used in devices I am currently maintaining.
All drivers are tested on actual hardware and confirmed to work as expected. MAX77663 GPIO cell is tested on wexler qc750 tegra 3 device and PALMAS GPIO cell is tested on tegra note 7 tegra 4 device.
These patches require "Add support for PMICs used on Tegra 3 devices" patches to be merged first for proper work.
Svyatoslav Ryhel (3): drivers: gpio-uclass: support PMIC GPIO children drivers: gpio: implement MAX77663 GPIO cell drivers: gpio: implement PALMAS GPIO cell
drivers/gpio/Kconfig | 16 +++ drivers/gpio/Makefile | 2 + drivers/gpio/gpio-uclass.c | 19 ++- drivers/gpio/max77663_gpio.c | 179 ++++++++++++++++++++++++++++ drivers/gpio/palmas_gpio.c | 133 +++++++++++++++++++++ drivers/power/pmic/max77663.c | 9 ++ drivers/power/pmic/palmas.c | 10 +- include/dt-bindings/pmic/max77663.h | 18 +++ include/power/max77663.h | 1 + include/power/palmas.h | 12 ++ 10 files changed, 395 insertions(+), 4 deletions(-) create mode 100644 drivers/gpio/max77663_gpio.c create mode 100644 drivers/gpio/palmas_gpio.c create mode 100644 include/dt-bindings/pmic/max77663.h

UCLASS_PMIC may have GPIO children without exposed fdt node, in this case if requesting fails, check if uclass is PMIC. If so, iterate through its children to find correct device.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/gpio/gpio-uclass.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 7aece85a70..a3fc470a5d 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -1143,9 +1143,22 @@ static int gpio_request_tail(int ret, const char *nodename, ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node, &desc->dev); if (ret) { - debug("%s: uclass_get_device_by_ofnode failed\n", - __func__); - goto err; + struct udevice *pmic; + ret = uclass_get_device_by_ofnode(UCLASS_PMIC, args->node, + &pmic); + if (ret) { + log_err("PMIC device get failed, err %d\n", ret); + goto err; + } + + device_foreach_child(desc->dev, pmic) { + if (device_get_uclass_id(desc->dev) == UCLASS_GPIO) + break; + } + + /* if loop exits without GPIO device return error */ + if (device_get_uclass_id(desc->dev) != UCLASS_GPIO) + goto err; } } ret = gpio_find_and_xlate(desc, args);

On Tue, Oct 24, 2023 at 11:37:59AM +0300, Svyatoslav Ryhel wrote:
UCLASS_PMIC may have GPIO children without exposed fdt node, in this case if requesting fails, check if uclass is PMIC. If so, iterate through its children to find correct device.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/gpio/gpio-uclass.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 7aece85a70..a3fc470a5d 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -1143,9 +1143,22 @@ static int gpio_request_tail(int ret, const char *nodename, ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node, &desc->dev); if (ret) {
debug("%s: uclass_get_device_by_ofnode failed\n",
__func__);
goto err;
struct udevice *pmic;
ret = uclass_get_device_by_ofnode(UCLASS_PMIC, args->node,
&pmic);
if (ret) {
log_err("PMIC device get failed, err %d\n", ret);
goto err;
}
device_foreach_child(desc->dev, pmic) {
if (device_get_uclass_id(desc->dev) == UCLASS_GPIO)
break;
}
/* if loop exits without GPIO device return error */
if (device_get_uclass_id(desc->dev) != UCLASS_GPIO)
} } ret = gpio_find_and_xlate(desc, args);goto err;
This needs to check for DM_PMIC or so as it leads to a lot of size increase and one failure to link due to running out of space now.

3 листопада 2023 р. 21:03:39 GMT+02:00, Tom Rini trini@konsulko.com написав(-ла):
On Tue, Oct 24, 2023 at 11:37:59AM +0300, Svyatoslav Ryhel wrote:
UCLASS_PMIC may have GPIO children without exposed fdt node, in this case if requesting fails, check if uclass is PMIC. If so, iterate through its children to find correct device.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/gpio/gpio-uclass.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 7aece85a70..a3fc470a5d 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -1143,9 +1143,22 @@ static int gpio_request_tail(int ret, const char *nodename, ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node, &desc->dev); if (ret) {
debug("%s: uclass_get_device_by_ofnode failed\n",
__func__);
goto err;
struct udevice *pmic;
ret = uclass_get_device_by_ofnode(UCLASS_PMIC, args->node,
&pmic);
if (ret) {
log_err("PMIC device get failed, err %d\n", ret);
goto err;
}
device_foreach_child(desc->dev, pmic) {
if (device_get_uclass_id(desc->dev) == UCLASS_GPIO)
break;
}
/* if loop exits without GPIO device return error */
if (device_get_uclass_id(desc->dev) != UCLASS_GPIO)
} } ret = gpio_find_and_xlate(desc, args);goto err;
This needs to check for DM_PMIC or so as it leads to a lot of size increase and one failure to link due to running out of space now.
Thanks for checking! I will look into it deeper to reduce the size inflation, not sure though why it occurs at all. Best regards, Svyatoslav R.

On Fri, Nov 03, 2023 at 09:39:51PM +0200, Svyatoslav Ryhel wrote:
3 листопада 2023 р. 21:03:39 GMT+02:00, Tom Rini trini@konsulko.com написав(-ла):
On Tue, Oct 24, 2023 at 11:37:59AM +0300, Svyatoslav Ryhel wrote:
UCLASS_PMIC may have GPIO children without exposed fdt node, in this case if requesting fails, check if uclass is PMIC. If so, iterate through its children to find correct device.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/gpio/gpio-uclass.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 7aece85a70..a3fc470a5d 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -1143,9 +1143,22 @@ static int gpio_request_tail(int ret, const char *nodename, ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node, &desc->dev); if (ret) {
debug("%s: uclass_get_device_by_ofnode failed\n",
__func__);
goto err;
struct udevice *pmic;
ret = uclass_get_device_by_ofnode(UCLASS_PMIC, args->node,
&pmic);
if (ret) {
log_err("PMIC device get failed, err %d\n", ret);
goto err;
}
device_foreach_child(desc->dev, pmic) {
if (device_get_uclass_id(desc->dev) == UCLASS_GPIO)
break;
}
/* if loop exits without GPIO device return error */
if (device_get_uclass_id(desc->dev) != UCLASS_GPIO)
} } ret = gpio_find_and_xlate(desc, args);goto err;
This needs to check for DM_PMIC or so as it leads to a lot of size increase and one failure to link due to running out of space now.
Thanks for checking! I will look into it deeper to reduce the size inflation, not sure though why it occurs at all.
Note that it's not a huge inflation (it's 128 bytes), but for every board that includes this file, in SPL, it grows that much, and then sometimes might also be "and now we pull in funcX" as well.

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 clamor95@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 clamor95@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 clamor95@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

Add gpio driver for TI Palmas series PMIC. This has 8 gpio which can work as input/output.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/palmas_gpio.c | 133 ++++++++++++++++++++++++++++++++++++ drivers/power/pmic/palmas.c | 10 ++- include/power/palmas.h | 12 ++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 drivers/gpio/palmas_gpio.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3c701eff5c..86c5f124a7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -428,6 +428,13 @@ config VYBRID_GPIO help Say yes here to support Vybrid vf610 GPIOs.
+config PALMAS_GPIO + bool "TI PALMAS series PMICs GPIO" + depends on DM_GPIO && PMIC_PALMAS + help + Select this option to enable GPIO driver for the TI PALMAS + series chip family. + config PIC32_GPIO bool "Microchip PIC32 GPIO driver" depends on DM_GPIO && MACH_PIC32 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index dab3eb93a3..da3da5da2b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o obj-$(CONFIG_HSDK_CREG_GPIO) += hsdk-creg-gpio.o obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o +obj-$(CONFIG_$(SPL_)PALMAS_GPIO) += palmas_gpio.o obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o obj-$(CONFIG_OCTEON_GPIO) += octeon_gpio.o obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o diff --git a/drivers/gpio/palmas_gpio.c b/drivers/gpio/palmas_gpio.c new file mode 100644 index 0000000000..d2f0e0090a --- /dev/null +++ b/drivers/gpio/palmas_gpio.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on mainline Linux palmas GPIO driver + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <asm/gpio.h> +#include <power/palmas.h> + +#define NUM_GPIOS 8 + +static int palmas_gpio_set_value(struct udevice *dev, unsigned int offset, + int value) +{ + struct palmas_priv *priv = dev_get_priv(dev->parent); + u32 reg; + int ret; + + reg = (value) ? PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT; + + ret = dm_i2c_reg_write(priv->chip2, reg, BIT(offset)); + if (ret < 0) + log_err("Reg 0x%02x write failed, %d\n", reg, ret); + + return ret; +} + +static int palmas_gpio_get_value(struct udevice *dev, unsigned int offset) +{ + struct palmas_priv *priv = dev_get_priv(dev->parent); + u32 reg; + int ret; + + ret = dm_i2c_reg_read(priv->chip2, PALMAS_GPIO_DATA_DIR); + if (ret < 0) { + log_err("GPIO_DATA_DIR read failed, %d\n", ret); + return ret; + } + + if (ret & BIT(offset)) + reg = PALMAS_GPIO_DATA_OUT; + else + reg = PALMAS_GPIO_DATA_IN; + + ret = dm_i2c_reg_read(priv->chip2, reg); + if (ret < 0) { + log_err("Reg 0x%02x read failed, %d\n", reg, ret); + return ret; + } + + return !!(ret & BIT(offset)); +} + +static int palmas_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + struct palmas_priv *priv = dev_get_priv(dev->parent); + int ret; + + ret = dm_i2c_reg_clrset(priv->chip2, PALMAS_GPIO_DATA_DIR, + BIT(offset), 0); + if (ret < 0) + log_err("GPIO_DATA_DIR val update failed: %d\n", ret); + + return ret; +} + +static int palmas_gpio_direction_output(struct udevice *dev, unsigned int offset, + int value) +{ + struct palmas_priv *priv = dev_get_priv(dev->parent); + int ret; + + /* Set the initial value */ + palmas_gpio_set_value(dev, offset, value); + + ret = dm_i2c_reg_clrset(priv->chip2, PALMAS_GPIO_DATA_DIR, + BIT(offset), BIT(offset)); + if (ret < 0) + log_err("GPIO_DATA_DIR val update failed: %d\n", ret); + + return ret; +} + +static int palmas_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + struct palmas_priv *priv = dev_get_priv(dev->parent); + int ret; + + ret = dm_i2c_reg_read(priv->chip2, PALMAS_GPIO_DATA_DIR); + if (ret < 0) { + log_err("GPIO_DATA_DIR read failed, %d\n", ret); + return ret; + } + + if (ret & BIT(offset)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static const struct dm_gpio_ops palmas_gpio_ops = { + .direction_input = palmas_gpio_direction_input, + .direction_output = palmas_gpio_direction_output, + .get_value = palmas_gpio_get_value, + .set_value = palmas_gpio_set_value, + .get_function = palmas_gpio_get_function, +}; + +static int palmas_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = NUM_GPIOS; + uc_priv->bank_name = "GPIO"; + + return 0; +} + +static const struct udevice_id palmas_ids[] = { + { .compatible = "ti,palmas-gpio" }, + { } +}; + +U_BOOT_DRIVER(palmas_gpio) = { + .name = PALMAS_GPIO_DRIVER, + .id = UCLASS_GPIO, + .of_match = palmas_ids, + .probe = palmas_gpio_probe, + .ops = &palmas_gpio_ops, +}; diff --git a/drivers/power/pmic/palmas.c b/drivers/power/pmic/palmas.c index 32f2a938b2..e340a32279 100644 --- a/drivers/power/pmic/palmas.c +++ b/drivers/power/pmic/palmas.c @@ -46,7 +46,7 @@ static int palmas_read(struct udevice *dev, uint reg, uint8_t *buff, int len) static int palmas_bind(struct udevice *dev) { ofnode pmic_node = ofnode_null(), regulators_node; - ofnode subnode; + ofnode subnode, gpio_node; int children, ret;
if (IS_ENABLED(CONFIG_SYSRESET_PALMAS)) { @@ -58,6 +58,14 @@ static int palmas_bind(struct udevice *dev) } }
+ gpio_node = ofnode_find_subnode(dev_ofnode(dev), "gpio"); + if (ofnode_valid(gpio_node)) { + ret = device_bind_driver_to_node(dev, PALMAS_GPIO_DRIVER, + "gpio", gpio_node, NULL); + if (ret) + log_err("cannot bind GPIOs (ret = %d)\n", ret); + } + dev_for_each_subnode(subnode, dev) { const char *name; char *temp; diff --git a/include/power/palmas.h b/include/power/palmas.h index 0a612052f0..94c99dd411 100644 --- a/include/power/palmas.h +++ b/include/power/palmas.h @@ -15,6 +15,7 @@ struct palmas_priv { #define PALMAS_LDO_DRIVER "palmas_ldo" #define PALMAS_SMPS_DRIVER "palmas_smps" #define PALMAS_RST_DRIVER "palmas_rst" +#define PALMAS_GPIO_DRIVER "palmas_gpio"
#define PALMAS_SMPS_VOLT_MASK 0x7F #define PALMAS_SMPS_RANGE_MASK 0x80 @@ -35,3 +36,14 @@ struct palmas_priv { #define DEV_OFF 0x00 #define PALMAS_INT3_MASK 0x1B #define MASK_VBUS BIT(7) + +/* second chip */ +#define PALMAS_GPIO_DATA_IN 0x80 +#define PALMAS_GPIO_DATA_DIR 0x81 +#define PALMAS_GPIO_DATA_OUT 0x82 +#define PALMAS_GPIO_DEBOUNCE_EN 0x83 +#define PALMAS_GPIO_CLEAR_DATA_OUT 0x84 +#define PALMAS_GPIO_SET_DATA_OUT 0x85 +#define PALMAS_PU_PD_GPIO_CTRL1 0x86 +#define PALMAS_PU_PD_GPIO_CTRL2 0x87 +#define PALMAS_OD_OUTPUT_GPIO_CTRL 0x88
participants (2)
-
Svyatoslav Ryhel
-
Tom Rini