xref: /openbmc/linux/drivers/usb/dwc2/platform.c (revision e1c08cf2)
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  * Redistribution and use in source and binary forms, with or without
8197ba5f4SPaul Zimmerman  * modification, are permitted provided that the following conditions
9197ba5f4SPaul Zimmerman  * are met:
10197ba5f4SPaul Zimmerman  * 1. Redistributions of source code must retain the above copyright
11197ba5f4SPaul Zimmerman  *    notice, this list of conditions, and the following disclaimer,
12197ba5f4SPaul Zimmerman  *    without modification.
13197ba5f4SPaul Zimmerman  * 2. Redistributions in binary form must reproduce the above copyright
14197ba5f4SPaul Zimmerman  *    notice, this list of conditions and the following disclaimer in the
15197ba5f4SPaul Zimmerman  *    documentation and/or other materials provided with the distribution.
16197ba5f4SPaul Zimmerman  * 3. The names of the above-listed copyright holders may not be used
17197ba5f4SPaul Zimmerman  *    to endorse or promote products derived from this software without
18197ba5f4SPaul Zimmerman  *    specific prior written permission.
19197ba5f4SPaul Zimmerman  *
20197ba5f4SPaul Zimmerman  * ALTERNATIVELY, this software may be distributed under the terms of the
21197ba5f4SPaul Zimmerman  * GNU General Public License ("GPL") as published by the Free Software
22197ba5f4SPaul Zimmerman  * Foundation; either version 2 of the License, or (at your option) any
23197ba5f4SPaul Zimmerman  * later version.
24197ba5f4SPaul Zimmerman  *
25197ba5f4SPaul Zimmerman  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
26197ba5f4SPaul Zimmerman  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27197ba5f4SPaul Zimmerman  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28197ba5f4SPaul Zimmerman  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29197ba5f4SPaul Zimmerman  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30197ba5f4SPaul Zimmerman  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31197ba5f4SPaul Zimmerman  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32197ba5f4SPaul Zimmerman  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33197ba5f4SPaul Zimmerman  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34197ba5f4SPaul Zimmerman  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35197ba5f4SPaul Zimmerman  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36197ba5f4SPaul Zimmerman  */
37197ba5f4SPaul Zimmerman 
38197ba5f4SPaul Zimmerman #include <linux/kernel.h>
39197ba5f4SPaul Zimmerman #include <linux/module.h>
40197ba5f4SPaul Zimmerman #include <linux/slab.h>
4109a75e85SMarek Szyprowski #include <linux/clk.h>
42197ba5f4SPaul Zimmerman #include <linux/device.h>
43197ba5f4SPaul Zimmerman #include <linux/dma-mapping.h>
44197ba5f4SPaul Zimmerman #include <linux/of_device.h>
457ad8096eSMarek Szyprowski #include <linux/mutex.h>
46197ba5f4SPaul Zimmerman #include <linux/platform_device.h>
4709a75e85SMarek Szyprowski #include <linux/phy/phy.h>
4809a75e85SMarek Szyprowski #include <linux/platform_data/s3c-hsotg.h>
4983f8da56SDinh Nguyen #include <linux/reset.h>
50197ba5f4SPaul Zimmerman 
51c0155b9dSKever Yang #include <linux/usb/of.h>
52c0155b9dSKever Yang 
53197ba5f4SPaul Zimmerman #include "core.h"
54197ba5f4SPaul Zimmerman #include "hcd.h"
55f91eea44SMian Yousaf Kaukab #include "debug.h"
56197ba5f4SPaul Zimmerman 
57197ba5f4SPaul Zimmerman static const char dwc2_driver_name[] = "dwc2";
58197ba5f4SPaul Zimmerman 
595268ed9dSJohn Youn /*
605268ed9dSJohn Youn  * Check the dr_mode against the module configuration and hardware
615268ed9dSJohn Youn  * capabilities.
625268ed9dSJohn Youn  *
635268ed9dSJohn Youn  * The hardware, module, and dr_mode, can each be set to host, device,
645268ed9dSJohn Youn  * or otg. Check that all these values are compatible and adjust the
655268ed9dSJohn Youn  * value of dr_mode if possible.
665268ed9dSJohn Youn  *
675268ed9dSJohn Youn  *                      actual
685268ed9dSJohn Youn  *    HW  MOD dr_mode   dr_mode
695268ed9dSJohn Youn  *  ------------------------------
705268ed9dSJohn Youn  *   HST  HST  any    :  HST
715268ed9dSJohn Youn  *   HST  DEV  any    :  ---
725268ed9dSJohn Youn  *   HST  OTG  any    :  HST
735268ed9dSJohn Youn  *
745268ed9dSJohn Youn  *   DEV  HST  any    :  ---
755268ed9dSJohn Youn  *   DEV  DEV  any    :  DEV
765268ed9dSJohn Youn  *   DEV  OTG  any    :  DEV
775268ed9dSJohn Youn  *
785268ed9dSJohn Youn  *   OTG  HST  any    :  HST
795268ed9dSJohn Youn  *   OTG  DEV  any    :  DEV
805268ed9dSJohn Youn  *   OTG  OTG  any    :  dr_mode
815268ed9dSJohn Youn  */
825268ed9dSJohn Youn static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
835268ed9dSJohn Youn {
845268ed9dSJohn Youn 	enum usb_dr_mode mode;
855268ed9dSJohn Youn 
865268ed9dSJohn Youn 	hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
875268ed9dSJohn Youn 	if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
885268ed9dSJohn Youn 		hsotg->dr_mode = USB_DR_MODE_OTG;
895268ed9dSJohn Youn 
905268ed9dSJohn Youn 	mode = hsotg->dr_mode;
915268ed9dSJohn Youn 
925268ed9dSJohn Youn 	if (dwc2_hw_is_device(hsotg)) {
935268ed9dSJohn Youn 		if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
945268ed9dSJohn Youn 			dev_err(hsotg->dev,
955268ed9dSJohn Youn 				"Controller does not support host mode.\n");
965268ed9dSJohn Youn 			return -EINVAL;
975268ed9dSJohn Youn 		}
985268ed9dSJohn Youn 		mode = USB_DR_MODE_PERIPHERAL;
995268ed9dSJohn Youn 	} else if (dwc2_hw_is_host(hsotg)) {
1005268ed9dSJohn Youn 		if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
1015268ed9dSJohn Youn 			dev_err(hsotg->dev,
1025268ed9dSJohn Youn 				"Controller does not support device mode.\n");
1035268ed9dSJohn Youn 			return -EINVAL;
1045268ed9dSJohn Youn 		}
1055268ed9dSJohn Youn 		mode = USB_DR_MODE_HOST;
1065268ed9dSJohn Youn 	} else {
1075268ed9dSJohn Youn 		if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
1085268ed9dSJohn Youn 			mode = USB_DR_MODE_HOST;
1095268ed9dSJohn Youn 		else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
1105268ed9dSJohn Youn 			mode = USB_DR_MODE_PERIPHERAL;
1115268ed9dSJohn Youn 	}
1125268ed9dSJohn Youn 
1135268ed9dSJohn Youn 	if (mode != hsotg->dr_mode) {
1145268ed9dSJohn Youn 		dev_warn(hsotg->dev,
1155268ed9dSJohn Youn 			 "Configuration mismatch. dr_mode forced to %s\n",
1165268ed9dSJohn Youn 			mode == USB_DR_MODE_HOST ? "host" : "device");
1175268ed9dSJohn Youn 
1185268ed9dSJohn Youn 		hsotg->dr_mode = mode;
1195268ed9dSJohn Youn 	}
1205268ed9dSJohn Youn 
1215268ed9dSJohn Youn 	return 0;
1225268ed9dSJohn Youn }
1235268ed9dSJohn Youn 
12409a75e85SMarek Szyprowski static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
12509a75e85SMarek Szyprowski {
12609a75e85SMarek Szyprowski 	struct platform_device *pdev = to_platform_device(hsotg->dev);
12709a75e85SMarek Szyprowski 	int ret;
12809a75e85SMarek Szyprowski 
12909a75e85SMarek Szyprowski 	ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
13009a75e85SMarek Szyprowski 				    hsotg->supplies);
13109a75e85SMarek Szyprowski 	if (ret)
13209a75e85SMarek Szyprowski 		return ret;
13309a75e85SMarek Szyprowski 
1348aa90cf2SStefan Wahren 	if (hsotg->clk) {
13509a75e85SMarek Szyprowski 		ret = clk_prepare_enable(hsotg->clk);
13609a75e85SMarek Szyprowski 		if (ret)
13709a75e85SMarek Szyprowski 			return ret;
1388aa90cf2SStefan Wahren 	}
13909a75e85SMarek Szyprowski 
14034c0887fSJohn Youn 	if (hsotg->uphy) {
14109a75e85SMarek Szyprowski 		ret = usb_phy_init(hsotg->uphy);
14234c0887fSJohn Youn 	} else if (hsotg->plat && hsotg->plat->phy_init) {
14309a75e85SMarek Szyprowski 		ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
14434c0887fSJohn Youn 	} else {
14509a75e85SMarek Szyprowski 		ret = phy_power_on(hsotg->phy);
14609a75e85SMarek Szyprowski 		if (ret == 0)
14709a75e85SMarek Szyprowski 			ret = phy_init(hsotg->phy);
14809a75e85SMarek Szyprowski 	}
14909a75e85SMarek Szyprowski 
15009a75e85SMarek Szyprowski 	return ret;
15109a75e85SMarek Szyprowski }
15209a75e85SMarek Szyprowski 
15309a75e85SMarek Szyprowski /**
15409a75e85SMarek Szyprowski  * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
15509a75e85SMarek Szyprowski  * @hsotg: The driver state
15609a75e85SMarek Szyprowski  *
15709a75e85SMarek Szyprowski  * A wrapper for platform code responsible for controlling
15809a75e85SMarek Szyprowski  * low-level USB platform resources (phy, clock, regulators)
15909a75e85SMarek Szyprowski  */
16009a75e85SMarek Szyprowski int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
16109a75e85SMarek Szyprowski {
16209a75e85SMarek Szyprowski 	int ret = __dwc2_lowlevel_hw_enable(hsotg);
16309a75e85SMarek Szyprowski 
16409a75e85SMarek Szyprowski 	if (ret == 0)
16509a75e85SMarek Szyprowski 		hsotg->ll_hw_enabled = true;
16609a75e85SMarek Szyprowski 	return ret;
16709a75e85SMarek Szyprowski }
16809a75e85SMarek Szyprowski 
16909a75e85SMarek Szyprowski static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
17009a75e85SMarek Szyprowski {
17109a75e85SMarek Szyprowski 	struct platform_device *pdev = to_platform_device(hsotg->dev);
17209a75e85SMarek Szyprowski 	int ret = 0;
17309a75e85SMarek Szyprowski 
17434c0887fSJohn Youn 	if (hsotg->uphy) {
17509a75e85SMarek Szyprowski 		usb_phy_shutdown(hsotg->uphy);
17634c0887fSJohn Youn 	} else if (hsotg->plat && hsotg->plat->phy_exit) {
17709a75e85SMarek Szyprowski 		ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
17834c0887fSJohn Youn 	} else {
17909a75e85SMarek Szyprowski 		ret = phy_exit(hsotg->phy);
18009a75e85SMarek Szyprowski 		if (ret == 0)
18109a75e85SMarek Szyprowski 			ret = phy_power_off(hsotg->phy);
18209a75e85SMarek Szyprowski 	}
18309a75e85SMarek Szyprowski 	if (ret)
18409a75e85SMarek Szyprowski 		return ret;
18509a75e85SMarek Szyprowski 
1868aa90cf2SStefan Wahren 	if (hsotg->clk)
18709a75e85SMarek Szyprowski 		clk_disable_unprepare(hsotg->clk);
18809a75e85SMarek Szyprowski 
18909a75e85SMarek Szyprowski 	ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
19009a75e85SMarek Szyprowski 				     hsotg->supplies);
19109a75e85SMarek Szyprowski 
19209a75e85SMarek Szyprowski 	return ret;
19309a75e85SMarek Szyprowski }
19409a75e85SMarek Szyprowski 
19509a75e85SMarek Szyprowski /**
19609a75e85SMarek Szyprowski  * dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources
19709a75e85SMarek Szyprowski  * @hsotg: The driver state
19809a75e85SMarek Szyprowski  *
19909a75e85SMarek Szyprowski  * A wrapper for platform code responsible for controlling
20009a75e85SMarek Szyprowski  * low-level USB platform resources (phy, clock, regulators)
20109a75e85SMarek Szyprowski  */
20209a75e85SMarek Szyprowski int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
20309a75e85SMarek Szyprowski {
20409a75e85SMarek Szyprowski 	int ret = __dwc2_lowlevel_hw_disable(hsotg);
20509a75e85SMarek Szyprowski 
20609a75e85SMarek Szyprowski 	if (ret == 0)
20709a75e85SMarek Szyprowski 		hsotg->ll_hw_enabled = false;
20809a75e85SMarek Szyprowski 	return ret;
20909a75e85SMarek Szyprowski }
21009a75e85SMarek Szyprowski 
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");
21683f8da56SDinh Nguyen 	if (IS_ERR(hsotg->reset)) {
21783f8da56SDinh Nguyen 		ret = PTR_ERR(hsotg->reset);
2188ec32c38SPhilipp Zabel 		dev_err(hsotg->dev, "error getting reset control %d\n", ret);
21983f8da56SDinh Nguyen 		return ret;
22083f8da56SDinh Nguyen 	}
22183f8da56SDinh Nguyen 
22283f8da56SDinh Nguyen 	reset_control_deassert(hsotg->reset);
22383f8da56SDinh Nguyen 
224f2830ad4SDinh Nguyen 	hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc");
225f2830ad4SDinh Nguyen 	if (IS_ERR(hsotg->reset_ecc)) {
226f2830ad4SDinh Nguyen 		ret = PTR_ERR(hsotg->reset_ecc);
227f2830ad4SDinh Nguyen 		dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret);
228f2830ad4SDinh Nguyen 		return ret;
229f2830ad4SDinh Nguyen 	}
230f2830ad4SDinh Nguyen 
231f2830ad4SDinh Nguyen 	reset_control_deassert(hsotg->reset_ecc);
232f2830ad4SDinh Nguyen 
23309a75e85SMarek Szyprowski 	/*
23409a75e85SMarek Szyprowski 	 * Attempt to find a generic PHY, then look for an old style
23509a75e85SMarek Szyprowski 	 * USB PHY and then fall back to pdata
23609a75e85SMarek Szyprowski 	 */
23709a75e85SMarek Szyprowski 	hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy");
23809a75e85SMarek Szyprowski 	if (IS_ERR(hsotg->phy)) {
2396c2dad69SStefan Wahren 		ret = PTR_ERR(hsotg->phy);
2406c2dad69SStefan Wahren 		switch (ret) {
2416c2dad69SStefan Wahren 		case -ENODEV:
2426c2dad69SStefan Wahren 		case -ENOSYS:
24309a75e85SMarek Szyprowski 			hsotg->phy = NULL;
2446c2dad69SStefan Wahren 			break;
2456c2dad69SStefan Wahren 		case -EPROBE_DEFER:
2466c2dad69SStefan Wahren 			return ret;
2476c2dad69SStefan Wahren 		default:
2486c2dad69SStefan Wahren 			dev_err(hsotg->dev, "error getting phy %d\n", ret);
2496c2dad69SStefan Wahren 			return ret;
25009a75e85SMarek Szyprowski 		}
2516c2dad69SStefan Wahren 	}
2526c2dad69SStefan Wahren 
2536c2dad69SStefan Wahren 	if (!hsotg->phy) {
2546c2dad69SStefan Wahren 		hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
2556c2dad69SStefan Wahren 		if (IS_ERR(hsotg->uphy)) {
2566c2dad69SStefan Wahren 			ret = PTR_ERR(hsotg->uphy);
2576c2dad69SStefan Wahren 			switch (ret) {
2586c2dad69SStefan Wahren 			case -ENODEV:
2596c2dad69SStefan Wahren 			case -ENXIO:
2606c2dad69SStefan Wahren 				hsotg->uphy = NULL;
2616c2dad69SStefan Wahren 				break;
2626c2dad69SStefan Wahren 			case -EPROBE_DEFER:
2636c2dad69SStefan Wahren 				return ret;
2646c2dad69SStefan Wahren 			default:
2656c2dad69SStefan Wahren 				dev_err(hsotg->dev, "error getting usb phy %d\n",
2666c2dad69SStefan Wahren 					ret);
2676c2dad69SStefan Wahren 				return ret;
2686c2dad69SStefan Wahren 			}
2696c2dad69SStefan Wahren 		}
2706c2dad69SStefan Wahren 	}
2716c2dad69SStefan Wahren 
2726c2dad69SStefan Wahren 	hsotg->plat = dev_get_platdata(hsotg->dev);
27309a75e85SMarek Szyprowski 
27409a75e85SMarek Szyprowski 	/* Clock */
27560722c4eSChunfeng Yun 	hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg");
27609a75e85SMarek Szyprowski 	if (IS_ERR(hsotg->clk)) {
27760722c4eSChunfeng Yun 		dev_err(hsotg->dev, "cannot get otg clock\n");
27860722c4eSChunfeng Yun 		return PTR_ERR(hsotg->clk);
27909a75e85SMarek Szyprowski 	}
28009a75e85SMarek Szyprowski 
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);
28709a75e85SMarek Szyprowski 	if (ret) {
2888e11a977SMarek Szyprowski 		if (ret != -EPROBE_DEFER)
2898e11a977SMarek Szyprowski 			dev_err(hsotg->dev, "failed to request supplies: %d\n",
2908e11a977SMarek Szyprowski 				ret);
29109a75e85SMarek Szyprowski 		return ret;
29209a75e85SMarek Szyprowski 	}
29309a75e85SMarek Szyprowski 	return 0;
29409a75e85SMarek Szyprowski }
29509a75e85SMarek Szyprowski 
296197ba5f4SPaul Zimmerman /**
297197ba5f4SPaul Zimmerman  * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
298197ba5f4SPaul Zimmerman  * DWC_otg driver
299197ba5f4SPaul Zimmerman  *
300197ba5f4SPaul Zimmerman  * @dev: Platform device
301197ba5f4SPaul Zimmerman  *
302197ba5f4SPaul Zimmerman  * This routine is called, for example, when the rmmod command is executed. The
303197ba5f4SPaul Zimmerman  * device may or may not be electrically present. If it is present, the driver
304197ba5f4SPaul Zimmerman  * stops device processing. Any resources used on behalf of this device are
305197ba5f4SPaul Zimmerman  * freed.
306197ba5f4SPaul Zimmerman  */
307197ba5f4SPaul Zimmerman static int dwc2_driver_remove(struct platform_device *dev)
308197ba5f4SPaul Zimmerman {
309197ba5f4SPaul Zimmerman 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
310197ba5f4SPaul Zimmerman 
311f91eea44SMian Yousaf Kaukab 	dwc2_debugfs_exit(hsotg);
312e39af88fSMarek Szyprowski 	if (hsotg->hcd_enabled)
313197ba5f4SPaul Zimmerman 		dwc2_hcd_remove(hsotg);
314e39af88fSMarek Szyprowski 	if (hsotg->gadget_enabled)
3151f91b4ccSFelipe Balbi 		dwc2_hsotg_remove(hsotg);
316197ba5f4SPaul Zimmerman 
317a415083aSAmelie Delaunay 	if (hsotg->params.activate_stm_id_vb_detection)
318a415083aSAmelie Delaunay 		regulator_disable(hsotg->usb33d);
319a415083aSAmelie Delaunay 
32009a75e85SMarek Szyprowski 	if (hsotg->ll_hw_enabled)
32109a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
32209a75e85SMarek Szyprowski 
32383f8da56SDinh Nguyen 	reset_control_assert(hsotg->reset);
324f2830ad4SDinh Nguyen 	reset_control_assert(hsotg->reset_ecc);
32583f8da56SDinh Nguyen 
326197ba5f4SPaul Zimmerman 	return 0;
327197ba5f4SPaul Zimmerman }
328197ba5f4SPaul Zimmerman 
329a40a0031SHeiko Stübner /**
330a40a0031SHeiko Stübner  * dwc2_driver_shutdown() - Called on device shutdown
331a40a0031SHeiko Stübner  *
332a40a0031SHeiko Stübner  * @dev: Platform device
333a40a0031SHeiko Stübner  *
334a40a0031SHeiko Stübner  * In specific conditions (involving usb hubs) dwc2 devices can create a
335a40a0031SHeiko Stübner  * lot of interrupts, even to the point of overwhelming devices running
336a40a0031SHeiko Stübner  * at low frequencies. Some devices need to do special clock handling
337a40a0031SHeiko Stübner  * at shutdown-time which may bring the system clock below the threshold
338a40a0031SHeiko Stübner  * of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
339a40a0031SHeiko Stübner  * prevents reboots/poweroffs from getting stuck in such cases.
340a40a0031SHeiko Stübner  */
341a40a0031SHeiko Stübner static void dwc2_driver_shutdown(struct platform_device *dev)
342a40a0031SHeiko Stübner {
343a40a0031SHeiko Stübner 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
344a40a0031SHeiko Stübner 
3454fdf228cSMinas Harutyunyan 	dwc2_disable_global_interrupts(hsotg);
3464fdf228cSMinas Harutyunyan 	synchronize_irq(hsotg->irq);
347a40a0031SHeiko Stübner }
348a40a0031SHeiko Stübner 
349197ba5f4SPaul Zimmerman /**
350fe369e18SGevorg Sahakyan  * dwc2_check_core_endianness() - Returns true if core and AHB have
351fe369e18SGevorg Sahakyan  * opposite endianness.
352fe369e18SGevorg Sahakyan  * @hsotg:	Programming view of the DWC_otg controller.
353fe369e18SGevorg Sahakyan  */
354fe369e18SGevorg Sahakyan static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
355fe369e18SGevorg Sahakyan {
356fe369e18SGevorg Sahakyan 	u32 snpsid;
357fe369e18SGevorg Sahakyan 
358fe369e18SGevorg Sahakyan 	snpsid = ioread32(hsotg->regs + GSNPSID);
359fe369e18SGevorg Sahakyan 	if ((snpsid & GSNPSID_ID_MASK) == DWC2_OTG_ID ||
360fe369e18SGevorg Sahakyan 	    (snpsid & GSNPSID_ID_MASK) == DWC2_FS_IOT_ID ||
361fe369e18SGevorg Sahakyan 	    (snpsid & GSNPSID_ID_MASK) == DWC2_HS_IOT_ID)
362fe369e18SGevorg Sahakyan 		return false;
363fe369e18SGevorg Sahakyan 	return true;
364fe369e18SGevorg Sahakyan }
365fe369e18SGevorg Sahakyan 
366fe369e18SGevorg Sahakyan /**
36765dc2e72SMinas Harutyunyan  * Check core version
36865dc2e72SMinas Harutyunyan  *
36965dc2e72SMinas Harutyunyan  * @hsotg: Programming view of the DWC_otg controller
37065dc2e72SMinas Harutyunyan  *
37165dc2e72SMinas Harutyunyan  */
37265dc2e72SMinas Harutyunyan int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
37365dc2e72SMinas Harutyunyan {
37465dc2e72SMinas Harutyunyan 	struct dwc2_hw_params *hw = &hsotg->hw_params;
37565dc2e72SMinas Harutyunyan 
37665dc2e72SMinas Harutyunyan 	/*
37765dc2e72SMinas Harutyunyan 	 * Attempt to ensure this device is really a DWC_otg Controller.
37865dc2e72SMinas Harutyunyan 	 * Read and verify the GSNPSID register contents. The value should be
37965dc2e72SMinas Harutyunyan 	 * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
38065dc2e72SMinas Harutyunyan 	 */
38165dc2e72SMinas Harutyunyan 
38265dc2e72SMinas Harutyunyan 	hw->snpsid = dwc2_readl(hsotg, GSNPSID);
38365dc2e72SMinas Harutyunyan 	if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
38465dc2e72SMinas Harutyunyan 	    (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
38565dc2e72SMinas Harutyunyan 	    (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
38665dc2e72SMinas Harutyunyan 		dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
38765dc2e72SMinas Harutyunyan 			hw->snpsid);
38865dc2e72SMinas Harutyunyan 		return -ENODEV;
38965dc2e72SMinas Harutyunyan 	}
39065dc2e72SMinas Harutyunyan 
39165dc2e72SMinas Harutyunyan 	dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
39265dc2e72SMinas Harutyunyan 		hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
39365dc2e72SMinas Harutyunyan 		hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
39465dc2e72SMinas Harutyunyan 	return 0;
39565dc2e72SMinas Harutyunyan }
39665dc2e72SMinas Harutyunyan 
39765dc2e72SMinas Harutyunyan /**
398197ba5f4SPaul Zimmerman  * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
399197ba5f4SPaul Zimmerman  * driver
400197ba5f4SPaul Zimmerman  *
401197ba5f4SPaul Zimmerman  * @dev: Platform device
402197ba5f4SPaul Zimmerman  *
403197ba5f4SPaul Zimmerman  * This routine creates the driver components required to control the device
404197ba5f4SPaul Zimmerman  * (core, HCD, and PCD) and initializes the device. The driver components are
405197ba5f4SPaul Zimmerman  * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
406197ba5f4SPaul Zimmerman  * in the device private data. This allows the driver to access the dwc2_hsotg
407197ba5f4SPaul Zimmerman  * structure on subsequent calls to driver methods for this device.
408197ba5f4SPaul Zimmerman  */
409197ba5f4SPaul Zimmerman static int dwc2_driver_probe(struct platform_device *dev)
410197ba5f4SPaul Zimmerman {
411197ba5f4SPaul Zimmerman 	struct dwc2_hsotg *hsotg;
412197ba5f4SPaul Zimmerman 	struct resource *res;
413197ba5f4SPaul Zimmerman 	int retval;
414197ba5f4SPaul Zimmerman 
415197ba5f4SPaul Zimmerman 	hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
416197ba5f4SPaul Zimmerman 	if (!hsotg)
417197ba5f4SPaul Zimmerman 		return -ENOMEM;
418197ba5f4SPaul Zimmerman 
419197ba5f4SPaul Zimmerman 	hsotg->dev = &dev->dev;
420197ba5f4SPaul Zimmerman 
421197ba5f4SPaul Zimmerman 	/*
422197ba5f4SPaul Zimmerman 	 * Use reasonable defaults so platforms don't have to provide these.
423197ba5f4SPaul Zimmerman 	 */
424197ba5f4SPaul Zimmerman 	if (!dev->dev.dma_mask)
425197ba5f4SPaul Zimmerman 		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
426197ba5f4SPaul Zimmerman 	retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
42742c6a252SStefan Wahren 	if (retval) {
42842c6a252SStefan Wahren 		dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval);
429197ba5f4SPaul Zimmerman 		return retval;
43042c6a252SStefan Wahren 	}
431197ba5f4SPaul Zimmerman 
4325bf7e288SDejin Zheng 	hsotg->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res);
433197ba5f4SPaul Zimmerman 	if (IS_ERR(hsotg->regs))
434197ba5f4SPaul Zimmerman 		return PTR_ERR(hsotg->regs);
435197ba5f4SPaul Zimmerman 
436197ba5f4SPaul Zimmerman 	dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
437197ba5f4SPaul Zimmerman 		(unsigned long)res->start, hsotg->regs);
438197ba5f4SPaul Zimmerman 
43909a75e85SMarek Szyprowski 	retval = dwc2_lowlevel_hw_init(hsotg);
440ecb176c6SMian Yousaf Kaukab 	if (retval)
441ecb176c6SMian Yousaf Kaukab 		return retval;
442ecb176c6SMian Yousaf Kaukab 
44309a75e85SMarek Szyprowski 	spin_lock_init(&hsotg->lock);
44409a75e85SMarek Szyprowski 
445a40a0031SHeiko Stübner 	hsotg->irq = platform_get_irq(dev, 0);
446b33f3706SStephen Boyd 	if (hsotg->irq < 0)
447a40a0031SHeiko Stübner 		return hsotg->irq;
448f74875dcSStefan Wahren 
449f74875dcSStefan Wahren 	dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
450a40a0031SHeiko Stübner 		hsotg->irq);
451a40a0031SHeiko Stübner 	retval = devm_request_irq(hsotg->dev, hsotg->irq,
452f74875dcSStefan Wahren 				  dwc2_handle_common_intr, IRQF_SHARED,
453f74875dcSStefan Wahren 				  dev_name(hsotg->dev), hsotg);
454f74875dcSStefan Wahren 	if (retval)
455f74875dcSStefan Wahren 		return retval;
456f74875dcSStefan Wahren 
457e0f681c2SFabrice Gasnier 	hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
458e0f681c2SFabrice Gasnier 	if (IS_ERR(hsotg->vbus_supply)) {
459e0f681c2SFabrice Gasnier 		retval = PTR_ERR(hsotg->vbus_supply);
460e0f681c2SFabrice Gasnier 		hsotg->vbus_supply = NULL;
461e0f681c2SFabrice Gasnier 		if (retval != -ENODEV)
462e0f681c2SFabrice Gasnier 			return retval;
463e0f681c2SFabrice Gasnier 	}
464e0f681c2SFabrice Gasnier 
46509a75e85SMarek Szyprowski 	retval = dwc2_lowlevel_hw_enable(hsotg);
46609a75e85SMarek Szyprowski 	if (retval)
46709a75e85SMarek Szyprowski 		return retval;
46809a75e85SMarek Szyprowski 
469d9707490SBruno Meirelles Herrera 	hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);
470d9707490SBruno Meirelles Herrera 
4715268ed9dSJohn Youn 	retval = dwc2_get_dr_mode(hsotg);
4725268ed9dSJohn Youn 	if (retval)
473a6ef3e02SJohn Youn 		goto error;
4745268ed9dSJohn Youn 
475c846b03fSDouglas Anderson 	hsotg->need_phy_for_wake =
476c846b03fSDouglas Anderson 		of_property_read_bool(dev->dev.of_node,
477c846b03fSDouglas Anderson 				      "snps,need-phy-for-wake");
478c846b03fSDouglas Anderson 
47903b32e4cSJohn Youn 	/*
48065dc2e72SMinas Harutyunyan 	 * Before performing any core related operations
48165dc2e72SMinas Harutyunyan 	 * check core version.
48265dc2e72SMinas Harutyunyan 	 */
48365dc2e72SMinas Harutyunyan 	retval = dwc2_check_core_version(hsotg);
48465dc2e72SMinas Harutyunyan 	if (retval)
48565dc2e72SMinas Harutyunyan 		goto error;
48665dc2e72SMinas Harutyunyan 
48765dc2e72SMinas Harutyunyan 	/*
48803b32e4cSJohn Youn 	 * Reset before dwc2_get_hwparams() then it could get power-on real
48903b32e4cSJohn Youn 	 * reset value form registers.
49003b32e4cSJohn Youn 	 */
49113b1f8e2SVardan Mikayelyan 	retval = dwc2_core_reset(hsotg, false);
49213b1f8e2SVardan Mikayelyan 	if (retval)
49313b1f8e2SVardan Mikayelyan 		goto error;
49403b32e4cSJohn Youn 
49503b32e4cSJohn Youn 	/* Detect config values from hardware */
49609a75e85SMarek Szyprowski 	retval = dwc2_get_hwparams(hsotg);
49709a75e85SMarek Szyprowski 	if (retval)
49809a75e85SMarek Szyprowski 		goto error;
49909a75e85SMarek Szyprowski 
50013b1f8e2SVardan Mikayelyan 	/*
50113b1f8e2SVardan Mikayelyan 	 * For OTG cores, set the force mode bits to reflect the value
50213b1f8e2SVardan Mikayelyan 	 * of dr_mode. Force mode bits should not be touched at any
50313b1f8e2SVardan Mikayelyan 	 * other time after this.
50413b1f8e2SVardan Mikayelyan 	 */
50525362d31SJohn Youn 	dwc2_force_dr_mode(hsotg);
506263b7fb5SJohn Youn 
507334bbd4eSJohn Youn 	retval = dwc2_init_params(hsotg);
508334bbd4eSJohn Youn 	if (retval)
509334bbd4eSJohn Youn 		goto error;
510334bbd4eSJohn Youn 
511a415083aSAmelie Delaunay 	if (hsotg->params.activate_stm_id_vb_detection) {
512a415083aSAmelie Delaunay 		u32 ggpio;
513a415083aSAmelie Delaunay 
514a415083aSAmelie Delaunay 		hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d");
515a415083aSAmelie Delaunay 		if (IS_ERR(hsotg->usb33d)) {
516a415083aSAmelie Delaunay 			retval = PTR_ERR(hsotg->usb33d);
517a415083aSAmelie Delaunay 			if (retval != -EPROBE_DEFER)
518a415083aSAmelie Delaunay 				dev_err(hsotg->dev,
519a415083aSAmelie Delaunay 					"failed to request usb33d supply: %d\n",
520a415083aSAmelie Delaunay 					retval);
521a415083aSAmelie Delaunay 			goto error;
522a415083aSAmelie Delaunay 		}
523a415083aSAmelie Delaunay 		retval = regulator_enable(hsotg->usb33d);
524a415083aSAmelie Delaunay 		if (retval) {
525a415083aSAmelie Delaunay 			dev_err(hsotg->dev,
526a415083aSAmelie Delaunay 				"failed to enable usb33d supply: %d\n", retval);
527a415083aSAmelie Delaunay 			goto error;
528a415083aSAmelie Delaunay 		}
529a415083aSAmelie Delaunay 
530a415083aSAmelie Delaunay 		ggpio = dwc2_readl(hsotg, GGPIO);
531a415083aSAmelie Delaunay 		ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
532a415083aSAmelie Delaunay 		ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
533a415083aSAmelie Delaunay 		dwc2_writel(hsotg, ggpio, GGPIO);
534a415083aSAmelie Delaunay 	}
535a415083aSAmelie Delaunay 
536e39af88fSMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_HOST) {
537f3768997SVardan Mikayelyan 		retval = dwc2_gadget_init(hsotg);
538117777b2SDinh Nguyen 		if (retval)
539a415083aSAmelie Delaunay 			goto error_init;
540e39af88fSMarek Szyprowski 		hsotg->gadget_enabled = 1;
541e39af88fSMarek Szyprowski 	}
542e39af88fSMarek Szyprowski 
543c846b03fSDouglas Anderson 	/*
544c846b03fSDouglas Anderson 	 * If we need PHY for wakeup we must be wakeup capable.
545c846b03fSDouglas Anderson 	 * When we have a device that can wake without the PHY we
546c846b03fSDouglas Anderson 	 * can adjust this condition.
547c846b03fSDouglas Anderson 	 */
548c846b03fSDouglas Anderson 	if (hsotg->need_phy_for_wake)
549c846b03fSDouglas Anderson 		device_set_wakeup_capable(&dev->dev, true);
550c846b03fSDouglas Anderson 
551c40cf770SDouglas Anderson 	hsotg->reset_phy_on_wake =
552c40cf770SDouglas Anderson 		of_property_read_bool(dev->dev.of_node,
553c40cf770SDouglas Anderson 				      "snps,reset-phy-on-wake");
554c40cf770SDouglas Anderson 	if (hsotg->reset_phy_on_wake && !hsotg->phy) {
555c40cf770SDouglas Anderson 		dev_warn(hsotg->dev,
556c40cf770SDouglas Anderson 			 "Quirk reset-phy-on-wake only supports generic PHYs\n");
557c40cf770SDouglas Anderson 		hsotg->reset_phy_on_wake = false;
558c40cf770SDouglas Anderson 	}
559c40cf770SDouglas Anderson 
560e39af88fSMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
5614fe160d5SHeiner Kallweit 		retval = dwc2_hcd_init(hsotg);
562e39af88fSMarek Szyprowski 		if (retval) {
563e39af88fSMarek Szyprowski 			if (hsotg->gadget_enabled)
5641f91b4ccSFelipe Balbi 				dwc2_hsotg_remove(hsotg);
565a415083aSAmelie Delaunay 			goto error_init;
566e39af88fSMarek Szyprowski 		}
567e39af88fSMarek Szyprowski 		hsotg->hcd_enabled = 1;
568e39af88fSMarek Szyprowski 	}
569197ba5f4SPaul Zimmerman 
570197ba5f4SPaul Zimmerman 	platform_set_drvdata(dev, hsotg);
57120fe4409SVardan Mikayelyan 	hsotg->hibernated = 0;
572197ba5f4SPaul Zimmerman 
573f91eea44SMian Yousaf Kaukab 	dwc2_debugfs_init(hsotg);
574f91eea44SMian Yousaf Kaukab 
57509a75e85SMarek Szyprowski 	/* Gadget code manages lowlevel hw on its own */
57609a75e85SMarek Szyprowski 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
57709a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
57809a75e85SMarek Szyprowski 
579207324a3SMinas Harutyunyan #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
580207324a3SMinas Harutyunyan 	IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
581207324a3SMinas Harutyunyan 	/* Postponed adding a new gadget to the udc class driver list */
582207324a3SMinas Harutyunyan 	if (hsotg->gadget_enabled) {
583207324a3SMinas Harutyunyan 		retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget);
584207324a3SMinas Harutyunyan 		if (retval) {
58533a06f13SMarek Szyprowski 			hsotg->gadget.udc = NULL;
586207324a3SMinas Harutyunyan 			dwc2_hsotg_remove(hsotg);
587e1c08cf2SMartin Blumenstingl 			goto error_debugfs;
588207324a3SMinas Harutyunyan 		}
589207324a3SMinas Harutyunyan 	}
590207324a3SMinas Harutyunyan #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
59109a75e85SMarek Szyprowski 	return 0;
59209a75e85SMarek Szyprowski 
593e1c08cf2SMartin Blumenstingl error_debugfs:
594e1c08cf2SMartin Blumenstingl 	dwc2_debugfs_exit(hsotg);
595e1c08cf2SMartin Blumenstingl 	if (hsotg->hcd_enabled)
596e1c08cf2SMartin Blumenstingl 		dwc2_hcd_remove(hsotg);
597a415083aSAmelie Delaunay error_init:
598a415083aSAmelie Delaunay 	if (hsotg->params.activate_stm_id_vb_detection)
599a415083aSAmelie Delaunay 		regulator_disable(hsotg->usb33d);
60009a75e85SMarek Szyprowski error:
60133a06f13SMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL)
60209a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
603197ba5f4SPaul Zimmerman 	return retval;
604197ba5f4SPaul Zimmerman }
605197ba5f4SPaul Zimmerman 
606da9f3289SFabio Estevam static int __maybe_unused dwc2_suspend(struct device *dev)
607117777b2SDinh Nguyen {
608bcc06078SDinh Nguyen 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
609c846b03fSDouglas Anderson 	bool is_device_mode = dwc2_is_device_mode(dwc2);
610117777b2SDinh Nguyen 	int ret = 0;
611117777b2SDinh Nguyen 
612c846b03fSDouglas Anderson 	if (is_device_mode)
61309a75e85SMarek Szyprowski 		dwc2_hsotg_suspend(dwc2);
614135b3c43SYunzhi Li 
615a415083aSAmelie Delaunay 	if (dwc2->params.activate_stm_id_vb_detection) {
616a415083aSAmelie Delaunay 		unsigned long flags;
617a415083aSAmelie Delaunay 		u32 ggpio, gotgctl;
618a415083aSAmelie Delaunay 
619a415083aSAmelie Delaunay 		/*
620a415083aSAmelie Delaunay 		 * Need to force the mode to the current mode to avoid Mode
621a415083aSAmelie Delaunay 		 * Mismatch Interrupt when ID detection will be disabled.
622a415083aSAmelie Delaunay 		 */
623a415083aSAmelie Delaunay 		dwc2_force_mode(dwc2, !is_device_mode);
624a415083aSAmelie Delaunay 
625a415083aSAmelie Delaunay 		spin_lock_irqsave(&dwc2->lock, flags);
626a415083aSAmelie Delaunay 		gotgctl = dwc2_readl(dwc2, GOTGCTL);
627a415083aSAmelie Delaunay 		/* bypass debounce filter, enable overrides */
628a415083aSAmelie Delaunay 		gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
629a415083aSAmelie Delaunay 		gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN;
630a415083aSAmelie Delaunay 		/* Force A / B session if needed */
631a415083aSAmelie Delaunay 		if (gotgctl & GOTGCTL_ASESVLD)
632a415083aSAmelie Delaunay 			gotgctl |= GOTGCTL_AVALOVAL;
633a415083aSAmelie Delaunay 		if (gotgctl & GOTGCTL_BSESVLD)
634a415083aSAmelie Delaunay 			gotgctl |= GOTGCTL_BVALOVAL;
635a415083aSAmelie Delaunay 		dwc2_writel(dwc2, gotgctl, GOTGCTL);
636a415083aSAmelie Delaunay 		spin_unlock_irqrestore(&dwc2->lock, flags);
637a415083aSAmelie Delaunay 
638a415083aSAmelie Delaunay 		ggpio = dwc2_readl(dwc2, GGPIO);
639a415083aSAmelie Delaunay 		ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN;
640a415083aSAmelie Delaunay 		ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN;
641a415083aSAmelie Delaunay 		dwc2_writel(dwc2, ggpio, GGPIO);
642a415083aSAmelie Delaunay 
643a415083aSAmelie Delaunay 		regulator_disable(dwc2->usb33d);
644a415083aSAmelie Delaunay 	}
645a415083aSAmelie Delaunay 
646c846b03fSDouglas Anderson 	if (dwc2->ll_hw_enabled &&
647c846b03fSDouglas Anderson 	    (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
64809a75e85SMarek Szyprowski 		ret = __dwc2_lowlevel_hw_disable(dwc2);
649c846b03fSDouglas Anderson 		dwc2->phy_off_for_suspend = true;
650c846b03fSDouglas Anderson 	}
65109a75e85SMarek Szyprowski 
652117777b2SDinh Nguyen 	return ret;
653117777b2SDinh Nguyen }
654117777b2SDinh Nguyen 
655da9f3289SFabio Estevam static int __maybe_unused dwc2_resume(struct device *dev)
656117777b2SDinh Nguyen {
657bcc06078SDinh Nguyen 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
658117777b2SDinh Nguyen 	int ret = 0;
659117777b2SDinh Nguyen 
660c846b03fSDouglas Anderson 	if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
66109a75e85SMarek Szyprowski 		ret = __dwc2_lowlevel_hw_enable(dwc2);
66209a75e85SMarek Szyprowski 		if (ret)
66309a75e85SMarek Szyprowski 			return ret;
664135b3c43SYunzhi Li 	}
665c846b03fSDouglas Anderson 	dwc2->phy_off_for_suspend = false;
66609a75e85SMarek Szyprowski 
667a415083aSAmelie Delaunay 	if (dwc2->params.activate_stm_id_vb_detection) {
668a415083aSAmelie Delaunay 		unsigned long flags;
669a415083aSAmelie Delaunay 		u32 ggpio, gotgctl;
670a415083aSAmelie Delaunay 
671a415083aSAmelie Delaunay 		ret = regulator_enable(dwc2->usb33d);
672a415083aSAmelie Delaunay 		if (ret)
673a415083aSAmelie Delaunay 			return ret;
674a415083aSAmelie Delaunay 
675a415083aSAmelie Delaunay 		ggpio = dwc2_readl(dwc2, GGPIO);
676a415083aSAmelie Delaunay 		ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
677a415083aSAmelie Delaunay 		ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
678a415083aSAmelie Delaunay 		dwc2_writel(dwc2, ggpio, GGPIO);
679a415083aSAmelie Delaunay 
680a415083aSAmelie Delaunay 		/* ID/VBUS detection startup time */
681a415083aSAmelie Delaunay 		usleep_range(5000, 7000);
682a415083aSAmelie Delaunay 
683a415083aSAmelie Delaunay 		spin_lock_irqsave(&dwc2->lock, flags);
684a415083aSAmelie Delaunay 		gotgctl = dwc2_readl(dwc2, GOTGCTL);
685a415083aSAmelie Delaunay 		gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS;
686a415083aSAmelie Delaunay 		gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
687a415083aSAmelie Delaunay 			     GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL);
688a415083aSAmelie Delaunay 		dwc2_writel(dwc2, gotgctl, GOTGCTL);
689a415083aSAmelie Delaunay 		spin_unlock_irqrestore(&dwc2->lock, flags);
690a415083aSAmelie Delaunay 	}
691a415083aSAmelie Delaunay 
692a415083aSAmelie Delaunay 	/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
693a415083aSAmelie Delaunay 	dwc2_force_dr_mode(dwc2);
694a415083aSAmelie Delaunay 
69509a75e85SMarek Szyprowski 	if (dwc2_is_device_mode(dwc2))
69609a75e85SMarek Szyprowski 		ret = dwc2_hsotg_resume(dwc2);
69709a75e85SMarek Szyprowski 
698117777b2SDinh Nguyen 	return ret;
699117777b2SDinh Nguyen }
700117777b2SDinh Nguyen 
701bcc06078SDinh Nguyen static const struct dev_pm_ops dwc2_dev_pm_ops = {
702bcc06078SDinh Nguyen 	SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
703bcc06078SDinh Nguyen };
704bcc06078SDinh Nguyen 
705197ba5f4SPaul Zimmerman static struct platform_driver dwc2_platform_driver = {
706197ba5f4SPaul Zimmerman 	.driver = {
707197ba5f4SPaul Zimmerman 		.name = dwc2_driver_name,
708197ba5f4SPaul Zimmerman 		.of_match_table = dwc2_of_match_table,
709bcc06078SDinh Nguyen 		.pm = &dwc2_dev_pm_ops,
710197ba5f4SPaul Zimmerman 	},
711197ba5f4SPaul Zimmerman 	.probe = dwc2_driver_probe,
712197ba5f4SPaul Zimmerman 	.remove = dwc2_driver_remove,
713a40a0031SHeiko Stübner 	.shutdown = dwc2_driver_shutdown,
714197ba5f4SPaul Zimmerman };
715197ba5f4SPaul Zimmerman 
716197ba5f4SPaul Zimmerman module_platform_driver(dwc2_platform_driver);
717197ba5f4SPaul Zimmerman 
718197ba5f4SPaul Zimmerman MODULE_DESCRIPTION("DESIGNWARE HS OTG Platform Glue");
719197ba5f4SPaul Zimmerman MODULE_AUTHOR("Matthijs Kooijman <matthijs@stdin.nl>");
720197ba5f4SPaul Zimmerman MODULE_LICENSE("Dual BSD/GPL");
721