xref: /openbmc/linux/drivers/usb/dwc2/platform.c (revision 8ec32c38)
1197ba5f4SPaul Zimmerman /*
2197ba5f4SPaul Zimmerman  * platform.c - DesignWare HS OTG Controller platform driver
3197ba5f4SPaul Zimmerman  *
4197ba5f4SPaul Zimmerman  * Copyright (C) Matthijs Kooijman <matthijs@stdin.nl>
5197ba5f4SPaul Zimmerman  *
6197ba5f4SPaul Zimmerman  * Redistribution and use in source and binary forms, with or without
7197ba5f4SPaul Zimmerman  * modification, are permitted provided that the following conditions
8197ba5f4SPaul Zimmerman  * are met:
9197ba5f4SPaul Zimmerman  * 1. Redistributions of source code must retain the above copyright
10197ba5f4SPaul Zimmerman  *    notice, this list of conditions, and the following disclaimer,
11197ba5f4SPaul Zimmerman  *    without modification.
12197ba5f4SPaul Zimmerman  * 2. Redistributions in binary form must reproduce the above copyright
13197ba5f4SPaul Zimmerman  *    notice, this list of conditions and the following disclaimer in the
14197ba5f4SPaul Zimmerman  *    documentation and/or other materials provided with the distribution.
15197ba5f4SPaul Zimmerman  * 3. The names of the above-listed copyright holders may not be used
16197ba5f4SPaul Zimmerman  *    to endorse or promote products derived from this software without
17197ba5f4SPaul Zimmerman  *    specific prior written permission.
18197ba5f4SPaul Zimmerman  *
19197ba5f4SPaul Zimmerman  * ALTERNATIVELY, this software may be distributed under the terms of the
20197ba5f4SPaul Zimmerman  * GNU General Public License ("GPL") as published by the Free Software
21197ba5f4SPaul Zimmerman  * Foundation; either version 2 of the License, or (at your option) any
22197ba5f4SPaul Zimmerman  * later version.
23197ba5f4SPaul Zimmerman  *
24197ba5f4SPaul Zimmerman  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25197ba5f4SPaul Zimmerman  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26197ba5f4SPaul Zimmerman  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27197ba5f4SPaul Zimmerman  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28197ba5f4SPaul Zimmerman  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29197ba5f4SPaul Zimmerman  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30197ba5f4SPaul Zimmerman  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31197ba5f4SPaul Zimmerman  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32197ba5f4SPaul Zimmerman  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33197ba5f4SPaul Zimmerman  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34197ba5f4SPaul Zimmerman  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35197ba5f4SPaul Zimmerman  */
36197ba5f4SPaul Zimmerman 
37197ba5f4SPaul Zimmerman #include <linux/kernel.h>
38197ba5f4SPaul Zimmerman #include <linux/module.h>
39197ba5f4SPaul Zimmerman #include <linux/slab.h>
4009a75e85SMarek Szyprowski #include <linux/clk.h>
41197ba5f4SPaul Zimmerman #include <linux/device.h>
42197ba5f4SPaul Zimmerman #include <linux/dma-mapping.h>
43197ba5f4SPaul Zimmerman #include <linux/of_device.h>
447ad8096eSMarek Szyprowski #include <linux/mutex.h>
45197ba5f4SPaul Zimmerman #include <linux/platform_device.h>
4609a75e85SMarek Szyprowski #include <linux/phy/phy.h>
4709a75e85SMarek Szyprowski #include <linux/platform_data/s3c-hsotg.h>
4883f8da56SDinh Nguyen #include <linux/reset.h>
49197ba5f4SPaul Zimmerman 
50c0155b9dSKever Yang #include <linux/usb/of.h>
51c0155b9dSKever Yang 
52197ba5f4SPaul Zimmerman #include "core.h"
53197ba5f4SPaul Zimmerman #include "hcd.h"
54f91eea44SMian Yousaf Kaukab #include "debug.h"
55197ba5f4SPaul Zimmerman 
56197ba5f4SPaul Zimmerman static const char dwc2_driver_name[] = "dwc2";
57197ba5f4SPaul Zimmerman 
585268ed9dSJohn Youn /*
595268ed9dSJohn Youn  * Check the dr_mode against the module configuration and hardware
605268ed9dSJohn Youn  * capabilities.
615268ed9dSJohn Youn  *
625268ed9dSJohn Youn  * The hardware, module, and dr_mode, can each be set to host, device,
635268ed9dSJohn Youn  * or otg. Check that all these values are compatible and adjust the
645268ed9dSJohn Youn  * value of dr_mode if possible.
655268ed9dSJohn Youn  *
665268ed9dSJohn Youn  *                      actual
675268ed9dSJohn Youn  *    HW  MOD dr_mode   dr_mode
685268ed9dSJohn Youn  *  ------------------------------
695268ed9dSJohn Youn  *   HST  HST  any    :  HST
705268ed9dSJohn Youn  *   HST  DEV  any    :  ---
715268ed9dSJohn Youn  *   HST  OTG  any    :  HST
725268ed9dSJohn Youn  *
735268ed9dSJohn Youn  *   DEV  HST  any    :  ---
745268ed9dSJohn Youn  *   DEV  DEV  any    :  DEV
755268ed9dSJohn Youn  *   DEV  OTG  any    :  DEV
765268ed9dSJohn Youn  *
775268ed9dSJohn Youn  *   OTG  HST  any    :  HST
785268ed9dSJohn Youn  *   OTG  DEV  any    :  DEV
795268ed9dSJohn Youn  *   OTG  OTG  any    :  dr_mode
805268ed9dSJohn Youn  */
815268ed9dSJohn Youn static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
825268ed9dSJohn Youn {
835268ed9dSJohn Youn 	enum usb_dr_mode mode;
845268ed9dSJohn Youn 
855268ed9dSJohn Youn 	hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
865268ed9dSJohn Youn 	if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
875268ed9dSJohn Youn 		hsotg->dr_mode = USB_DR_MODE_OTG;
885268ed9dSJohn Youn 
895268ed9dSJohn Youn 	mode = hsotg->dr_mode;
905268ed9dSJohn Youn 
915268ed9dSJohn Youn 	if (dwc2_hw_is_device(hsotg)) {
925268ed9dSJohn Youn 		if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
935268ed9dSJohn Youn 			dev_err(hsotg->dev,
945268ed9dSJohn Youn 				"Controller does not support host mode.\n");
955268ed9dSJohn Youn 			return -EINVAL;
965268ed9dSJohn Youn 		}
975268ed9dSJohn Youn 		mode = USB_DR_MODE_PERIPHERAL;
985268ed9dSJohn Youn 	} else if (dwc2_hw_is_host(hsotg)) {
995268ed9dSJohn Youn 		if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
1005268ed9dSJohn Youn 			dev_err(hsotg->dev,
1015268ed9dSJohn Youn 				"Controller does not support device mode.\n");
1025268ed9dSJohn Youn 			return -EINVAL;
1035268ed9dSJohn Youn 		}
1045268ed9dSJohn Youn 		mode = USB_DR_MODE_HOST;
1055268ed9dSJohn Youn 	} else {
1065268ed9dSJohn Youn 		if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
1075268ed9dSJohn Youn 			mode = USB_DR_MODE_HOST;
1085268ed9dSJohn Youn 		else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
1095268ed9dSJohn Youn 			mode = USB_DR_MODE_PERIPHERAL;
1105268ed9dSJohn Youn 	}
1115268ed9dSJohn Youn 
1125268ed9dSJohn Youn 	if (mode != hsotg->dr_mode) {
1135268ed9dSJohn Youn 		dev_warn(hsotg->dev,
1145268ed9dSJohn Youn 			 "Configuration mismatch. dr_mode forced to %s\n",
1155268ed9dSJohn Youn 			mode == USB_DR_MODE_HOST ? "host" : "device");
1165268ed9dSJohn Youn 
1175268ed9dSJohn Youn 		hsotg->dr_mode = mode;
1185268ed9dSJohn Youn 	}
1195268ed9dSJohn Youn 
1205268ed9dSJohn Youn 	return 0;
1215268ed9dSJohn Youn }
1225268ed9dSJohn Youn 
12309a75e85SMarek Szyprowski static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
12409a75e85SMarek Szyprowski {
12509a75e85SMarek Szyprowski 	struct platform_device *pdev = to_platform_device(hsotg->dev);
12609a75e85SMarek Szyprowski 	int ret;
12709a75e85SMarek Szyprowski 
12809a75e85SMarek Szyprowski 	ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
12909a75e85SMarek Szyprowski 				    hsotg->supplies);
13009a75e85SMarek Szyprowski 	if (ret)
13109a75e85SMarek Szyprowski 		return ret;
13209a75e85SMarek Szyprowski 
1338aa90cf2SStefan Wahren 	if (hsotg->clk) {
13409a75e85SMarek Szyprowski 		ret = clk_prepare_enable(hsotg->clk);
13509a75e85SMarek Szyprowski 		if (ret)
13609a75e85SMarek Szyprowski 			return ret;
1378aa90cf2SStefan Wahren 	}
13809a75e85SMarek Szyprowski 
13934c0887fSJohn Youn 	if (hsotg->uphy) {
14009a75e85SMarek Szyprowski 		ret = usb_phy_init(hsotg->uphy);
14134c0887fSJohn Youn 	} else if (hsotg->plat && hsotg->plat->phy_init) {
14209a75e85SMarek Szyprowski 		ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
14334c0887fSJohn Youn 	} else {
14409a75e85SMarek Szyprowski 		ret = phy_power_on(hsotg->phy);
14509a75e85SMarek Szyprowski 		if (ret == 0)
14609a75e85SMarek Szyprowski 			ret = phy_init(hsotg->phy);
14709a75e85SMarek Szyprowski 	}
14809a75e85SMarek Szyprowski 
14909a75e85SMarek Szyprowski 	return ret;
15009a75e85SMarek Szyprowski }
15109a75e85SMarek Szyprowski 
15209a75e85SMarek Szyprowski /**
15309a75e85SMarek Szyprowski  * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
15409a75e85SMarek Szyprowski  * @hsotg: The driver state
15509a75e85SMarek Szyprowski  *
15609a75e85SMarek Szyprowski  * A wrapper for platform code responsible for controlling
15709a75e85SMarek Szyprowski  * low-level USB platform resources (phy, clock, regulators)
15809a75e85SMarek Szyprowski  */
15909a75e85SMarek Szyprowski int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
16009a75e85SMarek Szyprowski {
16109a75e85SMarek Szyprowski 	int ret = __dwc2_lowlevel_hw_enable(hsotg);
16209a75e85SMarek Szyprowski 
16309a75e85SMarek Szyprowski 	if (ret == 0)
16409a75e85SMarek Szyprowski 		hsotg->ll_hw_enabled = true;
16509a75e85SMarek Szyprowski 	return ret;
16609a75e85SMarek Szyprowski }
16709a75e85SMarek Szyprowski 
16809a75e85SMarek Szyprowski static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
16909a75e85SMarek Szyprowski {
17009a75e85SMarek Szyprowski 	struct platform_device *pdev = to_platform_device(hsotg->dev);
17109a75e85SMarek Szyprowski 	int ret = 0;
17209a75e85SMarek Szyprowski 
17334c0887fSJohn Youn 	if (hsotg->uphy) {
17409a75e85SMarek Szyprowski 		usb_phy_shutdown(hsotg->uphy);
17534c0887fSJohn Youn 	} else if (hsotg->plat && hsotg->plat->phy_exit) {
17609a75e85SMarek Szyprowski 		ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
17734c0887fSJohn Youn 	} else {
17809a75e85SMarek Szyprowski 		ret = phy_exit(hsotg->phy);
17909a75e85SMarek Szyprowski 		if (ret == 0)
18009a75e85SMarek Szyprowski 			ret = phy_power_off(hsotg->phy);
18109a75e85SMarek Szyprowski 	}
18209a75e85SMarek Szyprowski 	if (ret)
18309a75e85SMarek Szyprowski 		return ret;
18409a75e85SMarek Szyprowski 
1858aa90cf2SStefan Wahren 	if (hsotg->clk)
18609a75e85SMarek Szyprowski 		clk_disable_unprepare(hsotg->clk);
18709a75e85SMarek Szyprowski 
18809a75e85SMarek Szyprowski 	ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
18909a75e85SMarek Szyprowski 				     hsotg->supplies);
19009a75e85SMarek Szyprowski 
19109a75e85SMarek Szyprowski 	return ret;
19209a75e85SMarek Szyprowski }
19309a75e85SMarek Szyprowski 
19409a75e85SMarek Szyprowski /**
19509a75e85SMarek Szyprowski  * dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources
19609a75e85SMarek Szyprowski  * @hsotg: The driver state
19709a75e85SMarek Szyprowski  *
19809a75e85SMarek Szyprowski  * A wrapper for platform code responsible for controlling
19909a75e85SMarek Szyprowski  * low-level USB platform resources (phy, clock, regulators)
20009a75e85SMarek Szyprowski  */
20109a75e85SMarek Szyprowski int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
20209a75e85SMarek Szyprowski {
20309a75e85SMarek Szyprowski 	int ret = __dwc2_lowlevel_hw_disable(hsotg);
20409a75e85SMarek Szyprowski 
20509a75e85SMarek Szyprowski 	if (ret == 0)
20609a75e85SMarek Szyprowski 		hsotg->ll_hw_enabled = false;
20709a75e85SMarek Szyprowski 	return ret;
20809a75e85SMarek Szyprowski }
20909a75e85SMarek Szyprowski 
21009a75e85SMarek Szyprowski static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
21109a75e85SMarek Szyprowski {
21209a75e85SMarek Szyprowski 	int i, ret;
21309a75e85SMarek Szyprowski 
21483f8da56SDinh Nguyen 	hsotg->reset = devm_reset_control_get_optional(hsotg->dev, "dwc2");
21583f8da56SDinh Nguyen 	if (IS_ERR(hsotg->reset)) {
21683f8da56SDinh Nguyen 		ret = PTR_ERR(hsotg->reset);
2178ec32c38SPhilipp Zabel 		dev_err(hsotg->dev, "error getting reset control %d\n", ret);
21883f8da56SDinh Nguyen 		return ret;
21983f8da56SDinh Nguyen 	}
22083f8da56SDinh Nguyen 
22183f8da56SDinh Nguyen 	reset_control_deassert(hsotg->reset);
22283f8da56SDinh Nguyen 
22309a75e85SMarek Szyprowski 	/* Set default UTMI width */
22409a75e85SMarek Szyprowski 	hsotg->phyif = GUSBCFG_PHYIF16;
22509a75e85SMarek Szyprowski 
22609a75e85SMarek Szyprowski 	/*
22709a75e85SMarek Szyprowski 	 * Attempt to find a generic PHY, then look for an old style
22809a75e85SMarek Szyprowski 	 * USB PHY and then fall back to pdata
22909a75e85SMarek Szyprowski 	 */
23009a75e85SMarek Szyprowski 	hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy");
23109a75e85SMarek Szyprowski 	if (IS_ERR(hsotg->phy)) {
2326c2dad69SStefan Wahren 		ret = PTR_ERR(hsotg->phy);
2336c2dad69SStefan Wahren 		switch (ret) {
2346c2dad69SStefan Wahren 		case -ENODEV:
2356c2dad69SStefan Wahren 		case -ENOSYS:
23609a75e85SMarek Szyprowski 			hsotg->phy = NULL;
2376c2dad69SStefan Wahren 			break;
2386c2dad69SStefan Wahren 		case -EPROBE_DEFER:
2396c2dad69SStefan Wahren 			return ret;
2406c2dad69SStefan Wahren 		default:
2416c2dad69SStefan Wahren 			dev_err(hsotg->dev, "error getting phy %d\n", ret);
2426c2dad69SStefan Wahren 			return ret;
24309a75e85SMarek Szyprowski 		}
2446c2dad69SStefan Wahren 	}
2456c2dad69SStefan Wahren 
2466c2dad69SStefan Wahren 	if (!hsotg->phy) {
2476c2dad69SStefan Wahren 		hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
2486c2dad69SStefan Wahren 		if (IS_ERR(hsotg->uphy)) {
2496c2dad69SStefan Wahren 			ret = PTR_ERR(hsotg->uphy);
2506c2dad69SStefan Wahren 			switch (ret) {
2516c2dad69SStefan Wahren 			case -ENODEV:
2526c2dad69SStefan Wahren 			case -ENXIO:
2536c2dad69SStefan Wahren 				hsotg->uphy = NULL;
2546c2dad69SStefan Wahren 				break;
2556c2dad69SStefan Wahren 			case -EPROBE_DEFER:
2566c2dad69SStefan Wahren 				return ret;
2576c2dad69SStefan Wahren 			default:
2586c2dad69SStefan Wahren 				dev_err(hsotg->dev, "error getting usb phy %d\n",
2596c2dad69SStefan Wahren 					ret);
2606c2dad69SStefan Wahren 				return ret;
2616c2dad69SStefan Wahren 			}
2626c2dad69SStefan Wahren 		}
2636c2dad69SStefan Wahren 	}
2646c2dad69SStefan Wahren 
2656c2dad69SStefan Wahren 	hsotg->plat = dev_get_platdata(hsotg->dev);
26609a75e85SMarek Szyprowski 
26709a75e85SMarek Szyprowski 	if (hsotg->phy) {
26809a75e85SMarek Szyprowski 		/*
26909a75e85SMarek Szyprowski 		 * If using the generic PHY framework, check if the PHY bus
27009a75e85SMarek Szyprowski 		 * width is 8-bit and set the phyif appropriately.
27109a75e85SMarek Szyprowski 		 */
27209a75e85SMarek Szyprowski 		if (phy_get_bus_width(hsotg->phy) == 8)
27309a75e85SMarek Szyprowski 			hsotg->phyif = GUSBCFG_PHYIF8;
27409a75e85SMarek Szyprowski 	}
27509a75e85SMarek Szyprowski 
27609a75e85SMarek Szyprowski 	/* Clock */
27709a75e85SMarek Szyprowski 	hsotg->clk = devm_clk_get(hsotg->dev, "otg");
27809a75e85SMarek Szyprowski 	if (IS_ERR(hsotg->clk)) {
27909a75e85SMarek Szyprowski 		hsotg->clk = NULL;
28009a75e85SMarek Szyprowski 		dev_dbg(hsotg->dev, "cannot get otg clock\n");
28109a75e85SMarek Szyprowski 	}
28209a75e85SMarek Szyprowski 
28309a75e85SMarek Szyprowski 	/* Regulators */
28409a75e85SMarek Szyprowski 	for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++)
28509a75e85SMarek Szyprowski 		hsotg->supplies[i].supply = dwc2_hsotg_supply_names[i];
28609a75e85SMarek Szyprowski 
28709a75e85SMarek Szyprowski 	ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
28809a75e85SMarek Szyprowski 				      hsotg->supplies);
28909a75e85SMarek Szyprowski 	if (ret) {
29009a75e85SMarek Szyprowski 		dev_err(hsotg->dev, "failed to request supplies: %d\n", 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 
31709a75e85SMarek Szyprowski 	if (hsotg->ll_hw_enabled)
31809a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
31909a75e85SMarek Szyprowski 
32083f8da56SDinh Nguyen 	reset_control_assert(hsotg->reset);
32183f8da56SDinh Nguyen 
322197ba5f4SPaul Zimmerman 	return 0;
323197ba5f4SPaul Zimmerman }
324197ba5f4SPaul Zimmerman 
325a40a0031SHeiko Stübner /**
326a40a0031SHeiko Stübner  * dwc2_driver_shutdown() - Called on device shutdown
327a40a0031SHeiko Stübner  *
328a40a0031SHeiko Stübner  * @dev: Platform device
329a40a0031SHeiko Stübner  *
330a40a0031SHeiko Stübner  * In specific conditions (involving usb hubs) dwc2 devices can create a
331a40a0031SHeiko Stübner  * lot of interrupts, even to the point of overwhelming devices running
332a40a0031SHeiko Stübner  * at low frequencies. Some devices need to do special clock handling
333a40a0031SHeiko Stübner  * at shutdown-time which may bring the system clock below the threshold
334a40a0031SHeiko Stübner  * of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
335a40a0031SHeiko Stübner  * prevents reboots/poweroffs from getting stuck in such cases.
336a40a0031SHeiko Stübner  */
337a40a0031SHeiko Stübner static void dwc2_driver_shutdown(struct platform_device *dev)
338a40a0031SHeiko Stübner {
339a40a0031SHeiko Stübner 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
340a40a0031SHeiko Stübner 
341a40a0031SHeiko Stübner 	disable_irq(hsotg->irq);
342a40a0031SHeiko Stübner }
343a40a0031SHeiko Stübner 
344197ba5f4SPaul Zimmerman /**
345197ba5f4SPaul Zimmerman  * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
346197ba5f4SPaul Zimmerman  * driver
347197ba5f4SPaul Zimmerman  *
348197ba5f4SPaul Zimmerman  * @dev: Platform device
349197ba5f4SPaul Zimmerman  *
350197ba5f4SPaul Zimmerman  * This routine creates the driver components required to control the device
351197ba5f4SPaul Zimmerman  * (core, HCD, and PCD) and initializes the device. The driver components are
352197ba5f4SPaul Zimmerman  * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
353197ba5f4SPaul Zimmerman  * in the device private data. This allows the driver to access the dwc2_hsotg
354197ba5f4SPaul Zimmerman  * structure on subsequent calls to driver methods for this device.
355197ba5f4SPaul Zimmerman  */
356197ba5f4SPaul Zimmerman static int dwc2_driver_probe(struct platform_device *dev)
357197ba5f4SPaul Zimmerman {
358197ba5f4SPaul Zimmerman 	struct dwc2_hsotg *hsotg;
359197ba5f4SPaul Zimmerman 	struct resource *res;
360197ba5f4SPaul Zimmerman 	int retval;
361197ba5f4SPaul Zimmerman 
362197ba5f4SPaul Zimmerman 	hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
363197ba5f4SPaul Zimmerman 	if (!hsotg)
364197ba5f4SPaul Zimmerman 		return -ENOMEM;
365197ba5f4SPaul Zimmerman 
366197ba5f4SPaul Zimmerman 	hsotg->dev = &dev->dev;
367197ba5f4SPaul Zimmerman 
368197ba5f4SPaul Zimmerman 	/*
369197ba5f4SPaul Zimmerman 	 * Use reasonable defaults so platforms don't have to provide these.
370197ba5f4SPaul Zimmerman 	 */
371197ba5f4SPaul Zimmerman 	if (!dev->dev.dma_mask)
372197ba5f4SPaul Zimmerman 		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
373197ba5f4SPaul Zimmerman 	retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
374197ba5f4SPaul Zimmerman 	if (retval)
375197ba5f4SPaul Zimmerman 		return retval;
376197ba5f4SPaul Zimmerman 
377197ba5f4SPaul Zimmerman 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
378197ba5f4SPaul Zimmerman 	hsotg->regs = devm_ioremap_resource(&dev->dev, res);
379197ba5f4SPaul Zimmerman 	if (IS_ERR(hsotg->regs))
380197ba5f4SPaul Zimmerman 		return PTR_ERR(hsotg->regs);
381197ba5f4SPaul Zimmerman 
382197ba5f4SPaul Zimmerman 	dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
383197ba5f4SPaul Zimmerman 		(unsigned long)res->start, hsotg->regs);
384197ba5f4SPaul Zimmerman 
38509a75e85SMarek Szyprowski 	retval = dwc2_lowlevel_hw_init(hsotg);
386ecb176c6SMian Yousaf Kaukab 	if (retval)
387ecb176c6SMian Yousaf Kaukab 		return retval;
388ecb176c6SMian Yousaf Kaukab 
38909a75e85SMarek Szyprowski 	spin_lock_init(&hsotg->lock);
39009a75e85SMarek Szyprowski 
391a40a0031SHeiko Stübner 	hsotg->irq = platform_get_irq(dev, 0);
392a40a0031SHeiko Stübner 	if (hsotg->irq < 0) {
393f74875dcSStefan Wahren 		dev_err(&dev->dev, "missing IRQ resource\n");
394a40a0031SHeiko Stübner 		return hsotg->irq;
395f74875dcSStefan Wahren 	}
396f74875dcSStefan Wahren 
397f74875dcSStefan Wahren 	dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
398a40a0031SHeiko Stübner 		hsotg->irq);
399a40a0031SHeiko Stübner 	retval = devm_request_irq(hsotg->dev, hsotg->irq,
400f74875dcSStefan Wahren 				  dwc2_handle_common_intr, IRQF_SHARED,
401f74875dcSStefan Wahren 				  dev_name(hsotg->dev), hsotg);
402f74875dcSStefan Wahren 	if (retval)
403f74875dcSStefan Wahren 		return retval;
404f74875dcSStefan Wahren 
40509a75e85SMarek Szyprowski 	retval = dwc2_lowlevel_hw_enable(hsotg);
40609a75e85SMarek Szyprowski 	if (retval)
40709a75e85SMarek Szyprowski 		return retval;
40809a75e85SMarek Szyprowski 
4095268ed9dSJohn Youn 	retval = dwc2_get_dr_mode(hsotg);
4105268ed9dSJohn Youn 	if (retval)
411a6ef3e02SJohn Youn 		goto error;
4125268ed9dSJohn Youn 
41303b32e4cSJohn Youn 	/*
41403b32e4cSJohn Youn 	 * Reset before dwc2_get_hwparams() then it could get power-on real
41503b32e4cSJohn Youn 	 * reset value form registers.
41603b32e4cSJohn Youn 	 */
41703b32e4cSJohn Youn 	dwc2_core_reset_and_force_dr_mode(hsotg);
41803b32e4cSJohn Youn 
41903b32e4cSJohn Youn 	/* Detect config values from hardware */
42009a75e85SMarek Szyprowski 	retval = dwc2_get_hwparams(hsotg);
42109a75e85SMarek Szyprowski 	if (retval)
42209a75e85SMarek Szyprowski 		goto error;
42309a75e85SMarek Szyprowski 
42425362d31SJohn Youn 	dwc2_force_dr_mode(hsotg);
425263b7fb5SJohn Youn 
426334bbd4eSJohn Youn 	retval = dwc2_init_params(hsotg);
427334bbd4eSJohn Youn 	if (retval)
428334bbd4eSJohn Youn 		goto error;
429334bbd4eSJohn Youn 
430e39af88fSMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_HOST) {
431a40a0031SHeiko Stübner 		retval = dwc2_gadget_init(hsotg, hsotg->irq);
432117777b2SDinh Nguyen 		if (retval)
43309a75e85SMarek Szyprowski 			goto error;
434e39af88fSMarek Szyprowski 		hsotg->gadget_enabled = 1;
435e39af88fSMarek Szyprowski 	}
436e39af88fSMarek Szyprowski 
437e39af88fSMarek Szyprowski 	if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
4384fe160d5SHeiner Kallweit 		retval = dwc2_hcd_init(hsotg);
439e39af88fSMarek Szyprowski 		if (retval) {
440e39af88fSMarek Szyprowski 			if (hsotg->gadget_enabled)
4411f91b4ccSFelipe Balbi 				dwc2_hsotg_remove(hsotg);
44209a75e85SMarek Szyprowski 			goto error;
443e39af88fSMarek Szyprowski 		}
444e39af88fSMarek Szyprowski 		hsotg->hcd_enabled = 1;
445e39af88fSMarek Szyprowski 	}
446197ba5f4SPaul Zimmerman 
447197ba5f4SPaul Zimmerman 	platform_set_drvdata(dev, hsotg);
448197ba5f4SPaul Zimmerman 
449f91eea44SMian Yousaf Kaukab 	dwc2_debugfs_init(hsotg);
450f91eea44SMian Yousaf Kaukab 
45109a75e85SMarek Szyprowski 	/* Gadget code manages lowlevel hw on its own */
45209a75e85SMarek Szyprowski 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
45309a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
45409a75e85SMarek Szyprowski 
45509a75e85SMarek Szyprowski 	return 0;
45609a75e85SMarek Szyprowski 
45709a75e85SMarek Szyprowski error:
45809a75e85SMarek Szyprowski 	dwc2_lowlevel_hw_disable(hsotg);
459197ba5f4SPaul Zimmerman 	return retval;
460197ba5f4SPaul Zimmerman }
461197ba5f4SPaul Zimmerman 
462da9f3289SFabio Estevam static int __maybe_unused dwc2_suspend(struct device *dev)
463117777b2SDinh Nguyen {
464bcc06078SDinh Nguyen 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
465117777b2SDinh Nguyen 	int ret = 0;
466117777b2SDinh Nguyen 
46709a75e85SMarek Szyprowski 	if (dwc2_is_device_mode(dwc2))
46809a75e85SMarek Szyprowski 		dwc2_hsotg_suspend(dwc2);
469135b3c43SYunzhi Li 
47009a75e85SMarek Szyprowski 	if (dwc2->ll_hw_enabled)
47109a75e85SMarek Szyprowski 		ret = __dwc2_lowlevel_hw_disable(dwc2);
47209a75e85SMarek Szyprowski 
473117777b2SDinh Nguyen 	return ret;
474117777b2SDinh Nguyen }
475117777b2SDinh Nguyen 
476da9f3289SFabio Estevam static int __maybe_unused dwc2_resume(struct device *dev)
477117777b2SDinh Nguyen {
478bcc06078SDinh Nguyen 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
479117777b2SDinh Nguyen 	int ret = 0;
480117777b2SDinh Nguyen 
48109a75e85SMarek Szyprowski 	if (dwc2->ll_hw_enabled) {
48209a75e85SMarek Szyprowski 		ret = __dwc2_lowlevel_hw_enable(dwc2);
48309a75e85SMarek Szyprowski 		if (ret)
48409a75e85SMarek Szyprowski 			return ret;
485135b3c43SYunzhi Li 	}
48609a75e85SMarek Szyprowski 
48709a75e85SMarek Szyprowski 	if (dwc2_is_device_mode(dwc2))
48809a75e85SMarek Szyprowski 		ret = dwc2_hsotg_resume(dwc2);
48909a75e85SMarek Szyprowski 
490117777b2SDinh Nguyen 	return ret;
491117777b2SDinh Nguyen }
492117777b2SDinh Nguyen 
493bcc06078SDinh Nguyen static const struct dev_pm_ops dwc2_dev_pm_ops = {
494bcc06078SDinh Nguyen 	SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
495bcc06078SDinh Nguyen };
496bcc06078SDinh Nguyen 
497197ba5f4SPaul Zimmerman static struct platform_driver dwc2_platform_driver = {
498197ba5f4SPaul Zimmerman 	.driver = {
499197ba5f4SPaul Zimmerman 		.name = dwc2_driver_name,
500197ba5f4SPaul Zimmerman 		.of_match_table = dwc2_of_match_table,
501bcc06078SDinh Nguyen 		.pm = &dwc2_dev_pm_ops,
502197ba5f4SPaul Zimmerman 	},
503197ba5f4SPaul Zimmerman 	.probe = dwc2_driver_probe,
504197ba5f4SPaul Zimmerman 	.remove = dwc2_driver_remove,
505a40a0031SHeiko Stübner 	.shutdown = dwc2_driver_shutdown,
506197ba5f4SPaul Zimmerman };
507197ba5f4SPaul Zimmerman 
508197ba5f4SPaul Zimmerman module_platform_driver(dwc2_platform_driver);
509197ba5f4SPaul Zimmerman 
510197ba5f4SPaul Zimmerman MODULE_DESCRIPTION("DESIGNWARE HS OTG Platform Glue");
511197ba5f4SPaul Zimmerman MODULE_AUTHOR("Matthijs Kooijman <matthijs@stdin.nl>");
512197ba5f4SPaul Zimmerman MODULE_LICENSE("Dual BSD/GPL");
513