diff options
Diffstat (limited to 'drivers/gpio/gpio-vf610.c')
-rw-r--r-- | drivers/gpio/gpio-vf610.c | 50 |
1 files changed, 49 insertions, 1 deletions
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 3edb09cb9ee0..0e3771f89040 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -16,6 +16,7 @@ */ #include <linux/bitops.h> +#include <linux/clk.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/init.h> @@ -43,6 +44,7 @@ struct vf610_gpio_port { #define GPIO_PCOR 0x08 #define GPIO_PTOR 0x0c #define GPIO_PDIR 0x10 +#define GPIO_PDDR 0x14 #define PORT_PCR(n) ((n) * 0x4) #define PORT_PCR_IRQC_OFFSET 16 @@ -79,8 +81,16 @@ static inline u32 vf610_gpio_readl(void __iomem *reg) static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio) { struct vf610_gpio_port *port = gpiochip_get_data(gc); + unsigned long mask = BIT(gpio); + + mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR); - return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) & BIT(gpio)); + if (mask) + return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDOR) + & BIT(gpio)); + else + return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) + & BIT(gpio)); } static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) @@ -96,12 +106,24 @@ static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { + struct vf610_gpio_port *port = gpiochip_get_data(chip); + unsigned long mask = BIT(gpio); + u32 val; + + val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR); + val &= ~mask; + vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR); return pinctrl_gpio_direction_input(chip->base + gpio); } static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) { + struct vf610_gpio_port *port = gpiochip_get_data(chip); + unsigned long mask = BIT(gpio); + + vf610_gpio_writel(mask, port->gpio_base + GPIO_PDDR); + vf610_gpio_set(chip, gpio, value); return pinctrl_gpio_direction_output(chip->base + gpio); @@ -218,6 +240,7 @@ static int vf610_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; + struct clk *clk_port, *clk_gpio; struct vf610_gpio_port *port; struct resource *iores; struct gpio_chip *gc; @@ -241,6 +264,23 @@ static int vf610_gpio_probe(struct platform_device *pdev) if (port->irq < 0) return port->irq; + clk_port = devm_clk_get(&pdev->dev, "port"); + clk_gpio = devm_clk_get(&pdev->dev, "gpio"); + if (PTR_ERR(clk_port) == -EPROBE_DEFER || + PTR_ERR(clk_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (!IS_ERR(clk_port) && !IS_ERR(clk_gpio)) { + ret = clk_prepare_enable(clk_port); + if (ret) + return ret; + ret = clk_prepare_enable(clk_gpio); + if (ret) { + clk_disable_unprepare(clk_port); + return ret; + } + } + gc = &port->gc; gc->of_node = np; gc->parent = dev; @@ -262,6 +302,14 @@ static int vf610_gpio_probe(struct platform_device *pdev) /* Clear the interrupt status register for all GPIO's */ vf610_gpio_writel(~0, port->base + PORT_ISFR); + /* + * At imx7ulp, any interrupts can wake system up from "standby" mode, + * so, mask interrupt at suspend mode by default, and the user + * can still enable wakeup through /sys entry. + */ + if (of_machine_is_compatible("fsl,imx7ulp")) + vf610_gpio_irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; + ret = gpiochip_irqchip_add(gc, &vf610_gpio_irq_chip, 0, handle_edge_irq, IRQ_TYPE_NONE); if (ret) { |