summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-vf610.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-vf610.c')
-rw-r--r--drivers/gpio/gpio-vf610.c50
1 files changed, 49 insertions, 1 deletions
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index 1f599bc08237..74a2af8360ef 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;
@@ -242,6 +265,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;
@@ -267,6 +307,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) {