diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 16 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-uclass.c | 20 | ||||
-rw-r--r-- | drivers/gpio/max77663_gpio.c | 178 | ||||
-rw-r--r-- | drivers/gpio/palmas_gpio.c | 132 |
5 files changed, 348 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ba42b0768e1..63e62e1acd2 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 @@ -426,6 +435,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 c8b3fd78141..da3da5da2b3 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 @@ -68,6 +69,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/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 7aece85a70a..4234cd912c9 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -1143,9 +1143,29 @@ static int gpio_request_tail(int ret, const char *nodename, ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node, &desc->dev); if (ret) { +#if CONFIG_IS_ENABLED(MAX77663_GPIO) || CONFIG_IS_ENABLED(PALMAS_GPIO) + struct udevice *pmic; + ret = uclass_get_device_by_ofnode(UCLASS_PMIC, args->node, + &pmic); + if (ret) { + log_debug("%s: PMIC device get failed, err %d\n", + __func__, 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; +#else debug("%s: uclass_get_device_by_ofnode failed\n", __func__); goto err; +#endif } } ret = gpio_find_and_xlate(desc, args); diff --git a/drivers/gpio/max77663_gpio.c b/drivers/gpio/max77663_gpio.c new file mode 100644 index 00000000000..ecb60478088 --- /dev/null +++ b/drivers/gpio/max77663_gpio.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#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_debug("%s: CNFG_GPIOx dir update failed: %d\n", __func__, 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_debug("%s: CNFG_GPIOx val update failed: %d\n", __func__, 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_debug("%s: CNFG_GPIOx dir update failed: %d\n", __func__, 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_debug("%s: ONOFFSTAT_REG read failed: %d\n", __func__, ret); + return ret; + } + + return !!(ret & BIT(offset - NUM_GPIOS)); + } + + ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset)); + if (ret < 0) { + log_debug("%s: CNFG_GPIOx read failed: %d\n", __func__, 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_debug("%s: CNFG_GPIO_OUT update failed: %d\n", __func__, 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_debug("%s: CNFG_GPIOx read failed: %d\n", __func__, 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_debug("%s: failed to disable interrupt: %d\n", __func__, 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/gpio/palmas_gpio.c b/drivers/gpio/palmas_gpio.c new file mode 100644 index 00000000000..15039351dd5 --- /dev/null +++ b/drivers/gpio/palmas_gpio.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on mainline Linux palmas GPIO driver + * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#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_debug("%s: Reg 0x%02x write failed, %d\n", __func__, 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_debug("%s: GPIO_DATA_DIR read failed, %d\n", __func__, 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_debug("%s: Reg 0x%02x read failed, %d\n", __func__, 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_debug("%s: GPIO_DATA_DIR val update failed: %d\n", __func__, 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_debug("%s: GPIO_DATA_DIR val update failed: %d\n", __func__, 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_debug("%s: GPIO_DATA_DIR read failed, %d\n", __func__, 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, +}; |