|
@@ -10,11 +10,20 @@
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/usb/gadget.h>
|
|
|
#include <linux/usb/chipidea.h>
|
|
|
+#include <linux/clk.h>
|
|
|
+#include <linux/reset.h>
|
|
|
|
|
|
#include "ci.h"
|
|
|
|
|
|
#define HS_PHY_AHB_MODE 0x0098
|
|
|
|
|
|
+struct ci_hdrc_msm {
|
|
|
+ struct platform_device *ci;
|
|
|
+ struct clk *core_clk;
|
|
|
+ struct clk *iface_clk;
|
|
|
+ struct clk *fs_clk;
|
|
|
+};
|
|
|
+
|
|
|
static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
|
|
|
{
|
|
|
struct device *dev = ci->gadget.dev.parent;
|
|
@@ -52,11 +61,20 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
|
|
|
|
|
|
static int ci_hdrc_msm_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
+ struct ci_hdrc_msm *ci;
|
|
|
struct platform_device *plat_ci;
|
|
|
struct usb_phy *phy;
|
|
|
+ struct clk *clk;
|
|
|
+ struct reset_control *reset;
|
|
|
+ int ret;
|
|
|
|
|
|
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
|
|
|
|
|
|
+ ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
|
|
|
+ if (!ci)
|
|
|
+ return -ENOMEM;
|
|
|
+ platform_set_drvdata(pdev, ci);
|
|
|
+
|
|
|
/*
|
|
|
* OTG(PHY) driver takes care of PHY initialization, clock management,
|
|
|
* powering up VBUS, mapping of registers address space and power
|
|
@@ -68,29 +86,75 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
|
|
|
|
|
|
ci_hdrc_msm_platdata.usb_phy = phy;
|
|
|
|
|
|
+ reset = devm_reset_control_get(&pdev->dev, "core");
|
|
|
+ if (IS_ERR(reset))
|
|
|
+ return PTR_ERR(reset);
|
|
|
+
|
|
|
+ ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
|
|
|
+ if (IS_ERR(clk))
|
|
|
+ return PTR_ERR(clk);
|
|
|
+
|
|
|
+ ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
|
|
|
+ if (IS_ERR(clk))
|
|
|
+ return PTR_ERR(clk);
|
|
|
+
|
|
|
+ ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
|
|
|
+ if (IS_ERR(clk)) {
|
|
|
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
|
|
|
+ return -EPROBE_DEFER;
|
|
|
+ ci->fs_clk = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = clk_prepare_enable(ci->fs_clk);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ reset_control_assert(reset);
|
|
|
+ usleep_range(10000, 12000);
|
|
|
+ reset_control_deassert(reset);
|
|
|
+
|
|
|
+ clk_disable_unprepare(ci->fs_clk);
|
|
|
+
|
|
|
+ ret = clk_prepare_enable(ci->core_clk);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = clk_prepare_enable(ci->iface_clk);
|
|
|
+ if (ret)
|
|
|
+ goto err_iface;
|
|
|
+
|
|
|
plat_ci = ci_hdrc_add_device(&pdev->dev,
|
|
|
pdev->resource, pdev->num_resources,
|
|
|
&ci_hdrc_msm_platdata);
|
|
|
if (IS_ERR(plat_ci)) {
|
|
|
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
|
|
|
- return PTR_ERR(plat_ci);
|
|
|
+ ret = PTR_ERR(plat_ci);
|
|
|
+ goto err_mux;
|
|
|
}
|
|
|
|
|
|
- platform_set_drvdata(pdev, plat_ci);
|
|
|
+ ci->ci = plat_ci;
|
|
|
|
|
|
pm_runtime_set_active(&pdev->dev);
|
|
|
pm_runtime_no_callbacks(&pdev->dev);
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+err_mux:
|
|
|
+ clk_disable_unprepare(ci->iface_clk);
|
|
|
+err_iface:
|
|
|
+ clk_disable_unprepare(ci->core_clk);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int ci_hdrc_msm_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
- struct platform_device *plat_ci = platform_get_drvdata(pdev);
|
|
|
+ struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
|
|
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
- ci_hdrc_remove_device(plat_ci);
|
|
|
+ ci_hdrc_remove_device(ci->ci);
|
|
|
+ clk_disable_unprepare(ci->iface_clk);
|
|
|
+ clk_disable_unprepare(ci->core_clk);
|
|
|
|
|
|
return 0;
|
|
|
}
|