|
@@ -33,6 +33,7 @@
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/gpio.h>
|
|
|
#include <linux/slab.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
|
|
|
/*
|
|
|
* Langwell chip has 64 pins and thus there are 2 32bit registers to control
|
|
@@ -63,6 +64,7 @@ struct lnw_gpio {
|
|
|
void *reg_base;
|
|
|
spinlock_t lock;
|
|
|
unsigned irq_base;
|
|
|
+ struct pci_dev *pdev;
|
|
|
};
|
|
|
|
|
|
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
|
|
@@ -104,11 +106,18 @@ static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
|
|
u32 value;
|
|
|
unsigned long flags;
|
|
|
|
|
|
+ if (lnw->pdev)
|
|
|
+ pm_runtime_get(&lnw->pdev->dev);
|
|
|
+
|
|
|
spin_lock_irqsave(&lnw->lock, flags);
|
|
|
value = readl(gpdr);
|
|
|
value &= ~BIT(offset % 32);
|
|
|
writel(value, gpdr);
|
|
|
spin_unlock_irqrestore(&lnw->lock, flags);
|
|
|
+
|
|
|
+ if (lnw->pdev)
|
|
|
+ pm_runtime_put(&lnw->pdev->dev);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -120,11 +129,19 @@ static int lnw_gpio_direction_output(struct gpio_chip *chip,
|
|
|
unsigned long flags;
|
|
|
|
|
|
lnw_gpio_set(chip, offset, value);
|
|
|
+
|
|
|
+ if (lnw->pdev)
|
|
|
+ pm_runtime_get(&lnw->pdev->dev);
|
|
|
+
|
|
|
spin_lock_irqsave(&lnw->lock, flags);
|
|
|
value = readl(gpdr);
|
|
|
value |= BIT(offset % 32);
|
|
|
writel(value, gpdr);
|
|
|
spin_unlock_irqrestore(&lnw->lock, flags);
|
|
|
+
|
|
|
+ if (lnw->pdev)
|
|
|
+ pm_runtime_put(&lnw->pdev->dev);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -145,6 +162,10 @@ static int lnw_irq_type(struct irq_data *d, unsigned type)
|
|
|
|
|
|
if (gpio >= lnw->chip.ngpio)
|
|
|
return -EINVAL;
|
|
|
+
|
|
|
+ if (lnw->pdev)
|
|
|
+ pm_runtime_get(&lnw->pdev->dev);
|
|
|
+
|
|
|
spin_lock_irqsave(&lnw->lock, flags);
|
|
|
if (type & IRQ_TYPE_EDGE_RISING)
|
|
|
value = readl(grer) | BIT(gpio % 32);
|
|
@@ -159,6 +180,9 @@ static int lnw_irq_type(struct irq_data *d, unsigned type)
|
|
|
writel(value, gfer);
|
|
|
spin_unlock_irqrestore(&lnw->lock, flags);
|
|
|
|
|
|
+ if (lnw->pdev)
|
|
|
+ pm_runtime_put(&lnw->pdev->dev);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -211,6 +235,39 @@ static void lnw_irq_handler(unsigned irq, struct irq_desc *desc)
|
|
|
chip->irq_eoi(data);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_PM
|
|
|
+static int lnw_gpio_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int lnw_gpio_runtime_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int lnw_gpio_runtime_idle(struct device *dev)
|
|
|
+{
|
|
|
+ int err = pm_schedule_suspend(dev, 500);
|
|
|
+
|
|
|
+ if (!err)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return -EBUSY;
|
|
|
+}
|
|
|
+
|
|
|
+#else
|
|
|
+#define lnw_gpio_runtime_suspend NULL
|
|
|
+#define lnw_gpio_runtime_resume NULL
|
|
|
+#define lnw_gpio_runtime_idle NULL
|
|
|
+#endif
|
|
|
+
|
|
|
+static const struct dev_pm_ops lnw_gpio_pm_ops = {
|
|
|
+ .runtime_suspend = lnw_gpio_runtime_suspend,
|
|
|
+ .runtime_resume = lnw_gpio_runtime_resume,
|
|
|
+ .runtime_idle = lnw_gpio_runtime_idle,
|
|
|
+};
|
|
|
+
|
|
|
static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
|
|
|
const struct pci_device_id *id)
|
|
|
{
|
|
@@ -270,6 +327,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
|
|
|
lnw->chip.base = gpio_base;
|
|
|
lnw->chip.ngpio = id->driver_data;
|
|
|
lnw->chip.can_sleep = 0;
|
|
|
+ lnw->pdev = pdev;
|
|
|
pci_set_drvdata(pdev, lnw);
|
|
|
retval = gpiochip_add(&lnw->chip);
|
|
|
if (retval) {
|
|
@@ -285,6 +343,10 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
|
|
|
}
|
|
|
|
|
|
spin_lock_init(&lnw->lock);
|
|
|
+
|
|
|
+ pm_runtime_put_noidle(&pdev->dev);
|
|
|
+ pm_runtime_allow(&pdev->dev);
|
|
|
+
|
|
|
goto done;
|
|
|
err5:
|
|
|
kfree(lnw);
|
|
@@ -302,6 +364,9 @@ static struct pci_driver lnw_gpio_driver = {
|
|
|
.name = "langwell_gpio",
|
|
|
.id_table = lnw_gpio_ids,
|
|
|
.probe = lnw_gpio_probe,
|
|
|
+ .driver = {
|
|
|
+ .pm = &lnw_gpio_pm_ops,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
|