Skip to content

Commit

Permalink
gpio: gpio-xilinx: Add support for suspend and resume
Browse files Browse the repository at this point in the history
Add support for suspend and resume, pm runtime suspend and resume.
Added free and request calls.

Signed-off-by: Srinivas Neeli <srinivas.neeli@xilinx.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
State: upstream (26b0477)
  • Loading branch information
Srinivas Neeli authored and Michal Simek committed Mar 22, 2021
1 parent b399d38 commit 8702721
Showing 1 changed file with 89 additions and 3 deletions.
92 changes: 89 additions & 3 deletions drivers/gpio/gpio-xilinx.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>

/* Register Offset Definitions */
Expand Down Expand Up @@ -278,6 +279,39 @@ static void xgpio_save_regs(struct xgpio_instance *chip)
chip->gpio_dir[1]);
}

static int xgpio_request(struct gpio_chip *chip, unsigned int offset)
{
int ret;

ret = pm_runtime_get_sync(chip->parent);
/*
* If the device is already active pm_runtime_get() will return 1 on
* success, but gpio_request still needs to return 0.
*/
return ret < 0 ? ret : 0;
}

static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
{
pm_runtime_put(chip->parent);
}

static int __maybe_unused xgpio_suspend(struct device *dev)
{
struct xgpio_instance *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 (!irqd_is_wakeup_set(data))
return pm_runtime_force_suspend(dev);

return 0;
}

/**
* xgpio_remove - Remove method for the GPIO device.
* @pdev: pointer to the platform device
Expand All @@ -290,6 +324,9 @@ static int xgpio_remove(struct platform_device *pdev)
{
struct xgpio_instance *gpio = platform_get_drvdata(pdev);

pm_runtime_get_sync(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(gpio->clk);

return 0;
Expand All @@ -305,6 +342,46 @@ static void xgpio_irq_ack(struct irq_data *irq_data)
{
}

static int __maybe_unused xgpio_resume(struct device *dev)
{
struct xgpio_instance *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 (!irqd_is_wakeup_set(data))
return pm_runtime_force_resume(dev);

return 0;
}

static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct xgpio_instance *gpio = platform_get_drvdata(pdev);

clk_disable(gpio->clk);

return 0;
}

static int __maybe_unused xgpio_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct xgpio_instance *gpio = platform_get_drvdata(pdev);

return clk_enable(gpio->clk);
}

static const struct dev_pm_ops xgpio_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
SET_RUNTIME_PM_OPS(xgpio_runtime_suspend,
xgpio_runtime_resume, NULL)
};

/**
* xgpio_irq_mask - Write the specified signal of the GPIO device.
* @irq_data: per IRQ and chip data passed down to chip functions
Expand Down Expand Up @@ -546,6 +623,8 @@ static int xgpio_probe(struct platform_device *pdev)
chip->gc.of_gpio_n_cells = cells;
chip->gc.get = xgpio_get;
chip->gc.set = xgpio_set;
chip->gc.request = xgpio_request;
chip->gc.free = xgpio_free;
chip->gc.set_multiple = xgpio_set_multiple;

chip->gc.label = dev_name(&pdev->dev);
Expand All @@ -565,6 +644,9 @@ static int xgpio_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to prepare clk\n");
return status;
}
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);

xgpio_save_regs(chip);

Expand Down Expand Up @@ -595,7 +677,7 @@ static int xgpio_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!girq->parents) {
status = -ENOMEM;
goto err_unprepare_clk;
goto err_pm_put;
}
girq->parents[0] = chip->irq;
girq->default_type = IRQ_TYPE_NONE;
Expand All @@ -605,12 +687,15 @@ static int xgpio_probe(struct platform_device *pdev)
status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip);
if (status) {
dev_err(&pdev->dev, "failed to add GPIO chip\n");
goto err_unprepare_clk;
goto err_pm_put;
}

pm_runtime_put(&pdev->dev);
return 0;

err_unprepare_clk:
err_pm_put:
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable_unprepare(chip->clk);
return status;
}
Expand All @@ -628,6 +713,7 @@ static struct platform_driver xgpio_plat_driver = {
.driver = {
.name = "gpio-xilinx",
.of_match_table = xgpio_of_match,
.pm = &xgpio_dev_pm_ops,
},
};

Expand Down

0 comments on commit 8702721

Please sign in to comment.