diff options
Diffstat (limited to 'drivers/gpio/gpio-zynq.c')
-rw-r--r-- | drivers/gpio/gpio-zynq.c | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 211728c8ede2..2cdee9dd2381 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,8 @@ /* Maximum banks */ #define ZYNQ_GPIO_MAX_BANK 4 #define ZYNQMP_GPIO_MAX_BANK 6 +#define VERSAL_GPIO_MAX_BANK 4 +#define VERSAL_UNUSED_BANKS 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -95,6 +98,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 +120,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 +129,7 @@ struct zynq_gpio { int irq; const struct zynq_platform_data *p_data; struct gpio_regs context; + spinlock_t dirlock; }; /** @@ -196,6 +202,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 */ @@ -297,6 +305,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 +319,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 +345,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 +360,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); @@ -644,6 +658,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); @@ -673,6 +689,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; } } @@ -704,6 +722,8 @@ 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; } } @@ -712,6 +732,9 @@ static int __maybe_unused 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 (!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); @@ -726,6 +749,9 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev) struct irq_data *data = irq_get_irq_data(gpio->irq); int ret; + 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); @@ -775,6 +801,17 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { 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 zynqmp_gpio_def = { .label = "zynqmp_gpio", .quirks = GPIO_QUIRK_DATA_RO_BUG, @@ -812,6 +849,7 @@ 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 }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); @@ -883,6 +921,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) return ret; } + spin_lock_init(&gpio->dirlock); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -897,9 +937,12 @@ static int zynq_gpio_probe(struct platform_device *pdev) } /* 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; + } ret = gpiochip_irqchip_add(chip, &zynq_gpio_edge_irqchip, 0, handle_level_irq, IRQ_TYPE_NONE); @@ -911,6 +954,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq, zynq_gpio_irqhandler); + irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY); + device_init_wakeup(&pdev->dev, 1); pm_runtime_put(&pdev->dev); return 0; @@ -963,7 +1008,7 @@ static int __init zynq_gpio_init(void) { return platform_driver_register(&zynq_gpio_driver); } -postcore_initcall(zynq_gpio_init); +subsys_initcall(zynq_gpio_init); static void __exit zynq_gpio_exit(void) { |