forked from Freescale/linux-fslc
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
usb: dwc3: support clocks and resets for DWC3 core
Historically, the clocks and resets are handled on the glue layer side instead of the DWC3 core. For simple cases, dwc3-of-simple.c takes care of arbitrary number of clocks and resets. The DT node structure typically looks like as follows: dwc3-glue { compatible = "foo,dwc3"; clocks = ...; resets = ...; ... dwc3 { compatible = "snps,dwc3"; ... }; } By supporting the clocks and the reset in the dwc3/core.c, it will be turned into a single node: dwc3 { compatible = "foo,dwc3", "snps,dwc3"; clocks = ...; resets = ...; ... } This commit adds the binding of clocks and resets specific to this IP. The number of clocks should generally be the same across SoCs, it is just some SoCs either tie clocks together or do not provide software control of some of the clocks. I took the clock names from the Synopsys datasheet: "ref" (ref_clk), "bus_early" (bus_clk_early), and "suspend" (suspend_clk). I found only one reset line in the datasheet, hence the reset-names property is omitted. Those clocks are required for new platforms. Enforcing the new binding breaks existing platforms since they specify clocks (and resets) in their glue layer node, but nothing in the core node. I listed such exceptional cases in the DT binding. The driver code has been relaxed to accept no clock. This change is based on the discussion [1]. I inserted reset_control_deassert() and clk_bulk_enable() before the first register access, i.e. dwc3_cache_hwparams(). [1] https://patchwork.kernel.org/patch/10284265/ Signed-off-by: Masahiro Yamada <[email protected]> Reviewed-by: Rob Herring <[email protected]> Signed-off-by: Felipe Balbi <[email protected]>
- Loading branch information
Showing
3 changed files
with
115 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
* Sebastian Andrzej Siewior <[email protected]> | ||
*/ | ||
|
||
#include <linux/clk.h> | ||
#include <linux/version.h> | ||
#include <linux/module.h> | ||
#include <linux/kernel.h> | ||
|
@@ -24,6 +25,7 @@ | |
#include <linux/of.h> | ||
#include <linux/acpi.h> | ||
#include <linux/pinctrl/consumer.h> | ||
#include <linux/reset.h> | ||
|
||
#include <linux/usb/ch9.h> | ||
#include <linux/usb/gadget.h> | ||
|
@@ -266,6 +268,12 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) | |
return 0; | ||
} | ||
|
||
static const struct clk_bulk_data dwc3_core_clks[] = { | ||
{ .id = "ref" }, | ||
{ .id = "bus_early" }, | ||
{ .id = "suspend" }, | ||
}; | ||
|
||
/* | ||
* dwc3_frame_length_adjustment - Adjusts frame length if required | ||
* @dwc3: Pointer to our controller context structure | ||
|
@@ -667,6 +675,9 @@ static void dwc3_core_exit(struct dwc3 *dwc) | |
usb_phy_set_suspend(dwc->usb3_phy, 1); | ||
phy_power_off(dwc->usb2_generic_phy); | ||
phy_power_off(dwc->usb3_generic_phy); | ||
clk_bulk_disable(dwc->num_clks, dwc->clks); | ||
clk_bulk_unprepare(dwc->num_clks, dwc->clks); | ||
reset_control_assert(dwc->reset); | ||
} | ||
|
||
static bool dwc3_core_is_valid(struct dwc3 *dwc) | ||
|
@@ -1256,6 +1267,12 @@ static int dwc3_probe(struct platform_device *pdev) | |
if (!dwc) | ||
return -ENOMEM; | ||
|
||
dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks), | ||
GFP_KERNEL); | ||
if (!dwc->clks) | ||
return -ENOMEM; | ||
|
||
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); | ||
dwc->dev = dev; | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
|
@@ -1286,6 +1303,32 @@ static int dwc3_probe(struct platform_device *pdev) | |
|
||
dwc3_get_properties(dwc); | ||
|
||
dwc->reset = devm_reset_control_get_optional_shared(dev, NULL); | ||
if (IS_ERR(dwc->reset)) | ||
return PTR_ERR(dwc->reset); | ||
|
||
ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); | ||
if (ret == -EPROBE_DEFER) | ||
return ret; | ||
/* | ||
* Clocks are optional, but new DT platforms should support all clocks | ||
* as required by the DT-binding. | ||
*/ | ||
if (ret) | ||
dwc->num_clks = 0; | ||
|
||
ret = reset_control_deassert(dwc->reset); | ||
if (ret) | ||
goto put_clks; | ||
|
||
ret = clk_bulk_prepare(dwc->num_clks, dwc->clks); | ||
if (ret) | ||
goto assert_reset; | ||
|
||
ret = clk_bulk_enable(dwc->num_clks, dwc->clks); | ||
if (ret) | ||
goto unprepare_clks; | ||
|
||
platform_set_drvdata(pdev, dwc); | ||
dwc3_cache_hwparams(dwc); | ||
|
||
|
@@ -1349,6 +1392,14 @@ static int dwc3_probe(struct platform_device *pdev) | |
pm_runtime_put_sync(&pdev->dev); | ||
pm_runtime_disable(&pdev->dev); | ||
|
||
clk_bulk_disable(dwc->num_clks, dwc->clks); | ||
unprepare_clks: | ||
clk_bulk_unprepare(dwc->num_clks, dwc->clks); | ||
assert_reset: | ||
reset_control_assert(dwc->reset); | ||
put_clks: | ||
clk_bulk_put(dwc->num_clks, dwc->clks); | ||
|
||
return ret; | ||
} | ||
|
||
|
@@ -1370,11 +1421,44 @@ static int dwc3_remove(struct platform_device *pdev) | |
|
||
dwc3_free_event_buffers(dwc); | ||
dwc3_free_scratch_buffers(dwc); | ||
clk_bulk_put(dwc->num_clks, dwc->clks); | ||
|
||
return 0; | ||
} | ||
|
||
#ifdef CONFIG_PM | ||
static int dwc3_core_init_for_resume(struct dwc3 *dwc) | ||
{ | ||
int ret; | ||
|
||
ret = reset_control_deassert(dwc->reset); | ||
if (ret) | ||
return ret; | ||
|
||
ret = clk_bulk_prepare(dwc->num_clks, dwc->clks); | ||
if (ret) | ||
goto assert_reset; | ||
|
||
ret = clk_bulk_enable(dwc->num_clks, dwc->clks); | ||
if (ret) | ||
goto unprepare_clks; | ||
|
||
ret = dwc3_core_init(dwc); | ||
if (ret) | ||
goto disable_clks; | ||
|
||
return 0; | ||
|
||
disable_clks: | ||
clk_bulk_disable(dwc->num_clks, dwc->clks); | ||
unprepare_clks: | ||
clk_bulk_unprepare(dwc->num_clks, dwc->clks); | ||
assert_reset: | ||
reset_control_assert(dwc->reset); | ||
|
||
return ret; | ||
} | ||
|
||
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) | ||
{ | ||
unsigned long flags; | ||
|
@@ -1438,7 +1522,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) | |
|
||
switch (dwc->current_dr_role) { | ||
case DWC3_GCTL_PRTCAP_DEVICE: | ||
ret = dwc3_core_init(dwc); | ||
ret = dwc3_core_init_for_resume(dwc); | ||
if (ret) | ||
return ret; | ||
|
||
|
@@ -1449,7 +1533,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) | |
break; | ||
case DWC3_GCTL_PRTCAP_HOST: | ||
if (!PMSG_IS_AUTO(msg)) { | ||
ret = dwc3_core_init(dwc); | ||
ret = dwc3_core_init_for_resume(dwc); | ||
if (ret) | ||
return ret; | ||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters