irq-gic-pm.c (3548B)
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 return ret; 35 36 /* 37 * On the very first resume, the pointer to chip_pm->chip_data 38 * will be NULL and this is intentional, because we do not 39 * want to restore the GIC on the very first resume. So if 40 * the pointer is not valid just return. 41 */ 42 if (!gic) 43 return 0; 44 45 gic_dist_restore(gic); 46 gic_cpu_restore(gic); 47 48 return 0; 49} 50 51static int gic_runtime_suspend(struct device *dev) 52{ 53 struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); 54 struct gic_chip_data *gic = chip_pm->chip_data; 55 const struct gic_clk_data *data = chip_pm->clk_data; 56 57 gic_dist_save(gic); 58 gic_cpu_save(gic); 59 60 clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks); 61 62 return 0; 63} 64 65static int gic_probe(struct platform_device *pdev) 66{ 67 struct device *dev = &pdev->dev; 68 const struct gic_clk_data *data; 69 struct gic_chip_pm *chip_pm; 70 int ret, irq, i; 71 72 data = of_device_get_match_data(&pdev->dev); 73 if (!data) { 74 dev_err(&pdev->dev, "no device match found\n"); 75 return -ENODEV; 76 } 77 78 chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL); 79 if (!chip_pm) 80 return -ENOMEM; 81 82 irq = irq_of_parse_and_map(dev->of_node, 0); 83 if (!irq) { 84 dev_err(dev, "no parent interrupt found!\n"); 85 return -EINVAL; 86 } 87 88 chip_pm->clks = devm_kcalloc(dev, data->num_clocks, 89 sizeof(*chip_pm->clks), GFP_KERNEL); 90 if (!chip_pm->clks) 91 return -ENOMEM; 92 93 for (i = 0; i < data->num_clocks; i++) 94 chip_pm->clks[i].id = data->clocks[i]; 95 96 ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks); 97 if (ret) 98 goto irq_dispose; 99 100 chip_pm->clk_data = data; 101 dev_set_drvdata(dev, chip_pm); 102 103 pm_runtime_enable(dev); 104 105 ret = pm_runtime_get_sync(dev); 106 if (ret < 0) 107 goto rpm_disable; 108 109 ret = gic_of_init_child(dev, &chip_pm->chip_data, irq); 110 if (ret) 111 goto rpm_put; 112 113 pm_runtime_put(dev); 114 115 dev_info(dev, "GIC IRQ controller registered\n"); 116 117 return 0; 118 119rpm_put: 120 pm_runtime_put_sync(dev); 121rpm_disable: 122 pm_runtime_disable(dev); 123irq_dispose: 124 irq_dispose_mapping(irq); 125 126 return ret; 127} 128 129static const struct dev_pm_ops gic_pm_ops = { 130 SET_RUNTIME_PM_OPS(gic_runtime_suspend, 131 gic_runtime_resume, NULL) 132 SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 133 pm_runtime_force_resume) 134}; 135 136static const char * const gic400_clocks[] = { 137 "clk", 138}; 139 140static const struct gic_clk_data gic400_data = { 141 .num_clocks = ARRAY_SIZE(gic400_clocks), 142 .clocks = gic400_clocks, 143}; 144 145static const struct of_device_id gic_match[] = { 146 { .compatible = "nvidia,tegra210-agic", .data = &gic400_data }, 147 {}, 148}; 149MODULE_DEVICE_TABLE(of, gic_match); 150 151static struct platform_driver gic_driver = { 152 .probe = gic_probe, 153 .driver = { 154 .name = "gic", 155 .of_match_table = gic_match, 156 .pm = &gic_pm_ops, 157 } 158}; 159 160builtin_platform_driver(gic_driver);