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