146a88534SJon Hunter /*
246a88534SJon Hunter * Tegra ACONNECT Bus Driver
346a88534SJon Hunter *
446a88534SJon Hunter * Copyright (C) 2016, NVIDIA CORPORATION. All rights reserved.
546a88534SJon Hunter *
646a88534SJon Hunter * This file is subject to the terms and conditions of the GNU General Public
746a88534SJon Hunter * License. See the file "COPYING" in the main directory of this archive
846a88534SJon Hunter * for more details.
946a88534SJon Hunter */
1046a88534SJon Hunter
1146a88534SJon Hunter #include <linux/clk.h>
1246a88534SJon Hunter #include <linux/module.h>
1346a88534SJon Hunter #include <linux/of_platform.h>
1446a88534SJon Hunter #include <linux/platform_device.h>
1546a88534SJon Hunter #include <linux/pm_runtime.h>
1646a88534SJon Hunter
170d7dab92SSameer Pujar struct tegra_aconnect {
180d7dab92SSameer Pujar struct clk *ape_clk;
190d7dab92SSameer Pujar struct clk *apb2ape_clk;
200d7dab92SSameer Pujar };
210d7dab92SSameer Pujar
tegra_aconnect_probe(struct platform_device * pdev)2246a88534SJon Hunter static int tegra_aconnect_probe(struct platform_device *pdev)
2346a88534SJon Hunter {
240d7dab92SSameer Pujar struct tegra_aconnect *aconnect;
2546a88534SJon Hunter
2646a88534SJon Hunter if (!pdev->dev.of_node)
2746a88534SJon Hunter return -EINVAL;
2846a88534SJon Hunter
290d7dab92SSameer Pujar aconnect = devm_kzalloc(&pdev->dev, sizeof(struct tegra_aconnect),
300d7dab92SSameer Pujar GFP_KERNEL);
310d7dab92SSameer Pujar if (!aconnect)
320d7dab92SSameer Pujar return -ENOMEM;
3346a88534SJon Hunter
340d7dab92SSameer Pujar aconnect->ape_clk = devm_clk_get(&pdev->dev, "ape");
350d7dab92SSameer Pujar if (IS_ERR(aconnect->ape_clk)) {
360d7dab92SSameer Pujar dev_err(&pdev->dev, "Can't retrieve ape clock\n");
370d7dab92SSameer Pujar return PTR_ERR(aconnect->ape_clk);
380d7dab92SSameer Pujar }
3946a88534SJon Hunter
400d7dab92SSameer Pujar aconnect->apb2ape_clk = devm_clk_get(&pdev->dev, "apb2ape");
410d7dab92SSameer Pujar if (IS_ERR(aconnect->apb2ape_clk)) {
420d7dab92SSameer Pujar dev_err(&pdev->dev, "Can't retrieve apb2ape clock\n");
430d7dab92SSameer Pujar return PTR_ERR(aconnect->apb2ape_clk);
440d7dab92SSameer Pujar }
4546a88534SJon Hunter
460d7dab92SSameer Pujar dev_set_drvdata(&pdev->dev, aconnect);
4746a88534SJon Hunter pm_runtime_enable(&pdev->dev);
4846a88534SJon Hunter
4946a88534SJon Hunter of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
5046a88534SJon Hunter
5146a88534SJon Hunter dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n");
5246a88534SJon Hunter
5346a88534SJon Hunter return 0;
5446a88534SJon Hunter }
5546a88534SJon Hunter
tegra_aconnect_remove(struct platform_device * pdev)5646a88534SJon Hunter static int tegra_aconnect_remove(struct platform_device *pdev)
5746a88534SJon Hunter {
5846a88534SJon Hunter pm_runtime_disable(&pdev->dev);
5946a88534SJon Hunter
6046a88534SJon Hunter return 0;
6146a88534SJon Hunter }
6246a88534SJon Hunter
tegra_aconnect_runtime_resume(struct device * dev)6346a88534SJon Hunter static int tegra_aconnect_runtime_resume(struct device *dev)
6446a88534SJon Hunter {
650d7dab92SSameer Pujar struct tegra_aconnect *aconnect = dev_get_drvdata(dev);
660d7dab92SSameer Pujar int ret;
670d7dab92SSameer Pujar
680d7dab92SSameer Pujar ret = clk_prepare_enable(aconnect->ape_clk);
690d7dab92SSameer Pujar if (ret) {
700d7dab92SSameer Pujar dev_err(dev, "ape clk_enable failed: %d\n", ret);
710d7dab92SSameer Pujar return ret;
720d7dab92SSameer Pujar }
730d7dab92SSameer Pujar
740d7dab92SSameer Pujar ret = clk_prepare_enable(aconnect->apb2ape_clk);
750d7dab92SSameer Pujar if (ret) {
760d7dab92SSameer Pujar clk_disable_unprepare(aconnect->ape_clk);
770d7dab92SSameer Pujar dev_err(dev, "apb2ape clk_enable failed: %d\n", ret);
780d7dab92SSameer Pujar return ret;
790d7dab92SSameer Pujar }
800d7dab92SSameer Pujar
810d7dab92SSameer Pujar return 0;
8246a88534SJon Hunter }
8346a88534SJon Hunter
tegra_aconnect_runtime_suspend(struct device * dev)8446a88534SJon Hunter static int tegra_aconnect_runtime_suspend(struct device *dev)
8546a88534SJon Hunter {
860d7dab92SSameer Pujar struct tegra_aconnect *aconnect = dev_get_drvdata(dev);
870d7dab92SSameer Pujar
880d7dab92SSameer Pujar clk_disable_unprepare(aconnect->ape_clk);
890d7dab92SSameer Pujar clk_disable_unprepare(aconnect->apb2ape_clk);
900d7dab92SSameer Pujar
910d7dab92SSameer Pujar return 0;
9246a88534SJon Hunter }
9346a88534SJon Hunter
9446a88534SJon Hunter static const struct dev_pm_ops tegra_aconnect_pm_ops = {
9546a88534SJon Hunter SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend,
9646a88534SJon Hunter tegra_aconnect_runtime_resume, NULL)
971427736eSSameer Pujar SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
981427736eSSameer Pujar pm_runtime_force_resume)
9946a88534SJon Hunter };
10046a88534SJon Hunter
10146a88534SJon Hunter static const struct of_device_id tegra_aconnect_of_match[] = {
10246a88534SJon Hunter { .compatible = "nvidia,tegra210-aconnect", },
10346a88534SJon Hunter { }
10446a88534SJon Hunter };
10546a88534SJon Hunter MODULE_DEVICE_TABLE(of, tegra_aconnect_of_match);
10646a88534SJon Hunter
10746a88534SJon Hunter static struct platform_driver tegra_aconnect_driver = {
10846a88534SJon Hunter .probe = tegra_aconnect_probe,
10946a88534SJon Hunter .remove = tegra_aconnect_remove,
11046a88534SJon Hunter .driver = {
11146a88534SJon Hunter .name = "tegra-aconnect",
11246a88534SJon Hunter .of_match_table = tegra_aconnect_of_match,
11346a88534SJon Hunter .pm = &tegra_aconnect_pm_ops,
11446a88534SJon Hunter },
11546a88534SJon Hunter };
11646a88534SJon Hunter module_platform_driver(tegra_aconnect_driver);
11746a88534SJon Hunter
11846a88534SJon Hunter MODULE_DESCRIPTION("NVIDIA Tegra ACONNECT Bus Driver");
11946a88534SJon Hunter MODULE_AUTHOR("Jon Hunter <jonathanh@nvidia.com>");
12046a88534SJon Hunter MODULE_LICENSE("GPL v2");
121