linux/drivers/irqchip/irq-gic-pm.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 NVIDIA CORPORATION, All Rights Reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  15 */
  16#include <linux/module.h>
  17#include <linux/clk.h>
  18#include <linux/of_device.h>
  19#include <linux/of_irq.h>
  20#include <linux/irqchip/arm-gic.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm_clock.h>
  23#include <linux/pm_runtime.h>
  24#include <linux/slab.h>
  25
  26struct gic_clk_data {
  27        unsigned int num_clocks;
  28        const char *const *clocks;
  29};
  30
  31static int gic_runtime_resume(struct device *dev)
  32{
  33        struct gic_chip_data *gic = dev_get_drvdata(dev);
  34        int ret;
  35
  36        ret = pm_clk_resume(dev);
  37        if (ret)
  38                return ret;
  39
  40        /*
  41         * On the very first resume, the pointer to the driver data
  42         * will be NULL and this is intentional, because we do not
  43         * want to restore the GIC on the very first resume. So if
  44         * the pointer is not valid just return.
  45         */
  46        if (!gic)
  47                return 0;
  48
  49        gic_dist_restore(gic);
  50        gic_cpu_restore(gic);
  51
  52        return 0;
  53}
  54
  55static int gic_runtime_suspend(struct device *dev)
  56{
  57        struct gic_chip_data *gic = dev_get_drvdata(dev);
  58
  59        gic_dist_save(gic);
  60        gic_cpu_save(gic);
  61
  62        return pm_clk_suspend(dev);
  63}
  64
  65static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
  66{
  67        unsigned int i;
  68        int ret;
  69
  70        if (!dev || !data)
  71                return -EINVAL;
  72
  73        ret = pm_clk_create(dev);
  74        if (ret)
  75                return ret;
  76
  77        for (i = 0; i < data->num_clocks; i++) {
  78                ret = of_pm_clk_add_clk(dev, data->clocks[i]);
  79                if (ret) {
  80                        dev_err(dev, "failed to add clock %s\n",
  81                                data->clocks[i]);
  82                        pm_clk_destroy(dev);
  83                        return ret;
  84                }
  85        }
  86
  87        return 0;
  88}
  89
  90static int gic_probe(struct platform_device *pdev)
  91{
  92        struct device *dev = &pdev->dev;
  93        const struct gic_clk_data *data;
  94        struct gic_chip_data *gic;
  95        int ret, irq;
  96
  97        data = of_device_get_match_data(&pdev->dev);
  98        if (!data) {
  99                dev_err(&pdev->dev, "no device match found\n");
 100                return -ENODEV;
 101        }
 102
 103        irq = irq_of_parse_and_map(dev->of_node, 0);
 104        if (!irq) {
 105                dev_err(dev, "no parent interrupt found!\n");
 106                return -EINVAL;
 107        }
 108
 109        ret = gic_get_clocks(dev, data);
 110        if (ret)
 111                goto irq_dispose;
 112
 113        pm_runtime_enable(dev);
 114
 115        ret = pm_runtime_get_sync(dev);
 116        if (ret < 0)
 117                goto rpm_disable;
 118
 119        ret = gic_of_init_child(dev, &gic, irq);
 120        if (ret)
 121                goto rpm_put;
 122
 123        platform_set_drvdata(pdev, gic);
 124
 125        pm_runtime_put(dev);
 126
 127        dev_info(dev, "GIC IRQ controller registered\n");
 128
 129        return 0;
 130
 131rpm_put:
 132        pm_runtime_put_sync(dev);
 133rpm_disable:
 134        pm_runtime_disable(dev);
 135        pm_clk_destroy(dev);
 136irq_dispose:
 137        irq_dispose_mapping(irq);
 138
 139        return ret;
 140}
 141
 142static const struct dev_pm_ops gic_pm_ops = {
 143        SET_RUNTIME_PM_OPS(gic_runtime_suspend,
 144                           gic_runtime_resume, NULL)
 145};
 146
 147static const char * const gic400_clocks[] = {
 148        "clk",
 149};
 150
 151static const struct gic_clk_data gic400_data = {
 152        .num_clocks = ARRAY_SIZE(gic400_clocks),
 153        .clocks = gic400_clocks,
 154};
 155
 156static const struct of_device_id gic_match[] = {
 157        { .compatible = "nvidia,tegra210-agic", .data = &gic400_data },
 158        {},
 159};
 160MODULE_DEVICE_TABLE(of, gic_match);
 161
 162static struct platform_driver gic_driver = {
 163        .probe          = gic_probe,
 164        .driver         = {
 165                .name   = "gic",
 166                .of_match_table = gic_match,
 167                .pm     = &gic_pm_ops,
 168        }
 169};
 170
 171builtin_platform_driver(gic_driver);
 172