linux/drivers/irqchip/irq-gic-pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2016 NVIDIA CORPORATION, All Rights Reserved.
   4 */
   5#include <linux/module.h>
   6#include <linux/clk.h>
   7#include <linux/of_device.h>
   8#include <linux/of_irq.h>
   9#include <linux/irqchip/arm-gic.h>
  10#include <linux/platform_device.h>
  11#include <linux/pm_runtime.h>
  12#include <linux/slab.h>
  13
  14struct gic_clk_data {
  15        unsigned int num_clocks;
  16        const char *const *clocks;
  17};
  18
  19struct gic_chip_pm {
  20        struct gic_chip_data *chip_data;
  21        const struct gic_clk_data *clk_data;
  22        struct clk_bulk_data *clks;
  23};
  24
  25static int gic_runtime_resume(struct device *dev)
  26{
  27        struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
  28        struct gic_chip_data *gic = chip_pm->chip_data;
  29        const struct gic_clk_data *data = chip_pm->clk_data;
  30        int ret;
  31
  32        ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks);
  33        if (ret) {
  34                dev_err(dev, "clk_enable failed: %d\n", ret);
  35                return ret;
  36        }
  37
  38        /*
  39         * On the very first resume, the pointer to chip_pm->chip_data
  40         * will be NULL and this is intentional, because we do not
  41         * want to restore the GIC on the very first resume. So if
  42         * the pointer is not valid just return.
  43         */
  44        if (!gic)
  45                return 0;
  46
  47        gic_dist_restore(gic);
  48        gic_cpu_restore(gic);
  49
  50        return 0;
  51}
  52
  53static int gic_runtime_suspend(struct device *dev)
  54{
  55        struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
  56        struct gic_chip_data *gic = chip_pm->chip_data;
  57        const struct gic_clk_data *data = chip_pm->clk_data;
  58
  59        gic_dist_save(gic);
  60        gic_cpu_save(gic);
  61
  62        clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks);
  63
  64        return 0;
  65}
  66
  67static int gic_probe(struct platform_device *pdev)
  68{
  69        struct device *dev = &pdev->dev;
  70        const struct gic_clk_data *data;
  71        struct gic_chip_pm *chip_pm;
  72        int ret, irq, i;
  73
  74        data = of_device_get_match_data(&pdev->dev);
  75        if (!data) {
  76                dev_err(&pdev->dev, "no device match found\n");
  77                return -ENODEV;
  78        }
  79
  80        chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL);
  81        if (!chip_pm)
  82                return -ENOMEM;
  83
  84        irq = irq_of_parse_and_map(dev->of_node, 0);
  85        if (!irq) {
  86                dev_err(dev, "no parent interrupt found!\n");
  87                return -EINVAL;
  88        }
  89
  90        chip_pm->clks = devm_kcalloc(dev, data->num_clocks,
  91                                     sizeof(*chip_pm->clks), GFP_KERNEL);
  92        if (!chip_pm->clks)
  93                return -ENOMEM;
  94
  95        for (i = 0; i < data->num_clocks; i++)
  96                chip_pm->clks[i].id = data->clocks[i];
  97
  98        ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks);
  99        if (ret)
 100                goto irq_dispose;
 101
 102        chip_pm->clk_data = data;
 103        dev_set_drvdata(dev, chip_pm);
 104
 105        pm_runtime_enable(dev);
 106
 107        ret = pm_runtime_get_sync(dev);
 108        if (ret < 0)
 109                goto rpm_disable;
 110
 111        ret = gic_of_init_child(dev, &chip_pm->chip_data, irq);
 112        if (ret)
 113                goto rpm_put;
 114
 115        pm_runtime_put(dev);
 116
 117        dev_info(dev, "GIC IRQ controller registered\n");
 118
 119        return 0;
 120
 121rpm_put:
 122        pm_runtime_put_sync(dev);
 123rpm_disable:
 124        pm_runtime_disable(dev);
 125irq_dispose:
 126        irq_dispose_mapping(irq);
 127
 128        return ret;
 129}
 130
 131static const struct dev_pm_ops gic_pm_ops = {
 132        SET_RUNTIME_PM_OPS(gic_runtime_suspend,
 133                           gic_runtime_resume, NULL)
 134        SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 135                                     pm_runtime_force_resume)
 136};
 137
 138static const char * const gic400_clocks[] = {
 139        "clk",
 140};
 141
 142static const struct gic_clk_data gic400_data = {
 143        .num_clocks = ARRAY_SIZE(gic400_clocks),
 144        .clocks = gic400_clocks,
 145};
 146
 147static const struct of_device_id gic_match[] = {
 148        { .compatible = "nvidia,tegra210-agic", .data = &gic400_data },
 149        {},
 150};
 151MODULE_DEVICE_TABLE(of, gic_match);
 152
 153static struct platform_driver gic_driver = {
 154        .probe          = gic_probe,
 155        .driver         = {
 156                .name   = "gic",
 157                .of_match_table = gic_match,
 158                .pm     = &gic_pm_ops,
 159        }
 160};
 161
 162builtin_platform_driver(gic_driver);
 163