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 exynos5433_drvdata
= {
166 .clk_names
= { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
168 .suspend_clk_idx
= 1,
171 static const struct dwc3_exynos_driverdata exynos7_drvdata
= {
172 .clk_names
= { "usbdrd30", "usbdrd30_susp_clk", "usbdrd30_axius_clk" },
174 .suspend_clk_idx
= 1,
177 static const struct of_device_id exynos_dwc3_match
[] = {
179 .compatible
= "samsung,exynos5250-dwusb3",
180 .data
= &exynos5250_drvdata
,
182 .compatible
= "samsung,exynos5433-dwusb3",
183 .data
= &exynos5433_drvdata
,
185 .compatible
= "samsung,exynos7-dwusb3",
186 .data
= &exynos7_drvdata
,
190 MODULE_DEVICE_TABLE(of
, exynos_dwc3_match
);
192 #ifdef CONFIG_PM_SLEEP
193 static int dwc3_exynos_suspend(struct device
*dev
)
195 struct dwc3_exynos
*exynos
= dev_get_drvdata(dev
);
198 for (i
= exynos
->num_clks
- 1; i
>= 0; i
--)
199 clk_disable_unprepare(exynos
->clks
[i
]);
201 regulator_disable(exynos
->vdd33
);
202 regulator_disable(exynos
->vdd10
);
207 static int dwc3_exynos_resume(struct device
*dev
)
209 struct dwc3_exynos
*exynos
= dev_get_drvdata(dev
);
212 ret
= regulator_enable(exynos
->vdd33
);
214 dev_err(dev
, "Failed to enable VDD33 supply\n");
217 ret
= regulator_enable(exynos
->vdd10
);
219 dev_err(dev
, "Failed to enable VDD10 supply\n");
223 for (i
= 0; i
< exynos
->num_clks
; i
++) {
224 ret
= clk_prepare_enable(exynos
->clks
[i
]);
227 clk_disable_unprepare(exynos
->clks
[i
]);
235 static const struct dev_pm_ops dwc3_exynos_dev_pm_ops
= {
236 SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend
, dwc3_exynos_resume
)
239 #define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
241 #define DEV_PM_OPS NULL
242 #endif /* CONFIG_PM_SLEEP */
244 static struct platform_driver dwc3_exynos_driver
= {
245 .probe
= dwc3_exynos_probe
,
246 .remove
= dwc3_exynos_remove
,
248 .name
= "exynos-dwc3",
249 .of_match_table
= exynos_dwc3_match
,
254 module_platform_driver(dwc3_exynos_driver
);
256 MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
257 MODULE_LICENSE("GPL v2");
258 MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");