| /* SPDX-License-Identifier: GPL-2.0+ */ | |
| /* | |
| * AMD ISP Pinctrl Driver | |
| * | |
| * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved. | |
| * | |
| */ | |
| #include <linux/gpio/driver.h> | |
| #include <linux/module.h> | |
| #include <linux/platform_device.h> | |
| #include "pinctrl-amdisp.h" | |
| #define DRV_NAME "amdisp-pinctrl" | |
| #define GPIO_CONTROL_PIN 4 | |
| #define GPIO_OFFSET_0 0x0 | |
| #define GPIO_OFFSET_1 0x4 | |
| #define GPIO_OFFSET_2 0x50 | |
| static const u32 gpio_offset[] = { | |
| GPIO_OFFSET_0, | |
| GPIO_OFFSET_1, | |
| GPIO_OFFSET_2 | |
| }; | |
| struct amdisp_pinctrl_data { | |
| const struct pinctrl_pin_desc *pins; | |
| unsigned int npins; | |
| const struct amdisp_function *functions; | |
| unsigned int nfunctions; | |
| const struct amdisp_pingroup *groups; | |
| unsigned int ngroups; | |
| }; | |
| static const struct amdisp_pinctrl_data amdisp_pinctrl_data = { | |
| .pins = amdisp_pins, | |
| .npins = ARRAY_SIZE(amdisp_pins), | |
| .functions = amdisp_functions, | |
| .nfunctions = ARRAY_SIZE(amdisp_functions), | |
| .groups = amdisp_groups, | |
| .ngroups = ARRAY_SIZE(amdisp_groups), | |
| }; | |
| struct amdisp_pinctrl { | |
| struct device *dev; | |
| struct pinctrl_dev *pctrl; | |
| struct pinctrl_desc desc; | |
| struct pinctrl_gpio_range gpio_range; | |
| struct gpio_chip gc; | |
| const struct amdisp_pinctrl_data *data; | |
| void __iomem *gpiobase; | |
| raw_spinlock_t lock; | |
| }; | |
| static int amdisp_get_groups_count(struct pinctrl_dev *pctldev) | |
| { | |
| struct amdisp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); | |
| return pctrl->data->ngroups; | |
| } | |
| static const char *amdisp_get_group_name(struct pinctrl_dev *pctldev, | |
| unsigned int group) | |
| { | |
| struct amdisp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); | |
| return pctrl->data->groups[group].name; | |
| } | |
| static int amdisp_get_group_pins(struct pinctrl_dev *pctldev, | |
| unsigned int group, | |
| const unsigned int **pins, | |
| unsigned int *num_pins) | |
| { | |
| struct amdisp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); | |
| *pins = pctrl->data->groups[group].pins; | |
| *num_pins = pctrl->data->groups[group].npins; | |
| return 0; | |
| } | |
| static const struct pinctrl_ops amdisp_pinctrl_ops = { | |
| .get_groups_count = amdisp_get_groups_count, | |
| .get_group_name = amdisp_get_group_name, | |
| .get_group_pins = amdisp_get_group_pins, | |
| }; | |
| static int amdisp_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) | |
| { | |
| /* amdisp gpio only has output mode */ | |
| return GPIO_LINE_DIRECTION_OUT; | |
| } | |
| static int amdisp_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) | |
| { | |
| return -EOPNOTSUPP; | |
| } | |
| static int amdisp_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, | |
| int value) | |
| { | |
| /* Nothing to do, amdisp gpio only has output mode */ | |
| return 0; | |
| } | |
| static int amdisp_gpio_get(struct gpio_chip *gc, unsigned int gpio) | |
| { | |
| unsigned long flags; | |
| u32 pin_reg; | |
| struct amdisp_pinctrl *pctrl = gpiochip_get_data(gc); | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| pin_reg = readl(pctrl->gpiobase + gpio_offset[gpio]); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return !!(pin_reg & BIT(GPIO_CONTROL_PIN)); | |
| } | |
| static int amdisp_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) | |
| { | |
| unsigned long flags; | |
| u32 pin_reg; | |
| struct amdisp_pinctrl *pctrl = gpiochip_get_data(gc); | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| pin_reg = readl(pctrl->gpiobase + gpio_offset[gpio]); | |
| if (value) | |
| pin_reg |= BIT(GPIO_CONTROL_PIN); | |
| else | |
| pin_reg &= ~BIT(GPIO_CONTROL_PIN); | |
| writel(pin_reg, pctrl->gpiobase + gpio_offset[gpio]); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return 0; | |
| } | |
| static int amdisp_gpiochip_add(struct platform_device *pdev, | |
| struct amdisp_pinctrl *pctrl) | |
| { | |
| struct gpio_chip *gc = &pctrl->gc; | |
| struct pinctrl_gpio_range *grange = &pctrl->gpio_range; | |
| int ret; | |
| gc->label = dev_name(pctrl->dev); | |
| gc->parent = &pdev->dev; | |
| gc->names = amdisp_range_pins_name; | |
| gc->request = gpiochip_generic_request; | |
| gc->free = gpiochip_generic_free; | |
| gc->get_direction = amdisp_gpio_get_direction; | |
| gc->direction_input = amdisp_gpio_direction_input; | |
| gc->direction_output = amdisp_gpio_direction_output; | |
| gc->get = amdisp_gpio_get; | |
| gc->set = amdisp_gpio_set; | |
| gc->base = -1; | |
| gc->ngpio = ARRAY_SIZE(amdisp_range_pins); | |
| grange->id = 0; | |
| grange->pin_base = 0; | |
| grange->base = 0; | |
| grange->pins = amdisp_range_pins; | |
| grange->npins = ARRAY_SIZE(amdisp_range_pins); | |
| grange->name = gc->label; | |
| grange->gc = gc; | |
| ret = devm_gpiochip_add_data(&pdev->dev, gc, pctrl); | |
| if (ret) | |
| return ret; | |
| pinctrl_add_gpio_range(pctrl->pctrl, grange); | |
| return 0; | |
| } | |
| static int amdisp_pinctrl_probe(struct platform_device *pdev) | |
| { | |
| struct amdisp_pinctrl *pctrl; | |
| struct resource *res; | |
| int ret; | |
| pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); | |
| if (!pctrl) | |
| return -ENOMEM; | |
| pdev->dev.init_name = DRV_NAME; | |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| if (!res) | |
| return -EINVAL; | |
| pctrl->gpiobase = devm_ioremap_resource(&pdev->dev, res); | |
| if (IS_ERR(pctrl->gpiobase)) | |
| return PTR_ERR(pctrl->gpiobase); | |
| platform_set_drvdata(pdev, pctrl); | |
| pctrl->dev = &pdev->dev; | |
| pctrl->data = &amdisp_pinctrl_data; | |
| pctrl->desc.owner = THIS_MODULE; | |
| pctrl->desc.pctlops = &amdisp_pinctrl_ops; | |
| pctrl->desc.pmxops = NULL; | |
| pctrl->desc.name = dev_name(&pdev->dev); | |
| pctrl->desc.pins = pctrl->data->pins; | |
| pctrl->desc.npins = pctrl->data->npins; | |
| ret = devm_pinctrl_register_and_init(&pdev->dev, &pctrl->desc, | |
| pctrl, &pctrl->pctrl); | |
| if (ret) | |
| return ret; | |
| ret = pinctrl_enable(pctrl->pctrl); | |
| if (ret) | |
| return ret; | |
| ret = amdisp_gpiochip_add(pdev, pctrl); | |
| if (ret) | |
| return ret; | |
| return 0; | |
| } | |
| static struct platform_driver amdisp_pinctrl_driver = { | |
| .driver = { | |
| .name = DRV_NAME, | |
| }, | |
| .probe = amdisp_pinctrl_probe, | |
| }; | |
| module_platform_driver(amdisp_pinctrl_driver); | |
| MODULE_AUTHOR("Benjamin Chan <benjamin.chan@amd.com>"); | |
| MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>"); | |
| MODULE_DESCRIPTION("AMDISP pinctrl driver"); | |
| MODULE_LICENSE("GPL v2"); | |
| MODULE_ALIAS("platform:" DRV_NAME); |