mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-27 15:36:48 +00:00
pinctrl: amd: isp411: Add amdisp GPIO pinctrl
Add pinctrl driver support for AMD SoC with isp41 hw ip block. Signed-off-by: Pratap Nirujogi <pratap.nirujogi@amd.com> Link: https://lore.kernel.org/20250304232051.2936557-1-pratap.nirujogi@amd.com Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
7f7a793d71
commit
e97435ab09
@ -49,6 +49,19 @@ config PINCTRL_AMD
|
||||
Requires ACPI/FDT device enumeration code to set up a platform
|
||||
device.
|
||||
|
||||
config PINCTRL_AMDISP
|
||||
tristate "AMDISP GPIO pin control"
|
||||
depends on HAS_IOMEM
|
||||
select GPIOLIB
|
||||
select PINCONF
|
||||
select GENERIC_PINCONF
|
||||
help
|
||||
The driver for memory mapped GPIO functionality on AMD platforms
|
||||
with ISP support. All the pins are output controlled only
|
||||
|
||||
Requires AMDGPU to MFD add device for enumeration to set up as
|
||||
platform device.
|
||||
|
||||
config PINCTRL_APPLE_GPIO
|
||||
tristate "Apple SoC GPIO pin controller driver"
|
||||
depends on ARCH_APPLE
|
||||
|
@ -10,6 +10,7 @@ obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
|
||||
obj-$(CONFIG_OF) += devicetree.o
|
||||
|
||||
obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o
|
||||
obj-$(CONFIG_PINCTRL_AMDISP) += pinctrl-amdisp.o
|
||||
obj-$(CONFIG_PINCTRL_APPLE_GPIO) += pinctrl-apple-gpio.o
|
||||
obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl-artpec6.o
|
||||
obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o
|
||||
|
231
drivers/pinctrl/pinctrl-amdisp.c
Normal file
231
drivers/pinctrl/pinctrl-amdisp.c
Normal file
@ -0,0 +1,231 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 void 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);
|
||||
}
|
||||
|
||||
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 (IS_ERR(res))
|
||||
return PTR_ERR(res);
|
||||
|
||||
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);
|
95
drivers/pinctrl/pinctrl-amdisp.h
Normal file
95
drivers/pinctrl/pinctrl-amdisp.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* AMD ISP Pinctrl Driver
|
||||
*
|
||||
* Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
static const struct pinctrl_pin_desc amdisp_pins[] = {
|
||||
PINCTRL_PIN(0, "GPIO_0"), /* sensor0 control */
|
||||
PINCTRL_PIN(1, "GPIO_1"), /* sensor1 control */
|
||||
PINCTRL_PIN(2, "GPIO_2"), /* sensor2 control */
|
||||
};
|
||||
|
||||
#define AMDISP_GPIO_PINS(pin) \
|
||||
static const unsigned int gpio##pin##_pins[] = { pin }
|
||||
AMDISP_GPIO_PINS(0);
|
||||
AMDISP_GPIO_PINS(1);
|
||||
AMDISP_GPIO_PINS(2);
|
||||
|
||||
static const unsigned int amdisp_range_pins[] = {
|
||||
0, 1, 2
|
||||
};
|
||||
|
||||
static const char * const amdisp_range_pins_name[] = {
|
||||
"gpio0", "gpio1", "gpio2"
|
||||
};
|
||||
|
||||
enum amdisp_functions {
|
||||
mux_gpio,
|
||||
mux_NA
|
||||
};
|
||||
|
||||
static const char * const gpio_groups[] = {
|
||||
"gpio0", "gpio1", "gpio2"
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdisp_function - a pinmux function
|
||||
* @name: Name of the pinmux function.
|
||||
* @groups: List of pingroups for this function.
|
||||
* @ngroups: Number of entries in @groups.
|
||||
*/
|
||||
struct amdisp_function {
|
||||
const char *name;
|
||||
const char * const *groups;
|
||||
unsigned int ngroups;
|
||||
};
|
||||
|
||||
#define FUNCTION(fname) \
|
||||
[mux_##fname] = { \
|
||||
.name = #fname, \
|
||||
.groups = fname##_groups, \
|
||||
.ngroups = ARRAY_SIZE(fname##_groups), \
|
||||
}
|
||||
|
||||
static const struct amdisp_function amdisp_functions[] = {
|
||||
FUNCTION(gpio),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdisp_pingroup - a pinmux group
|
||||
* @name: Name of the pinmux group.
|
||||
* @pins: List of pins for this group.
|
||||
* @npins: Number of entries in @pins.
|
||||
* @funcs: List of functions belongs to this group.
|
||||
* @nfuncs: Number of entries in @funcs.
|
||||
* @offset: Group offset in amdisp pinmux groups.
|
||||
*/
|
||||
struct amdisp_pingroup {
|
||||
const char *name;
|
||||
const unsigned int *pins;
|
||||
unsigned int npins;
|
||||
unsigned int *funcs;
|
||||
unsigned int nfuncs;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
#define PINGROUP(id, f0) \
|
||||
{ \
|
||||
.name = "gpio" #id, \
|
||||
.pins = gpio##id##_pins, \
|
||||
.npins = ARRAY_SIZE(gpio##id##_pins), \
|
||||
.funcs = (int[]){ \
|
||||
mux_##f0, \
|
||||
}, \
|
||||
.nfuncs = 1, \
|
||||
.offset = id, \
|
||||
}
|
||||
|
||||
static const struct amdisp_pingroup amdisp_groups[] = {
|
||||
PINGROUP(0, gpio),
|
||||
PINGROUP(1, gpio),
|
||||
PINGROUP(2, gpio),
|
||||
};
|
Loading…
Reference in New Issue
Block a user