diff options
author | Tom Rini <trini@konsulko.com> | 2023-12-19 15:16:13 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-12-20 08:12:56 -0500 |
commit | 1f115bdeb804b34a6456fd1f5907ffc98a2977ba (patch) | |
tree | e155297a5e4ce7b08c7a0d9802bb032ab2427c34 /drivers/gpio/max77663_gpio.c | |
parent | 936d0f9dd713a913fe952eae576c893e1d5ecbd1 (diff) | |
parent | 4989628c1d2b6ea19a38aae34b1c08b12141c64b (diff) |
Merge branch 'staging' of https://source.denx.de/u-boot/custodians/u-boot-tegra into next
This PR contains 4 patchsets:
1. PMIC GPIO cells bringup. Created drivers for MAX7663 and Palmas
PMICs and gpio-uclass patch isolated behind configs for these 2
drivers. No unintentional size increase on any board. (proposed
2023-11-06 without any reaction)
2. Simple PLL clocks support in common tegra clock code which allows
use of simple PLL the same way main PLLs are used (before only
clock_start_pll was available). PLLD2 is an example of simple PLL, it
is used as a video subsystem parent clock and was used to test this
code. So far everything worked as expected. (proposed 2023-11-16
without any reaction)
3. A small patch for tegra emmc to allow pass max frequency from
device tree since some devices may not support full speed.
4. Pinmux DM conversion. Patchset consists of commit with DM wrapper
for existing pinmux code for t20/t30/t114, pinmux and funcmux files
relocation into a dedicated folder inside pinctrl, conversion of some
tegra boards to device tree pinmux setup.
Diffstat (limited to 'drivers/gpio/max77663_gpio.c')
-rw-r--r-- | drivers/gpio/max77663_gpio.c | 178 |
1 files changed, 178 insertions, 0 deletions
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, +}; |