// SPDX-License-Identifier: GPL-2.0+ /* * Copyright(C) 2023 Svyatoslav Ryhel */ #include #include #include #include #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, };