1 // SPDX-License-Identifier: GPL-2.0
3 * dwc3-exynos.c - Samsung Exynos DWC3 Specific Glue layer
5 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
6 * http://www.samsung.com
8 * Author: Anton Tikhomirov <av.tikhomirov@samsung.com>
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
14 #include <linux/platform_device.h>
15 #include <linux/clk.h>
17 #include <linux/of_platform.h>
18 #include <linux/regulator/consumer.h>
20 #define DWC3_EXYNOS_MAX_CLOCKS 4
22 struct dwc3_exynos_driverdata
{
23 const char *clk_names
[DWC3_EXYNOS_MAX_CLOCKS
];
31 const char **clk_names
;
32 struct clk
*clks
[DWC3_EXYNOS_MAX_CLOCKS
];
36 struct regulator
*vdd33
;
37 struct regulator
*vdd10
;
40 static int dwc3_exynos_remove_child(struct device
*dev
, void *unused
)
42 struct platform_device
*pdev
= to_platform_device(dev
);
44 platform_device_unregister(pdev
);
49 static int dwc3_exynos_probe(struct platform_device
*pdev
)
51 struct dwc3_exynos
*exynos
;
52 struct device
*dev
= &pdev
->dev
;
53 struct device_node
*node
= dev
->of_node
;
54 const struct dwc3_exynos_driverdata
*driver_data
;
57 exynos
= devm_kzalloc(dev
, sizeof(*exynos
), GFP_KERNEL
);
61 driver_data
= of_device_get_match_data(dev
);
63 exynos
->num_clks
= driver_data
->num_clks
;
64 exynos
->clk_names
= (const char **)driver_data
->clk_names
;
65 exynos
->suspend_clk_idx
= driver_data
->suspend_clk_idx
;
67 platform_set_drvdata(pdev
, exynos
);
69 for (i
= 0; i
< exynos
->num_clks
; i
++) {
70 exynos
->clks
[i
] = devm_clk_get(dev
, exynos
->clk_names
[i
]);
71 if (IS_ERR(exynos
->clks
[i
])) {
72 dev_err(dev
, "failed to get clock: %s\n",
73 exynos
->clk_names
[i
]);
74 return PTR_ERR(exynos
->clks
[i
]);
78 for (i
= 0; i
< exynos
->num_clks
; i
++) {
79 ret
= clk_prepare_enable(exynos
->clks
[i
]);
82 clk_disable_unprepare(exynos
->clks
[i
]);
87 if (exynos
->suspend_clk_idx
>= 0)
88 clk_prepare_enable(exynos
->clks
[exynos
->suspend_clk_idx
]);
90 exynos
->vdd33
= devm_regulator_get(dev
, "vdd33");
91 if (IS_ERR(exynos
->vdd33
)) {
92 ret
= PTR_ERR(exynos
->vdd33
);
95 ret
= regulator_enable(exynos
->vdd33
);
97 dev_err(dev
, "Failed to enable VDD33 supply\n");
101 exynos
->vdd10
= devm_regulator_get(dev
, "vdd10");
102 if (IS_ERR(exynos
->vdd10
)) {
103 ret
= PTR_ERR(exynos
->vdd10
);
106 ret
= regulator_enable(exynos
->vdd10
);
108 dev_err(dev
, "Failed to enable VDD10 supply\n");
113 ret
= of_platform_populate(node
, NULL
, NULL
, dev
);
115 dev_err(dev
, "failed to add dwc3 core\n");
119 dev_err(dev
, "no device node, failed to add dwc3 core\n");
127 regulator_disable(exynos
->vdd10
);
129 regulator_disable(exynos
->vdd33
);
131 for (i
= exynos
->num_clks
- 1; i
>= 0; i
--)
132 clk_disable_unprepare(exynos
->clks
[i
]);
134 if (exynos
->suspend_clk_idx
>= 0)
135 clk_disable_unprepare(exynos
->clks
[exynos
->suspend_clk_idx
]);
140 static int dwc3_exynos_remove(struct platform_device
*pdev
)
142 struct dwc3_exynos
*exynos
= platform_get_drvdata(pdev
);
145 device_for_each_child(&pdev
->dev
, NULL
, dwc3_exynos_remove_child
);
147 for (i
= exynos
->num_clks
- 1; i
>= 0; i
--)
148 clk_disable_unprepare(exynos
->clks
[i
]);
150 if (exynos
->suspend_clk_idx
>= 0)
151 clk_disable_unprepare(exynos
->clks
[exynos
->suspend_clk_idx
]);
153 regulator_disable(exynos
->vdd33
);
154 regulator_disable(exynos
->vdd10
);
159 static const struct dwc3_exynos_driverdata exynos5250_drvdata
= {
160 .clk_names
= { "usbdrd30" },
162 .suspend_clk_idx
= -1,
165 static const struct dwc3_exynos_driverdata exynos5420_drvdata
= {
166 .clk_names
= { "usbdrd30", "usbdrd30_susp_clk"},
168 .suspend_clk_idx
= 1,
171 static const struct dwc3_exynos_driverdata exynos5433_drvdata
= {
172 .clk_names
= { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
174 .suspend_clk_idx
= 1,
177 static const struct dwc3_exynos_driverdata exynos7_drvdata
= {
178 .clk_names
= { "usbdrd30", "usbdrd30_susp_clk", "usbdrd30_axius_clk" },
180 .suspend_clk_idx
= 1,
183 static const struct of_device_id exynos_dwc3_match
[] = {
185 .compatible
= "samsung,exynos5250-dwusb3",
186 .data
= &exynos5250_drvdata
,
188 .compatible
= "samsung,exynos5420-dwusb3",
189 .data
= &exynos5420_drvdata
,
191 .compatible
= "samsung,exynos5433-dwusb3",
192 .data
= &exynos5433_drvdata
,
194 .compatible
= "samsung,exynos7-dwusb3",
195 .data
= &exynos7_drvdata
,
199 MODULE_DEVICE_TABLE(of
, exynos_dwc3_match
);
201 #ifdef CONFIG_PM_SLEEP
202 static int dwc3_exynos_suspend(struct device
*dev
)
204 struct dwc3_exynos
*exynos
= dev_get_drvdata(dev
);
207 for (i
= exynos
->num_clks
- 1; i
>= 0; i
--)
208 clk_disable_unprepare(exynos
->clks
[i
]);
210 regulator_disable(exynos
->vdd33
);
211 regulator_disable(exynos
->vdd10
);
216 static int dwc3_exynos_resume(struct device
*dev
)
218 struct dwc3_exynos
*exynos
= dev_get_drvdata(dev
);
221 ret
= regulator_enable(exynos
->vdd33
);
223 dev_err(dev
, "Failed to enable VDD33 supply\n");
226 ret
= regulator_enable(exynos
->vdd10
);
228 dev_err(dev
, "Failed to enable VDD10 supply\n");
232 for (i
= 0; i
< exynos
->num_clks
; i
++) {
233 ret
= clk_prepare_enable(exynos
->clks
[i
]);
236 clk_disable_unprepare(exynos
->clks
[i
]);
244 static const struct dev_pm_ops dwc3_exynos_dev_pm_ops
= {
245 SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend
, dwc3_exynos_resume
)
248 #define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
250 #define DEV_PM_OPS NULL
251 #endif /* CONFIG_PM_SLEEP */
253 static struct platform_driver dwc3_exynos_driver
= {
254 .probe
= dwc3_exynos_probe
,
255 .remove
= dwc3_exynos_remove
,
257 .name
= "exynos-dwc3",
258 .of_match_table
= exynos_dwc3_match
,
263 module_platform_driver(dwc3_exynos_driver
);
265 MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
266 MODULE_LICENSE("GPL v2");
267 MODULE_DESCRIPTION("DesignWare USB3 Exynos Glue Layer");