|
@@ -8,6 +8,7 @@
|
|
|
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
|
*/
|
|
|
|
|
|
+#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 @@ done:
|
|
|
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 @@ err1:
|
|
|
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);
|