15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28e22978cSAlexander Shishkin /*
38e22978cSAlexander Shishkin * Copyright 2012 Freescale Semiconductor, Inc.
48e22978cSAlexander Shishkin * Copyright (C) 2012 Marek Vasut <marex@denx.de>
58e22978cSAlexander Shishkin * on behalf of DENX Software Engineering GmbH
68e22978cSAlexander Shishkin */
78e22978cSAlexander Shishkin
88e22978cSAlexander Shishkin #include <linux/module.h>
9484468fbSRob Herring #include <linux/of.h>
108e22978cSAlexander Shishkin #include <linux/of_platform.h>
118e22978cSAlexander Shishkin #include <linux/platform_device.h>
128e22978cSAlexander Shishkin #include <linux/pm_runtime.h>
138e22978cSAlexander Shishkin #include <linux/usb/chipidea.h>
14d13631bbSFabien Lahoudere #include <linux/usb/of.h>
158e22978cSAlexander Shishkin #include <linux/clk.h>
167c8e8909SPeter Chen #include <linux/pinctrl/consumer.h>
17d1609c31SPeter Chen #include <linux/pm_qos.h>
188e22978cSAlexander Shishkin
198e22978cSAlexander Shishkin #include "ci.h"
208e22978cSAlexander Shishkin #include "ci_hdrc_imx.h"
218e22978cSAlexander Shishkin
221071055eSPeter Chen struct ci_hdrc_imx_platform_flag {
231071055eSPeter Chen unsigned int flags;
241071055eSPeter Chen };
251071055eSPeter Chen
2681345722SStefan Wahren static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
2781345722SStefan Wahren .flags = CI_HDRC_TURN_VBUS_EARLY_ON |
2881345722SStefan Wahren CI_HDRC_DISABLE_STREAMING,
2981345722SStefan Wahren };
3081345722SStefan Wahren
311071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
3204e16379SSebastian Reichel .flags = CI_HDRC_DISABLE_STREAMING,
331071055eSPeter Chen };
341071055eSPeter Chen
351071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
366adb9b7bSLi Jun .flags = CI_HDRC_IMX28_WRITE_FIX |
370ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON |
380ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING,
391071055eSPeter Chen };
401071055eSPeter Chen
41e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
426adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
430ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON |
440ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING,
45e14db48dSPeter Chen };
46e14db48dSPeter Chen
47e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
486adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
490ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON |
500ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING,
51e14db48dSPeter Chen };
52e14db48dSPeter Chen
53e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
546adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
550ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON |
560ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING,
57e14db48dSPeter Chen };
58e14db48dSPeter Chen
5952fe568eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
6052fe568eSPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
61c7721e15SFabio Estevam CI_HDRC_TURN_VBUS_EARLY_ON |
62c7721e15SFabio Estevam CI_HDRC_DISABLE_DEVICE_STREAMING,
6352fe568eSPeter Chen };
6452fe568eSPeter Chen
655cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
665cb377c5SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
675cb377c5SPeter Chen };
685cb377c5SPeter Chen
69d1609c31SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = {
70d1609c31SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
7112e6ac69SXu Yang CI_HDRC_HAS_PORTSC_PEC_MISSED |
72d1609c31SPeter Chen CI_HDRC_PMQOS,
73d1609c31SPeter Chen };
74d1609c31SPeter Chen
759a070e8eSXu Yang static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
7612e6ac69SXu Yang .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
7712e6ac69SXu Yang CI_HDRC_HAS_PORTSC_PEC_MISSED,
789a070e8eSXu Yang };
799a070e8eSXu Yang
801071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
8181345722SStefan Wahren { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
821071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
831071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
84e14db48dSPeter Chen { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
85e14db48dSPeter Chen { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
868315b77dSLi Jun { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
8752fe568eSPeter Chen { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
885cb377c5SPeter Chen { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
89d1609c31SPeter Chen { .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data},
909a070e8eSXu Yang { .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data},
911071055eSPeter Chen { /* sentinel */ }
921071055eSPeter Chen };
931071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
941071055eSPeter Chen
958e22978cSAlexander Shishkin struct ci_hdrc_imx_data {
968e22978cSAlexander Shishkin struct usb_phy *phy;
978e22978cSAlexander Shishkin struct platform_device *ci_pdev;
988e22978cSAlexander Shishkin struct clk *clk;
995dbe9ac2SXu Yang struct clk *clk_wakeup;
10005986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data;
101e14db48dSPeter Chen bool supports_runtime_pm;
102be9cae24SSebastian Reichel bool override_phy_control;
103e14db48dSPeter Chen bool in_lpm;
1047c8e8909SPeter Chen struct pinctrl *pinctrl;
1057c8e8909SPeter Chen struct pinctrl_state *pinctrl_hsic_active;
1067c8e8909SPeter Chen struct regulator *hsic_pad_regulator;
107ae3e57aeSPeter Chen /* SoC before i.mx6 (except imx23/imx28) needs three clks */
108ae3e57aeSPeter Chen bool need_three_clks;
109ae3e57aeSPeter Chen struct clk *clk_ipg;
110ae3e57aeSPeter Chen struct clk *clk_ahb;
111ae3e57aeSPeter Chen struct clk *clk_per;
112ae3e57aeSPeter Chen /* --------------------------------- */
113d1609c31SPeter Chen struct pm_qos_request pm_qos_req;
114d1609c31SPeter Chen const struct ci_hdrc_imx_platform_flag *plat_data;
1158e22978cSAlexander Shishkin };
1168e22978cSAlexander Shishkin
1178e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */
1188e22978cSAlexander Shishkin
usbmisc_get_init_data(struct device * dev)11905986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
1208e22978cSAlexander Shishkin {
121f40017e0SStefan Agner struct platform_device *misc_pdev;
1228e22978cSAlexander Shishkin struct device_node *np = dev->of_node;
1238e22978cSAlexander Shishkin struct of_phandle_args args;
12405986ba9SSascha Hauer struct imx_usbmisc_data *data;
1258e22978cSAlexander Shishkin int ret;
1268e22978cSAlexander Shishkin
12705986ba9SSascha Hauer /*
12805986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't
12905986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here)
13005986ba9SSascha Hauer */
131*1380f158SRob Herring (Arm) if (!of_property_present(np, "fsl,usbmisc"))
13205986ba9SSascha Hauer return NULL;
13305986ba9SSascha Hauer
13405986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
13505986ba9SSascha Hauer if (!data)
13605986ba9SSascha Hauer return ERR_PTR(-ENOMEM);
1378e22978cSAlexander Shishkin
1388e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
1398e22978cSAlexander Shishkin 0, &args);
1408e22978cSAlexander Shishkin if (ret) {
1418e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
1428e22978cSAlexander Shishkin ret);
14305986ba9SSascha Hauer return ERR_PTR(ret);
1448e22978cSAlexander Shishkin }
14505986ba9SSascha Hauer
14605986ba9SSascha Hauer data->index = args.args[0];
147f40017e0SStefan Agner
148f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np);
1498e22978cSAlexander Shishkin of_node_put(args.np);
1508e22978cSAlexander Shishkin
15183a43ff8SYu Kuai if (!misc_pdev)
152f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER);
153f40017e0SStefan Agner
15483a43ff8SYu Kuai if (!platform_get_drvdata(misc_pdev)) {
15583a43ff8SYu Kuai put_device(&misc_pdev->dev);
15683a43ff8SYu Kuai return ERR_PTR(-EPROBE_DEFER);
15783a43ff8SYu Kuai }
158f40017e0SStefan Agner data->dev = &misc_pdev->dev;
159f40017e0SStefan Agner
160a82bf696SUwe Kleine-König /*
161a82bf696SUwe Kleine-König * Check the various over current related properties. If over current
162a82bf696SUwe Kleine-König * detection is disabled we're not interested in the polarity.
163a82bf696SUwe Kleine-König */
164f977caeaSRob Herring if (of_property_read_bool(np, "disable-over-current")) {
16505986ba9SSascha Hauer data->disable_oc = 1;
166f977caeaSRob Herring } else if (of_property_read_bool(np, "over-current-active-high")) {
167a82bf696SUwe Kleine-König data->oc_pol_active_low = 0;
168a82bf696SUwe Kleine-König data->oc_pol_configured = 1;
169f977caeaSRob Herring } else if (of_property_read_bool(np, "over-current-active-low")) {
170a82bf696SUwe Kleine-König data->oc_pol_active_low = 1;
171a82bf696SUwe Kleine-König data->oc_pol_configured = 1;
1721bf4743fSUwe Kleine-König } else {
1731bf4743fSUwe Kleine-König dev_warn(dev, "No over current polarity defined\n");
174a82bf696SUwe Kleine-König }
1759dba516eSLi Jun
1765f0632c4SPhilipp Puschmann data->pwr_pol = of_property_read_bool(np, "power-active-high");
1775f0632c4SPhilipp Puschmann data->evdo = of_property_read_bool(np, "external-vbus-divider");
1788e22978cSAlexander Shishkin
179d13631bbSFabien Lahoudere if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
180d13631bbSFabien Lahoudere data->ulpi = 1;
181d13631bbSFabien Lahoudere
18236668515SXu Yang if (of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
18336668515SXu Yang &data->emp_curr_control))
18436668515SXu Yang data->emp_curr_control = -1;
18536668515SXu Yang if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
18636668515SXu Yang &data->dc_vol_level_adjust))
18736668515SXu Yang data->dc_vol_level_adjust = -1;
1883bd442e4SXu Yang if (of_property_read_u32(np, "fsl,picophy-rise-fall-time-adjust",
1893bd442e4SXu Yang &data->rise_fall_time_adjust))
1903bd442e4SXu Yang data->rise_fall_time_adjust = -1;
19158a3cefbSPeter Chen
19205986ba9SSascha Hauer return data;
1938e22978cSAlexander Shishkin }
1948e22978cSAlexander Shishkin
1958e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/
imx_get_clks(struct device * dev)196ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev)
197ae3e57aeSPeter Chen {
198ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
199ae3e57aeSPeter Chen int ret = 0;
200ae3e57aeSPeter Chen
201ae3e57aeSPeter Chen data->clk_ipg = devm_clk_get(dev, "ipg");
202ae3e57aeSPeter Chen if (IS_ERR(data->clk_ipg)) {
2035dbe9ac2SXu Yang /* If the platform only needs one primary clock */
204ae3e57aeSPeter Chen data->clk = devm_clk_get(dev, NULL);
205ae3e57aeSPeter Chen if (IS_ERR(data->clk)) {
206ae3e57aeSPeter Chen ret = PTR_ERR(data->clk);
207ae3e57aeSPeter Chen dev_err(dev,
208ae3e57aeSPeter Chen "Failed to get clks, err=%ld,%ld\n",
209ae3e57aeSPeter Chen PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
210ae3e57aeSPeter Chen return ret;
211ae3e57aeSPeter Chen }
2125dbe9ac2SXu Yang /* Get wakeup clock. Not all of the platforms need to
2135dbe9ac2SXu Yang * handle this clock. So make it optional.
2145dbe9ac2SXu Yang */
21543590333SXu Yang data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup");
2165dbe9ac2SXu Yang if (IS_ERR(data->clk_wakeup))
2175dbe9ac2SXu Yang ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup),
2185dbe9ac2SXu Yang "Failed to get wakeup clk\n");
219ae3e57aeSPeter Chen return ret;
220ae3e57aeSPeter Chen }
221ae3e57aeSPeter Chen
222ae3e57aeSPeter Chen data->clk_ahb = devm_clk_get(dev, "ahb");
223ae3e57aeSPeter Chen if (IS_ERR(data->clk_ahb)) {
224ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_ahb);
225ae3e57aeSPeter Chen dev_err(dev,
226ae3e57aeSPeter Chen "Failed to get ahb clock, err=%d\n", ret);
227ae3e57aeSPeter Chen return ret;
228ae3e57aeSPeter Chen }
229ae3e57aeSPeter Chen
230ae3e57aeSPeter Chen data->clk_per = devm_clk_get(dev, "per");
231ae3e57aeSPeter Chen if (IS_ERR(data->clk_per)) {
232ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_per);
233ae3e57aeSPeter Chen dev_err(dev,
234ae3e57aeSPeter Chen "Failed to get per clock, err=%d\n", ret);
235ae3e57aeSPeter Chen return ret;
236ae3e57aeSPeter Chen }
237ae3e57aeSPeter Chen
238ae3e57aeSPeter Chen data->need_three_clks = true;
239ae3e57aeSPeter Chen return ret;
240ae3e57aeSPeter Chen }
241ae3e57aeSPeter Chen
imx_prepare_enable_clks(struct device * dev)242ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev)
243ae3e57aeSPeter Chen {
244ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
245ae3e57aeSPeter Chen int ret = 0;
246ae3e57aeSPeter Chen
247ae3e57aeSPeter Chen if (data->need_three_clks) {
248ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ipg);
249ae3e57aeSPeter Chen if (ret) {
250ae3e57aeSPeter Chen dev_err(dev,
251ae3e57aeSPeter Chen "Failed to prepare/enable ipg clk, err=%d\n",
252ae3e57aeSPeter Chen ret);
253ae3e57aeSPeter Chen return ret;
254ae3e57aeSPeter Chen }
255ae3e57aeSPeter Chen
256ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ahb);
257ae3e57aeSPeter Chen if (ret) {
258ae3e57aeSPeter Chen dev_err(dev,
259ae3e57aeSPeter Chen "Failed to prepare/enable ahb clk, err=%d\n",
260ae3e57aeSPeter Chen ret);
261ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg);
262ae3e57aeSPeter Chen return ret;
263ae3e57aeSPeter Chen }
264ae3e57aeSPeter Chen
265ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_per);
266ae3e57aeSPeter Chen if (ret) {
267ae3e57aeSPeter Chen dev_err(dev,
268ae3e57aeSPeter Chen "Failed to prepare/enable per clk, err=%d\n",
269ae3e57aeSPeter Chen ret);
270ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb);
271ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg);
272ae3e57aeSPeter Chen return ret;
273ae3e57aeSPeter Chen }
274ae3e57aeSPeter Chen } else {
275ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk);
276ae3e57aeSPeter Chen if (ret) {
277ae3e57aeSPeter Chen dev_err(dev,
278ae3e57aeSPeter Chen "Failed to prepare/enable clk, err=%d\n",
279ae3e57aeSPeter Chen ret);
280ae3e57aeSPeter Chen return ret;
281ae3e57aeSPeter Chen }
282ae3e57aeSPeter Chen }
283ae3e57aeSPeter Chen
284ae3e57aeSPeter Chen return ret;
285ae3e57aeSPeter Chen }
286ae3e57aeSPeter Chen
imx_disable_unprepare_clks(struct device * dev)287ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev)
288ae3e57aeSPeter Chen {
289ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
290ae3e57aeSPeter Chen
291ae3e57aeSPeter Chen if (data->need_three_clks) {
292ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_per);
293ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb);
294ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg);
295ae3e57aeSPeter Chen } else {
296ae3e57aeSPeter Chen clk_disable_unprepare(data->clk);
297ae3e57aeSPeter Chen }
298ae3e57aeSPeter Chen }
2998e22978cSAlexander Shishkin
ci_hdrc_imx_notify_event(struct ci_hdrc * ci,unsigned int event)3007c8e8909SPeter Chen static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
3017c8e8909SPeter Chen {
3027c8e8909SPeter Chen struct device *dev = ci->dev->parent;
3037c8e8909SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
3047c8e8909SPeter Chen int ret = 0;
305746f316bSJun Li struct imx_usbmisc_data *mdata = data->usbmisc_data;
3067c8e8909SPeter Chen
3077c8e8909SPeter Chen switch (event) {
3087c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
3094d614128SPeter Chen if (data->pinctrl) {
3107c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl,
3117c8e8909SPeter Chen data->pinctrl_hsic_active);
3127c8e8909SPeter Chen if (ret)
3134d614128SPeter Chen dev_err(dev,
3144d614128SPeter Chen "hsic_active select failed, err=%d\n",
3157c8e8909SPeter Chen ret);
3164d614128SPeter Chen }
3177c8e8909SPeter Chen break;
3187c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
319746f316bSJun Li ret = imx_usbmisc_hsic_set_connect(mdata);
3207c8e8909SPeter Chen if (ret)
3217c8e8909SPeter Chen dev_err(dev,
3227c8e8909SPeter Chen "hsic_set_connect failed, err=%d\n", ret);
3237c8e8909SPeter Chen break;
324746f316bSJun Li case CI_HDRC_CONTROLLER_VBUS_EVENT:
325746f316bSJun Li if (ci->vbus_active)
326746f316bSJun Li ret = imx_usbmisc_charger_detection(mdata, true);
327746f316bSJun Li else
328746f316bSJun Li ret = imx_usbmisc_charger_detection(mdata, false);
329746f316bSJun Li if (ci->usb_phy)
330746f316bSJun Li schedule_work(&ci->usb_phy->chg_work);
331746f316bSJun Li break;
3327c8e8909SPeter Chen default:
3337c8e8909SPeter Chen break;
3347c8e8909SPeter Chen }
3357c8e8909SPeter Chen
3367c8e8909SPeter Chen return ret;
3377c8e8909SPeter Chen }
3387c8e8909SPeter Chen
ci_hdrc_imx_probe(struct platform_device * pdev)3398e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev)
3408e22978cSAlexander Shishkin {
3418e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data;
3428e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = {
343c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev),
3448e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET,
3457c8e8909SPeter Chen .notify_event = ci_hdrc_imx_notify_event,
3468e22978cSAlexander Shishkin };
3478e22978cSAlexander Shishkin int ret;
3486f51bc34SLABBE Corentin const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
349be9cae24SSebastian Reichel struct device_node *np = pdev->dev.of_node;
3507c8e8909SPeter Chen struct device *dev = &pdev->dev;
3516f51bc34SLABBE Corentin
35259b7c6a8SFabio Estevam imx_platform_flag = of_device_get_match_data(&pdev->dev);
3538e22978cSAlexander Shishkin
3548e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
35573529828SFabio Estevam if (!data)
3568e22978cSAlexander Shishkin return -ENOMEM;
3578e22978cSAlexander Shishkin
358d1609c31SPeter Chen data->plat_data = imx_platform_flag;
359d1609c31SPeter Chen pdata.flags |= imx_platform_flag->flags;
360ae3e57aeSPeter Chen platform_set_drvdata(pdev, data);
3617c8e8909SPeter Chen data->usbmisc_data = usbmisc_get_init_data(dev);
36205986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data))
36305986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data);
36405986ba9SSascha Hauer
3658ff396feSPeter Chen if ((of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC)
3668ff396feSPeter Chen && data->usbmisc_data) {
3677c8e8909SPeter Chen pdata.flags |= CI_HDRC_IMX_IS_HSIC;
3687c8e8909SPeter Chen data->usbmisc_data->hsic = 1;
3697c8e8909SPeter Chen data->pinctrl = devm_pinctrl_get(dev);
3703f4aad6eSPeter Chen if (PTR_ERR(data->pinctrl) == -ENODEV)
3713f4aad6eSPeter Chen data->pinctrl = NULL;
37218171cfcSAlexander Stein else if (IS_ERR(data->pinctrl))
37318171cfcSAlexander Stein return dev_err_probe(dev, PTR_ERR(data->pinctrl),
37418171cfcSAlexander Stein "pinctrl get failed\n");
375ae3e57aeSPeter Chen
3764d614128SPeter Chen data->hsic_pad_regulator =
3774d614128SPeter Chen devm_regulator_get_optional(dev, "hsic");
3784d614128SPeter Chen if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
3799c3959bbSJonathan Neuschäfer /* no pad regulator is needed */
3804d614128SPeter Chen data->hsic_pad_regulator = NULL;
38118171cfcSAlexander Stein } else if (IS_ERR(data->hsic_pad_regulator))
38218171cfcSAlexander Stein return dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator),
38318171cfcSAlexander Stein "Get HSIC pad regulator error\n");
3844d614128SPeter Chen
3854d614128SPeter Chen if (data->hsic_pad_regulator) {
3864d614128SPeter Chen ret = regulator_enable(data->hsic_pad_regulator);
3874d614128SPeter Chen if (ret) {
3884d614128SPeter Chen dev_err(dev,
3894d614128SPeter Chen "Failed to enable HSIC pad regulator\n");
3904d614128SPeter Chen return ret;
3914d614128SPeter Chen }
3924d614128SPeter Chen }
3934d614128SPeter Chen }
3944d614128SPeter Chen
3954d614128SPeter Chen /* HSIC pinctrl handling */
3964d614128SPeter Chen if (data->pinctrl) {
3974d614128SPeter Chen struct pinctrl_state *pinctrl_hsic_idle;
3984d614128SPeter Chen
3997c8e8909SPeter Chen pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
4007c8e8909SPeter Chen if (IS_ERR(pinctrl_hsic_idle)) {
4017c8e8909SPeter Chen dev_err(dev,
4027c8e8909SPeter Chen "pinctrl_hsic_idle lookup failed, err=%ld\n",
4037c8e8909SPeter Chen PTR_ERR(pinctrl_hsic_idle));
4047c8e8909SPeter Chen return PTR_ERR(pinctrl_hsic_idle);
4057c8e8909SPeter Chen }
4068e22978cSAlexander Shishkin
4077c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle);
4087c8e8909SPeter Chen if (ret) {
4097c8e8909SPeter Chen dev_err(dev, "hsic_idle select failed, err=%d\n", ret);
4107c8e8909SPeter Chen return ret;
4117c8e8909SPeter Chen }
4127c8e8909SPeter Chen
4137c8e8909SPeter Chen data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl,
4147c8e8909SPeter Chen "active");
4157c8e8909SPeter Chen if (IS_ERR(data->pinctrl_hsic_active)) {
4167c8e8909SPeter Chen dev_err(dev,
4177c8e8909SPeter Chen "pinctrl_hsic_active lookup failed, err=%ld\n",
4187c8e8909SPeter Chen PTR_ERR(data->pinctrl_hsic_active));
4197c8e8909SPeter Chen return PTR_ERR(data->pinctrl_hsic_active);
4207c8e8909SPeter Chen }
4217c8e8909SPeter Chen }
422d1609c31SPeter Chen
423d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS)
42477b35245SRafael J. Wysocki cpu_latency_qos_add_request(&data->pm_qos_req, 0);
425d1609c31SPeter Chen
4267c8e8909SPeter Chen ret = imx_get_clks(dev);
4277c8e8909SPeter Chen if (ret)
4287c8e8909SPeter Chen goto disable_hsic_regulator;
4297c8e8909SPeter Chen
4307c8e8909SPeter Chen ret = imx_prepare_enable_clks(dev);
4317c8e8909SPeter Chen if (ret)
4327c8e8909SPeter Chen goto disable_hsic_regulator;
4337c8e8909SPeter Chen
4345dbe9ac2SXu Yang ret = clk_prepare_enable(data->clk_wakeup);
4355dbe9ac2SXu Yang if (ret)
4365dbe9ac2SXu Yang goto err_wakeup_clk;
4375dbe9ac2SXu Yang
4387c8e8909SPeter Chen data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0);
439af59a8b1SPeter Chen if (IS_ERR(data->phy)) {
440af59a8b1SPeter Chen ret = PTR_ERR(data->phy);
4413a1bd049SAlexander Stein if (ret != -ENODEV) {
4423a1bd049SAlexander Stein dev_err_probe(dev, ret, "Failed to parse fsl,usbphy\n");
443d4d2e532SDan Carpenter goto err_clk;
4443a1bd049SAlexander Stein }
4458253a34bSFabio Estevam data->phy = devm_usb_get_phy_by_phandle(dev, "phys", 0);
4468253a34bSFabio Estevam if (IS_ERR(data->phy)) {
4478253a34bSFabio Estevam ret = PTR_ERR(data->phy);
4483a1bd049SAlexander Stein if (ret == -ENODEV) {
449ed5a419bSPeter Chen data->phy = NULL;
4503a1bd049SAlexander Stein } else {
4513a1bd049SAlexander Stein dev_err_probe(dev, ret, "Failed to parse phys\n");
4528e22978cSAlexander Shishkin goto err_clk;
4538e22978cSAlexander Shishkin }
4548253a34bSFabio Estevam }
4553a1bd049SAlexander Stein }
4568e22978cSAlexander Shishkin
457ef44cb42SAntoine Tenart pdata.usb_phy = data->phy;
458746f316bSJun Li if (data->usbmisc_data)
459746f316bSJun Li data->usbmisc_data->usb_phy = data->phy;
460be9cae24SSebastian Reichel
46103e6275aSAndrey Smirnov if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
46203e6275aSAndrey Smirnov of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
463be9cae24SSebastian Reichel of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
464be9cae24SSebastian Reichel pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
465be9cae24SSebastian Reichel data->override_phy_control = true;
466be9cae24SSebastian Reichel usb_phy_init(pdata.usb_phy);
467be9cae24SSebastian Reichel }
468be9cae24SSebastian Reichel
469e14db48dSPeter Chen if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
470e14db48dSPeter Chen data->supports_runtime_pm = true;
4711071055eSPeter Chen
47205986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data);
4738e22978cSAlexander Shishkin if (ret) {
4747c8e8909SPeter Chen dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
475af59a8b1SPeter Chen goto err_clk;
4768e22978cSAlexander Shishkin }
4778e22978cSAlexander Shishkin
4787c8e8909SPeter Chen data->ci_pdev = ci_hdrc_add_device(dev,
4798e22978cSAlexander Shishkin pdev->resource, pdev->num_resources,
4808e22978cSAlexander Shishkin &pdata);
4818e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) {
4828e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev);
48318171cfcSAlexander Stein dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n");
484af59a8b1SPeter Chen goto err_clk;
4858e22978cSAlexander Shishkin }
4868e22978cSAlexander Shishkin
487df17aa9fSLi Jun if (data->usbmisc_data) {
48893c2c733SLi Jun if (!IS_ERR(pdata.id_extcon.edev) ||
48993c2c733SLi Jun of_property_read_bool(np, "usb-role-switch"))
49093c2c733SLi Jun data->usbmisc_data->ext_id = 1;
49193c2c733SLi Jun
49293c2c733SLi Jun if (!IS_ERR(pdata.vbus_extcon.edev) ||
49393c2c733SLi Jun of_property_read_bool(np, "usb-role-switch"))
49493c2c733SLi Jun data->usbmisc_data->ext_vbus = 1;
495d6f93d21SPeter Chen
496d6f93d21SPeter Chen /* usbmisc needs to know dr mode to choose wakeup setting */
497d6f93d21SPeter Chen data->usbmisc_data->available_role =
498d6f93d21SPeter Chen ci_hdrc_query_available_role(data->ci_pdev);
499df17aa9fSLi Jun }
50093c2c733SLi Jun
50105986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data);
5028e22978cSAlexander Shishkin if (ret) {
5037c8e8909SPeter Chen dev_err(dev, "usbmisc post failed, ret=%d\n", ret);
5048e22978cSAlexander Shishkin goto disable_device;
5058e22978cSAlexander Shishkin }
5068e22978cSAlexander Shishkin
507e14db48dSPeter Chen if (data->supports_runtime_pm) {
5087c8e8909SPeter Chen pm_runtime_set_active(dev);
5097c8e8909SPeter Chen pm_runtime_enable(dev);
510e14db48dSPeter Chen }
5118e22978cSAlexander Shishkin
5127c8e8909SPeter Chen device_set_wakeup_capable(dev, true);
5136d653110SPeter Chen
5148e22978cSAlexander Shishkin return 0;
5158e22978cSAlexander Shishkin
5168e22978cSAlexander Shishkin disable_device:
5178e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev);
5188e22978cSAlexander Shishkin err_clk:
5195dbe9ac2SXu Yang clk_disable_unprepare(data->clk_wakeup);
5205dbe9ac2SXu Yang err_wakeup_clk:
5217c8e8909SPeter Chen imx_disable_unprepare_clks(dev);
5227c8e8909SPeter Chen disable_hsic_regulator:
5237c8e8909SPeter Chen if (data->hsic_pad_regulator)
524141822aaSAndré Draszik /* don't overwrite original ret (cf. EPROBE_DEFER) */
525141822aaSAndré Draszik regulator_disable(data->hsic_pad_regulator);
526d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS)
52777b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req);
528141822aaSAndré Draszik data->ci_pdev = NULL;
5298e22978cSAlexander Shishkin return ret;
5308e22978cSAlexander Shishkin }
5318e22978cSAlexander Shishkin
ci_hdrc_imx_remove(struct platform_device * pdev)532ad593ed6SUwe Kleine-König static void ci_hdrc_imx_remove(struct platform_device *pdev)
5338e22978cSAlexander Shishkin {
5348e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
5358e22978cSAlexander Shishkin
536e14db48dSPeter Chen if (data->supports_runtime_pm) {
537e14db48dSPeter Chen pm_runtime_get_sync(&pdev->dev);
5388e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev);
539e14db48dSPeter Chen pm_runtime_put_noidle(&pdev->dev);
540e14db48dSPeter Chen }
541141822aaSAndré Draszik if (data->ci_pdev)
5428e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev);
543be9cae24SSebastian Reichel if (data->override_phy_control)
544be9cae24SSebastian Reichel usb_phy_shutdown(data->phy);
545141822aaSAndré Draszik if (data->ci_pdev) {
546ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev);
5475dbe9ac2SXu Yang clk_disable_unprepare(data->clk_wakeup);
548d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS)
54977b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req);
5507c8e8909SPeter Chen if (data->hsic_pad_regulator)
5517c8e8909SPeter Chen regulator_disable(data->hsic_pad_regulator);
552141822aaSAndré Draszik }
5538e22978cSAlexander Shishkin }
5548e22978cSAlexander Shishkin
ci_hdrc_imx_shutdown(struct platform_device * pdev)555b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
556b09b5224SAndreas Fenkart {
557b09b5224SAndreas Fenkart ci_hdrc_imx_remove(pdev);
558b09b5224SAndreas Fenkart }
559b09b5224SAndreas Fenkart
imx_controller_suspend(struct device * dev,pm_message_t msg)56026faae34SFabio Estevam static int imx_controller_suspend(struct device *dev,
561b332d6d5SLi Jun pm_message_t msg)
5622558c1f5SPeter Chen {
5632558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
5647c8e8909SPeter Chen int ret = 0;
5652558c1f5SPeter Chen
5662558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__);
5672558c1f5SPeter Chen
568b332d6d5SLi Jun ret = imx_usbmisc_suspend(data->usbmisc_data,
569b332d6d5SLi Jun PMSG_IS_AUTO(msg) || device_may_wakeup(dev));
5707c8e8909SPeter Chen if (ret) {
571b332d6d5SLi Jun dev_err(dev,
572b332d6d5SLi Jun "usbmisc suspend failed, ret=%d\n", ret);
5737c8e8909SPeter Chen return ret;
5747c8e8909SPeter Chen }
5757c8e8909SPeter Chen
576ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev);
577d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS)
57877b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req);
579d1609c31SPeter Chen
580e14db48dSPeter Chen data->in_lpm = true;
5812558c1f5SPeter Chen
5822558c1f5SPeter Chen return 0;
5832558c1f5SPeter Chen }
5842558c1f5SPeter Chen
imx_controller_resume(struct device * dev,pm_message_t msg)58526faae34SFabio Estevam static int imx_controller_resume(struct device *dev,
586b332d6d5SLi Jun pm_message_t msg)
5872558c1f5SPeter Chen {
5882558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
589e14db48dSPeter Chen int ret = 0;
5902558c1f5SPeter Chen
5912558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__);
5922558c1f5SPeter Chen
593e14db48dSPeter Chen if (!data->in_lpm) {
594e14db48dSPeter Chen WARN_ON(1);
595e14db48dSPeter Chen return 0;
5962558c1f5SPeter Chen }
5972558c1f5SPeter Chen
598d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS)
59977b35245SRafael J. Wysocki cpu_latency_qos_add_request(&data->pm_qos_req, 0);
600d1609c31SPeter Chen
601ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(dev);
602e14db48dSPeter Chen if (ret)
603e14db48dSPeter Chen return ret;
604e14db48dSPeter Chen
605e14db48dSPeter Chen data->in_lpm = false;
606e14db48dSPeter Chen
607b332d6d5SLi Jun ret = imx_usbmisc_resume(data->usbmisc_data,
608b332d6d5SLi Jun PMSG_IS_AUTO(msg) || device_may_wakeup(dev));
609e14db48dSPeter Chen if (ret) {
610b332d6d5SLi Jun dev_err(dev, "usbmisc resume failed, ret=%d\n", ret);
611e14db48dSPeter Chen goto clk_disable;
612e14db48dSPeter Chen }
613e14db48dSPeter Chen
614e14db48dSPeter Chen return 0;
615e14db48dSPeter Chen
616e14db48dSPeter Chen clk_disable:
617ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev);
618e14db48dSPeter Chen return ret;
619e14db48dSPeter Chen }
620e14db48dSPeter Chen
ci_hdrc_imx_suspend(struct device * dev)62126faae34SFabio Estevam static int ci_hdrc_imx_suspend(struct device *dev)
6222558c1f5SPeter Chen {
6236d653110SPeter Chen int ret;
6246d653110SPeter Chen
625e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
626e14db48dSPeter Chen
627e14db48dSPeter Chen if (data->in_lpm)
628e14db48dSPeter Chen /* The core's suspend doesn't run */
629e14db48dSPeter Chen return 0;
630e14db48dSPeter Chen
631b332d6d5SLi Jun ret = imx_controller_suspend(dev, PMSG_SUSPEND);
63271ac680eSPeter Chen if (ret)
63371ac680eSPeter Chen return ret;
63471ac680eSPeter Chen
63571ac680eSPeter Chen pinctrl_pm_select_sleep_state(dev);
63671ac680eSPeter Chen return ret;
6372558c1f5SPeter Chen }
6382558c1f5SPeter Chen
ci_hdrc_imx_resume(struct device * dev)63926faae34SFabio Estevam static int ci_hdrc_imx_resume(struct device *dev)
6402558c1f5SPeter Chen {
641e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
642e14db48dSPeter Chen int ret;
643e14db48dSPeter Chen
64471ac680eSPeter Chen pinctrl_pm_select_default_state(dev);
645b332d6d5SLi Jun ret = imx_controller_resume(dev, PMSG_RESUME);
646e14db48dSPeter Chen if (!ret && data->supports_runtime_pm) {
647e14db48dSPeter Chen pm_runtime_disable(dev);
648e14db48dSPeter Chen pm_runtime_set_active(dev);
649e14db48dSPeter Chen pm_runtime_enable(dev);
650e14db48dSPeter Chen }
651e14db48dSPeter Chen
652e14db48dSPeter Chen return ret;
6532558c1f5SPeter Chen }
6542558c1f5SPeter Chen
ci_hdrc_imx_runtime_suspend(struct device * dev)65526faae34SFabio Estevam static int ci_hdrc_imx_runtime_suspend(struct device *dev)
656e14db48dSPeter Chen {
657e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
658e14db48dSPeter Chen
659e14db48dSPeter Chen if (data->in_lpm) {
660e14db48dSPeter Chen WARN_ON(1);
661e14db48dSPeter Chen return 0;
662e14db48dSPeter Chen }
663e14db48dSPeter Chen
664b332d6d5SLi Jun return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
665e14db48dSPeter Chen }
666e14db48dSPeter Chen
ci_hdrc_imx_runtime_resume(struct device * dev)66726faae34SFabio Estevam static int ci_hdrc_imx_runtime_resume(struct device *dev)
668e14db48dSPeter Chen {
669b332d6d5SLi Jun return imx_controller_resume(dev, PMSG_AUTO_RESUME);
670e14db48dSPeter Chen }
671e14db48dSPeter Chen
6722558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
67326faae34SFabio Estevam SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
67426faae34SFabio Estevam RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, ci_hdrc_imx_runtime_resume, NULL)
6752558c1f5SPeter Chen };
6768e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = {
6778e22978cSAlexander Shishkin .probe = ci_hdrc_imx_probe,
678ad593ed6SUwe Kleine-König .remove_new = ci_hdrc_imx_remove,
679b09b5224SAndreas Fenkart .shutdown = ci_hdrc_imx_shutdown,
6808e22978cSAlexander Shishkin .driver = {
6818e22978cSAlexander Shishkin .name = "imx_usb",
6828e22978cSAlexander Shishkin .of_match_table = ci_hdrc_imx_dt_ids,
68326faae34SFabio Estevam .pm = pm_ptr(&ci_hdrc_imx_pm_ops),
6848e22978cSAlexander Shishkin },
6858e22978cSAlexander Shishkin };
6868e22978cSAlexander Shishkin
6878e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver);
6888e22978cSAlexander Shishkin
6898e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb");
6901f06072cSMarcus Folkesson MODULE_LICENSE("GPL");
6918e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
6928e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
6938e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
694