[RFC,v2,4/5] gpio: add pinctrl based generic gpio driver
Commit Message
Some pin controllers provide not only a method to set up lines but
also gpio function. With this commit, a new generic gpio driver will
be provided. It is implemented purely by using pinctrl interfaces.
One of such pin controllers is Arm's SCMI.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
RFC v2 (Oct 5, 2023)
* rename the driver to pin-control-gpio (CONFIG_GPIO_BY_PINCTRL)
* return meaningful error codes instead of -1
* remove the masking at PIN_CONFIG_PACKED
* handle emulated OPEN_DRAIN configuration at get_direction()
* define config_set in gpio_chip
* drop remove hook
RFC (Oct 2, 2023)
---
drivers/gpio/Kconfig | 7 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-by-pinctrl.c | 165 +++++++++++++++++++++++++++++++++
3 files changed, 173 insertions(+)
create mode 100644 drivers/gpio/gpio-by-pinctrl.c
Comments
On Thu, Oct 5, 2023 at 4:59 AM AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
> Some pin controllers provide not only a method to set up lines but
> also gpio function. With this commit, a new generic gpio driver will
> be provided. It is implemented purely by using pinctrl interfaces.
> One of such pin controllers is Arm's SCMI.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
> RFC v2 (Oct 5, 2023)
RFC v2 looks very good to me, definitely something that can be merged
as a starting point once the hardware has been tested.
> +static int pin_control_gpio_direction_input(struct gpio_chip *chip,
> + unsigned int offset)
> +{
> + return pinctrl_gpio_direction_input(chip->gpiodev->base + offset);
> +}
> +
> +static int pin_control_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int val)
> +{
> + return pinctrl_gpio_direction_output(chip->gpiodev->base + offset);
> +}
IIRC Bartosz is working on a patch set getting rid of this kludge having to
call with base + offset in every driver, replacing it with generic calls that
you can just assign in the gpio_chip.
When this gets applied these changes will likely be in place so you will
get rid of this too.
Yours,
Linus Walleij
Hi Linus and Oleksii,
On Tue, Oct 10, 2023 at 02:00:40PM +0200, Linus Walleij wrote:
> On Thu, Oct 5, 2023 at 4:59???AM AKASHI Takahiro
> <takahiro.akashi@linaro.org> wrote:
>
>
> > Some pin controllers provide not only a method to set up lines but
> > also gpio function. With this commit, a new generic gpio driver will
> > be provided. It is implemented purely by using pinctrl interfaces.
> > One of such pin controllers is Arm's SCMI.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> > RFC v2 (Oct 5, 2023)
>
> RFC v2 looks very good to me, definitely something that can be merged
> as a starting point once the hardware has been tested.
Thank you for your support.
I think the easiest and best way to test the code is that Oleskii will try
my patch on his platform, r-car, on which I believe that SCMI FW for pin
controller is already available since he tested his pinctrl driver.
@Oleskii, can you please take a time for the test?
(I will assist you in case of any error.)
> > +static int pin_control_gpio_direction_input(struct gpio_chip *chip,
> > + unsigned int offset)
> > +{
> > + return pinctrl_gpio_direction_input(chip->gpiodev->base + offset);
> > +}
> > +
> > +static int pin_control_gpio_direction_output(struct gpio_chip *chip,
> > + unsigned int offset, int val)
> > +{
> > + return pinctrl_gpio_direction_output(chip->gpiodev->base + offset);
> > +}
>
> IIRC Bartosz is working on a patch set getting rid of this kludge having to
> call with base + offset in every driver, replacing it with generic calls that
> you can just assign in the gpio_chip.
>
> When this gets applied these changes will likely be in place so you will
> get rid of this too.
I will try to keep eyes on Bartosz's patch.
Thanks,
-Takahiro Akashi
> Yours,
> Linus Walleij
@@ -216,6 +216,13 @@ config GPIO_BRCMSTB
help
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
+config GPIO_BY_PINCTRL
+ tristate "GPIO support based on a pure pin control backend"
+ depends on GPIOLIB
+ help
+ Select this option to support GPIO devices based solely on pin
+ control, specifically pin configuration, such as SCMI.
+
config GPIO_CADENCE
tristate "Cadence GPIO support"
depends on OF_GPIO
@@ -43,6 +43,7 @@ obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
+obj-$(CONFIG_GPIO_BY_PINCTRL) += gpio-by-pinctrl.o
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
new file mode 100644
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2023 Linaro Inc.
+// Author: AKASHI takahiro <takahiro.akashi@linaro.org>
+
+#include <linux/gpio/driver.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include "gpiolib.h"
+
+struct pin_control_gpio_priv {
+ struct gpio_chip chip;
+};
+
+static int pin_control_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ unsigned long config;
+ bool out_en, in_en;
+ int ret;
+
+ config = PIN_CONFIG_OUTPUT_ENABLE;
+ ret = pinctrl_gpio_get_config(chip->gpiodev->base + offset, &config);
+ if (!ret)
+ out_en = !!config;
+ else if (ret == -EINVAL)
+ out_en = false;
+ else
+ return ret;
+
+ config = PIN_CONFIG_INPUT_ENABLE;
+ ret = pinctrl_gpio_get_config(chip->gpiodev->base + offset, &config);
+ if (!ret)
+ in_en = !!config;
+ else if (ret == -EINVAL)
+ in_en = false;
+ else
+ return ret;
+
+ if (in_en && !out_en)
+ return GPIO_LINE_DIRECTION_IN;
+
+ if (!in_en && out_en)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ if (in_en && out_en) {
+ /* This may be an emulation for output with open drain */
+ config = PIN_CONFIG_DRIVE_OPEN_DRAIN;
+ ret = pinctrl_gpio_get_config(chip->gpiodev->base + offset,
+ &config);
+ if (!ret && config)
+ return GPIO_LINE_DIRECTION_OUT;
+ }
+
+ return -EINVAL;
+}
+
+static int pin_control_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return pinctrl_gpio_direction_input(chip->gpiodev->base + offset);
+}
+
+static int pin_control_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int val)
+{
+ return pinctrl_gpio_direction_output(chip->gpiodev->base + offset);
+}
+
+static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ unsigned long config;
+ int ret;
+
+ config = PIN_CONFIG_INPUT;
+ ret = pinctrl_gpio_get_config(chip->gpiodev->base + offset, &config);
+ if (ret)
+ return ret;
+
+ if (config >> 8)
+ return 1;
+
+ return 0;
+}
+
+static void pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int val)
+{
+ unsigned long config;
+
+ config = PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, val);
+
+ pinctrl_gpio_set_config(chip->gpiodev->base + offset, config);
+}
+
+static u16 sum_up_ngpios(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *range;
+ struct gpio_device *gdev = chip->gpiodev;
+ u16 ngpios = 0;
+
+ list_for_each_entry(range, &gdev->pin_ranges, node) {
+ ngpios += range->range.npins;
+ }
+
+ return ngpios;
+}
+
+static int pin_control_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pin_control_gpio_priv *priv;
+ struct gpio_chip *chip;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ chip = &priv->chip;
+ chip->label = dev_name(dev);
+ chip->parent = dev;
+ chip->base = -1;
+
+ chip->request = gpiochip_generic_request;
+ chip->free = gpiochip_generic_free;
+ chip->get_direction = pin_control_gpio_get_direction;
+ chip->direction_input = pin_control_gpio_direction_input;
+ chip->direction_output = pin_control_gpio_direction_output;
+ chip->get = pin_control_gpio_get;
+ chip->set = pin_control_gpio_set;
+ chip->set_config = gpiochip_generic_config;
+
+ ret = devm_gpiochip_add_data(dev, chip, priv);
+ if (ret)
+ return ret;
+
+ chip->ngpio = sum_up_ngpios(chip);
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static const struct of_device_id pin_control_gpio_match[] = {
+ { .compatible = "pin-control-gpio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pin_control_gpio_match);
+
+static struct platform_driver pin_control_gpio_driver = {
+ .probe = pin_control_gpio_probe,
+ .driver = {
+ .name = "pin-control-gpio",
+ .of_match_table = pin_control_gpio_match,
+ },
+};
+module_platform_driver(pin_control_gpio_driver);
+
+MODULE_AUTHOR("AKASHI Takahiro <takahiro.akashi@linaro.org>");
+MODULE_DESCRIPTION("Pinctrl based GPIO driver");
+MODULE_LICENSE("GPL");