1 | /* |
2 | * Tegra ACONNECT Bus Driver |
3 | * |
4 | * Copyright (C) 2016, NVIDIA CORPORATION. All rights reserved. |
5 | * |
6 | * This file is subject to the terms and conditions of the GNU General Public |
7 | * License. See the file "COPYING" in the main directory of this archive |
8 | * for more details. |
9 | */ |
10 | |
11 | #include <linux/clk.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of_platform.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/pm_runtime.h> |
16 | |
17 | struct tegra_aconnect { |
18 | struct clk *ape_clk; |
19 | struct clk *apb2ape_clk; |
20 | }; |
21 | |
22 | static int tegra_aconnect_probe(struct platform_device *pdev) |
23 | { |
24 | struct tegra_aconnect *aconnect; |
25 | |
26 | if (!pdev->dev.of_node) |
27 | return -EINVAL; |
28 | |
29 | aconnect = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct tegra_aconnect), |
30 | GFP_KERNEL); |
31 | if (!aconnect) |
32 | return -ENOMEM; |
33 | |
34 | aconnect->ape_clk = devm_clk_get(dev: &pdev->dev, id: "ape" ); |
35 | if (IS_ERR(ptr: aconnect->ape_clk)) { |
36 | dev_err(&pdev->dev, "Can't retrieve ape clock\n" ); |
37 | return PTR_ERR(ptr: aconnect->ape_clk); |
38 | } |
39 | |
40 | aconnect->apb2ape_clk = devm_clk_get(dev: &pdev->dev, id: "apb2ape" ); |
41 | if (IS_ERR(ptr: aconnect->apb2ape_clk)) { |
42 | dev_err(&pdev->dev, "Can't retrieve apb2ape clock\n" ); |
43 | return PTR_ERR(ptr: aconnect->apb2ape_clk); |
44 | } |
45 | |
46 | dev_set_drvdata(dev: &pdev->dev, data: aconnect); |
47 | pm_runtime_enable(dev: &pdev->dev); |
48 | |
49 | of_platform_populate(root: pdev->dev.of_node, NULL, NULL, parent: &pdev->dev); |
50 | |
51 | dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n" ); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static int tegra_aconnect_remove(struct platform_device *pdev) |
57 | { |
58 | pm_runtime_disable(dev: &pdev->dev); |
59 | |
60 | return 0; |
61 | } |
62 | |
63 | static int tegra_aconnect_runtime_resume(struct device *dev) |
64 | { |
65 | struct tegra_aconnect *aconnect = dev_get_drvdata(dev); |
66 | int ret; |
67 | |
68 | ret = clk_prepare_enable(clk: aconnect->ape_clk); |
69 | if (ret) { |
70 | dev_err(dev, "ape clk_enable failed: %d\n" , ret); |
71 | return ret; |
72 | } |
73 | |
74 | ret = clk_prepare_enable(clk: aconnect->apb2ape_clk); |
75 | if (ret) { |
76 | clk_disable_unprepare(clk: aconnect->ape_clk); |
77 | dev_err(dev, "apb2ape clk_enable failed: %d\n" , ret); |
78 | return ret; |
79 | } |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | static int tegra_aconnect_runtime_suspend(struct device *dev) |
85 | { |
86 | struct tegra_aconnect *aconnect = dev_get_drvdata(dev); |
87 | |
88 | clk_disable_unprepare(clk: aconnect->ape_clk); |
89 | clk_disable_unprepare(clk: aconnect->apb2ape_clk); |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | static const struct dev_pm_ops tegra_aconnect_pm_ops = { |
95 | SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend, |
96 | tegra_aconnect_runtime_resume, NULL) |
97 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
98 | pm_runtime_force_resume) |
99 | }; |
100 | |
101 | static const struct of_device_id tegra_aconnect_of_match[] = { |
102 | { .compatible = "nvidia,tegra210-aconnect" , }, |
103 | { } |
104 | }; |
105 | MODULE_DEVICE_TABLE(of, tegra_aconnect_of_match); |
106 | |
107 | static struct platform_driver tegra_aconnect_driver = { |
108 | .probe = tegra_aconnect_probe, |
109 | .remove = tegra_aconnect_remove, |
110 | .driver = { |
111 | .name = "tegra-aconnect" , |
112 | .of_match_table = tegra_aconnect_of_match, |
113 | .pm = &tegra_aconnect_pm_ops, |
114 | }, |
115 | }; |
116 | module_platform_driver(tegra_aconnect_driver); |
117 | |
118 | MODULE_DESCRIPTION("NVIDIA Tegra ACONNECT Bus Driver" ); |
119 | MODULE_AUTHOR("Jon Hunter <jonathanh@nvidia.com>" ); |
120 | MODULE_LICENSE("GPL v2" ); |
121 | |