Skip to content
This repository has been archived by the owner on Sep 11, 2019. It is now read-only.

Commit

Permalink
usb: chipidea: imx: add HSIC support
Browse files Browse the repository at this point in the history
To support imx HSIC, there are some special requirement:
- The HSIC pad is 1.2v, it may need to supply from external
- The data/strobe pin needs to be pulled down first, and after
  host mode is initialized, the strobe pin needs to be pulled up
- During the USB suspend/resume, special setting is needed

Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Tested-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
  • Loading branch information
Peter Chen committed Dec 11, 2018
1 parent 014abe3 commit 7c8e890
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 19 deletions.
140 changes: 124 additions & 16 deletions drivers/usb/chipidea/ci_hdrc_imx.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/usb/chipidea.h>
#include <linux/usb/of.h>
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>

#include "ci.h"
#include "ci_hdrc_imx.h"
Expand Down Expand Up @@ -85,6 +86,9 @@ struct ci_hdrc_imx_data {
bool supports_runtime_pm;
bool override_phy_control;
bool in_lpm;
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_hsic_active;
struct regulator *hsic_pad_regulator;
/* SoC before i.mx6 (except imx23/imx28) needs three clks */
bool need_three_clks;
struct clk *clk_ipg;
Expand Down Expand Up @@ -245,19 +249,49 @@ static void imx_disable_unprepare_clks(struct device *dev)
}
}

static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
{
struct device *dev = ci->dev->parent;
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;

switch (event) {
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
ret = pinctrl_select_state(data->pinctrl,
data->pinctrl_hsic_active);
if (ret)
dev_err(dev, "hsic_active select failed, err=%d\n",
ret);
break;
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
if (ret)
dev_err(dev,
"hsic_set_connect failed, err=%d\n", ret);
break;
default:
break;
}

return ret;
}

static int ci_hdrc_imx_probe(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data;
struct ci_hdrc_platform_data pdata = {
.name = dev_name(&pdev->dev),
.capoffset = DEF_CAPOFFSET,
.notify_event = ci_hdrc_imx_notify_event,
};
int ret;
const struct of_device_id *of_id;
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct pinctrl_state *pinctrl_hsic_idle;

of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
of_id = of_match_device(ci_hdrc_imx_dt_ids, dev);
if (!of_id)
return -ENODEV;

Expand All @@ -268,19 +302,73 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
return -ENOMEM;

platform_set_drvdata(pdev, data);
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
data->usbmisc_data = usbmisc_get_init_data(dev);
if (IS_ERR(data->usbmisc_data))
return PTR_ERR(data->usbmisc_data);

ret = imx_get_clks(&pdev->dev);
if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) {
pdata.flags |= CI_HDRC_IMX_IS_HSIC;
data->usbmisc_data->hsic = 1;
data->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(data->pinctrl)) {
dev_err(dev, "pinctrl get failed, err=%ld\n",
PTR_ERR(data->pinctrl));
return PTR_ERR(data->pinctrl);
}

pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
if (IS_ERR(pinctrl_hsic_idle)) {
dev_err(dev,
"pinctrl_hsic_idle lookup failed, err=%ld\n",
PTR_ERR(pinctrl_hsic_idle));
return PTR_ERR(pinctrl_hsic_idle);
}

ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle);
if (ret) {
dev_err(dev, "hsic_idle select failed, err=%d\n", ret);
return ret;
}

data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl,
"active");
if (IS_ERR(data->pinctrl_hsic_active)) {
dev_err(dev,
"pinctrl_hsic_active lookup failed, err=%ld\n",
PTR_ERR(data->pinctrl_hsic_active));
return PTR_ERR(data->pinctrl_hsic_active);
}

data->hsic_pad_regulator = devm_regulator_get(dev, "hsic");
if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
/* no pad regualator is needed */
data->hsic_pad_regulator = NULL;
} else if (IS_ERR(data->hsic_pad_regulator)) {
dev_err(dev, "Get HSIC pad regulator error: %ld\n",
PTR_ERR(data->hsic_pad_regulator));
return PTR_ERR(data->hsic_pad_regulator);
}

if (data->hsic_pad_regulator) {
ret = regulator_enable(data->hsic_pad_regulator);
if (ret) {
dev_err(dev,
"Failed to enable HSIC pad regulator\n");
return ret;
}
}
}
ret = imx_get_clks(dev);
if (ret)
return ret;
goto disable_hsic_regulator;

ret = imx_prepare_enable_clks(&pdev->dev);
ret = imx_prepare_enable_clks(dev);
if (ret)
return ret;
goto disable_hsic_regulator;

data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0);
if (IS_ERR(data->phy)) {
ret = PTR_ERR(data->phy);
/* Return -EINVAL if no usbphy is available */
Expand All @@ -305,40 +393,43 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)

ret = imx_usbmisc_init(data->usbmisc_data);
if (ret) {
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
goto err_clk;
}

data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
data->ci_pdev = ci_hdrc_add_device(dev,
pdev->resource, pdev->num_resources,
&pdata);
if (IS_ERR(data->ci_pdev)) {
ret = PTR_ERR(data->ci_pdev);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"ci_hdrc_add_device failed, err=%d\n", ret);
dev_err(dev, "ci_hdrc_add_device failed, err=%d\n",
ret);
goto err_clk;
}

ret = imx_usbmisc_init_post(data->usbmisc_data);
if (ret) {
dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
dev_err(dev, "usbmisc post failed, ret=%d\n", ret);
goto disable_device;
}

if (data->supports_runtime_pm) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}

device_set_wakeup_capable(&pdev->dev, true);
device_set_wakeup_capable(dev, true);

return 0;

disable_device:
ci_hdrc_remove_device(data->ci_pdev);
err_clk:
imx_disable_unprepare_clks(&pdev->dev);
imx_disable_unprepare_clks(dev);
disable_hsic_regulator:
if (data->hsic_pad_regulator)
ret = regulator_disable(data->hsic_pad_regulator);
return ret;
}

Expand All @@ -355,6 +446,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
if (data->override_phy_control)
usb_phy_shutdown(data->phy);
imx_disable_unprepare_clks(&pdev->dev);
if (data->hsic_pad_regulator)
regulator_disable(data->hsic_pad_regulator);

return 0;
}
Expand All @@ -367,9 +460,16 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
static int __maybe_unused imx_controller_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;

dev_dbg(dev, "at %s\n", __func__);

ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false);
if (ret) {
dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
return ret;
}

imx_disable_unprepare_clks(dev);
data->in_lpm = true;

Expand Down Expand Up @@ -400,8 +500,16 @@ static int __maybe_unused imx_controller_resume(struct device *dev)
goto clk_disable;
}

ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true);
if (ret) {
dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
goto hsic_set_clk_fail;
}

return 0;

hsic_set_clk_fail:
imx_usbmisc_set_wakeup(data->usbmisc_data, true);
clk_disable:
imx_disable_unprepare_clks(dev);
return ret;
Expand Down
9 changes: 6 additions & 3 deletions drivers/usb/chipidea/ci_hdrc_imx.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ struct imx_usbmisc_data {
unsigned int oc_polarity:1; /* over current polarity if oc enabled */
unsigned int evdo:1; /* set external vbus divider option */
unsigned int ulpi:1; /* connected to an ULPI phy */
unsigned int hsic:1; /* HSIC controlller */
};

int imx_usbmisc_init(struct imx_usbmisc_data *);
int imx_usbmisc_init_post(struct imx_usbmisc_data *);
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool);
int imx_usbmisc_init(struct imx_usbmisc_data *data);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data);
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);

#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
Loading

0 comments on commit 7c8e890

Please sign in to comment.