gpio: modepin: Add driver support for modepin GPIO controller
authorPiyush Mehta <piyush.mehta@xilinx.com>
Wed, 22 Sep 2021 13:53:19 +0000 (19:23 +0530)
committerBartosz Golaszewski <brgl@bgdev.pl>
Thu, 23 Sep 2021 08:09:58 +0000 (10:09 +0200)
This patch adds driver support for the zynqmp modepin GPIO controller.
GPIO modepin driver set and get the value and status of the PS_MODE pin,
based on device-tree pin configuration. These four mode pins are
configurable as input/output. The mode pin has a control register, which
have lower four-bits [0:3] are configurable as input/output, next four-bits
can be used for reading the data  as input[4:7], and next setting the
output pin state output[8:11]. By default value of mode pin register is 0.

Signed-off-by: Piyush Mehta <piyush.mehta@xilinx.com>
Acked-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-zynqmp-modepin.c [new file with mode: 0644]

index fae5141251e5da9ea5dcb5bc0bb882aa403512eb..37a6f77c86fe3a9ce0439a7811b3c58f505b5b0d 100644 (file)
@@ -763,6 +763,18 @@ config GPIO_ZYNQ
        help
          Say yes here to support Xilinx Zynq GPIO controller.
 
+config GPIO_ZYNQMP_MODEPIN
+       tristate "ZynqMP ps-mode pin gpio configuration driver"
+       depends on ZYNQMP_FIRMWARE
+       default ZYNQMP_FIRMWARE
+       help
+         Say yes here to support the ZynqMP ps-mode pin gpio configuration
+         driver.
+
+         This ps-mode pin gpio driver is based on GPIO framework, PS_MODE
+         is 4-bits boot mode pins. It sets and gets the status of
+         the ps-mode pin. Every pin can be configured as input/output.
+
 config GPIO_LOONGSON1
        tristate "Loongson1 GPIO support"
        depends on MACH_LOONGSON32
index fbcda637d5e1e7636aa21efc676932d12ba41f82..71ee9fc2ff83762dc9d8ebf900cf8f16eb7b29c4 100644 (file)
@@ -184,3 +184,4 @@ obj-$(CONFIG_GPIO_XRA1403)          += gpio-xra1403.o
 obj-$(CONFIG_GPIO_XTENSA)              += gpio-xtensa.o
 obj-$(CONFIG_GPIO_ZEVIO)               += gpio-zevio.o
 obj-$(CONFIG_GPIO_ZYNQ)                        += gpio-zynq.o
+obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN)      += gpio-zynqmp-modepin.o
diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c
new file mode 100644 (file)
index 0000000..a0d6938
--- /dev/null
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the ps-mode pin configuration.
+ *
+ * Copyright (c) 2021 Xilinx, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+
+/* 4-bit boot mode pins */
+#define MODE_PINS                      4
+
+/**
+ * modepin_gpio_get_value - Get the state of the specified pin of GPIO device
+ * @chip:      gpio_chip instance to be worked on
+ * @pin:       gpio pin number within the device
+ *
+ * This function reads the state of the specified pin of the GPIO device.
+ *
+ * Return: 0 if the pin is low, 1 if pin is high, -EINVAL wrong pin configured
+ *         or error value.
+ */
+static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
+{
+       u32 regval = 0;
+       int ret;
+
+       ret = zynqmp_pm_bootmode_read(&regval);
+       if (ret)
+               return ret;
+
+       /* When [0:3] corresponding bit is set, then read output bit [8:11],
+        * if the bit is clear then read input bit [4:7] for status or value.
+        */
+       if (regval & BIT(pin))
+               return !!(regval & BIT(pin + 8));
+       else
+               return !!(regval & BIT(pin + 4));
+}
+
+/**
+ * modepin_gpio_set_value - Modify the state of the pin with specified value
+ * @chip:      gpio_chip instance to be worked on
+ * @pin:       gpio pin number within the device
+ * @state:     value used to modify the state of the specified pin
+ *
+ * This function reads the state of the specified pin of the GPIO device, mask
+ * with the capture state of GPIO pin, and update pin of GPIO device.
+ *
+ * Return:     None.
+ */
+static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
+                                  int state)
+{
+       u32 bootpin_val = 0;
+       int ret;
+
+       zynqmp_pm_bootmode_read(&bootpin_val);
+
+       /* Configure pin as an output by set bit [0:3] */
+       bootpin_val |= BIT(pin);
+
+       if (state)
+               bootpin_val |= BIT(pin + 8);
+       else
+               bootpin_val &= ~BIT(pin + 8);
+
+       /* Configure bootpin value */
+       ret = zynqmp_pm_bootmode_write(bootpin_val);
+       if (ret)
+               pr_err("modepin: set value error %d for pin %d\n", ret, pin);
+}
+
+/**
+ * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input
+ * @chip:      gpio_chip instance to be worked on
+ * @pin:       gpio pin number within the device
+ *
+ * Return: 0 always
+ */
+static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
+{
+       return 0;
+}
+
+/**
+ * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output
+ * @chip:      gpio_chip instance to be worked on
+ * @pin:       gpio pin number within the device
+ * @state:     value to be written to specified pin
+ *
+ * Return: 0 always
+ */
+static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
+                               int state)
+{
+       return 0;
+}
+
+/**
+ * modepin_gpio_probe - Initialization method for modepin_gpio
+ * @pdev:              platform device instance
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+static int modepin_gpio_probe(struct platform_device *pdev)
+{
+       struct gpio_chip *chip;
+       int status;
+
+       chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, chip);
+
+       /* configure the gpio chip */
+       chip->base = -1;
+       chip->ngpio = MODE_PINS;
+       chip->owner = THIS_MODULE;
+       chip->parent = &pdev->dev;
+       chip->get = modepin_gpio_get_value;
+       chip->set = modepin_gpio_set_value;
+       chip->direction_input = modepin_gpio_dir_in;
+       chip->direction_output = modepin_gpio_dir_out;
+       chip->label = dev_name(&pdev->dev);
+
+       /* modepin gpio registration */
+       status = devm_gpiochip_add_data(&pdev->dev, chip, chip);
+       if (status)
+               return dev_err_probe(&pdev->dev, status,
+                             "Failed to add GPIO chip\n");
+
+       return status;
+}
+
+static const struct of_device_id modepin_platform_id[] = {
+       { .compatible = "xlnx,zynqmp-gpio-modepin", },
+       { }
+};
+
+static struct platform_driver modepin_platform_driver = {
+       .driver = {
+               .name = "modepin-gpio",
+               .of_match_table = modepin_platform_id,
+       },
+       .probe = modepin_gpio_probe,
+};
+
+module_platform_driver(modepin_platform_driver);
+
+MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xilinx.com>");
+MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration");
+MODULE_LICENSE("GPL v2");