diff options
Diffstat (limited to 'drivers/usb/dwc3/dwc3-qcom.c')
-rw-r--r-- | drivers/usb/dwc3/dwc3-qcom.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c new file mode 100644 index 000000000000..8c2e8eec80c2 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-qcom.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | struct dwc3_qcom { | ||
22 | struct device *dev; | ||
23 | |||
24 | struct clk *core_clk; | ||
25 | struct clk *iface_clk; | ||
26 | struct clk *sleep_clk; | ||
27 | }; | ||
28 | |||
29 | static int dwc3_qcom_probe(struct platform_device *pdev) | ||
30 | { | ||
31 | struct device_node *node = pdev->dev.of_node; | ||
32 | struct dwc3_qcom *qdwc; | ||
33 | int ret; | ||
34 | |||
35 | qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL); | ||
36 | if (!qdwc) | ||
37 | return -ENOMEM; | ||
38 | |||
39 | platform_set_drvdata(pdev, qdwc); | ||
40 | |||
41 | qdwc->dev = &pdev->dev; | ||
42 | |||
43 | qdwc->core_clk = devm_clk_get(qdwc->dev, "core"); | ||
44 | if (IS_ERR(qdwc->core_clk)) { | ||
45 | dev_err(qdwc->dev, "failed to get core clock\n"); | ||
46 | return PTR_ERR(qdwc->core_clk); | ||
47 | } | ||
48 | |||
49 | qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface"); | ||
50 | if (IS_ERR(qdwc->iface_clk)) { | ||
51 | dev_dbg(qdwc->dev, "failed to get optional iface clock\n"); | ||
52 | qdwc->iface_clk = NULL; | ||
53 | } | ||
54 | |||
55 | qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep"); | ||
56 | if (IS_ERR(qdwc->sleep_clk)) { | ||
57 | dev_dbg(qdwc->dev, "failed to get optional sleep clock\n"); | ||
58 | qdwc->sleep_clk = NULL; | ||
59 | } | ||
60 | |||
61 | ret = clk_prepare_enable(qdwc->core_clk); | ||
62 | if (ret) { | ||
63 | dev_err(qdwc->dev, "failed to enable core clock\n"); | ||
64 | goto err_core; | ||
65 | } | ||
66 | |||
67 | ret = clk_prepare_enable(qdwc->iface_clk); | ||
68 | if (ret) { | ||
69 | dev_err(qdwc->dev, "failed to enable optional iface clock\n"); | ||
70 | goto err_iface; | ||
71 | } | ||
72 | |||
73 | ret = clk_prepare_enable(qdwc->sleep_clk); | ||
74 | if (ret) { | ||
75 | dev_err(qdwc->dev, "failed to enable optional sleep clock\n"); | ||
76 | goto err_sleep; | ||
77 | } | ||
78 | |||
79 | ret = of_platform_populate(node, NULL, NULL, qdwc->dev); | ||
80 | if (ret) { | ||
81 | dev_err(qdwc->dev, "failed to register core - %d\n", ret); | ||
82 | goto err_clks; | ||
83 | } | ||
84 | |||
85 | return 0; | ||
86 | |||
87 | err_clks: | ||
88 | clk_disable_unprepare(qdwc->sleep_clk); | ||
89 | err_sleep: | ||
90 | clk_disable_unprepare(qdwc->iface_clk); | ||
91 | err_iface: | ||
92 | clk_disable_unprepare(qdwc->core_clk); | ||
93 | err_core: | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | static int dwc3_qcom_remove(struct platform_device *pdev) | ||
98 | { | ||
99 | struct dwc3_qcom *qdwc = platform_get_drvdata(pdev); | ||
100 | |||
101 | of_platform_depopulate(&pdev->dev); | ||
102 | |||
103 | clk_disable_unprepare(qdwc->sleep_clk); | ||
104 | clk_disable_unprepare(qdwc->iface_clk); | ||
105 | clk_disable_unprepare(qdwc->core_clk); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static const struct of_device_id of_dwc3_match[] = { | ||
111 | { .compatible = "qcom,dwc3" }, | ||
112 | { /* Sentinel */ } | ||
113 | }; | ||
114 | MODULE_DEVICE_TABLE(of, of_dwc3_match); | ||
115 | |||
116 | static struct platform_driver dwc3_qcom_driver = { | ||
117 | .probe = dwc3_qcom_probe, | ||
118 | .remove = dwc3_qcom_remove, | ||
119 | .driver = { | ||
120 | .name = "qcom-dwc3", | ||
121 | .of_match_table = of_dwc3_match, | ||
122 | }, | ||
123 | }; | ||
124 | |||
125 | module_platform_driver(dwc3_qcom_driver); | ||
126 | |||
127 | MODULE_ALIAS("platform:qcom-dwc3"); | ||
128 | MODULE_LICENSE("GPL v2"); | ||
129 | MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); | ||
130 | MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); | ||