xref: /linux/drivers/irqchip/irq-gic-pm.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29c8edddfSJon Hunter /*
39c8edddfSJon Hunter  * Copyright (C) 2016 NVIDIA CORPORATION, All Rights Reserved.
49c8edddfSJon Hunter  */
59c8edddfSJon Hunter #include <linux/module.h>
69c8edddfSJon Hunter #include <linux/clk.h>
79c8edddfSJon Hunter #include <linux/of_device.h>
89c8edddfSJon Hunter #include <linux/of_irq.h>
99c8edddfSJon Hunter #include <linux/irqchip/arm-gic.h>
109c8edddfSJon Hunter #include <linux/platform_device.h>
119c8edddfSJon Hunter #include <linux/pm_runtime.h>
129c8edddfSJon Hunter #include <linux/slab.h>
139c8edddfSJon Hunter 
149c8edddfSJon Hunter struct gic_clk_data {
159c8edddfSJon Hunter 	unsigned int num_clocks;
169c8edddfSJon Hunter 	const char *const *clocks;
179c8edddfSJon Hunter };
189c8edddfSJon Hunter 
19fe00f890SSameer Pujar struct gic_chip_pm {
20fe00f890SSameer Pujar 	struct gic_chip_data *chip_data;
21fe00f890SSameer Pujar 	const struct gic_clk_data *clk_data;
22fe00f890SSameer Pujar 	struct clk_bulk_data *clks;
23fe00f890SSameer Pujar };
24fe00f890SSameer Pujar 
259c8edddfSJon Hunter static int gic_runtime_resume(struct device *dev)
269c8edddfSJon Hunter {
27fe00f890SSameer Pujar 	struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
28fe00f890SSameer Pujar 	struct gic_chip_data *gic = chip_pm->chip_data;
29fe00f890SSameer Pujar 	const struct gic_clk_data *data = chip_pm->clk_data;
309c8edddfSJon Hunter 	int ret;
319c8edddfSJon Hunter 
32fe00f890SSameer Pujar 	ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks);
33*21a49617SChunfeng Yun 	if (ret)
349c8edddfSJon Hunter 		return ret;
359c8edddfSJon Hunter 
369c8edddfSJon Hunter 	/*
37fe00f890SSameer Pujar 	 * On the very first resume, the pointer to chip_pm->chip_data
389c8edddfSJon Hunter 	 * will be NULL and this is intentional, because we do not
399c8edddfSJon Hunter 	 * want to restore the GIC on the very first resume. So if
409c8edddfSJon Hunter 	 * the pointer is not valid just return.
419c8edddfSJon Hunter 	 */
429c8edddfSJon Hunter 	if (!gic)
439c8edddfSJon Hunter 		return 0;
449c8edddfSJon Hunter 
459c8edddfSJon Hunter 	gic_dist_restore(gic);
469c8edddfSJon Hunter 	gic_cpu_restore(gic);
479c8edddfSJon Hunter 
489c8edddfSJon Hunter 	return 0;
499c8edddfSJon Hunter }
509c8edddfSJon Hunter 
519c8edddfSJon Hunter static int gic_runtime_suspend(struct device *dev)
529c8edddfSJon Hunter {
53fe00f890SSameer Pujar 	struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
54fe00f890SSameer Pujar 	struct gic_chip_data *gic = chip_pm->chip_data;
55fe00f890SSameer Pujar 	const struct gic_clk_data *data = chip_pm->clk_data;
569c8edddfSJon Hunter 
579c8edddfSJon Hunter 	gic_dist_save(gic);
589c8edddfSJon Hunter 	gic_cpu_save(gic);
599c8edddfSJon Hunter 
60fe00f890SSameer Pujar 	clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks);
619c8edddfSJon Hunter 
629c8edddfSJon Hunter 	return 0;
639c8edddfSJon Hunter }
649c8edddfSJon Hunter 
659c8edddfSJon Hunter static int gic_probe(struct platform_device *pdev)
669c8edddfSJon Hunter {
679c8edddfSJon Hunter 	struct device *dev = &pdev->dev;
689c8edddfSJon Hunter 	const struct gic_clk_data *data;
69fe00f890SSameer Pujar 	struct gic_chip_pm *chip_pm;
70fe00f890SSameer Pujar 	int ret, irq, i;
719c8edddfSJon Hunter 
729c8edddfSJon Hunter 	data = of_device_get_match_data(&pdev->dev);
739c8edddfSJon Hunter 	if (!data) {
749c8edddfSJon Hunter 		dev_err(&pdev->dev, "no device match found\n");
759c8edddfSJon Hunter 		return -ENODEV;
769c8edddfSJon Hunter 	}
779c8edddfSJon Hunter 
78fe00f890SSameer Pujar 	chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL);
79fe00f890SSameer Pujar 	if (!chip_pm)
80fe00f890SSameer Pujar 		return -ENOMEM;
81fe00f890SSameer Pujar 
829c8edddfSJon Hunter 	irq = irq_of_parse_and_map(dev->of_node, 0);
839c8edddfSJon Hunter 	if (!irq) {
849c8edddfSJon Hunter 		dev_err(dev, "no parent interrupt found!\n");
859c8edddfSJon Hunter 		return -EINVAL;
869c8edddfSJon Hunter 	}
879c8edddfSJon Hunter 
88fe00f890SSameer Pujar 	chip_pm->clks = devm_kcalloc(dev, data->num_clocks,
89fe00f890SSameer Pujar 				     sizeof(*chip_pm->clks), GFP_KERNEL);
90fe00f890SSameer Pujar 	if (!chip_pm->clks)
91fe00f890SSameer Pujar 		return -ENOMEM;
92fe00f890SSameer Pujar 
93fe00f890SSameer Pujar 	for (i = 0; i < data->num_clocks; i++)
94fe00f890SSameer Pujar 		chip_pm->clks[i].id = data->clocks[i];
95fe00f890SSameer Pujar 
96fe00f890SSameer Pujar 	ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks);
979c8edddfSJon Hunter 	if (ret)
989c8edddfSJon Hunter 		goto irq_dispose;
999c8edddfSJon Hunter 
100fe00f890SSameer Pujar 	chip_pm->clk_data = data;
101fe00f890SSameer Pujar 	dev_set_drvdata(dev, chip_pm);
102fe00f890SSameer Pujar 
1039c8edddfSJon Hunter 	pm_runtime_enable(dev);
1049c8edddfSJon Hunter 
1059c8edddfSJon Hunter 	ret = pm_runtime_get_sync(dev);
1069c8edddfSJon Hunter 	if (ret < 0)
1079c8edddfSJon Hunter 		goto rpm_disable;
1089c8edddfSJon Hunter 
109fe00f890SSameer Pujar 	ret = gic_of_init_child(dev, &chip_pm->chip_data, irq);
1109c8edddfSJon Hunter 	if (ret)
1119c8edddfSJon Hunter 		goto rpm_put;
1129c8edddfSJon Hunter 
1139c8edddfSJon Hunter 	pm_runtime_put(dev);
1149c8edddfSJon Hunter 
1159c8edddfSJon Hunter 	dev_info(dev, "GIC IRQ controller registered\n");
1169c8edddfSJon Hunter 
1179c8edddfSJon Hunter 	return 0;
1189c8edddfSJon Hunter 
1199c8edddfSJon Hunter rpm_put:
1209c8edddfSJon Hunter 	pm_runtime_put_sync(dev);
1219c8edddfSJon Hunter rpm_disable:
1229c8edddfSJon Hunter 	pm_runtime_disable(dev);
1239c8edddfSJon Hunter irq_dispose:
1249c8edddfSJon Hunter 	irq_dispose_mapping(irq);
1259c8edddfSJon Hunter 
1269c8edddfSJon Hunter 	return ret;
1279c8edddfSJon Hunter }
1289c8edddfSJon Hunter 
1299c8edddfSJon Hunter static const struct dev_pm_ops gic_pm_ops = {
1309c8edddfSJon Hunter 	SET_RUNTIME_PM_OPS(gic_runtime_suspend,
1319c8edddfSJon Hunter 			   gic_runtime_resume, NULL)
132960164f7SSameer Pujar 	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
133960164f7SSameer Pujar 				     pm_runtime_force_resume)
1349c8edddfSJon Hunter };
1359c8edddfSJon Hunter 
1369c8edddfSJon Hunter static const char * const gic400_clocks[] = {
1379c8edddfSJon Hunter 	"clk",
1389c8edddfSJon Hunter };
1399c8edddfSJon Hunter 
1409c8edddfSJon Hunter static const struct gic_clk_data gic400_data = {
1419c8edddfSJon Hunter 	.num_clocks = ARRAY_SIZE(gic400_clocks),
1429c8edddfSJon Hunter 	.clocks = gic400_clocks,
1439c8edddfSJon Hunter };
1449c8edddfSJon Hunter 
1459c8edddfSJon Hunter static const struct of_device_id gic_match[] = {
1469c8edddfSJon Hunter 	{ .compatible = "nvidia,tegra210-agic",	.data = &gic400_data },
1479c8edddfSJon Hunter 	{},
1489c8edddfSJon Hunter };
1499c8edddfSJon Hunter MODULE_DEVICE_TABLE(of, gic_match);
1509c8edddfSJon Hunter 
1519c8edddfSJon Hunter static struct platform_driver gic_driver = {
1529c8edddfSJon Hunter 	.probe		= gic_probe,
1539c8edddfSJon Hunter 	.driver		= {
1549c8edddfSJon Hunter 		.name	= "gic",
1559c8edddfSJon Hunter 		.of_match_table	= gic_match,
1569c8edddfSJon Hunter 		.pm	= &gic_pm_ops,
1579c8edddfSJon Hunter 	}
1589c8edddfSJon Hunter };
1599c8edddfSJon Hunter 
1609c8edddfSJon Hunter builtin_platform_driver(gic_driver);
161