summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-zynq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-zynq.c')
-rw-r--r--drivers/gpio/gpio-zynq.c167
1 files changed, 110 insertions, 57 deletions
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 05ba16fffdad..97780c57ab56 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -10,6 +10,7 @@
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -21,6 +22,9 @@
/* Maximum banks */
#define ZYNQ_GPIO_MAX_BANK 4
#define ZYNQMP_GPIO_MAX_BANK 6
+#define VERSAL_GPIO_MAX_BANK 4
+#define PMC_GPIO_MAX_BANK 5
+#define VERSAL_UNUSED_BANKS 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
@@ -95,6 +99,7 @@
/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */
#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0)
#define GPIO_QUIRK_DATA_RO_BUG BIT(1)
+#define GPIO_QUIRK_VERSAL BIT(2)
struct gpio_regs {
u32 datamsw[ZYNQMP_GPIO_MAX_BANK];
@@ -116,6 +121,7 @@ struct gpio_regs {
* @irq: interrupt for the GPIO device
* @p_data: pointer to platform data
* @context: context registers
+ * @dirlock: lock used for direction in/out synchronization
*/
struct zynq_gpio {
struct gpio_chip chip;
@@ -124,6 +130,7 @@ struct zynq_gpio {
int irq;
const struct zynq_platform_data *p_data;
struct gpio_regs context;
+ spinlock_t dirlock; /* lock */
};
/**
@@ -144,8 +151,8 @@ struct zynq_platform_data {
int bank_max[ZYNQMP_GPIO_MAX_BANK];
};
-static struct irq_chip zynq_gpio_level_irqchip;
-static struct irq_chip zynq_gpio_edge_irqchip;
+static const struct irq_chip zynq_gpio_level_irqchip;
+static const struct irq_chip zynq_gpio_edge_irqchip;
/**
* zynq_gpio_is_zynq - test if HW is zynq or zynqmp
@@ -196,6 +203,8 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
gpio->p_data->bank_min[bank];
return;
}
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank = bank + VERSAL_UNUSED_BANKS;
}
/* default */
@@ -256,8 +265,8 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
* upper 16 bits) based on the given pin number and sets the state of a
* gpio pin to the specified value. The state is either 0 or non-zero.
*/
-static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
- int state)
+static int zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
+ int state)
{
unsigned int reg_offset, bank_num, bank_pin_num;
struct zynq_gpio *gpio = gpiochip_get_data(chip);
@@ -281,6 +290,8 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
((state << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK);
writel_relaxed(state, gpio->base_addr + reg_offset);
+
+ return 0;
}
/**
@@ -297,6 +308,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
{
u32 reg;
unsigned int bank_num, bank_pin_num;
+ unsigned long flags;
struct zynq_gpio *gpio = gpiochip_get_data(chip);
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
@@ -310,9 +322,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
return -EINVAL;
/* clear the bit in direction mode reg to set the pin as input */
+ spin_lock_irqsave(&gpio->dirlock, flags);
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg &= ~BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+ spin_unlock_irqrestore(&gpio->dirlock, flags);
return 0;
}
@@ -334,11 +348,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
{
u32 reg;
unsigned int bank_num, bank_pin_num;
+ unsigned long flags;
struct zynq_gpio *gpio = gpiochip_get_data(chip);
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
/* set the GPIO pin as output */
+ spin_lock_irqsave(&gpio->dirlock, flags);
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg |= BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
@@ -347,6 +363,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
reg |= BIT(bank_pin_num);
writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
+ spin_unlock_irqrestore(&gpio->dirlock, flags);
/* set the state of the pin */
zynq_gpio_set_value(chip, pin, state);
@@ -389,9 +406,12 @@ static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
static void zynq_gpio_irq_mask(struct irq_data *irq_data)
{
unsigned int device_pin_num, bank_num, bank_pin_num;
+ const unsigned long offset = irqd_to_hwirq(irq_data);
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(irq_data);
struct zynq_gpio *gpio =
gpiochip_get_data(irq_data_get_irq_chip_data(irq_data));
+ gpiochip_disable_irq(chip, offset);
device_pin_num = irq_data->hwirq;
zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
writel_relaxed(BIT(bank_pin_num),
@@ -410,9 +430,12 @@ static void zynq_gpio_irq_mask(struct irq_data *irq_data)
static void zynq_gpio_irq_unmask(struct irq_data *irq_data)
{
unsigned int device_pin_num, bank_num, bank_pin_num;
+ const unsigned long offset = irqd_to_hwirq(irq_data);
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(irq_data);
struct zynq_gpio *gpio =
gpiochip_get_data(irq_data_get_irq_chip_data(irq_data));
+ gpiochip_enable_irq(chip, offset);
device_pin_num = irq_data->hwirq;
zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
writel_relaxed(BIT(bank_pin_num),
@@ -559,7 +582,7 @@ static int zynq_gpio_irq_reqres(struct irq_data *d)
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
int ret;
- ret = pm_runtime_get_sync(chip->parent);
+ ret = pm_runtime_resume_and_get(chip->parent);
if (ret < 0)
return ret;
@@ -575,7 +598,7 @@ static void zynq_gpio_irq_relres(struct irq_data *d)
}
/* irq chip descriptor */
-static struct irq_chip zynq_gpio_level_irqchip = {
+static const struct irq_chip zynq_gpio_level_irqchip = {
.name = DRIVER_NAME,
.irq_enable = zynq_gpio_irq_enable,
.irq_eoi = zynq_gpio_irq_ack,
@@ -586,10 +609,10 @@ static struct irq_chip zynq_gpio_level_irqchip = {
.irq_request_resources = zynq_gpio_irq_reqres,
.irq_release_resources = zynq_gpio_irq_relres,
.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED |
- IRQCHIP_MASK_ON_SUSPEND,
+ IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
};
-static struct irq_chip zynq_gpio_edge_irqchip = {
+static const struct irq_chip zynq_gpio_edge_irqchip = {
.name = DRIVER_NAME,
.irq_enable = zynq_gpio_irq_enable,
.irq_ack = zynq_gpio_irq_ack,
@@ -599,7 +622,7 @@ static struct irq_chip zynq_gpio_edge_irqchip = {
.irq_set_wake = zynq_gpio_set_wake,
.irq_request_resources = zynq_gpio_irq_reqres,
.irq_release_resources = zynq_gpio_irq_relres,
- .flags = IRQCHIP_MASK_ON_SUSPEND,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
};
static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
@@ -613,12 +636,8 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
if (!pending)
return;
- for_each_set_bit(offset, &pending, 32) {
- unsigned int gpio_irq;
-
- gpio_irq = irq_find_mapping(irqdomain, offset + bank_offset);
- generic_handle_irq(gpio_irq);
- }
+ for_each_set_bit(offset, &pending, 32)
+ generic_handle_domain_irq(irqdomain, offset + bank_offset);
}
/**
@@ -647,6 +666,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc)
int_enb = readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTMASK_OFFSET(bank_num));
zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb);
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
}
chained_irq_exit(irqchip, desc);
@@ -676,6 +697,8 @@ static void zynq_gpio_save_context(struct zynq_gpio *gpio)
gpio->context.int_any[bank_num] =
readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTANY_OFFSET(bank_num));
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
}
}
@@ -707,14 +730,24 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
writel_relaxed(~(gpio->context.int_en[bank_num]),
gpio->base_addr +
ZYNQ_GPIO_INTEN_OFFSET(bank_num));
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
}
}
-static int __maybe_unused zynq_gpio_suspend(struct device *dev)
+static int zynq_gpio_suspend(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
+ if (!data) {
+ dev_err(dev, "irq_get_irq_data() failed\n");
+ return -EINVAL;
+ }
+
+ if (!device_may_wakeup(dev))
+ disable_irq(gpio->irq);
+
if (!irqd_is_wakeup_set(data)) {
zynq_gpio_save_context(gpio);
return pm_runtime_force_suspend(dev);
@@ -723,12 +756,20 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused zynq_gpio_resume(struct device *dev)
+static int zynq_gpio_resume(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
int ret;
+ if (!data) {
+ dev_err(dev, "irq_get_irq_data() failed\n");
+ return -EINVAL;
+ }
+
+ if (!device_may_wakeup(dev))
+ enable_irq(gpio->irq);
+
if (!irqd_is_wakeup_set(data)) {
ret = pm_runtime_force_resume(dev);
zynq_gpio_restore_context(gpio);
@@ -738,7 +779,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
return 0;
}
-static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
+static int zynq_gpio_runtime_suspend(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
@@ -747,7 +788,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev)
+static int zynq_gpio_runtime_resume(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
@@ -773,9 +814,33 @@ static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset)
}
static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume)
- SET_RUNTIME_PM_OPS(zynq_gpio_runtime_suspend,
- zynq_gpio_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume)
+ RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
+};
+
+static const struct zynq_platform_data versal_gpio_def = {
+ .label = "versal_gpio",
+ .quirks = GPIO_QUIRK_VERSAL,
+ .ngpio = 58,
+ .max_bank = VERSAL_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
+ .bank_min[3] = 26,
+ .bank_max[3] = 57, /* Bank 3 is connected to FMIOs (32 pins) */
+};
+
+static const struct zynq_platform_data pmc_gpio_def = {
+ .label = "pmc_gpio",
+ .ngpio = 116,
+ .max_bank = PMC_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
+ .bank_min[1] = 26,
+ .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */
+ .bank_min[3] = 52,
+ .bank_max[3] = 83, /* Bank 3 is connected to EMIOs (32 pins) */
+ .bank_min[4] = 84,
+ .bank_max[4] = 115, /* Bank 4 is connected to EMIOs (32 pins) */
};
static const struct zynq_platform_data zynqmp_gpio_def = {
@@ -815,6 +880,8 @@ static const struct zynq_platform_data zynq_gpio_def = {
static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def },
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
+ { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def },
+ { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
@@ -874,31 +941,29 @@ static int zynq_gpio_probe(struct platform_device *pdev)
chip->ngpio = gpio->p_data->ngpio;
/* Retrieve GPIO clock */
- gpio->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(gpio->clk)) {
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(gpio->clk);
- }
- ret = clk_prepare_enable(gpio->clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to enable clock.\n");
- return ret;
- }
+ gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(gpio->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(gpio->clk), "input clock not found.\n");
+
+ spin_lock_init(&gpio->dirlock);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- ret = pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
goto err_pm_dis;
/* disable interrupts for all banks */
- for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++)
+ for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) {
writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
+ if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL)
+ bank_num = bank_num + VERSAL_UNUSED_BANKS;
+ }
/* Set up the GPIO irqchip */
girq = &chip->irq;
- girq->chip = &zynq_gpio_edge_irqchip;
+ gpio_irq_chip_set_chip(girq, &zynq_gpio_edge_irqchip);
girq->parent_handler = zynq_gpio_irqhandler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(&pdev->dev, 1,
@@ -919,6 +984,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
goto err_pm_put;
}
+ irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY);
+ device_init_wakeup(&pdev->dev, 1);
pm_runtime_put(&pdev->dev);
return 0;
@@ -927,7 +994,6 @@ err_pm_put:
pm_runtime_put(&pdev->dev);
err_pm_dis:
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(gpio->clk);
return ret;
}
@@ -938,44 +1004,31 @@ err_pm_dis:
*
* Return: 0 always
*/
-static int zynq_gpio_remove(struct platform_device *pdev)
+static void zynq_gpio_remove(struct platform_device *pdev)
{
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+ int ret;
- pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "pm_runtime_get_sync() Failed\n");
+ device_init_wakeup(&pdev->dev, 0);
gpiochip_remove(&gpio->chip);
- clk_disable_unprepare(gpio->clk);
device_set_wakeup_capable(&pdev->dev, 0);
pm_runtime_disable(&pdev->dev);
- return 0;
}
static struct platform_driver zynq_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
- .pm = &zynq_gpio_dev_pm_ops,
+ .pm = pm_ptr(&zynq_gpio_dev_pm_ops),
.of_match_table = zynq_gpio_of_match,
},
.probe = zynq_gpio_probe,
.remove = zynq_gpio_remove,
};
-/**
- * zynq_gpio_init - Initial driver registration call
- *
- * Return: value from platform_driver_register
- */
-static int __init zynq_gpio_init(void)
-{
- return platform_driver_register(&zynq_gpio_driver);
-}
-postcore_initcall(zynq_gpio_init);
-
-static void __exit zynq_gpio_exit(void)
-{
- platform_driver_unregister(&zynq_gpio_driver);
-}
-module_exit(zynq_gpio_exit);
+module_platform_driver(zynq_gpio_driver);
MODULE_AUTHOR("Xilinx Inc.");
MODULE_DESCRIPTION("Zynq GPIO driver");