xref: /openbmc/linux/drivers/usb/dwc2/platform.c (revision d9707490)
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 	/* Set default UTMI width */
23409a75e85SMarek Szyprowski 	hsotg->phyif = GUSBCFG_PHYIF16;
23509a75e85SMarek Szyprowski 
23609a75e85SMarek Szyprowski 	/*
23709a75e85SMarek Szyprowski 	 * Attempt to find a generic PHY, then look for an old style
23809a75e85SMarek Szyprowski 	 * USB PHY and then fall back to pdata
23909a75e85SMarek Szyprowski 	 */
24009a75e85SMarek Szyprowski 	hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy");
24109a75e85SMarek Szyprowski 	if (IS_ERR(hsotg->phy)) {
2426c2dad69SStefan Wahren 		ret = PTR_ERR(hsotg->phy);
2436c2dad69SStefan Wahren 		switch (ret) {
2446c2dad69SStefan Wahren 		case -ENODEV:
2456c2dad69SStefan Wahren 		case -ENOSYS:
24609a75e85SMarek Szyprowski 			hsotg->phy = NULL;
2476c2dad69SStefan Wahren 			break;
2486c2dad69SStefan Wahren 		case -EPROBE_DEFER:
2496c2dad69SStefan Wahren 			return ret;
2506c2dad69SStefan Wahren 		default:
2516c2dad69SStefan Wahren 			dev_err(hsotg->dev, "error getting phy %d\n", ret);
2526c2dad69SStefan Wahren 			return ret;
25309a75e85SMarek Szyprowski 		}
2546c2dad69SStefan Wahren 	}
2556c2dad69SStefan Wahren 
2566c2dad69SStefan Wahren 	if (!hsotg->phy) {
2576c2dad69SStefan Wahren 		hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
2586c2dad69SStefan Wahren 		if (IS_ERR(hsotg->uphy)) {
2596c2dad69SStefan Wahren 			ret = PTR_ERR(hsotg->uphy);
2606c2dad69SStefan Wahren 			switch (ret) {
2616c2dad69SStefan Wahren 			case -ENODEV:
2626c2dad69SStefan Wahren 			case -ENXIO:
2636c2dad69SStefan Wahren 				hsotg->uphy = NULL;
2646c2dad69SStefan Wahren 				break;
2656c2dad69SStefan Wahren 			case -EPROBE_DEFER:
2666c2dad69SStefan Wahren 				return ret;
2676c2dad69SStefan Wahren 			default:
2686c2dad69SStefan Wahren 				dev_err(hsotg->dev, "error getting usb phy %d\n",
2696c2dad69SStefan Wahren 					ret);
2706c2dad69SStefan Wahren 				return ret;
2716c2dad69SStefan Wahren 			}
2726c2dad69SStefan Wahren 		}
2736c2dad69SStefan Wahren 	}
2746c2dad69SStefan Wahren 
2756c2dad69SStefan Wahren 	hsotg->plat = dev_get_platdata(hsotg->dev);
27609a75e85SMarek Szyprowski 
27709a75e85SMarek Szyprowski 	if (hsotg->phy) {
27809a75e85SMarek Szyprowski 		/*
27909a75e85SMarek Szyprowski 		 * If using the generic PHY framework, check if the PHY bus
28009a75e85SMarek Szyprowski 		 * width is 8-bit and set the phyif appropriately.
28109a75e85SMarek Szyprowski 		 */
28209a75e85SMarek Szyprowski 		if (phy_get_bus_width(hsotg->phy) == 8)
28309a75e85SMarek Szyprowski 			hsotg->phyif = GUSBCFG_PHYIF8;
28409a75e85SMarek Szyprowski 	}
28509a75e85SMarek Szyprowski 
28609a75e85SMarek Szyprowski 	/* Clock */
28709a75e85SMarek Szyprowski 	hsotg->clk = devm_clk_get(hsotg->dev, "otg");
28809a75e85SMarek Szyprowski 	if (IS_ERR(hsotg->clk)) {
28909a75e85SMarek Szyprowski 		hsotg->clk = NULL;
29009a75e85SMarek Szyprowski 		dev_dbg(hsotg->dev, "cannot get otg clock\n");
29109a75e85SMarek Szyprowski 	}
29209a75e85SMarek Szyprowski 
29309a75e85SMarek Szyprowski 	/* Regulators */
29409a75e85SMarek Szyprowski 	for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++)
29509a75e85SMarek Szyprowski 		hsotg->supplies[i].supply = dwc2_hsotg_supply_names[i];
29609a75e85SMarek Szyprowski 
29709a75e85SMarek Szyprowski 	ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
29809a75e85SMarek Szyprowski 				      hsotg->supplies);
29909a75e85SMarek Szyprowski 	if (ret) {
30009a75e85SMarek Szyprowski 		dev_err(hsotg->dev, "failed to request supplies: %d\n", ret);
30109a75e85SMarek Szyprowski 		return ret;
30209a75e85SMarek Szyprowski 	}
30309a75e85SMarek Szyprowski 	return 0;
30409a75e85SMarek Szyprowski }
30509a75e85SMarek Szyprowski 
306197ba5f4SPaul Zimmerman /**
307197ba5f4SPaul Zimmerman  * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
308197ba5f4SPaul Zimmerman  * DWC_otg driver
309197ba5f4SPaul Zimmerman  *
310197ba5f4SPaul Zimmerman  * @dev: Platform device
311197ba5f4SPaul Zimmerman  *
312197ba5f4SPaul Zimmerman  * This routine is called, for example, when the rmmod command is executed. The
313197ba5f4SPaul Zimmerman  * device may or may not be electrically present. If it is present, the driver
314197ba5f4SPaul Zimmerman  * stops device processing. Any resources used on behalf of this device are
315197ba5f4SPaul Zimmerman  * freed.
316197ba5f4SPaul Zimmerman  */
317197ba5f4SPaul Zimmerman static int dwc2_driver_remove(struct platform_device *dev)
318197ba5f4SPaul Zimmerman {
319197ba5f4SPaul Zimmerman 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
320197ba5f4SPaul Zimmerman 
321f91eea44SMian Yousaf Kaukab 	dwc2_debugfs_exit(hsotg);
322e39af88fSMarek Szyprowski 	if (hsotg->hcd_enabled)
323197ba5f4SPaul Zimmerman 		dwc2_hcd_remove(hsotg);
324e39af88fSMarek Szyprowski 	if (hsotg->gadget_enabled)
3251f91b4ccSFelipe Balbi 		dwc2_hsotg_remove(hsotg);
326197ba5f4SPaul Zimmerman 
32709a75e85SMarek Szyprowski 	if (hsotg->ll_hw_enabled)
32809a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
32909a75e85SMarek Szyprowski 
33083f8da56SDinh Nguyen 	reset_control_assert(hsotg->reset);
331f2830ad4SDinh Nguyen 	reset_control_assert(hsotg->reset_ecc);
33283f8da56SDinh Nguyen 
333197ba5f4SPaul Zimmerman 	return 0;
334197ba5f4SPaul Zimmerman }
335197ba5f4SPaul Zimmerman 
336a40a0031SHeiko Stübner /**
337a40a0031SHeiko Stübner  * dwc2_driver_shutdown() - Called on device shutdown
338a40a0031SHeiko Stübner  *
339a40a0031SHeiko Stübner  * @dev: Platform device
340a40a0031SHeiko Stübner  *
341a40a0031SHeiko Stübner  * In specific conditions (involving usb hubs) dwc2 devices can create a
342a40a0031SHeiko Stübner  * lot of interrupts, even to the point of overwhelming devices running
343a40a0031SHeiko Stübner  * at low frequencies. Some devices need to do special clock handling
344a40a0031SHeiko Stübner  * at shutdown-time which may bring the system clock below the threshold
345a40a0031SHeiko Stübner  * of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
346a40a0031SHeiko Stübner  * prevents reboots/poweroffs from getting stuck in such cases.
347a40a0031SHeiko Stübner  */
348a40a0031SHeiko Stübner static void dwc2_driver_shutdown(struct platform_device *dev)
349a40a0031SHeiko Stübner {
350a40a0031SHeiko Stübner 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
351a40a0031SHeiko Stübner 
352a40a0031SHeiko Stübner 	disable_irq(hsotg->irq);
353a40a0031SHeiko Stübner }
354a40a0031SHeiko Stübner 
355197ba5f4SPaul Zimmerman /**
356fe369e18SGevorg Sahakyan  * dwc2_check_core_endianness() - Returns true if core and AHB have
357fe369e18SGevorg Sahakyan  * opposite endianness.
358fe369e18SGevorg Sahakyan  * @hsotg:	Programming view of the DWC_otg controller.
359fe369e18SGevorg Sahakyan  */
360fe369e18SGevorg Sahakyan static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
361fe369e18SGevorg Sahakyan {
362fe369e18SGevorg Sahakyan 	u32 snpsid;
363fe369e18SGevorg Sahakyan 
364fe369e18SGevorg Sahakyan 	snpsid = ioread32(hsotg->regs + GSNPSID);
365fe369e18SGevorg Sahakyan 	if ((snpsid & GSNPSID_ID_MASK) == DWC2_OTG_ID ||
366fe369e18SGevorg Sahakyan 	    (snpsid & GSNPSID_ID_MASK) == DWC2_FS_IOT_ID ||
367fe369e18SGevorg Sahakyan 	    (snpsid & GSNPSID_ID_MASK) == DWC2_HS_IOT_ID)
368fe369e18SGevorg Sahakyan 		return false;
369fe369e18SGevorg Sahakyan 	return true;
370fe369e18SGevorg Sahakyan }
371fe369e18SGevorg Sahakyan 
372fe369e18SGevorg Sahakyan /**
373197ba5f4SPaul Zimmerman  * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
374197ba5f4SPaul Zimmerman  * driver
375197ba5f4SPaul Zimmerman  *
376197ba5f4SPaul Zimmerman  * @dev: Platform device
377197ba5f4SPaul Zimmerman  *
378197ba5f4SPaul Zimmerman  * This routine creates the driver components required to control the device
379197ba5f4SPaul Zimmerman  * (core, HCD, and PCD) and initializes the device. The driver components are
380197ba5f4SPaul Zimmerman  * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
381197ba5f4SPaul Zimmerman  * in the device private data. This allows the driver to access the dwc2_hsotg
382197ba5f4SPaul Zimmerman  * structure on subsequent calls to driver methods for this device.
383197ba5f4SPaul Zimmerman  */
384197ba5f4SPaul Zimmerman static int dwc2_driver_probe(struct platform_device *dev)
385197ba5f4SPaul Zimmerman {
386197ba5f4SPaul Zimmerman 	struct dwc2_hsotg *hsotg;
387197ba5f4SPaul Zimmerman 	struct resource *res;
388197ba5f4SPaul Zimmerman 	int retval;
389197ba5f4SPaul Zimmerman 
390197ba5f4SPaul Zimmerman 	hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
391197ba5f4SPaul Zimmerman 	if (!hsotg)
392197ba5f4SPaul Zimmerman 		return -ENOMEM;
393197ba5f4SPaul Zimmerman 
394197ba5f4SPaul Zimmerman 	hsotg->dev = &dev->dev;
395197ba5f4SPaul Zimmerman 
396197ba5f4SPaul Zimmerman 	/*
397197ba5f4SPaul Zimmerman 	 * Use reasonable defaults so platforms don't have to provide these.
398197ba5f4SPaul Zimmerman 	 */
399197ba5f4SPaul Zimmerman 	if (!dev->dev.dma_mask)
400197ba5f4SPaul Zimmerman 		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
401197ba5f4SPaul Zimmerman 	retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
40242c6a252SStefan Wahren 	if (retval) {
40342c6a252SStefan Wahren 		dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval);
404197ba5f4SPaul Zimmerman 		return retval;
40542c6a252SStefan Wahren 	}
406197ba5f4SPaul Zimmerman 
407197ba5f4SPaul Zimmerman 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
408197ba5f4SPaul Zimmerman 	hsotg->regs = devm_ioremap_resource(&dev->dev, res);
409197ba5f4SPaul Zimmerman 	if (IS_ERR(hsotg->regs))
410197ba5f4SPaul Zimmerman 		return PTR_ERR(hsotg->regs);
411197ba5f4SPaul Zimmerman 
412197ba5f4SPaul Zimmerman 	dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
413197ba5f4SPaul Zimmerman 		(unsigned long)res->start, hsotg->regs);
414197ba5f4SPaul Zimmerman 
41509a75e85SMarek Szyprowski 	retval = dwc2_lowlevel_hw_init(hsotg);
416ecb176c6SMian Yousaf Kaukab 	if (retval)
417ecb176c6SMian Yousaf Kaukab 		return retval;
418ecb176c6SMian Yousaf Kaukab 
41909a75e85SMarek Szyprowski 	spin_lock_init(&hsotg->lock);
42009a75e85SMarek Szyprowski 
421a40a0031SHeiko Stübner 	hsotg->irq = platform_get_irq(dev, 0);
422a40a0031SHeiko Stübner 	if (hsotg->irq < 0) {
423f74875dcSStefan Wahren 		dev_err(&dev->dev, "missing IRQ resource\n");
424a40a0031SHeiko Stübner 		return hsotg->irq;
425f74875dcSStefan Wahren 	}
426f74875dcSStefan Wahren 
427f74875dcSStefan Wahren 	dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
428a40a0031SHeiko Stübner 		hsotg->irq);
429a40a0031SHeiko Stübner 	retval = devm_request_irq(hsotg->dev, hsotg->irq,
430f74875dcSStefan Wahren 				  dwc2_handle_common_intr, IRQF_SHARED,
431f74875dcSStefan Wahren 				  dev_name(hsotg->dev), hsotg);
432f74875dcSStefan Wahren 	if (retval)
433f74875dcSStefan Wahren 		return retval;
434f74875dcSStefan Wahren 
43509a75e85SMarek Szyprowski 	retval = dwc2_lowlevel_hw_enable(hsotg);
43609a75e85SMarek Szyprowski 	if (retval)
43709a75e85SMarek Szyprowski 		return retval;
43809a75e85SMarek Szyprowski 
439d9707490SBruno Meirelles Herrera 	hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);
440d9707490SBruno Meirelles Herrera 
4415268ed9dSJohn Youn 	retval = dwc2_get_dr_mode(hsotg);
4425268ed9dSJohn Youn 	if (retval)
443a6ef3e02SJohn Youn 		goto error;
4445268ed9dSJohn Youn 
44503b32e4cSJohn Youn 	/*
44603b32e4cSJohn Youn 	 * Reset before dwc2_get_hwparams() then it could get power-on real
44703b32e4cSJohn Youn 	 * reset value form registers.
44803b32e4cSJohn Youn 	 */
44913b1f8e2SVardan Mikayelyan 	retval = dwc2_core_reset(hsotg, false);
45013b1f8e2SVardan Mikayelyan 	if (retval)
45113b1f8e2SVardan Mikayelyan 		goto error;
45203b32e4cSJohn Youn 
45303b32e4cSJohn Youn 	/* Detect config values from hardware */
45409a75e85SMarek Szyprowski 	retval = dwc2_get_hwparams(hsotg);
45509a75e85SMarek Szyprowski 	if (retval)
45609a75e85SMarek Szyprowski 		goto error;
45709a75e85SMarek Szyprowski 
45813b1f8e2SVardan Mikayelyan 	/*
45913b1f8e2SVardan Mikayelyan 	 * For OTG cores, set the force mode bits to reflect the value
46013b1f8e2SVardan Mikayelyan 	 * of dr_mode. Force mode bits should not be touched at any
46113b1f8e2SVardan Mikayelyan 	 * other time after this.
46213b1f8e2SVardan Mikayelyan 	 */
46325362d31SJohn Youn 	dwc2_force_dr_mode(hsotg);
464263b7fb5SJohn Youn 
465334bbd4eSJohn Youn 	retval = dwc2_init_params(hsotg);
466334bbd4eSJohn Youn 	if (retval)
467334bbd4eSJohn Youn 		goto error;
468334bbd4eSJohn Youn 
469e39af88fSMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_HOST) {
470f3768997SVardan Mikayelyan 		retval = dwc2_gadget_init(hsotg);
471117777b2SDinh Nguyen 		if (retval)
47209a75e85SMarek Szyprowski 			goto error;
473e39af88fSMarek Szyprowski 		hsotg->gadget_enabled = 1;
474e39af88fSMarek Szyprowski 	}
475e39af88fSMarek Szyprowski 
476e39af88fSMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
4774fe160d5SHeiner Kallweit 		retval = dwc2_hcd_init(hsotg);
478e39af88fSMarek Szyprowski 		if (retval) {
479e39af88fSMarek Szyprowski 			if (hsotg->gadget_enabled)
4801f91b4ccSFelipe Balbi 				dwc2_hsotg_remove(hsotg);
48109a75e85SMarek Szyprowski 			goto error;
482e39af88fSMarek Szyprowski 		}
483e39af88fSMarek Szyprowski 		hsotg->hcd_enabled = 1;
484e39af88fSMarek Szyprowski 	}
485197ba5f4SPaul Zimmerman 
486197ba5f4SPaul Zimmerman 	platform_set_drvdata(dev, hsotg);
48720fe4409SVardan Mikayelyan 	hsotg->hibernated = 0;
488197ba5f4SPaul Zimmerman 
489f91eea44SMian Yousaf Kaukab 	dwc2_debugfs_init(hsotg);
490f91eea44SMian Yousaf Kaukab 
49109a75e85SMarek Szyprowski 	/* Gadget code manages lowlevel hw on its own */
49209a75e85SMarek Szyprowski 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
49309a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
49409a75e85SMarek Szyprowski 
49509a75e85SMarek Szyprowski 	return 0;
49609a75e85SMarek Szyprowski 
49709a75e85SMarek Szyprowski error:
49809a75e85SMarek Szyprowski 	dwc2_lowlevel_hw_disable(hsotg);
499197ba5f4SPaul Zimmerman 	return retval;
500197ba5f4SPaul Zimmerman }
501197ba5f4SPaul Zimmerman 
502da9f3289SFabio Estevam static int __maybe_unused dwc2_suspend(struct device *dev)
503117777b2SDinh Nguyen {
504bcc06078SDinh Nguyen 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
505117777b2SDinh Nguyen 	int ret = 0;
506117777b2SDinh Nguyen 
50709a75e85SMarek Szyprowski 	if (dwc2_is_device_mode(dwc2))
50809a75e85SMarek Szyprowski 		dwc2_hsotg_suspend(dwc2);
509135b3c43SYunzhi Li 
51009a75e85SMarek Szyprowski 	if (dwc2->ll_hw_enabled)
51109a75e85SMarek Szyprowski 		ret = __dwc2_lowlevel_hw_disable(dwc2);
51209a75e85SMarek Szyprowski 
513117777b2SDinh Nguyen 	return ret;
514117777b2SDinh Nguyen }
515117777b2SDinh Nguyen 
516da9f3289SFabio Estevam static int __maybe_unused dwc2_resume(struct device *dev)
517117777b2SDinh Nguyen {
518bcc06078SDinh Nguyen 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
519117777b2SDinh Nguyen 	int ret = 0;
520117777b2SDinh Nguyen 
52109a75e85SMarek Szyprowski 	if (dwc2->ll_hw_enabled) {
52209a75e85SMarek Szyprowski 		ret = __dwc2_lowlevel_hw_enable(dwc2);
52309a75e85SMarek Szyprowski 		if (ret)
52409a75e85SMarek Szyprowski 			return ret;
525135b3c43SYunzhi Li 	}
52609a75e85SMarek Szyprowski 
52709a75e85SMarek Szyprowski 	if (dwc2_is_device_mode(dwc2))
52809a75e85SMarek Szyprowski 		ret = dwc2_hsotg_resume(dwc2);
52909a75e85SMarek Szyprowski 
530117777b2SDinh Nguyen 	return ret;
531117777b2SDinh Nguyen }
532117777b2SDinh Nguyen 
533bcc06078SDinh Nguyen static const struct dev_pm_ops dwc2_dev_pm_ops = {
534bcc06078SDinh Nguyen 	SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
535bcc06078SDinh Nguyen };
536bcc06078SDinh Nguyen 
537197ba5f4SPaul Zimmerman static struct platform_driver dwc2_platform_driver = {
538197ba5f4SPaul Zimmerman 	.driver = {
539197ba5f4SPaul Zimmerman 		.name = dwc2_driver_name,
540197ba5f4SPaul Zimmerman 		.of_match_table = dwc2_of_match_table,
541bcc06078SDinh Nguyen 		.pm = &dwc2_dev_pm_ops,
542197ba5f4SPaul Zimmerman 	},
543197ba5f4SPaul Zimmerman 	.probe = dwc2_driver_probe,
544197ba5f4SPaul Zimmerman 	.remove = dwc2_driver_remove,
545a40a0031SHeiko Stübner 	.shutdown = dwc2_driver_shutdown,
546197ba5f4SPaul Zimmerman };
547197ba5f4SPaul Zimmerman 
548197ba5f4SPaul Zimmerman module_platform_driver(dwc2_platform_driver);
549197ba5f4SPaul Zimmerman 
550197ba5f4SPaul Zimmerman MODULE_DESCRIPTION("DESIGNWARE HS OTG Platform Glue");
551197ba5f4SPaul Zimmerman MODULE_AUTHOR("Matthijs Kooijman <matthijs@stdin.nl>");
552197ba5f4SPaul Zimmerman MODULE_LICENSE("Dual BSD/GPL");
553