18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * platform.c - DesignWare HS OTG Controller platform driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) Matthijs Kooijman <matthijs@stdin.nl> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 98c2ecf20Sopenharmony_ci * are met: 108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 118c2ecf20Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 128c2ecf20Sopenharmony_ci * without modification. 138c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 148c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 158c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 168c2ecf20Sopenharmony_ci * 3. The names of the above-listed copyright holders may not be used 178c2ecf20Sopenharmony_ci * to endorse or promote products derived from this software without 188c2ecf20Sopenharmony_ci * specific prior written permission. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * ALTERNATIVELY, this software may be distributed under the terms of the 218c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") as published by the Free Software 228c2ecf20Sopenharmony_ci * Foundation; either version 2 of the License, or (at your option) any 238c2ecf20Sopenharmony_ci * later version. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 268c2ecf20Sopenharmony_ci * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 278c2ecf20Sopenharmony_ci * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 288c2ecf20Sopenharmony_ci * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 298c2ecf20Sopenharmony_ci * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 308c2ecf20Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 318c2ecf20Sopenharmony_ci * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 328c2ecf20Sopenharmony_ci * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 338c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 348c2ecf20Sopenharmony_ci * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 358c2ecf20Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <linux/kernel.h> 398c2ecf20Sopenharmony_ci#include <linux/module.h> 408c2ecf20Sopenharmony_ci#include <linux/slab.h> 418c2ecf20Sopenharmony_ci#include <linux/clk.h> 428c2ecf20Sopenharmony_ci#include <linux/device.h> 438c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 448c2ecf20Sopenharmony_ci#include <linux/of_device.h> 458c2ecf20Sopenharmony_ci#include <linux/mutex.h> 468c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 478c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 488c2ecf20Sopenharmony_ci#include <linux/platform_data/s3c-hsotg.h> 498c2ecf20Sopenharmony_ci#include <linux/reset.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <linux/usb/of.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include "core.h" 548c2ecf20Sopenharmony_ci#include "hcd.h" 558c2ecf20Sopenharmony_ci#include "debug.h" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const char dwc2_driver_name[] = "dwc2"; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * Check the dr_mode against the module configuration and hardware 618c2ecf20Sopenharmony_ci * capabilities. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * The hardware, module, and dr_mode, can each be set to host, device, 648c2ecf20Sopenharmony_ci * or otg. Check that all these values are compatible and adjust the 658c2ecf20Sopenharmony_ci * value of dr_mode if possible. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * actual 688c2ecf20Sopenharmony_ci * HW MOD dr_mode dr_mode 698c2ecf20Sopenharmony_ci * ------------------------------ 708c2ecf20Sopenharmony_ci * HST HST any : HST 718c2ecf20Sopenharmony_ci * HST DEV any : --- 728c2ecf20Sopenharmony_ci * HST OTG any : HST 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * DEV HST any : --- 758c2ecf20Sopenharmony_ci * DEV DEV any : DEV 768c2ecf20Sopenharmony_ci * DEV OTG any : DEV 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * OTG HST any : HST 798c2ecf20Sopenharmony_ci * OTG DEV any : DEV 808c2ecf20Sopenharmony_ci * OTG OTG any : dr_mode 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci enum usb_dr_mode mode; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci hsotg->dr_mode = usb_get_dr_mode(hsotg->dev); 878c2ecf20Sopenharmony_ci if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN) 888c2ecf20Sopenharmony_ci hsotg->dr_mode = USB_DR_MODE_OTG; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci mode = hsotg->dr_mode; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (dwc2_hw_is_device(hsotg)) { 938c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) { 948c2ecf20Sopenharmony_ci dev_err(hsotg->dev, 958c2ecf20Sopenharmony_ci "Controller does not support host mode.\n"); 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci mode = USB_DR_MODE_PERIPHERAL; 998c2ecf20Sopenharmony_ci } else if (dwc2_hw_is_host(hsotg)) { 1008c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) { 1018c2ecf20Sopenharmony_ci dev_err(hsotg->dev, 1028c2ecf20Sopenharmony_ci "Controller does not support device mode.\n"); 1038c2ecf20Sopenharmony_ci return -EINVAL; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci mode = USB_DR_MODE_HOST; 1068c2ecf20Sopenharmony_ci } else { 1078c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) 1088c2ecf20Sopenharmony_ci mode = USB_DR_MODE_HOST; 1098c2ecf20Sopenharmony_ci else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) 1108c2ecf20Sopenharmony_ci mode = USB_DR_MODE_PERIPHERAL; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (mode != hsotg->dr_mode) { 1148c2ecf20Sopenharmony_ci dev_warn(hsotg->dev, 1158c2ecf20Sopenharmony_ci "Configuration mismatch. dr_mode forced to %s\n", 1168c2ecf20Sopenharmony_ci mode == USB_DR_MODE_HOST ? "host" : "device"); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci hsotg->dr_mode = mode; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(hsotg->dev); 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), 1308c2ecf20Sopenharmony_ci hsotg->supplies); 1318c2ecf20Sopenharmony_ci if (ret) 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (hsotg->clk) { 1358c2ecf20Sopenharmony_ci ret = clk_prepare_enable(hsotg->clk); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (hsotg->uphy) { 1418c2ecf20Sopenharmony_ci ret = usb_phy_init(hsotg->uphy); 1428c2ecf20Sopenharmony_ci } else if (hsotg->plat && hsotg->plat->phy_init) { 1438c2ecf20Sopenharmony_ci ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci ret = phy_init(hsotg->phy); 1468c2ecf20Sopenharmony_ci if (ret == 0) 1478c2ecf20Sopenharmony_ci ret = phy_power_on(hsotg->phy); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources 1558c2ecf20Sopenharmony_ci * @hsotg: The driver state 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * A wrapper for platform code responsible for controlling 1588c2ecf20Sopenharmony_ci * low-level USB platform resources (phy, clock, regulators) 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ciint dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int ret = __dwc2_lowlevel_hw_enable(hsotg); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (ret == 0) 1658c2ecf20Sopenharmony_ci hsotg->ll_hw_enabled = true; 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(hsotg->dev); 1728c2ecf20Sopenharmony_ci int ret = 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (hsotg->uphy) { 1758c2ecf20Sopenharmony_ci usb_phy_shutdown(hsotg->uphy); 1768c2ecf20Sopenharmony_ci } else if (hsotg->plat && hsotg->plat->phy_exit) { 1778c2ecf20Sopenharmony_ci ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci ret = phy_power_off(hsotg->phy); 1808c2ecf20Sopenharmony_ci if (ret == 0) 1818c2ecf20Sopenharmony_ci ret = phy_exit(hsotg->phy); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (hsotg->clk) 1878c2ecf20Sopenharmony_ci clk_disable_unprepare(hsotg->clk); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/** 1938c2ecf20Sopenharmony_ci * dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources 1948c2ecf20Sopenharmony_ci * @hsotg: The driver state 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * A wrapper for platform code responsible for controlling 1978c2ecf20Sopenharmony_ci * low-level USB platform resources (phy, clock, regulators) 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ciint dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci int ret = __dwc2_lowlevel_hw_disable(hsotg); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (ret == 0) 2048c2ecf20Sopenharmony_ci hsotg->ll_hw_enabled = false; 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int i, ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci hsotg->reset = devm_reset_control_get_optional(hsotg->dev, "dwc2"); 2138c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->reset)) { 2148c2ecf20Sopenharmony_ci ret = PTR_ERR(hsotg->reset); 2158c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "error getting reset control %d\n", ret); 2168c2ecf20Sopenharmony_ci return ret; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci reset_control_deassert(hsotg->reset); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc"); 2228c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->reset_ecc)) { 2238c2ecf20Sopenharmony_ci ret = PTR_ERR(hsotg->reset_ecc); 2248c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret); 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci reset_control_deassert(hsotg->reset_ecc); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* 2318c2ecf20Sopenharmony_ci * Attempt to find a generic PHY, then look for an old style 2328c2ecf20Sopenharmony_ci * USB PHY and then fall back to pdata 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy"); 2358c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->phy)) { 2368c2ecf20Sopenharmony_ci ret = PTR_ERR(hsotg->phy); 2378c2ecf20Sopenharmony_ci switch (ret) { 2388c2ecf20Sopenharmony_ci case -ENODEV: 2398c2ecf20Sopenharmony_ci case -ENOSYS: 2408c2ecf20Sopenharmony_ci hsotg->phy = NULL; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case -EPROBE_DEFER: 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci default: 2458c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "error getting phy %d\n", ret); 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!hsotg->phy) { 2518c2ecf20Sopenharmony_ci hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2); 2528c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->uphy)) { 2538c2ecf20Sopenharmony_ci ret = PTR_ERR(hsotg->uphy); 2548c2ecf20Sopenharmony_ci switch (ret) { 2558c2ecf20Sopenharmony_ci case -ENODEV: 2568c2ecf20Sopenharmony_ci case -ENXIO: 2578c2ecf20Sopenharmony_ci hsotg->uphy = NULL; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case -EPROBE_DEFER: 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "error getting usb phy %d\n", 2638c2ecf20Sopenharmony_ci ret); 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci hsotg->plat = dev_get_platdata(hsotg->dev); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Clock */ 2728c2ecf20Sopenharmony_ci hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg"); 2738c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->clk)) { 2748c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "cannot get otg clock\n"); 2758c2ecf20Sopenharmony_ci return PTR_ERR(hsotg->clk); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Regulators */ 2798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++) 2808c2ecf20Sopenharmony_ci hsotg->supplies[i].supply = dwc2_hsotg_supply_names[i]; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies), 2838c2ecf20Sopenharmony_ci hsotg->supplies); 2848c2ecf20Sopenharmony_ci if (ret) { 2858c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 2868c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "failed to request supplies: %d\n", 2878c2ecf20Sopenharmony_ci ret); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/** 2948c2ecf20Sopenharmony_ci * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the 2958c2ecf20Sopenharmony_ci * DWC_otg driver 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * @dev: Platform device 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * This routine is called, for example, when the rmmod command is executed. The 3008c2ecf20Sopenharmony_ci * device may or may not be electrically present. If it is present, the driver 3018c2ecf20Sopenharmony_ci * stops device processing. Any resources used on behalf of this device are 3028c2ecf20Sopenharmony_ci * freed. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic int dwc2_driver_remove(struct platform_device *dev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dwc2_debugfs_exit(hsotg); 3098c2ecf20Sopenharmony_ci if (hsotg->hcd_enabled) 3108c2ecf20Sopenharmony_ci dwc2_hcd_remove(hsotg); 3118c2ecf20Sopenharmony_ci if (hsotg->gadget_enabled) 3128c2ecf20Sopenharmony_ci dwc2_hsotg_remove(hsotg); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci dwc2_drd_exit(hsotg); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (hsotg->params.activate_stm_id_vb_detection) 3178c2ecf20Sopenharmony_ci regulator_disable(hsotg->usb33d); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (hsotg->ll_hw_enabled) 3208c2ecf20Sopenharmony_ci dwc2_lowlevel_hw_disable(hsotg); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci reset_control_assert(hsotg->reset); 3238c2ecf20Sopenharmony_ci reset_control_assert(hsotg->reset_ecc); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/** 3298c2ecf20Sopenharmony_ci * dwc2_driver_shutdown() - Called on device shutdown 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * @dev: Platform device 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * In specific conditions (involving usb hubs) dwc2 devices can create a 3348c2ecf20Sopenharmony_ci * lot of interrupts, even to the point of overwhelming devices running 3358c2ecf20Sopenharmony_ci * at low frequencies. Some devices need to do special clock handling 3368c2ecf20Sopenharmony_ci * at shutdown-time which may bring the system clock below the threshold 3378c2ecf20Sopenharmony_ci * of being able to handle the dwc2 interrupts. Disabling dwc2-irqs 3388c2ecf20Sopenharmony_ci * prevents reboots/poweroffs from getting stuck in such cases. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_cistatic void dwc2_driver_shutdown(struct platform_device *dev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci dwc2_disable_global_interrupts(hsotg); 3458c2ecf20Sopenharmony_ci synchronize_irq(hsotg->irq); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/** 3498c2ecf20Sopenharmony_ci * dwc2_check_core_endianness() - Returns true if core and AHB have 3508c2ecf20Sopenharmony_ci * opposite endianness. 3518c2ecf20Sopenharmony_ci * @hsotg: Programming view of the DWC_otg controller. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci u32 snpsid; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci snpsid = ioread32(hsotg->regs + GSNPSID); 3588c2ecf20Sopenharmony_ci if ((snpsid & GSNPSID_ID_MASK) == DWC2_OTG_ID || 3598c2ecf20Sopenharmony_ci (snpsid & GSNPSID_ID_MASK) == DWC2_FS_IOT_ID || 3608c2ecf20Sopenharmony_ci (snpsid & GSNPSID_ID_MASK) == DWC2_HS_IOT_ID) 3618c2ecf20Sopenharmony_ci return false; 3628c2ecf20Sopenharmony_ci return true; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/** 3668c2ecf20Sopenharmony_ci * Check core version 3678c2ecf20Sopenharmony_ci * 3688c2ecf20Sopenharmony_ci * @hsotg: Programming view of the DWC_otg controller 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ciint dwc2_check_core_version(struct dwc2_hsotg *hsotg) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct dwc2_hw_params *hw = &hsotg->hw_params; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* 3768c2ecf20Sopenharmony_ci * Attempt to ensure this device is really a DWC_otg Controller. 3778c2ecf20Sopenharmony_ci * Read and verify the GSNPSID register contents. The value should be 3788c2ecf20Sopenharmony_ci * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci hw->snpsid = dwc2_readl(hsotg, GSNPSID); 3828c2ecf20Sopenharmony_ci if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && 3838c2ecf20Sopenharmony_ci (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && 3848c2ecf20Sopenharmony_ci (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { 3858c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n", 3868c2ecf20Sopenharmony_ci hw->snpsid); 3878c2ecf20Sopenharmony_ci return -ENODEV; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n", 3918c2ecf20Sopenharmony_ci hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, 3928c2ecf20Sopenharmony_ci hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/** 3978c2ecf20Sopenharmony_ci * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg 3988c2ecf20Sopenharmony_ci * driver 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * @dev: Platform device 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * This routine creates the driver components required to control the device 4038c2ecf20Sopenharmony_ci * (core, HCD, and PCD) and initializes the device. The driver components are 4048c2ecf20Sopenharmony_ci * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved 4058c2ecf20Sopenharmony_ci * in the device private data. This allows the driver to access the dwc2_hsotg 4068c2ecf20Sopenharmony_ci * structure on subsequent calls to driver methods for this device. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_cistatic int dwc2_driver_probe(struct platform_device *dev) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct dwc2_hsotg *hsotg; 4118c2ecf20Sopenharmony_ci struct resource *res; 4128c2ecf20Sopenharmony_ci int retval; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL); 4158c2ecf20Sopenharmony_ci if (!hsotg) 4168c2ecf20Sopenharmony_ci return -ENOMEM; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci hsotg->dev = &dev->dev; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Use reasonable defaults so platforms don't have to provide these. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci if (!dev->dev.dma_mask) 4248c2ecf20Sopenharmony_ci dev->dev.dma_mask = &dev->dev.coherent_dma_mask; 4258c2ecf20Sopenharmony_ci retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32)); 4268c2ecf20Sopenharmony_ci if (retval) { 4278c2ecf20Sopenharmony_ci dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval); 4288c2ecf20Sopenharmony_ci return retval; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci hsotg->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res); 4328c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->regs)) 4338c2ecf20Sopenharmony_ci return PTR_ERR(hsotg->regs); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", 4368c2ecf20Sopenharmony_ci (unsigned long)res->start, hsotg->regs); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci retval = dwc2_lowlevel_hw_init(hsotg); 4398c2ecf20Sopenharmony_ci if (retval) 4408c2ecf20Sopenharmony_ci return retval; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci spin_lock_init(&hsotg->lock); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci hsotg->irq = platform_get_irq(dev, 0); 4458c2ecf20Sopenharmony_ci if (hsotg->irq < 0) 4468c2ecf20Sopenharmony_ci return hsotg->irq; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci dev_dbg(hsotg->dev, "registering common handler for irq%d\n", 4498c2ecf20Sopenharmony_ci hsotg->irq); 4508c2ecf20Sopenharmony_ci retval = devm_request_irq(hsotg->dev, hsotg->irq, 4518c2ecf20Sopenharmony_ci dwc2_handle_common_intr, IRQF_SHARED, 4528c2ecf20Sopenharmony_ci dev_name(hsotg->dev), hsotg); 4538c2ecf20Sopenharmony_ci if (retval) 4548c2ecf20Sopenharmony_ci return retval; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); 4578c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->vbus_supply)) { 4588c2ecf20Sopenharmony_ci retval = PTR_ERR(hsotg->vbus_supply); 4598c2ecf20Sopenharmony_ci hsotg->vbus_supply = NULL; 4608c2ecf20Sopenharmony_ci if (retval != -ENODEV) 4618c2ecf20Sopenharmony_ci return retval; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci retval = dwc2_lowlevel_hw_enable(hsotg); 4658c2ecf20Sopenharmony_ci if (retval) 4668c2ecf20Sopenharmony_ci return retval; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci retval = dwc2_get_dr_mode(hsotg); 4718c2ecf20Sopenharmony_ci if (retval) 4728c2ecf20Sopenharmony_ci goto error; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci hsotg->need_phy_for_wake = 4758c2ecf20Sopenharmony_ci of_property_read_bool(dev->dev.of_node, 4768c2ecf20Sopenharmony_ci "snps,need-phy-for-wake"); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * Before performing any core related operations 4808c2ecf20Sopenharmony_ci * check core version. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci retval = dwc2_check_core_version(hsotg); 4838c2ecf20Sopenharmony_ci if (retval) 4848c2ecf20Sopenharmony_ci goto error; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci * Reset before dwc2_get_hwparams() then it could get power-on real 4888c2ecf20Sopenharmony_ci * reset value form registers. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci retval = dwc2_core_reset(hsotg, false); 4918c2ecf20Sopenharmony_ci if (retval) 4928c2ecf20Sopenharmony_ci goto error; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* Detect config values from hardware */ 4958c2ecf20Sopenharmony_ci retval = dwc2_get_hwparams(hsotg); 4968c2ecf20Sopenharmony_ci if (retval) 4978c2ecf20Sopenharmony_ci goto error; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* 5008c2ecf20Sopenharmony_ci * For OTG cores, set the force mode bits to reflect the value 5018c2ecf20Sopenharmony_ci * of dr_mode. Force mode bits should not be touched at any 5028c2ecf20Sopenharmony_ci * other time after this. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci dwc2_force_dr_mode(hsotg); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci retval = dwc2_init_params(hsotg); 5078c2ecf20Sopenharmony_ci if (retval) 5088c2ecf20Sopenharmony_ci goto error; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (hsotg->params.activate_stm_id_vb_detection) { 5118c2ecf20Sopenharmony_ci u32 ggpio; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d"); 5148c2ecf20Sopenharmony_ci if (IS_ERR(hsotg->usb33d)) { 5158c2ecf20Sopenharmony_ci retval = PTR_ERR(hsotg->usb33d); 5168c2ecf20Sopenharmony_ci if (retval != -EPROBE_DEFER) 5178c2ecf20Sopenharmony_ci dev_err(hsotg->dev, 5188c2ecf20Sopenharmony_ci "failed to request usb33d supply: %d\n", 5198c2ecf20Sopenharmony_ci retval); 5208c2ecf20Sopenharmony_ci goto error; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci retval = regulator_enable(hsotg->usb33d); 5238c2ecf20Sopenharmony_ci if (retval) { 5248c2ecf20Sopenharmony_ci dev_err(hsotg->dev, 5258c2ecf20Sopenharmony_ci "failed to enable usb33d supply: %d\n", retval); 5268c2ecf20Sopenharmony_ci goto error; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci ggpio = dwc2_readl(hsotg, GGPIO); 5308c2ecf20Sopenharmony_ci ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; 5318c2ecf20Sopenharmony_ci ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; 5328c2ecf20Sopenharmony_ci dwc2_writel(hsotg, ggpio, GGPIO); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* ID/VBUS detection startup time */ 5358c2ecf20Sopenharmony_ci usleep_range(5000, 7000); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci retval = dwc2_drd_init(hsotg); 5398c2ecf20Sopenharmony_ci if (retval) { 5408c2ecf20Sopenharmony_ci if (retval != -EPROBE_DEFER) 5418c2ecf20Sopenharmony_ci dev_err(hsotg->dev, "failed to initialize dual-role\n"); 5428c2ecf20Sopenharmony_ci goto error_init; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (hsotg->dr_mode != USB_DR_MODE_HOST) { 5468c2ecf20Sopenharmony_ci retval = dwc2_gadget_init(hsotg); 5478c2ecf20Sopenharmony_ci if (retval) 5488c2ecf20Sopenharmony_ci goto error_drd; 5498c2ecf20Sopenharmony_ci hsotg->gadget_enabled = 1; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * If we need PHY for wakeup we must be wakeup capable. 5548c2ecf20Sopenharmony_ci * When we have a device that can wake without the PHY we 5558c2ecf20Sopenharmony_ci * can adjust this condition. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci if (hsotg->need_phy_for_wake) 5588c2ecf20Sopenharmony_ci device_set_wakeup_capable(&dev->dev, true); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci hsotg->reset_phy_on_wake = 5618c2ecf20Sopenharmony_ci of_property_read_bool(dev->dev.of_node, 5628c2ecf20Sopenharmony_ci "snps,reset-phy-on-wake"); 5638c2ecf20Sopenharmony_ci if (hsotg->reset_phy_on_wake && !hsotg->phy) { 5648c2ecf20Sopenharmony_ci dev_warn(hsotg->dev, 5658c2ecf20Sopenharmony_ci "Quirk reset-phy-on-wake only supports generic PHYs\n"); 5668c2ecf20Sopenharmony_ci hsotg->reset_phy_on_wake = false; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { 5708c2ecf20Sopenharmony_ci retval = dwc2_hcd_init(hsotg); 5718c2ecf20Sopenharmony_ci if (retval) { 5728c2ecf20Sopenharmony_ci if (hsotg->gadget_enabled) 5738c2ecf20Sopenharmony_ci dwc2_hsotg_remove(hsotg); 5748c2ecf20Sopenharmony_ci goto error_drd; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci hsotg->hcd_enabled = 1; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci platform_set_drvdata(dev, hsotg); 5808c2ecf20Sopenharmony_ci hsotg->hibernated = 0; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci dwc2_debugfs_init(hsotg); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Gadget code manages lowlevel hw on its own */ 5858c2ecf20Sopenharmony_ci if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) 5868c2ecf20Sopenharmony_ci dwc2_lowlevel_hw_disable(hsotg); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ 5898c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) 5908c2ecf20Sopenharmony_ci /* Postponed adding a new gadget to the udc class driver list */ 5918c2ecf20Sopenharmony_ci if (hsotg->gadget_enabled) { 5928c2ecf20Sopenharmony_ci retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget); 5938c2ecf20Sopenharmony_ci if (retval) { 5948c2ecf20Sopenharmony_ci hsotg->gadget.udc = NULL; 5958c2ecf20Sopenharmony_ci dwc2_hsotg_remove(hsotg); 5968c2ecf20Sopenharmony_ci goto error_debugfs; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ 6038c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) 6048c2ecf20Sopenharmony_cierror_debugfs: 6058c2ecf20Sopenharmony_ci dwc2_debugfs_exit(hsotg); 6068c2ecf20Sopenharmony_ci if (hsotg->hcd_enabled) 6078c2ecf20Sopenharmony_ci dwc2_hcd_remove(hsotg); 6088c2ecf20Sopenharmony_ci#endif 6098c2ecf20Sopenharmony_cierror_drd: 6108c2ecf20Sopenharmony_ci dwc2_drd_exit(hsotg); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cierror_init: 6138c2ecf20Sopenharmony_ci if (hsotg->params.activate_stm_id_vb_detection) 6148c2ecf20Sopenharmony_ci regulator_disable(hsotg->usb33d); 6158c2ecf20Sopenharmony_cierror: 6168c2ecf20Sopenharmony_ci if (hsotg->ll_hw_enabled) 6178c2ecf20Sopenharmony_ci dwc2_lowlevel_hw_disable(hsotg); 6188c2ecf20Sopenharmony_ci return retval; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int __maybe_unused dwc2_suspend(struct device *dev) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); 6248c2ecf20Sopenharmony_ci bool is_device_mode = dwc2_is_device_mode(dwc2); 6258c2ecf20Sopenharmony_ci int ret = 0; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (is_device_mode) 6288c2ecf20Sopenharmony_ci dwc2_hsotg_suspend(dwc2); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci dwc2_drd_suspend(dwc2); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (dwc2->params.activate_stm_id_vb_detection) { 6338c2ecf20Sopenharmony_ci unsigned long flags; 6348c2ecf20Sopenharmony_ci u32 ggpio, gotgctl; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * Need to force the mode to the current mode to avoid Mode 6388c2ecf20Sopenharmony_ci * Mismatch Interrupt when ID detection will be disabled. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci dwc2_force_mode(dwc2, !is_device_mode); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc2->lock, flags); 6438c2ecf20Sopenharmony_ci gotgctl = dwc2_readl(dwc2, GOTGCTL); 6448c2ecf20Sopenharmony_ci /* bypass debounce filter, enable overrides */ 6458c2ecf20Sopenharmony_ci gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; 6468c2ecf20Sopenharmony_ci gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN; 6478c2ecf20Sopenharmony_ci /* Force A / B session if needed */ 6488c2ecf20Sopenharmony_ci if (gotgctl & GOTGCTL_ASESVLD) 6498c2ecf20Sopenharmony_ci gotgctl |= GOTGCTL_AVALOVAL; 6508c2ecf20Sopenharmony_ci if (gotgctl & GOTGCTL_BSESVLD) 6518c2ecf20Sopenharmony_ci gotgctl |= GOTGCTL_BVALOVAL; 6528c2ecf20Sopenharmony_ci dwc2_writel(dwc2, gotgctl, GOTGCTL); 6538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc2->lock, flags); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ggpio = dwc2_readl(dwc2, GGPIO); 6568c2ecf20Sopenharmony_ci ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; 6578c2ecf20Sopenharmony_ci ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; 6588c2ecf20Sopenharmony_ci dwc2_writel(dwc2, ggpio, GGPIO); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci regulator_disable(dwc2->usb33d); 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (dwc2->ll_hw_enabled && 6648c2ecf20Sopenharmony_ci (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { 6658c2ecf20Sopenharmony_ci ret = __dwc2_lowlevel_hw_disable(dwc2); 6668c2ecf20Sopenharmony_ci dwc2->phy_off_for_suspend = true; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return ret; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int __maybe_unused dwc2_resume(struct device *dev) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); 6758c2ecf20Sopenharmony_ci int ret = 0; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { 6788c2ecf20Sopenharmony_ci ret = __dwc2_lowlevel_hw_enable(dwc2); 6798c2ecf20Sopenharmony_ci if (ret) 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci dwc2->phy_off_for_suspend = false; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (dwc2->params.activate_stm_id_vb_detection) { 6858c2ecf20Sopenharmony_ci unsigned long flags; 6868c2ecf20Sopenharmony_ci u32 ggpio, gotgctl; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ret = regulator_enable(dwc2->usb33d); 6898c2ecf20Sopenharmony_ci if (ret) 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci ggpio = dwc2_readl(dwc2, GGPIO); 6938c2ecf20Sopenharmony_ci ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; 6948c2ecf20Sopenharmony_ci ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; 6958c2ecf20Sopenharmony_ci dwc2_writel(dwc2, ggpio, GGPIO); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* ID/VBUS detection startup time */ 6988c2ecf20Sopenharmony_ci usleep_range(5000, 7000); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc2->lock, flags); 7018c2ecf20Sopenharmony_ci gotgctl = dwc2_readl(dwc2, GOTGCTL); 7028c2ecf20Sopenharmony_ci gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS; 7038c2ecf20Sopenharmony_ci gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | 7048c2ecf20Sopenharmony_ci GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL); 7058c2ecf20Sopenharmony_ci dwc2_writel(dwc2, gotgctl, GOTGCTL); 7068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc2->lock, flags); 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ 7108c2ecf20Sopenharmony_ci dwc2_force_dr_mode(dwc2); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci dwc2_drd_resume(dwc2); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (dwc2_is_device_mode(dwc2)) 7158c2ecf20Sopenharmony_ci ret = dwc2_hsotg_resume(dwc2); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return ret; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dwc2_dev_pm_ops = { 7218c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume) 7228c2ecf20Sopenharmony_ci}; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic struct platform_driver dwc2_platform_driver = { 7258c2ecf20Sopenharmony_ci .driver = { 7268c2ecf20Sopenharmony_ci .name = dwc2_driver_name, 7278c2ecf20Sopenharmony_ci .of_match_table = dwc2_of_match_table, 7288c2ecf20Sopenharmony_ci .pm = &dwc2_dev_pm_ops, 7298c2ecf20Sopenharmony_ci }, 7308c2ecf20Sopenharmony_ci .probe = dwc2_driver_probe, 7318c2ecf20Sopenharmony_ci .remove = dwc2_driver_remove, 7328c2ecf20Sopenharmony_ci .shutdown = dwc2_driver_shutdown, 7338c2ecf20Sopenharmony_ci}; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cimodule_platform_driver(dwc2_platform_driver); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DESIGNWARE HS OTG Platform Glue"); 7388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthijs Kooijman <matthijs@stdin.nl>"); 7398c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 740