15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2197ba5f4SPaul Zimmerman /*
3197ba5f4SPaul Zimmerman * platform.c - DesignWare HS OTG Controller platform driver
4197ba5f4SPaul Zimmerman *
5197ba5f4SPaul Zimmerman * Copyright (C) Matthijs Kooijman <matthijs@stdin.nl>
6197ba5f4SPaul Zimmerman */
7197ba5f4SPaul Zimmerman
8197ba5f4SPaul Zimmerman #include <linux/kernel.h>
9197ba5f4SPaul Zimmerman #include <linux/module.h>
10197ba5f4SPaul Zimmerman #include <linux/slab.h>
1109a75e85SMarek Szyprowski #include <linux/clk.h>
12197ba5f4SPaul Zimmerman #include <linux/device.h>
13197ba5f4SPaul Zimmerman #include <linux/dma-mapping.h>
14484468fbSRob Herring #include <linux/of.h>
157ad8096eSMarek Szyprowski #include <linux/mutex.h>
16197ba5f4SPaul Zimmerman #include <linux/platform_device.h>
1709a75e85SMarek Szyprowski #include <linux/phy/phy.h>
1809a75e85SMarek Szyprowski #include <linux/platform_data/s3c-hsotg.h>
1983f8da56SDinh Nguyen #include <linux/reset.h>
20197ba5f4SPaul Zimmerman
21c0155b9dSKever Yang #include <linux/usb/of.h>
22c0155b9dSKever Yang
23197ba5f4SPaul Zimmerman #include "core.h"
24197ba5f4SPaul Zimmerman #include "hcd.h"
25f91eea44SMian Yousaf Kaukab #include "debug.h"
26197ba5f4SPaul Zimmerman
27197ba5f4SPaul Zimmerman static const char dwc2_driver_name[] = "dwc2";
28197ba5f4SPaul Zimmerman
295268ed9dSJohn Youn /*
305268ed9dSJohn Youn * Check the dr_mode against the module configuration and hardware
315268ed9dSJohn Youn * capabilities.
325268ed9dSJohn Youn *
335268ed9dSJohn Youn * The hardware, module, and dr_mode, can each be set to host, device,
345268ed9dSJohn Youn * or otg. Check that all these values are compatible and adjust the
355268ed9dSJohn Youn * value of dr_mode if possible.
365268ed9dSJohn Youn *
375268ed9dSJohn Youn * actual
385268ed9dSJohn Youn * HW MOD dr_mode dr_mode
395268ed9dSJohn Youn * ------------------------------
405268ed9dSJohn Youn * HST HST any : HST
415268ed9dSJohn Youn * HST DEV any : ---
425268ed9dSJohn Youn * HST OTG any : HST
435268ed9dSJohn Youn *
445268ed9dSJohn Youn * DEV HST any : ---
455268ed9dSJohn Youn * DEV DEV any : DEV
465268ed9dSJohn Youn * DEV OTG any : DEV
475268ed9dSJohn Youn *
485268ed9dSJohn Youn * OTG HST any : HST
495268ed9dSJohn Youn * OTG DEV any : DEV
505268ed9dSJohn Youn * OTG OTG any : dr_mode
515268ed9dSJohn Youn */
dwc2_get_dr_mode(struct dwc2_hsotg * hsotg)525268ed9dSJohn Youn static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
535268ed9dSJohn Youn {
545268ed9dSJohn Youn enum usb_dr_mode mode;
555268ed9dSJohn Youn
565268ed9dSJohn Youn hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
575268ed9dSJohn Youn if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
585268ed9dSJohn Youn hsotg->dr_mode = USB_DR_MODE_OTG;
595268ed9dSJohn Youn
605268ed9dSJohn Youn mode = hsotg->dr_mode;
615268ed9dSJohn Youn
625268ed9dSJohn Youn if (dwc2_hw_is_device(hsotg)) {
635268ed9dSJohn Youn if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
645268ed9dSJohn Youn dev_err(hsotg->dev,
655268ed9dSJohn Youn "Controller does not support host mode.\n");
665268ed9dSJohn Youn return -EINVAL;
675268ed9dSJohn Youn }
685268ed9dSJohn Youn mode = USB_DR_MODE_PERIPHERAL;
695268ed9dSJohn Youn } else if (dwc2_hw_is_host(hsotg)) {
705268ed9dSJohn Youn if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
715268ed9dSJohn Youn dev_err(hsotg->dev,
725268ed9dSJohn Youn "Controller does not support device mode.\n");
735268ed9dSJohn Youn return -EINVAL;
745268ed9dSJohn Youn }
755268ed9dSJohn Youn mode = USB_DR_MODE_HOST;
765268ed9dSJohn Youn } else {
775268ed9dSJohn Youn if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
785268ed9dSJohn Youn mode = USB_DR_MODE_HOST;
795268ed9dSJohn Youn else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
805268ed9dSJohn Youn mode = USB_DR_MODE_PERIPHERAL;
815268ed9dSJohn Youn }
825268ed9dSJohn Youn
835268ed9dSJohn Youn if (mode != hsotg->dr_mode) {
845268ed9dSJohn Youn dev_warn(hsotg->dev,
855268ed9dSJohn Youn "Configuration mismatch. dr_mode forced to %s\n",
865268ed9dSJohn Youn mode == USB_DR_MODE_HOST ? "host" : "device");
875268ed9dSJohn Youn
885268ed9dSJohn Youn hsotg->dr_mode = mode;
895268ed9dSJohn Youn }
905268ed9dSJohn Youn
915268ed9dSJohn Youn return 0;
925268ed9dSJohn Youn }
935268ed9dSJohn Youn
__dwc2_lowlevel_hw_enable(struct dwc2_hsotg * hsotg)9409a75e85SMarek Szyprowski static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
9509a75e85SMarek Szyprowski {
9609a75e85SMarek Szyprowski struct platform_device *pdev = to_platform_device(hsotg->dev);
9709a75e85SMarek Szyprowski int ret;
9809a75e85SMarek Szyprowski
9909a75e85SMarek Szyprowski ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
10009a75e85SMarek Szyprowski hsotg->supplies);
10109a75e85SMarek Szyprowski if (ret)
10209a75e85SMarek Szyprowski return ret;
10309a75e85SMarek Szyprowski
10402329adeSFabrice Gasnier if (hsotg->utmi_clk) {
10502329adeSFabrice Gasnier ret = clk_prepare_enable(hsotg->utmi_clk);
10602329adeSFabrice Gasnier if (ret)
10702329adeSFabrice Gasnier goto err_dis_reg;
10802329adeSFabrice Gasnier }
10902329adeSFabrice Gasnier
1108aa90cf2SStefan Wahren if (hsotg->clk) {
11109a75e85SMarek Szyprowski ret = clk_prepare_enable(hsotg->clk);
11209a75e85SMarek Szyprowski if (ret)
11302329adeSFabrice Gasnier goto err_dis_utmi_clk;
1148aa90cf2SStefan Wahren }
11509a75e85SMarek Szyprowski
11634c0887fSJohn Youn if (hsotg->uphy) {
11709a75e85SMarek Szyprowski ret = usb_phy_init(hsotg->uphy);
11834c0887fSJohn Youn } else if (hsotg->plat && hsotg->plat->phy_init) {
11909a75e85SMarek Szyprowski ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
12034c0887fSJohn Youn } else {
12109a75e85SMarek Szyprowski ret = phy_init(hsotg->phy);
122236d8353SFabrice Gasnier if (ret == 0) {
123f9b995b4SHeiner Kallweit ret = phy_power_on(hsotg->phy);
124236d8353SFabrice Gasnier if (ret)
125236d8353SFabrice Gasnier phy_exit(hsotg->phy);
12609a75e85SMarek Szyprowski }
127236d8353SFabrice Gasnier }
128236d8353SFabrice Gasnier
129236d8353SFabrice Gasnier if (ret)
130236d8353SFabrice Gasnier goto err_dis_clk;
131236d8353SFabrice Gasnier
132236d8353SFabrice Gasnier return 0;
133236d8353SFabrice Gasnier
134236d8353SFabrice Gasnier err_dis_clk:
135236d8353SFabrice Gasnier if (hsotg->clk)
136236d8353SFabrice Gasnier clk_disable_unprepare(hsotg->clk);
137236d8353SFabrice Gasnier
13802329adeSFabrice Gasnier err_dis_utmi_clk:
13902329adeSFabrice Gasnier if (hsotg->utmi_clk)
14002329adeSFabrice Gasnier clk_disable_unprepare(hsotg->utmi_clk);
14102329adeSFabrice Gasnier
142236d8353SFabrice Gasnier err_dis_reg:
143236d8353SFabrice Gasnier regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
14409a75e85SMarek Szyprowski
14509a75e85SMarek Szyprowski return ret;
14609a75e85SMarek Szyprowski }
14709a75e85SMarek Szyprowski
14809a75e85SMarek Szyprowski /**
14909a75e85SMarek Szyprowski * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
15009a75e85SMarek Szyprowski * @hsotg: The driver state
15109a75e85SMarek Szyprowski *
15209a75e85SMarek Szyprowski * A wrapper for platform code responsible for controlling
15309a75e85SMarek Szyprowski * low-level USB platform resources (phy, clock, regulators)
15409a75e85SMarek Szyprowski */
dwc2_lowlevel_hw_enable(struct dwc2_hsotg * hsotg)15509a75e85SMarek Szyprowski int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
15609a75e85SMarek Szyprowski {
15709a75e85SMarek Szyprowski int ret = __dwc2_lowlevel_hw_enable(hsotg);
15809a75e85SMarek Szyprowski
15909a75e85SMarek Szyprowski if (ret == 0)
16009a75e85SMarek Szyprowski hsotg->ll_hw_enabled = true;
16109a75e85SMarek Szyprowski return ret;
16209a75e85SMarek Szyprowski }
16309a75e85SMarek Szyprowski
__dwc2_lowlevel_hw_disable(struct dwc2_hsotg * hsotg)16409a75e85SMarek Szyprowski static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
16509a75e85SMarek Szyprowski {
16609a75e85SMarek Szyprowski struct platform_device *pdev = to_platform_device(hsotg->dev);
16709a75e85SMarek Szyprowski int ret = 0;
16809a75e85SMarek Szyprowski
16934c0887fSJohn Youn if (hsotg->uphy) {
17009a75e85SMarek Szyprowski usb_phy_shutdown(hsotg->uphy);
17134c0887fSJohn Youn } else if (hsotg->plat && hsotg->plat->phy_exit) {
17209a75e85SMarek Szyprowski ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
17334c0887fSJohn Youn } else {
17409a75e85SMarek Szyprowski ret = phy_power_off(hsotg->phy);
175f9b995b4SHeiner Kallweit if (ret == 0)
176f9b995b4SHeiner Kallweit ret = phy_exit(hsotg->phy);
17709a75e85SMarek Szyprowski }
17809a75e85SMarek Szyprowski if (ret)
17909a75e85SMarek Szyprowski return ret;
18009a75e85SMarek Szyprowski
1818aa90cf2SStefan Wahren if (hsotg->clk)
18209a75e85SMarek Szyprowski clk_disable_unprepare(hsotg->clk);
18309a75e85SMarek Szyprowski
18402329adeSFabrice Gasnier if (hsotg->utmi_clk)
18502329adeSFabrice Gasnier clk_disable_unprepare(hsotg->utmi_clk);
18602329adeSFabrice Gasnier
187f7473132SFabrice Gasnier return regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
18809a75e85SMarek Szyprowski }
18909a75e85SMarek Szyprowski
19009a75e85SMarek Szyprowski /**
19109a75e85SMarek Szyprowski * dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources
19209a75e85SMarek Szyprowski * @hsotg: The driver state
19309a75e85SMarek Szyprowski *
19409a75e85SMarek Szyprowski * A wrapper for platform code responsible for controlling
19509a75e85SMarek Szyprowski * low-level USB platform resources (phy, clock, regulators)
19609a75e85SMarek Szyprowski */
dwc2_lowlevel_hw_disable(struct dwc2_hsotg * hsotg)19709a75e85SMarek Szyprowski int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
19809a75e85SMarek Szyprowski {
19909a75e85SMarek Szyprowski int ret = __dwc2_lowlevel_hw_disable(hsotg);
20009a75e85SMarek Szyprowski
20109a75e85SMarek Szyprowski if (ret == 0)
20209a75e85SMarek Szyprowski hsotg->ll_hw_enabled = false;
20309a75e85SMarek Szyprowski return ret;
20409a75e85SMarek Szyprowski }
20509a75e85SMarek Szyprowski
dwc2_reset_control_assert(void * data)206ada050c6SChristophe JAILLET static void dwc2_reset_control_assert(void *data)
207ada050c6SChristophe JAILLET {
208ada050c6SChristophe JAILLET reset_control_assert(data);
209ada050c6SChristophe JAILLET }
210ada050c6SChristophe JAILLET
dwc2_lowlevel_hw_init(struct dwc2_hsotg * hsotg)21109a75e85SMarek Szyprowski static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
21209a75e85SMarek Szyprowski {
21309a75e85SMarek Szyprowski int i, ret;
21409a75e85SMarek Szyprowski
21583f8da56SDinh Nguyen hsotg->reset = devm_reset_control_get_optional(hsotg->dev, "dwc2");
216856d3624SAmelie Delaunay if (IS_ERR(hsotg->reset))
217856d3624SAmelie Delaunay return dev_err_probe(hsotg->dev, PTR_ERR(hsotg->reset),
218856d3624SAmelie Delaunay "error getting reset control\n");
21983f8da56SDinh Nguyen
22083f8da56SDinh Nguyen reset_control_deassert(hsotg->reset);
221ada050c6SChristophe JAILLET ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert,
222ada050c6SChristophe JAILLET hsotg->reset);
223ada050c6SChristophe JAILLET if (ret)
224ada050c6SChristophe JAILLET return ret;
22583f8da56SDinh Nguyen
226f2830ad4SDinh Nguyen hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc");
227856d3624SAmelie Delaunay if (IS_ERR(hsotg->reset_ecc))
228856d3624SAmelie Delaunay return dev_err_probe(hsotg->dev, PTR_ERR(hsotg->reset_ecc),
229856d3624SAmelie Delaunay "error getting reset control for ecc\n");
230f2830ad4SDinh Nguyen
231f2830ad4SDinh Nguyen reset_control_deassert(hsotg->reset_ecc);
232ada050c6SChristophe JAILLET ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert,
233ada050c6SChristophe JAILLET hsotg->reset_ecc);
234ada050c6SChristophe JAILLET if (ret)
235ada050c6SChristophe JAILLET return ret;
236f2830ad4SDinh Nguyen
23709a75e85SMarek Szyprowski /*
23809a75e85SMarek Szyprowski * Attempt to find a generic PHY, then look for an old style
23909a75e85SMarek Szyprowski * USB PHY and then fall back to pdata
24009a75e85SMarek Szyprowski */
24109a75e85SMarek Szyprowski hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy");
24209a75e85SMarek Szyprowski if (IS_ERR(hsotg->phy)) {
2436c2dad69SStefan Wahren ret = PTR_ERR(hsotg->phy);
2446c2dad69SStefan Wahren switch (ret) {
2456c2dad69SStefan Wahren case -ENODEV:
2466c2dad69SStefan Wahren case -ENOSYS:
24709a75e85SMarek Szyprowski hsotg->phy = NULL;
2486c2dad69SStefan Wahren break;
2496c2dad69SStefan Wahren default:
250856d3624SAmelie Delaunay return dev_err_probe(hsotg->dev, ret, "error getting phy\n");
25109a75e85SMarek Szyprowski }
2526c2dad69SStefan Wahren }
2536c2dad69SStefan Wahren
2546c2dad69SStefan Wahren if (!hsotg->phy) {
2556c2dad69SStefan Wahren hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
2566c2dad69SStefan Wahren if (IS_ERR(hsotg->uphy)) {
2576c2dad69SStefan Wahren ret = PTR_ERR(hsotg->uphy);
2586c2dad69SStefan Wahren switch (ret) {
2596c2dad69SStefan Wahren case -ENODEV:
2606c2dad69SStefan Wahren case -ENXIO:
2616c2dad69SStefan Wahren hsotg->uphy = NULL;
2626c2dad69SStefan Wahren break;
2636c2dad69SStefan Wahren default:
264856d3624SAmelie Delaunay return dev_err_probe(hsotg->dev, ret, "error getting usb phy\n");
2656c2dad69SStefan Wahren }
2666c2dad69SStefan Wahren }
2676c2dad69SStefan Wahren }
2686c2dad69SStefan Wahren
2696c2dad69SStefan Wahren hsotg->plat = dev_get_platdata(hsotg->dev);
27009a75e85SMarek Szyprowski
27109a75e85SMarek Szyprowski /* Clock */
27260722c4eSChunfeng Yun hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg");
273856d3624SAmelie Delaunay if (IS_ERR(hsotg->clk))
274856d3624SAmelie Delaunay return dev_err_probe(hsotg->dev, PTR_ERR(hsotg->clk), "cannot get otg clock\n");
27509a75e85SMarek Szyprowski
27602329adeSFabrice Gasnier hsotg->utmi_clk = devm_clk_get_optional(hsotg->dev, "utmi");
27702329adeSFabrice Gasnier if (IS_ERR(hsotg->utmi_clk))
27802329adeSFabrice Gasnier return dev_err_probe(hsotg->dev, PTR_ERR(hsotg->utmi_clk),
27902329adeSFabrice Gasnier "cannot get utmi clock\n");
28002329adeSFabrice Gasnier
28109a75e85SMarek Szyprowski /* Regulators */
28209a75e85SMarek Szyprowski for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++)
28309a75e85SMarek Szyprowski hsotg->supplies[i].supply = dwc2_hsotg_supply_names[i];
28409a75e85SMarek Szyprowski
28509a75e85SMarek Szyprowski ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
28609a75e85SMarek Szyprowski hsotg->supplies);
287856d3624SAmelie Delaunay if (ret)
288856d3624SAmelie Delaunay return dev_err_probe(hsotg->dev, ret, "failed to request supplies\n");
289856d3624SAmelie Delaunay
29009a75e85SMarek Szyprowski return 0;
29109a75e85SMarek Szyprowski }
29209a75e85SMarek Szyprowski
293197ba5f4SPaul Zimmerman /**
294197ba5f4SPaul Zimmerman * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
295197ba5f4SPaul Zimmerman * DWC_otg driver
296197ba5f4SPaul Zimmerman *
297197ba5f4SPaul Zimmerman * @dev: Platform device
298197ba5f4SPaul Zimmerman *
299197ba5f4SPaul Zimmerman * This routine is called, for example, when the rmmod command is executed. The
300197ba5f4SPaul Zimmerman * device may or may not be electrically present. If it is present, the driver
301197ba5f4SPaul Zimmerman * stops device processing. Any resources used on behalf of this device are
302197ba5f4SPaul Zimmerman * freed.
303197ba5f4SPaul Zimmerman */
dwc2_driver_remove(struct platform_device * dev)304b519f44bSUwe Kleine-König static void dwc2_driver_remove(struct platform_device *dev)
305197ba5f4SPaul Zimmerman {
306197ba5f4SPaul Zimmerman struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
307a94f0181SArtur Petrosyan struct dwc2_gregs_backup *gr;
308b46b1ef7SArtur Petrosyan int ret = 0;
309b46b1ef7SArtur Petrosyan
310a94f0181SArtur Petrosyan gr = &hsotg->gr_backup;
311a94f0181SArtur Petrosyan
312a94f0181SArtur Petrosyan /* Exit Hibernation when driver is removed. */
313a94f0181SArtur Petrosyan if (hsotg->hibernated) {
314a94f0181SArtur Petrosyan if (gr->gotgctl & GOTGCTL_CURMODE_HOST)
315a94f0181SArtur Petrosyan ret = dwc2_exit_hibernation(hsotg, 0, 0, 1);
316a94f0181SArtur Petrosyan else
317a94f0181SArtur Petrosyan ret = dwc2_exit_hibernation(hsotg, 0, 0, 0);
318a94f0181SArtur Petrosyan
319a94f0181SArtur Petrosyan if (ret)
320a94f0181SArtur Petrosyan dev_err(hsotg->dev,
321a94f0181SArtur Petrosyan "exit hibernation failed.\n");
322a94f0181SArtur Petrosyan }
323a94f0181SArtur Petrosyan
324b46b1ef7SArtur Petrosyan /* Exit Partial Power Down when driver is removed. */
325b46b1ef7SArtur Petrosyan if (hsotg->in_ppd) {
326b46b1ef7SArtur Petrosyan ret = dwc2_exit_partial_power_down(hsotg, 0, true);
327b46b1ef7SArtur Petrosyan if (ret)
328b46b1ef7SArtur Petrosyan dev_err(hsotg->dev,
329b46b1ef7SArtur Petrosyan "exit partial_power_down failed\n");
330b46b1ef7SArtur Petrosyan }
331197ba5f4SPaul Zimmerman
332401411bbSArtur Petrosyan /* Exit clock gating when driver is removed. */
333401411bbSArtur Petrosyan if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
334f047361fSMinas Harutyunyan hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
335401411bbSArtur Petrosyan if (dwc2_is_device_mode(hsotg))
336401411bbSArtur Petrosyan dwc2_gadget_exit_clock_gating(hsotg, 0);
337401411bbSArtur Petrosyan else
338401411bbSArtur Petrosyan dwc2_host_exit_clock_gating(hsotg, 0);
339401411bbSArtur Petrosyan }
340401411bbSArtur Petrosyan
341f91eea44SMian Yousaf Kaukab dwc2_debugfs_exit(hsotg);
342e39af88fSMarek Szyprowski if (hsotg->hcd_enabled)
343197ba5f4SPaul Zimmerman dwc2_hcd_remove(hsotg);
344e39af88fSMarek Szyprowski if (hsotg->gadget_enabled)
3451f91b4ccSFelipe Balbi dwc2_hsotg_remove(hsotg);
346197ba5f4SPaul Zimmerman
34717f93402SAmelie Delaunay dwc2_drd_exit(hsotg);
34817f93402SAmelie Delaunay
349a415083aSAmelie Delaunay if (hsotg->params.activate_stm_id_vb_detection)
350a415083aSAmelie Delaunay regulator_disable(hsotg->usb33d);
351a415083aSAmelie Delaunay
35209a75e85SMarek Szyprowski if (hsotg->ll_hw_enabled)
35309a75e85SMarek Szyprowski dwc2_lowlevel_hw_disable(hsotg);
354197ba5f4SPaul Zimmerman }
355197ba5f4SPaul Zimmerman
356a40a0031SHeiko Stübner /**
357a40a0031SHeiko Stübner * dwc2_driver_shutdown() - Called on device shutdown
358a40a0031SHeiko Stübner *
359a40a0031SHeiko Stübner * @dev: Platform device
360a40a0031SHeiko Stübner *
361a40a0031SHeiko Stübner * In specific conditions (involving usb hubs) dwc2 devices can create a
362a40a0031SHeiko Stübner * lot of interrupts, even to the point of overwhelming devices running
363a40a0031SHeiko Stübner * at low frequencies. Some devices need to do special clock handling
364a40a0031SHeiko Stübner * at shutdown-time which may bring the system clock below the threshold
365a40a0031SHeiko Stübner * of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
366a40a0031SHeiko Stübner * prevents reboots/poweroffs from getting stuck in such cases.
367a40a0031SHeiko Stübner */
dwc2_driver_shutdown(struct platform_device * dev)368a40a0031SHeiko Stübner static void dwc2_driver_shutdown(struct platform_device *dev)
369a40a0031SHeiko Stübner {
370a40a0031SHeiko Stübner struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
371a40a0031SHeiko Stübner
3724fdf228cSMinas Harutyunyan dwc2_disable_global_interrupts(hsotg);
3734fdf228cSMinas Harutyunyan synchronize_irq(hsotg->irq);
374a40a0031SHeiko Stübner }
375a40a0031SHeiko Stübner
376197ba5f4SPaul Zimmerman /**
377fe369e18SGevorg Sahakyan * dwc2_check_core_endianness() - Returns true if core and AHB have
378fe369e18SGevorg Sahakyan * opposite endianness.
379fe369e18SGevorg Sahakyan * @hsotg: Programming view of the DWC_otg controller.
380fe369e18SGevorg Sahakyan */
dwc2_check_core_endianness(struct dwc2_hsotg * hsotg)381fe369e18SGevorg Sahakyan static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
382fe369e18SGevorg Sahakyan {
383fe369e18SGevorg Sahakyan u32 snpsid;
384fe369e18SGevorg Sahakyan
385fe369e18SGevorg Sahakyan snpsid = ioread32(hsotg->regs + GSNPSID);
386fe369e18SGevorg Sahakyan if ((snpsid & GSNPSID_ID_MASK) == DWC2_OTG_ID ||
387fe369e18SGevorg Sahakyan (snpsid & GSNPSID_ID_MASK) == DWC2_FS_IOT_ID ||
388fe369e18SGevorg Sahakyan (snpsid & GSNPSID_ID_MASK) == DWC2_HS_IOT_ID)
389fe369e18SGevorg Sahakyan return false;
390fe369e18SGevorg Sahakyan return true;
391fe369e18SGevorg Sahakyan }
392fe369e18SGevorg Sahakyan
393fe369e18SGevorg Sahakyan /**
394c1fb8640SLee Jones * dwc2_check_core_version() - Check core version
39565dc2e72SMinas Harutyunyan *
39665dc2e72SMinas Harutyunyan * @hsotg: Programming view of the DWC_otg controller
39765dc2e72SMinas Harutyunyan *
39865dc2e72SMinas Harutyunyan */
dwc2_check_core_version(struct dwc2_hsotg * hsotg)39965dc2e72SMinas Harutyunyan int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
40065dc2e72SMinas Harutyunyan {
40165dc2e72SMinas Harutyunyan struct dwc2_hw_params *hw = &hsotg->hw_params;
40265dc2e72SMinas Harutyunyan
40365dc2e72SMinas Harutyunyan /*
40465dc2e72SMinas Harutyunyan * Attempt to ensure this device is really a DWC_otg Controller.
40565dc2e72SMinas Harutyunyan * Read and verify the GSNPSID register contents. The value should be
40665dc2e72SMinas Harutyunyan * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
40765dc2e72SMinas Harutyunyan */
40865dc2e72SMinas Harutyunyan
40965dc2e72SMinas Harutyunyan hw->snpsid = dwc2_readl(hsotg, GSNPSID);
41065dc2e72SMinas Harutyunyan if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
41165dc2e72SMinas Harutyunyan (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
41265dc2e72SMinas Harutyunyan (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
41365dc2e72SMinas Harutyunyan dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
41465dc2e72SMinas Harutyunyan hw->snpsid);
41565dc2e72SMinas Harutyunyan return -ENODEV;
41665dc2e72SMinas Harutyunyan }
41765dc2e72SMinas Harutyunyan
41865dc2e72SMinas Harutyunyan dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
41965dc2e72SMinas Harutyunyan hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
42065dc2e72SMinas Harutyunyan hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
42165dc2e72SMinas Harutyunyan return 0;
42265dc2e72SMinas Harutyunyan }
42365dc2e72SMinas Harutyunyan
42465dc2e72SMinas Harutyunyan /**
425197ba5f4SPaul Zimmerman * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
426197ba5f4SPaul Zimmerman * driver
427197ba5f4SPaul Zimmerman *
428197ba5f4SPaul Zimmerman * @dev: Platform device
429197ba5f4SPaul Zimmerman *
430197ba5f4SPaul Zimmerman * This routine creates the driver components required to control the device
431197ba5f4SPaul Zimmerman * (core, HCD, and PCD) and initializes the device. The driver components are
432197ba5f4SPaul Zimmerman * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
433197ba5f4SPaul Zimmerman * in the device private data. This allows the driver to access the dwc2_hsotg
434197ba5f4SPaul Zimmerman * structure on subsequent calls to driver methods for this device.
435197ba5f4SPaul Zimmerman */
dwc2_driver_probe(struct platform_device * dev)436197ba5f4SPaul Zimmerman static int dwc2_driver_probe(struct platform_device *dev)
437197ba5f4SPaul Zimmerman {
438197ba5f4SPaul Zimmerman struct dwc2_hsotg *hsotg;
439197ba5f4SPaul Zimmerman struct resource *res;
440197ba5f4SPaul Zimmerman int retval;
441197ba5f4SPaul Zimmerman
442197ba5f4SPaul Zimmerman hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
443197ba5f4SPaul Zimmerman if (!hsotg)
444197ba5f4SPaul Zimmerman return -ENOMEM;
445197ba5f4SPaul Zimmerman
446197ba5f4SPaul Zimmerman hsotg->dev = &dev->dev;
447197ba5f4SPaul Zimmerman
448197ba5f4SPaul Zimmerman /*
449197ba5f4SPaul Zimmerman * Use reasonable defaults so platforms don't have to provide these.
450197ba5f4SPaul Zimmerman */
451197ba5f4SPaul Zimmerman if (!dev->dev.dma_mask)
452197ba5f4SPaul Zimmerman dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
453197ba5f4SPaul Zimmerman retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
45442c6a252SStefan Wahren if (retval) {
45542c6a252SStefan Wahren dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval);
456197ba5f4SPaul Zimmerman return retval;
45742c6a252SStefan Wahren }
458197ba5f4SPaul Zimmerman
4595bf7e288SDejin Zheng hsotg->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res);
460197ba5f4SPaul Zimmerman if (IS_ERR(hsotg->regs))
461197ba5f4SPaul Zimmerman return PTR_ERR(hsotg->regs);
462197ba5f4SPaul Zimmerman
463197ba5f4SPaul Zimmerman dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
464197ba5f4SPaul Zimmerman (unsigned long)res->start, hsotg->regs);
465197ba5f4SPaul Zimmerman
46609a75e85SMarek Szyprowski retval = dwc2_lowlevel_hw_init(hsotg);
467ecb176c6SMian Yousaf Kaukab if (retval)
468ecb176c6SMian Yousaf Kaukab return retval;
469ecb176c6SMian Yousaf Kaukab
47009a75e85SMarek Szyprowski spin_lock_init(&hsotg->lock);
47109a75e85SMarek Szyprowski
472e0f681c2SFabrice Gasnier hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
473e0f681c2SFabrice Gasnier if (IS_ERR(hsotg->vbus_supply)) {
474e0f681c2SFabrice Gasnier retval = PTR_ERR(hsotg->vbus_supply);
475e0f681c2SFabrice Gasnier hsotg->vbus_supply = NULL;
476e0f681c2SFabrice Gasnier if (retval != -ENODEV)
477e0f681c2SFabrice Gasnier return retval;
478e0f681c2SFabrice Gasnier }
479e0f681c2SFabrice Gasnier
48009a75e85SMarek Szyprowski retval = dwc2_lowlevel_hw_enable(hsotg);
48109a75e85SMarek Szyprowski if (retval)
48209a75e85SMarek Szyprowski return retval;
48309a75e85SMarek Szyprowski
484d9707490SBruno Meirelles Herrera hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);
485d9707490SBruno Meirelles Herrera
4865268ed9dSJohn Youn retval = dwc2_get_dr_mode(hsotg);
4875268ed9dSJohn Youn if (retval)
488a6ef3e02SJohn Youn goto error;
4895268ed9dSJohn Youn
490c846b03fSDouglas Anderson hsotg->need_phy_for_wake =
491c846b03fSDouglas Anderson of_property_read_bool(dev->dev.of_node,
492c846b03fSDouglas Anderson "snps,need-phy-for-wake");
493c846b03fSDouglas Anderson
49403b32e4cSJohn Youn /*
49565dc2e72SMinas Harutyunyan * Before performing any core related operations
49665dc2e72SMinas Harutyunyan * check core version.
49765dc2e72SMinas Harutyunyan */
49865dc2e72SMinas Harutyunyan retval = dwc2_check_core_version(hsotg);
49965dc2e72SMinas Harutyunyan if (retval)
50065dc2e72SMinas Harutyunyan goto error;
50165dc2e72SMinas Harutyunyan
50265dc2e72SMinas Harutyunyan /*
50303b32e4cSJohn Youn * Reset before dwc2_get_hwparams() then it could get power-on real
50403b32e4cSJohn Youn * reset value form registers.
50503b32e4cSJohn Youn */
50613b1f8e2SVardan Mikayelyan retval = dwc2_core_reset(hsotg, false);
50713b1f8e2SVardan Mikayelyan if (retval)
50813b1f8e2SVardan Mikayelyan goto error;
50903b32e4cSJohn Youn
51003b32e4cSJohn Youn /* Detect config values from hardware */
51109a75e85SMarek Szyprowski retval = dwc2_get_hwparams(hsotg);
51209a75e85SMarek Szyprowski if (retval)
51309a75e85SMarek Szyprowski goto error;
51409a75e85SMarek Szyprowski
515*0be52823SShawn Shao hsotg->irq = platform_get_irq(dev, 0);
516*0be52823SShawn Shao if (hsotg->irq < 0) {
517*0be52823SShawn Shao retval = hsotg->irq;
518*0be52823SShawn Shao goto error;
519*0be52823SShawn Shao }
520*0be52823SShawn Shao
521*0be52823SShawn Shao dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
522*0be52823SShawn Shao hsotg->irq);
523*0be52823SShawn Shao retval = devm_request_irq(hsotg->dev, hsotg->irq,
524*0be52823SShawn Shao dwc2_handle_common_intr, IRQF_SHARED,
525*0be52823SShawn Shao dev_name(hsotg->dev), hsotg);
526*0be52823SShawn Shao if (retval)
527*0be52823SShawn Shao goto error;
528*0be52823SShawn Shao
52913b1f8e2SVardan Mikayelyan /*
53013b1f8e2SVardan Mikayelyan * For OTG cores, set the force mode bits to reflect the value
53113b1f8e2SVardan Mikayelyan * of dr_mode. Force mode bits should not be touched at any
53213b1f8e2SVardan Mikayelyan * other time after this.
53313b1f8e2SVardan Mikayelyan */
53425362d31SJohn Youn dwc2_force_dr_mode(hsotg);
535263b7fb5SJohn Youn
536334bbd4eSJohn Youn retval = dwc2_init_params(hsotg);
537334bbd4eSJohn Youn if (retval)
538334bbd4eSJohn Youn goto error;
539334bbd4eSJohn Youn
540a415083aSAmelie Delaunay if (hsotg->params.activate_stm_id_vb_detection) {
541a415083aSAmelie Delaunay u32 ggpio;
542a415083aSAmelie Delaunay
543a415083aSAmelie Delaunay hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d");
544a415083aSAmelie Delaunay if (IS_ERR(hsotg->usb33d)) {
545a415083aSAmelie Delaunay retval = PTR_ERR(hsotg->usb33d);
546856d3624SAmelie Delaunay dev_err_probe(hsotg->dev, retval, "failed to request usb33d supply\n");
547a415083aSAmelie Delaunay goto error;
548a415083aSAmelie Delaunay }
549a415083aSAmelie Delaunay retval = regulator_enable(hsotg->usb33d);
550a415083aSAmelie Delaunay if (retval) {
551856d3624SAmelie Delaunay dev_err_probe(hsotg->dev, retval, "failed to enable usb33d supply\n");
552a415083aSAmelie Delaunay goto error;
553a415083aSAmelie Delaunay }
554a415083aSAmelie Delaunay
555a415083aSAmelie Delaunay ggpio = dwc2_readl(hsotg, GGPIO);
556a415083aSAmelie Delaunay ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
557a415083aSAmelie Delaunay ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
558a415083aSAmelie Delaunay dwc2_writel(hsotg, ggpio, GGPIO);
559fac6bf87SAmelie Delaunay
560fac6bf87SAmelie Delaunay /* ID/VBUS detection startup time */
561fac6bf87SAmelie Delaunay usleep_range(5000, 7000);
562a415083aSAmelie Delaunay }
563a415083aSAmelie Delaunay
56417f93402SAmelie Delaunay retval = dwc2_drd_init(hsotg);
56517f93402SAmelie Delaunay if (retval) {
566856d3624SAmelie Delaunay dev_err_probe(hsotg->dev, retval, "failed to initialize dual-role\n");
56717f93402SAmelie Delaunay goto error_init;
56817f93402SAmelie Delaunay }
56917f93402SAmelie Delaunay
570e39af88fSMarek Szyprowski if (hsotg->dr_mode != USB_DR_MODE_HOST) {
571f3768997SVardan Mikayelyan retval = dwc2_gadget_init(hsotg);
572117777b2SDinh Nguyen if (retval)
57317f93402SAmelie Delaunay goto error_drd;
574e39af88fSMarek Szyprowski hsotg->gadget_enabled = 1;
575e39af88fSMarek Szyprowski }
576e39af88fSMarek Szyprowski
577c846b03fSDouglas Anderson /*
578c846b03fSDouglas Anderson * If we need PHY for wakeup we must be wakeup capable.
579c846b03fSDouglas Anderson * When we have a device that can wake without the PHY we
580c846b03fSDouglas Anderson * can adjust this condition.
581c846b03fSDouglas Anderson */
582c846b03fSDouglas Anderson if (hsotg->need_phy_for_wake)
583c846b03fSDouglas Anderson device_set_wakeup_capable(&dev->dev, true);
584c846b03fSDouglas Anderson
585c40cf770SDouglas Anderson hsotg->reset_phy_on_wake =
586c40cf770SDouglas Anderson of_property_read_bool(dev->dev.of_node,
587c40cf770SDouglas Anderson "snps,reset-phy-on-wake");
588c40cf770SDouglas Anderson if (hsotg->reset_phy_on_wake && !hsotg->phy) {
589c40cf770SDouglas Anderson dev_warn(hsotg->dev,
590c40cf770SDouglas Anderson "Quirk reset-phy-on-wake only supports generic PHYs\n");
591c40cf770SDouglas Anderson hsotg->reset_phy_on_wake = false;
592c40cf770SDouglas Anderson }
593c40cf770SDouglas Anderson
594e39af88fSMarek Szyprowski if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
5954fe160d5SHeiner Kallweit retval = dwc2_hcd_init(hsotg);
596e39af88fSMarek Szyprowski if (retval) {
597e39af88fSMarek Szyprowski if (hsotg->gadget_enabled)
5981f91b4ccSFelipe Balbi dwc2_hsotg_remove(hsotg);
59917f93402SAmelie Delaunay goto error_drd;
600e39af88fSMarek Szyprowski }
601e39af88fSMarek Szyprowski hsotg->hcd_enabled = 1;
602e39af88fSMarek Szyprowski }
603197ba5f4SPaul Zimmerman
604197ba5f4SPaul Zimmerman platform_set_drvdata(dev, hsotg);
60520fe4409SVardan Mikayelyan hsotg->hibernated = 0;
606197ba5f4SPaul Zimmerman
607f91eea44SMian Yousaf Kaukab dwc2_debugfs_init(hsotg);
608f91eea44SMian Yousaf Kaukab
60909a75e85SMarek Szyprowski /* Gadget code manages lowlevel hw on its own */
61050213832SFabrice Gasnier if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
61109a75e85SMarek Szyprowski dwc2_lowlevel_hw_disable(hsotg);
61209a75e85SMarek Szyprowski
613207324a3SMinas Harutyunyan #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
614207324a3SMinas Harutyunyan IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
615207324a3SMinas Harutyunyan /* Postponed adding a new gadget to the udc class driver list */
616207324a3SMinas Harutyunyan if (hsotg->gadget_enabled) {
617207324a3SMinas Harutyunyan retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget);
618207324a3SMinas Harutyunyan if (retval) {
61933a06f13SMarek Szyprowski hsotg->gadget.udc = NULL;
620207324a3SMinas Harutyunyan dwc2_hsotg_remove(hsotg);
621e1c08cf2SMartin Blumenstingl goto error_debugfs;
622207324a3SMinas Harutyunyan }
623207324a3SMinas Harutyunyan }
624207324a3SMinas Harutyunyan #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
62509a75e85SMarek Szyprowski return 0;
62609a75e85SMarek Szyprowski
627190bb01bSMartin Blumenstingl #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
628190bb01bSMartin Blumenstingl IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
629e1c08cf2SMartin Blumenstingl error_debugfs:
630e1c08cf2SMartin Blumenstingl dwc2_debugfs_exit(hsotg);
631e1c08cf2SMartin Blumenstingl if (hsotg->hcd_enabled)
632e1c08cf2SMartin Blumenstingl dwc2_hcd_remove(hsotg);
633190bb01bSMartin Blumenstingl #endif
63417f93402SAmelie Delaunay error_drd:
63517f93402SAmelie Delaunay dwc2_drd_exit(hsotg);
63617f93402SAmelie Delaunay
637a415083aSAmelie Delaunay error_init:
638a415083aSAmelie Delaunay if (hsotg->params.activate_stm_id_vb_detection)
639a415083aSAmelie Delaunay regulator_disable(hsotg->usb33d);
64009a75e85SMarek Szyprowski error:
641f7473132SFabrice Gasnier if (hsotg->ll_hw_enabled)
64209a75e85SMarek Szyprowski dwc2_lowlevel_hw_disable(hsotg);
643197ba5f4SPaul Zimmerman return retval;
644197ba5f4SPaul Zimmerman }
645197ba5f4SPaul Zimmerman
dwc2_suspend(struct device * dev)646da9f3289SFabio Estevam static int __maybe_unused dwc2_suspend(struct device *dev)
647117777b2SDinh Nguyen {
648bcc06078SDinh Nguyen struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
649c846b03fSDouglas Anderson bool is_device_mode = dwc2_is_device_mode(dwc2);
650117777b2SDinh Nguyen int ret = 0;
651117777b2SDinh Nguyen
652c846b03fSDouglas Anderson if (is_device_mode)
65309a75e85SMarek Szyprowski dwc2_hsotg_suspend(dwc2);
654135b3c43SYunzhi Li
65517f93402SAmelie Delaunay dwc2_drd_suspend(dwc2);
65617f93402SAmelie Delaunay
657a415083aSAmelie Delaunay if (dwc2->params.activate_stm_id_vb_detection) {
658a415083aSAmelie Delaunay unsigned long flags;
659a415083aSAmelie Delaunay u32 ggpio, gotgctl;
660a415083aSAmelie Delaunay
661a415083aSAmelie Delaunay /*
662a415083aSAmelie Delaunay * Need to force the mode to the current mode to avoid Mode
663a415083aSAmelie Delaunay * Mismatch Interrupt when ID detection will be disabled.
664a415083aSAmelie Delaunay */
665a415083aSAmelie Delaunay dwc2_force_mode(dwc2, !is_device_mode);
666a415083aSAmelie Delaunay
667a415083aSAmelie Delaunay spin_lock_irqsave(&dwc2->lock, flags);
668a415083aSAmelie Delaunay gotgctl = dwc2_readl(dwc2, GOTGCTL);
669a415083aSAmelie Delaunay /* bypass debounce filter, enable overrides */
670a415083aSAmelie Delaunay gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
671a415083aSAmelie Delaunay gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN;
672a415083aSAmelie Delaunay /* Force A / B session if needed */
673a415083aSAmelie Delaunay if (gotgctl & GOTGCTL_ASESVLD)
674a415083aSAmelie Delaunay gotgctl |= GOTGCTL_AVALOVAL;
675a415083aSAmelie Delaunay if (gotgctl & GOTGCTL_BSESVLD)
676a415083aSAmelie Delaunay gotgctl |= GOTGCTL_BVALOVAL;
677a415083aSAmelie Delaunay dwc2_writel(dwc2, gotgctl, GOTGCTL);
678a415083aSAmelie Delaunay spin_unlock_irqrestore(&dwc2->lock, flags);
679a415083aSAmelie Delaunay
680a415083aSAmelie Delaunay ggpio = dwc2_readl(dwc2, GGPIO);
681a415083aSAmelie Delaunay ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN;
682a415083aSAmelie Delaunay ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN;
683a415083aSAmelie Delaunay dwc2_writel(dwc2, ggpio, GGPIO);
684a415083aSAmelie Delaunay
685a415083aSAmelie Delaunay regulator_disable(dwc2->usb33d);
686a415083aSAmelie Delaunay }
687a415083aSAmelie Delaunay
688c846b03fSDouglas Anderson if (dwc2->ll_hw_enabled &&
689c846b03fSDouglas Anderson (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
69009a75e85SMarek Szyprowski ret = __dwc2_lowlevel_hw_disable(dwc2);
691c846b03fSDouglas Anderson dwc2->phy_off_for_suspend = true;
692c846b03fSDouglas Anderson }
69309a75e85SMarek Szyprowski
694117777b2SDinh Nguyen return ret;
695117777b2SDinh Nguyen }
696117777b2SDinh Nguyen
dwc2_resume(struct device * dev)697da9f3289SFabio Estevam static int __maybe_unused dwc2_resume(struct device *dev)
698117777b2SDinh Nguyen {
699bcc06078SDinh Nguyen struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
700117777b2SDinh Nguyen int ret = 0;
701117777b2SDinh Nguyen
702c846b03fSDouglas Anderson if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
70309a75e85SMarek Szyprowski ret = __dwc2_lowlevel_hw_enable(dwc2);
70409a75e85SMarek Szyprowski if (ret)
70509a75e85SMarek Szyprowski return ret;
706135b3c43SYunzhi Li }
707c846b03fSDouglas Anderson dwc2->phy_off_for_suspend = false;
70809a75e85SMarek Szyprowski
709a415083aSAmelie Delaunay if (dwc2->params.activate_stm_id_vb_detection) {
710a415083aSAmelie Delaunay unsigned long flags;
711a415083aSAmelie Delaunay u32 ggpio, gotgctl;
712a415083aSAmelie Delaunay
713a415083aSAmelie Delaunay ret = regulator_enable(dwc2->usb33d);
714a415083aSAmelie Delaunay if (ret)
715a415083aSAmelie Delaunay return ret;
716a415083aSAmelie Delaunay
717a415083aSAmelie Delaunay ggpio = dwc2_readl(dwc2, GGPIO);
718a415083aSAmelie Delaunay ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
719a415083aSAmelie Delaunay ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
720a415083aSAmelie Delaunay dwc2_writel(dwc2, ggpio, GGPIO);
721a415083aSAmelie Delaunay
722a415083aSAmelie Delaunay /* ID/VBUS detection startup time */
723a415083aSAmelie Delaunay usleep_range(5000, 7000);
724a415083aSAmelie Delaunay
725a415083aSAmelie Delaunay spin_lock_irqsave(&dwc2->lock, flags);
726a415083aSAmelie Delaunay gotgctl = dwc2_readl(dwc2, GOTGCTL);
727a415083aSAmelie Delaunay gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS;
728a415083aSAmelie Delaunay gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
729a415083aSAmelie Delaunay GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL);
730a415083aSAmelie Delaunay dwc2_writel(dwc2, gotgctl, GOTGCTL);
731a415083aSAmelie Delaunay spin_unlock_irqrestore(&dwc2->lock, flags);
732a415083aSAmelie Delaunay }
733a415083aSAmelie Delaunay
7343ad02e0eSFabrice Gasnier if (!dwc2->role_sw) {
735a415083aSAmelie Delaunay /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
736a415083aSAmelie Delaunay dwc2_force_dr_mode(dwc2);
7373ad02e0eSFabrice Gasnier } else {
73817f93402SAmelie Delaunay dwc2_drd_resume(dwc2);
7393ad02e0eSFabrice Gasnier }
74017f93402SAmelie Delaunay
74109a75e85SMarek Szyprowski if (dwc2_is_device_mode(dwc2))
74209a75e85SMarek Szyprowski ret = dwc2_hsotg_resume(dwc2);
74309a75e85SMarek Szyprowski
744117777b2SDinh Nguyen return ret;
745117777b2SDinh Nguyen }
746117777b2SDinh Nguyen
747bcc06078SDinh Nguyen static const struct dev_pm_ops dwc2_dev_pm_ops = {
748bcc06078SDinh Nguyen SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
749bcc06078SDinh Nguyen };
750bcc06078SDinh Nguyen
751197ba5f4SPaul Zimmerman static struct platform_driver dwc2_platform_driver = {
752197ba5f4SPaul Zimmerman .driver = {
753197ba5f4SPaul Zimmerman .name = dwc2_driver_name,
754197ba5f4SPaul Zimmerman .of_match_table = dwc2_of_match_table,
7552e5db2c0SJeremy Linton .acpi_match_table = ACPI_PTR(dwc2_acpi_match),
756bcc06078SDinh Nguyen .pm = &dwc2_dev_pm_ops,
757197ba5f4SPaul Zimmerman },
758197ba5f4SPaul Zimmerman .probe = dwc2_driver_probe,
759b519f44bSUwe Kleine-König .remove_new = dwc2_driver_remove,
760a40a0031SHeiko Stübner .shutdown = dwc2_driver_shutdown,
761197ba5f4SPaul Zimmerman };
762197ba5f4SPaul Zimmerman
763197ba5f4SPaul Zimmerman module_platform_driver(dwc2_platform_driver);
764