summaryrefslogtreecommitdiff
path: root/drivers/gpio/max77663_gpio.c
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-12-19 15:16:13 -0500
committerTom Rini <trini@konsulko.com>2023-12-20 08:12:56 -0500
commit1f115bdeb804b34a6456fd1f5907ffc98a2977ba (patch)
treee155297a5e4ce7b08c7a0d9802bb032ab2427c34 /drivers/gpio/max77663_gpio.c
parent936d0f9dd713a913fe952eae576c893e1d5ecbd1 (diff)
parent4989628c1d2b6ea19a38aae34b1c08b12141c64b (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.c178
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,
+};