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