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