diff --git a/Documentation/devicetree/bindings/gpio/gpio-max77851.yaml b/Documentation/devicetree/bindings/gpio/gpio-max77851.yaml new file mode 100644 index 00000000..d3d3ee5b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-max77851.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0-only +# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-max77851.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DT bindings MAX77851 Power Management IC GPIO + +maintainers: + - Joan Na + - Shubhi Garg + +description: | + DT bindings GPIO driver for MAX77851 Power management IC from Maxim Semiconductor. + + Device has 8 GPIO pins and 4 FPSO pins which can be configured as GPIO as well as the + special IO functions. + + For more details, please refer generic GPIO DT binding document + . + +properties: + + compatible: + const: "maxim,max77851-pmic" + + gpio-controller: true + + '#gpio-cells': + const: 2 + +examples: + - | + #include + #include + ... + max77851: max77851@3c { + compatible = "maxim,max77851-pmic"; + + gpio-controller; + #gpio-cells = <2>; + }; + ... diff --git a/drivers/Makefile b/drivers/Makefile index 3ab00739..fea76db6 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -# SPDX-FileCopyrightText: Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. LINUXINCLUDE += -I$(srctree.nvidia-oot)/include LINUXINCLUDE += -I$(srctree.nvidia-oot)/drivers/gpu/host1x/hw/ @@ -22,6 +22,7 @@ ifdef CONFIG_PM_DEVFREQ obj-m += devfreq/ endif obj-m += firmware/tegra/ +obj-m += gpio/ ifdef CONFIG_TEGRA_HOST1X obj-m += gpu/ endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile new file mode 100644 index 00000000..fe6af412 --- /dev/null +++ b/drivers/gpio/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# SPDX-FileCopyrightText: Copyright 2022 Maxim Integrated +# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# gpio support: vendor provided GPL-2.0 licensed MFD gpio driver + +obj-m += gpio-max77851.o + diff --git a/drivers/gpio/gpio-max77851.c b/drivers/gpio/gpio-max77851.c new file mode 100644 index 00000000..aa584433 --- /dev/null +++ b/drivers/gpio/gpio-max77851.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +/* + * Maxim MAX77851 GPIO driver + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_CNFG_OFFSET (5) +#define GPIO_CNFG0_REG_ADDR(offset) (GPIO0_CFG0_REG + (offset * GPIO_CNFG_OFFSET)) +#define GPIO_CNFG1_REG_ADDR(offset) (GPIO0_CFG1_REG + (offset * GPIO_CNFG_OFFSET)) + +#define SUPPORTED_IRQ_TYPE (IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW) + +#define GPIO_INT_RISING (BIT_IS_ZERO | BIT_IS_ZERO) +#define GPIO_INT_FALLING (BIT_IS_ZERO | BIT(2)) + +#define GPIO_INT_LEVEL_HIGH (BIT(3) | BIT_IS_ZERO) +#define GPIO_INT_LEVEL_LOW (BIT(3) | BIT(2)) +#define GPIO_INT_MASK BITS(3, 2) + +struct max77851_gpio { + struct gpio_chip gpio_chip; + struct regmap *rmap; + struct device *dev; +}; + +/* Interrupts */ +enum { + /* Addr : 0x2B */ + MAX77851_IRQ_GPIO0_FALLING, + MAX77851_IRQ_GPIO1_FALLING, + MAX77851_IRQ_GPIO2_FALLING, + MAX77851_IRQ_GPIO3_FALLING, + MAX77851_IRQ_GPIO4_FALLING, + MAX77851_IRQ_GPIO5_FALLING, + MAX77851_IRQ_GPIO6_FALLING, + MAX77851_IRQ_GPIO7_FALLING, + + /* Addr : 0x2C */ + MAX77851_IRQ_GPIO0_RISING, + MAX77851_IRQ_GPIO1_RISING, + MAX77851_IRQ_GPIO2_RISING, + MAX77851_IRQ_GPIO3_RISING, + MAX77851_IRQ_GPIO4_RISING, + MAX77851_IRQ_GPIO5_RISING, + MAX77851_IRQ_GPIO6_RISING, + MAX77851_IRQ_GPIO7_RISING +}; + +static const struct regmap_irq max77851_gpio_irqs[] = { + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO0_FALLING, 0, GPIO_INT0_GPIO0_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO1_FALLING, 0, GPIO_INT0_GPIO1_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO2_FALLING, 0, GPIO_INT0_GPIO2_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO3_FALLING, 0, GPIO_INT0_GPIO3_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO4_FALLING, 0, GPIO_INT0_GPIO4_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO5_FALLING, 0, GPIO_INT0_GPIO5_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO6_FALLING, 0, GPIO_INT0_GPIO6_FL_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO7_FALLING, 0, GPIO_INT0_GPIO7_FL_I), + + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO0_RISING, 1, GPIO_INT1_GPIO0_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO1_RISING, 1, GPIO_INT1_GPIO1_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO2_RISING, 1, GPIO_INT1_GPIO2_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO3_RISING, 1, GPIO_INT1_GPIO3_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO4_RISING, 1, GPIO_INT1_GPIO4_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO5_RISING, 1, GPIO_INT1_GPIO5_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO6_RISING, 1, GPIO_INT1_GPIO6_RH_I), + REGMAP_IRQ_REG(MAX77851_IRQ_GPIO7_RISING, 1, GPIO_INT1_GPIO7_RH_I), +}; + +static int max77851_gpio_irq_global_mask(void *irq_drv_data) +{ + int ret = 0; + + return ret; +} + +static int max77851_gpio_irq_global_unmask(void *irq_drv_data) +{ + int ret = 0; + + return ret; +} + +static const struct regmap_irq_chip max77851_gpio_irq_chip = { + .name = "max77851-gpio", + .status_base = GPIO_INT0_REG, + .mask_base = GPIO_MSK0_REG, + .num_regs = 2, + .irqs = max77851_gpio_irqs, + .num_irqs = ARRAY_SIZE(max77851_gpio_irqs), + .handle_pre_irq = max77851_gpio_irq_global_mask, + .handle_post_irq = max77851_gpio_irq_global_unmask, +}; + +static int max77851_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) +{ + struct max77851_gpio *mgpio = gpiochip_get_data(gc); + int ret; + + ret = regmap_update_bits(mgpio->rmap, GPIO_CNFG1_REG_ADDR(offset), + GPIO_CFG1_MODE, + GPIO_PINMUX_GPIO_INPUT); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); + + return ret; +} + +static int max77851_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct max77851_gpio *mgpio = gpiochip_get_data(gc); + unsigned int val; + int ret; + + ret = regmap_read(mgpio->rmap, GPIO_STAT0_REG, &val); + if (ret < 0) { + dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret); + return ret; + } + + if (val & BIT(offset)) + return 1; + else + return 0; +} + +static int max77851_gpio_dir_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct max77851_gpio *mgpio = gpiochip_get_data(gc); + u8 val; + int ret; + + val = (value) ? GPIO_OUTPUT_VAL_HIGH : GPIO_OUTPUT_VAL_LOW; + + ret = regmap_update_bits(mgpio->rmap, GPIO_CNFG1_REG_ADDR(offset), + GPIO_CFG1_OUTPUT, val); + if (ret < 0) { + dev_err(mgpio->dev, "CNFG_GPIOx val update failed: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(mgpio->rmap, GPIO_CNFG1_REG_ADDR(offset), + GPIO_CFG1_MODE, + GPIO_PINMUX_GPIO_OUTPUT); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); + + return ret; +} + +static int max77851_gpio_set_debounce(struct max77851_gpio *mgpio, + unsigned int offset, + unsigned int debounce) +{ + u8 val; + int ret; + + switch (debounce) { + case 0: + val = GPIO_DBNC_NONE; + break; + case 1 ... 100: + val = GPIO_DBNC_100US; + break; + case 101 ... 1000: + val = GPIO_DBNC_1MS; + break; + case 1001 ... 4000: + val = GPIO_DBNC_4MS; + break; + case 4001 ... 8000: + val = GPIO_DBNC_8MS; + break; + case 8001 ... 16000: + val = GPIO_DBNC_16MS; + break; + case 16001 ... 32000: + val = GPIO_DBNC_32MS; + break; + default: + dev_err(mgpio->dev, "Illegal value %u\n", debounce); + return -EINVAL; + } + + ret = regmap_update_bits(mgpio->rmap, GPIO_CNFG0_REG_ADDR(offset), + GPIO_CFG0_IFILTER, val); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIOx_DBNC update failed: %d\n", ret); + + return ret; +} + +static void max77851_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct max77851_gpio *mgpio = gpiochip_get_data(gc); + u8 val; + int ret; + + val = (value) ? GPIO_OUTPUT_VAL_HIGH : GPIO_OUTPUT_VAL_LOW; + + ret = regmap_update_bits(mgpio->rmap, GPIO_CNFG1_REG_ADDR(offset), + GPIO_CFG1_OUTPUT, val); + if (ret < 0) + dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret); +} + +static int max77851_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct max77851_gpio *mgpio = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(mgpio->rmap, GPIO_CNFG1_REG_ADDR(offset), + GPIO_CFG1_DRV, GPIO_DRV_OPENDRAIN); + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(mgpio->rmap, GPIO_CNFG1_REG_ADDR(offset), + GPIO_CFG1_DRV, GPIO_DRV_PUSHPULL); + case PIN_CONFIG_INPUT_DEBOUNCE: + return max77851_gpio_set_debounce(mgpio, offset, + pinconf_to_config_argument(config)); + default: + break; + } + + return -ENOTSUPP; +} + +static int max77851_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ +#if defined(CONFIG_REGMAP_IRQ) + struct max77851_gpio *mgpio = gpiochip_get_data(gc); + struct max77851_chip *chip = dev_get_drvdata(mgpio->dev->parent); + + return regmap_irq_get_virq(chip->gpio_irq_data, offset); +#else + return 0; +#endif +} + +static int max77851_gpio_probe(struct platform_device *pdev) +{ + struct max77851_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct max77851_gpio *mgpio; + int gpio_irq; + int ret; + + gpio_irq = platform_get_irq(pdev, 0); + if (gpio_irq <= 0) + return -ENODEV; + + mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); + if (!mgpio) + return -ENOMEM; + + mgpio->rmap = chip->rmap; + mgpio->dev = &pdev->dev; + + mgpio->gpio_chip.label = pdev->name; + mgpio->gpio_chip.parent = &pdev->dev; + mgpio->gpio_chip.direction_input = max77851_gpio_dir_input; + mgpio->gpio_chip.get = max77851_gpio_get; + mgpio->gpio_chip.direction_output = max77851_gpio_dir_output; + mgpio->gpio_chip.set = max77851_gpio_set; + mgpio->gpio_chip.set_config = max77851_gpio_set_config; + mgpio->gpio_chip.to_irq = max77851_gpio_to_irq; + mgpio->gpio_chip.ngpio = MAX77851_GPIO_NR; + mgpio->gpio_chip.can_sleep = 1; + mgpio->gpio_chip.base = -1; +#ifdef CONFIG_OF_GPIO +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0) + mgpio->gpio_chip.of_node = pdev->dev.parent->of_node; +#endif +#endif + + platform_set_drvdata(pdev, mgpio); + + ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); + if (ret < 0) { + dev_err(&pdev->dev, "gpio_init: Failed to add max77851_gpio\n"); + return ret; + } + +#if defined(CONFIG_REGMAP_IRQ) + ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, gpio_irq, + IRQF_ONESHOT, -1, + &max77851_gpio_irq_chip, + &chip->gpio_irq_data); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret); + return ret; + } +#endif + + return 0; +} + +static const struct platform_device_id max77851_gpio_devtype[] = { + { .name = "max77851-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(platform, max77851_gpio_devtype); + +static struct platform_driver max77851_gpio_driver = { + .driver.name = "max77851-gpio", + .probe = max77851_gpio_probe, + .id_table = max77851_gpio_devtype, +}; + +module_platform_driver(max77851_gpio_driver); + +MODULE_DESCRIPTION("MAX77851 GPIO driver"); +MODULE_AUTHOR("Shubhi Garg"); +MODULE_AUTHOR("Joan Na"); +MODULE_ALIAS("platform:max77851-gpio"); +MODULE_LICENSE("GPL v2");