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