diff options
Diffstat (limited to 'drivers/gpio/gpio-zynq.c')
| -rw-r--r-- | drivers/gpio/gpio-zynq.c | 442 |
1 files changed, 325 insertions, 117 deletions
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index df0851464006..97780c57ab56 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Xilinx Zynq GPIO device driver * * Copyright (C) 2009 - 2014 Xilinx, Inc. - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. */ #include <linux/bitops.h> @@ -14,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> @@ -25,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 @@ -60,13 +60,13 @@ #define ZYNQ_GPIO_BANK5_PIN_MAX(str) (ZYNQ_GPIO_BANK5_PIN_MIN(str) + \ ZYNQ##str##_GPIO_BANK5_NGPIO - 1) - /* Register offsets for the GPIO device */ /* LSW Mask & Data -WO */ #define ZYNQ_GPIO_DATA_LSW_OFFSET(BANK) (0x000 + (8 * BANK)) /* MSW Mask & Data -WO */ #define ZYNQ_GPIO_DATA_MSW_OFFSET(BANK) (0x004 + (8 * BANK)) /* Data Register-RW */ +#define ZYNQ_GPIO_DATA_OFFSET(BANK) (0x040 + (4 * BANK)) #define ZYNQ_GPIO_DATA_RO_OFFSET(BANK) (0x060 + (4 * BANK)) /* Direction mode reg-RW */ #define ZYNQ_GPIO_DIRM_OFFSET(BANK) (0x204 + (0x40 * BANK)) @@ -98,6 +98,20 @@ /* 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]; + u32 datalsw[ZYNQMP_GPIO_MAX_BANK]; + u32 dirm[ZYNQMP_GPIO_MAX_BANK]; + u32 outen[ZYNQMP_GPIO_MAX_BANK]; + u32 int_en[ZYNQMP_GPIO_MAX_BANK]; + u32 int_dis[ZYNQMP_GPIO_MAX_BANK]; + u32 int_type[ZYNQMP_GPIO_MAX_BANK]; + u32 int_polarity[ZYNQMP_GPIO_MAX_BANK]; + u32 int_any[ZYNQMP_GPIO_MAX_BANK]; +}; /** * struct zynq_gpio - gpio device private data structure @@ -106,6 +120,8 @@ * @clk: clock resource for this controller * @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; @@ -113,16 +129,19 @@ struct zynq_gpio { struct clk *clk; int irq; const struct zynq_platform_data *p_data; + struct gpio_regs context; + spinlock_t dirlock; /* lock */ }; /** * struct zynq_platform_data - zynq gpio platform data structure * @label: string to store in gpio->label + * @quirks: Flags is used to identify the platform * @ngpio: max number of gpio pins * @max_bank: maximum number of gpio banks * @bank_min: this array represents bank's min pin * @bank_max: this array represents bank's max pin -*/ + */ struct zynq_platform_data { const char *label; u32 quirks; @@ -132,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 @@ -147,6 +166,17 @@ static int zynq_gpio_is_zynq(struct zynq_gpio *gpio) } /** + * gpio_data_ro_bug - test if HW bug exists or not + * @gpio: Pointer to driver data struct + * + * Return: 0 if bug doesnot exist, 1 if bug exists. + */ +static int gpio_data_ro_bug(struct zynq_gpio *gpio) +{ + return !!(gpio->p_data->quirks & GPIO_QUIRK_DATA_RO_BUG); +} + +/** * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank * for a given pin in the GPIO device * @pin_num: gpio pin number within the device @@ -154,6 +184,7 @@ static int zynq_gpio_is_zynq(struct zynq_gpio *gpio) * pin * @bank_pin_num: an output parameter used to return pin number within a bank * for the given gpio pin + * @gpio: gpio device data structure * * Returns the bank number and pin offset within the bank. */ @@ -166,12 +197,14 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num, for (bank = 0; bank < gpio->p_data->max_bank; bank++) { if ((pin_num >= gpio->p_data->bank_min[bank]) && - (pin_num <= gpio->p_data->bank_max[bank])) { - *bank_num = bank; - *bank_pin_num = pin_num - - gpio->p_data->bank_min[bank]; - return; + (pin_num <= gpio->p_data->bank_max[bank])) { + *bank_num = bank; + *bank_pin_num = pin_num - + gpio->p_data->bank_min[bank]; + return; } + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank = bank + VERSAL_UNUSED_BANKS; } /* default */ @@ -197,9 +230,28 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin) zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); - data = readl_relaxed(gpio->base_addr + - ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); - + if (gpio_data_ro_bug(gpio)) { + if (zynq_gpio_is_zynq(gpio)) { + if (bank_num <= 1) { + data = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + } else { + data = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_OFFSET(bank_num)); + } + } else { + if (bank_num <= 2) { + data = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + } else { + data = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_OFFSET(bank_num)); + } + } + } else { + data = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + } return (data >> bank_pin_num) & 1; } @@ -213,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); @@ -238,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; } /** @@ -254,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); @@ -263,13 +318,15 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) * as inputs. */ if (zynq_gpio_is_zynq(gpio) && bank_num == 0 && - (bank_pin_num == 7 || bank_pin_num == 8)) + (bank_pin_num == 7 || bank_pin_num == 8)) 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; } @@ -291,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)); @@ -304,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); @@ -311,6 +371,31 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, } /** + * zynq_gpio_get_direction - Read the direction of the specified GPIO pin + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function returns the direction of the specified GPIO. + * + * Return: GPIO_LINE_DIRECTION_OUT or GPIO_LINE_DIRECTION_IN + */ +static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = gpiochip_get_data(chip); + + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); + + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + if (reg & BIT(bank_pin_num)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +/** * zynq_gpio_irq_mask - Disable the interrupts for a gpio pin * @irq_data: per irq and chip data passed down to chip functions * @@ -321,9 +406,12 @@ static int zynq_gpio_dir_out(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), @@ -342,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), @@ -464,13 +555,14 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type) writel_relaxed(int_any, gpio->base_addr + ZYNQ_GPIO_INTANY_OFFSET(bank_num)); - if (type & IRQ_TYPE_LEVEL_MASK) { + if (type & IRQ_TYPE_LEVEL_MASK) irq_set_chip_handler_name_locked(irq_data, - &zynq_gpio_level_irqchip, handle_fasteoi_irq, NULL); - } else { + &zynq_gpio_level_irqchip, + handle_fasteoi_irq, NULL); + else irq_set_chip_handler_name_locked(irq_data, - &zynq_gpio_edge_irqchip, handle_level_irq, NULL); - } + &zynq_gpio_edge_irqchip, + handle_level_irq, NULL); return 0; } @@ -485,8 +577,28 @@ static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on) return 0; } +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_resume_and_get(chip->parent); + if (ret < 0) + return ret; + + return gpiochip_reqres_irq(chip, d->hwirq); +} + +static void zynq_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + gpiochip_relres_irq(chip, d->hwirq); + pm_runtime_put(chip->parent); +} + /* 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, @@ -494,11 +606,13 @@ static struct irq_chip zynq_gpio_level_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .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, @@ -506,7 +620,9 @@ static struct irq_chip zynq_gpio_edge_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, }; static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, @@ -514,23 +630,18 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, unsigned long pending) { unsigned int bank_offset = gpio->p_data->bank_min[bank_num]; - struct irq_domain *irqdomain = gpio->chip.irqdomain; + struct irq_domain *irqdomain = gpio->chip.irq.domain; int offset; 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); } /** * zynq_gpio_irqhandler - IRQ handler for the gpio banks of a gpio device - * @irq: irq number of the gpio bank where interrupt has occurred * @desc: irq descriptor instance of the 'irq' * * This function reads the Interrupt Status Register of each bank to get the @@ -555,54 +666,136 @@ 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); } -static int __maybe_unused zynq_gpio_suspend(struct device *dev) +static void zynq_gpio_save_context(struct zynq_gpio *gpio) +{ + unsigned int bank_num; + + for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) { + gpio->context.datalsw[bank_num] = + readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num)); + gpio->context.datamsw[bank_num] = + readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_MSW_OFFSET(bank_num)); + gpio->context.dirm[bank_num] = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + gpio->context.int_en[bank_num] = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_INTMASK_OFFSET(bank_num)); + gpio->context.int_type[bank_num] = + readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_INTTYPE_OFFSET(bank_num)); + gpio->context.int_polarity[bank_num] = + readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_INTPOL_OFFSET(bank_num)); + 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; + } +} + +static void zynq_gpio_restore_context(struct zynq_gpio *gpio) +{ + unsigned int 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)); + writel_relaxed(gpio->context.datalsw[bank_num], + gpio->base_addr + + ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num)); + writel_relaxed(gpio->context.datamsw[bank_num], + gpio->base_addr + + ZYNQ_GPIO_DATA_MSW_OFFSET(bank_num)); + writel_relaxed(gpio->context.dirm[bank_num], + gpio->base_addr + + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + writel_relaxed(gpio->context.int_type[bank_num], + gpio->base_addr + + ZYNQ_GPIO_INTTYPE_OFFSET(bank_num)); + writel_relaxed(gpio->context.int_polarity[bank_num], + gpio->base_addr + + ZYNQ_GPIO_INTPOL_OFFSET(bank_num)); + writel_relaxed(gpio->context.int_any[bank_num], + gpio->base_addr + + ZYNQ_GPIO_INTANY_OFFSET(bank_num)); + 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 zynq_gpio_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - int irq = platform_get_irq(pdev, 0); - struct irq_data *data = irq_get_irq_data(irq); + 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)) + if (!irqd_is_wakeup_set(data)) { + zynq_gpio_save_context(gpio); return pm_runtime_force_suspend(dev); + } return 0; } -static int __maybe_unused zynq_gpio_resume(struct device *dev) +static int zynq_gpio_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - int irq = platform_get_irq(pdev, 0); - struct irq_data *data = irq_get_irq_data(irq); + struct zynq_gpio *gpio = dev_get_drvdata(dev); + struct irq_data *data = irq_get_irq_data(gpio->irq); + int ret; - if (!irqd_is_wakeup_set(data)) - return pm_runtime_force_resume(dev); + 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); + return ret; + } return 0; } -static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev) +static int zynq_gpio_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct zynq_gpio *gpio = platform_get_drvdata(pdev); + struct zynq_gpio *gpio = dev_get_drvdata(dev); clk_disable_unprepare(gpio->clk); return 0; } -static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev) +static int zynq_gpio_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct zynq_gpio *gpio = platform_get_drvdata(pdev); + struct zynq_gpio *gpio = dev_get_drvdata(dev); return clk_prepare_enable(gpio->clk); } -static int zynq_gpio_request(struct gpio_chip *chip, unsigned offset) +static int zynq_gpio_request(struct gpio_chip *chip, unsigned int offset) { int ret; @@ -615,19 +808,44 @@ static int zynq_gpio_request(struct gpio_chip *chip, unsigned offset) return ret < 0 ? ret : 0; } -static void zynq_gpio_free(struct gpio_chip *chip, unsigned offset) +static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset) { pm_runtime_put(chip->parent); } 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 = { .label = "zynqmp_gpio", + .quirks = GPIO_QUIRK_DATA_RO_BUG, .ngpio = ZYNQMP_GPIO_NR_GPIOS, .max_bank = ZYNQMP_GPIO_MAX_BANK, .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(MP), @@ -646,7 +864,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = { static const struct zynq_platform_data zynq_gpio_def = { .label = "zynq_gpio", - .quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ, + .quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ | GPIO_QUIRK_DATA_RO_BUG, .ngpio = ZYNQ_GPIO_NR_GPIOS, .max_bank = ZYNQ_GPIO_MAX_BANK, .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(), @@ -662,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); @@ -682,7 +902,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) int ret, bank_num; struct zynq_gpio *gpio; struct gpio_chip *chip; - struct resource *res; + struct gpio_irq_chip *girq; const struct of_device_id *match; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); @@ -697,16 +917,13 @@ static int zynq_gpio_probe(struct platform_device *pdev) gpio->p_data = match->data; platform_set_drvdata(pdev, gpio); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gpio->base_addr = devm_ioremap_resource(&pdev->dev, res); + gpio->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gpio->base_addr)) return PTR_ERR(gpio->base_addr); gpio->irq = platform_get_irq(pdev, 0); - if (gpio->irq < 0) { - dev_err(&pdev->dev, "invalid IRQ\n"); + if (gpio->irq < 0) return gpio->irq; - } /* configure the gpio chip */ chip = &gpio->chip; @@ -719,60 +936,64 @@ static int zynq_gpio_probe(struct platform_device *pdev) chip->free = zynq_gpio_free; chip->direction_input = zynq_gpio_dir_in; chip->direction_output = zynq_gpio_dir_out; - chip->base = -1; + chip->get_direction = zynq_gpio_get_direction; + chip->base = of_alias_get_id(pdev->dev.of_node, "gpio"); 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; - /* report a bug if gpio chip registration fails */ - ret = gpiochip_add_data(chip, gpio); - if (ret) { - dev_err(&pdev->dev, "Failed to add gpio chip\n"); - goto err_pm_put; - } - /* 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); - if (ret) { - dev_err(&pdev->dev, "Failed to add irq chip\n"); - goto err_rm_gpiochip; + /* Set up the GPIO irqchip */ + girq = &chip->irq; + 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, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) { + ret = -ENOMEM; + goto err_pm_put; } + girq->parents[0] = gpio->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; - gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq, - zynq_gpio_irqhandler); + /* report a bug if gpio chip registration fails */ + ret = gpiochip_add_data(chip, gpio); + if (ret) { + dev_err(&pdev->dev, "Failed to add gpio chip\n"); + 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; -err_rm_gpiochip: - gpiochip_remove(chip); err_pm_put: pm_runtime_put(&pdev->dev); err_pm_dis: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(gpio->clk); return ret; } @@ -783,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"); |
