Received: by 2002:ab2:6203:0:b0:1f5:f2ab:c469 with SMTP id o3csp1054594lqt; Fri, 19 Apr 2024 21:51:26 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCVqYgrhd9KbQHCFAWLIWKAAWyL4yVxbFeBnJQbsyMjq0HaLKmwKGGQ9FRgpBLoZLX7Yogh5DJ7qR0M+s1LoVlAZ2lWCW7nVZtK+9Wp02w== X-Google-Smtp-Source: AGHT+IFjWRo5F/iQNxTU6cs6jZ7Lr8jqWrvCUzQIiO+J5zKODEHXTuMHkWa9L87wPA6amIgGsb7Y X-Received: by 2002:a05:6a00:2e85:b0:6ed:4f2e:ef22 with SMTP id fd5-20020a056a002e8500b006ed4f2eef22mr4997441pfb.31.1713588686117; Fri, 19 Apr 2024 21:51:26 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1713588686; cv=pass; d=google.com; s=arc-20160816; b=I5tWEGbbmpXzhU0Y5faxdcPW4gPeqI1qtj++UGFtoIqloHXjbi9TMBewiqkEwT3ogC uK5SMvVNf+X76f0hIv1oFodWZME0v4+ZsxYXQV1avmGEoLBYrM64hUz0f9/3iq623odQ ZAg3duvVO7dC6UtQOyeq6Y1pdarHNIJVmZ2WgD/LAFlR55p+HslK4x/RXVE97mq3AZGo +z+ac8eXfhhkp2tYswqeF95fzKmEaQI8CCHZgyqJSnYQf7tB8/Idpwz5Dze+OnaabwSB LyX+kbE3pmLAjbFXhf2FBqHuyazzIfporNcZV/iBPCD6bgg8WaAhymdgxLKQOCbtXuKq Puvw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=5aGOhaLtzes1pMIxtcpxcLefMZcaCMeIwMiIr8RyHmE=; fh=wJPXdoqtQ0YOXy74oWJK2I5h89QLW8nuE3Wv4zOOrJE=; b=Mg0nrNOVTYpmBfNs1DSnniyQ8WUU9bx15jQe+ulxCTuImZs0PDYiJcj1L0qKhu1+aA wB/A1mrTml5kMBMwEWwRMPbaZ6+OLrmIs1s1ihXeqZBjC/PcDU6lCGlR9A2O/krOrifD ravyrzhR2sE+8EQMXR6vBM0NkVKiJPvUHa/kpWFbM+wuRR/dP5L/1PF6eIRNeTFee/oI /AkHvsqJtGOPunOpJS5x+nH7NRf4dgR6j7C9ephHTK9ejNTOHmf8H9dqwkYqXCsvY9Jj XVk6YPPnssPEqw5KFkU7LKHlsNXyC/QlwxCvBF9jlzi4+H08geofrJzrBU55BAWCVUDA RTDw==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=Ydwfp2J+; arc=pass (i=1 spf=pass spfdomain=quicinc.com dkim=pass dkdomain=quicinc.com dmarc=pass fromdomain=quicinc.com); spf=pass (google.com: domain of linux-kernel+bounces-152134-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-152134-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=quicinc.com Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id gq6-20020a056a003bc600b006eb00065c4esi4253164pfb.298.2024.04.19.21.51.25 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Apr 2024 21:51:26 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-152134-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=Ydwfp2J+; arc=pass (i=1 spf=pass spfdomain=quicinc.com dkim=pass dkdomain=quicinc.com dmarc=pass fromdomain=quicinc.com); spf=pass (google.com: domain of linux-kernel+bounces-152134-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-152134-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=quicinc.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id 8ED97B20A1C for ; Sat, 20 Apr 2024 04:51:24 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0F3B32940C; Sat, 20 Apr 2024 04:49:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="Ydwfp2J+" Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 630691759E; Sat, 20 Apr 2024 04:49:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713588597; cv=none; b=g2EMcnGnNJLhZWApRQo2hjx94868QMW85ZPrbLapsqxv65zZAwoSCVLOnPD1Jx40Yhd/qEFTU0EEHq3oAVP0kVUMUfVmLB2ED9xC9Ig/lBiH8pR/QC63D1yUFegrZvmmph7yTuLD0FD1CSlewnIlLw4uReUWOam9Jb/itzsQlzo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713588597; c=relaxed/simple; bh=B+Zd/8UaBwPbF9RjN/Rh/J6HZw0xFM4+wK/RwNnLEtY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HiH79OG6WON4RFkiwu5sLGMfBJPMgbqU86NpSMDeSJ4B3ZvQK7pi92pirigdl2AOhb8AZ+lDr1iKD9DVLl1nXW7Qp3zUyPXyXCRNvbgE1E/RuCEWtSAtm/gzaTXZAz88ZJh3jGWRil9/vmScD0sOlfIgaTIqPxnL2xCwAoxG41c= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=Ydwfp2J+; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Received: from pps.filterd (m0279867.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 43K4d5kM015750; Sat, 20 Apr 2024 04:49:45 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:content-type; s= qcppdkim1; bh=5aGOhaLtzes1pMIxtcpxcLefMZcaCMeIwMiIr8RyHmE=; b=Yd wfp2J+SUcj6bChMEyI02LiVsl44PugzS+DX4lIw+khxnV8ToOuoIM80ra8WHG6zt CgNcvH6bwOFBKFz8OXqIHv1t7nBfXPRsCq+4uRXCvb1qC9eqQdw1G+drzZ4JloDa PBiIeIWTVmfoYRXt++ikmKx8ofbaTLTHMPNmuGQpyBsRAM8JkwMERm73adcRVE3B 2/Q1VHxvmp9j01N+QP4yJb/CCiIJAM0md8JPkJJbNpgW6XOkZ/gOzfMJD7KrotP9 nPjPlqMS4ZE0M35jM7vKFxNXMOpefFOoLWwi7sOVyJ3g7C717wl3W+754PjU1bxs iHUJg0P4mmogKJiY4ZUg== Received: from nalasppmta02.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3xm49ng6y1-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sat, 20 Apr 2024 04:49:44 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA02.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 43K4niul001537 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sat, 20 Apr 2024 04:49:44 GMT Received: from hu-kriskura-hyd.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Fri, 19 Apr 2024 21:49:38 -0700 From: Krishna Kurapati To: Krzysztof Kozlowski , Rob Herring , Bjorn Andersson , Wesley Cheng , Konrad Dybcio , "Greg Kroah-Hartman" , Conor Dooley , Thinh Nguyen , Felipe Balbi , Johan Hovold CC: , , , , , , Krishna Kurapati , Bjorn Andersson Subject: [PATCH v21 4/9] usb: dwc3: core: Refactor PHY logic to support Multiport Controller Date: Sat, 20 Apr 2024 10:18:56 +0530 Message-ID: <20240420044901.884098-5-quic_kriskura@quicinc.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240420044901.884098-1-quic_kriskura@quicinc.com> References: <20240420044901.884098-1-quic_kriskura@quicinc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: nZfNdEHHWS-1fudANFbiel83CfVYt7sq X-Proofpoint-GUID: nZfNdEHHWS-1fudANFbiel83CfVYt7sq X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2024-04-20_03,2024-04-19_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 lowpriorityscore=0 spamscore=0 priorityscore=1501 adultscore=0 mlxlogscore=999 malwarescore=0 impostorscore=0 mlxscore=0 clxscore=1015 suspectscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2404010003 definitions=main-2404200033 Currently the DWC3 driver supports only single port controller which requires at least one HS PHY and at most one SS PHY. But the DWC3 USB controller can be connected to multiple ports and each port can have their own PHYs. Each port of the multiport controller can either be HS+SS capable or HS only capable Proper quantification of them is required to modify GUSB2PHYCFG and GUSB3PIPECTL registers appropriately. DWC3 multiport controllers are capable to service at most 15 High Speed PHYs and 4 Supser Speed PHYs. Add support for detecting, obtaining and configuring PHYs supported by a multiport controller. Signed-off-by: Krishna Kurapati Reviewed-by: Bjorn Andersson --- drivers/usb/dwc3/core.c | 255 ++++++++++++++++++++++++++++------------ drivers/usb/dwc3/core.h | 15 ++- drivers/usb/dwc3/drd.c | 15 ++- 3 files changed, 201 insertions(+), 84 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 733b1e24af54..4dc6fc79c6d9 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -124,6 +124,7 @@ static void __dwc3_set_mode(struct work_struct *work) int ret; u32 reg; u32 desired_dr_role; + int i; mutex_lock(&dwc->mutex); spin_lock_irqsave(&dwc->lock, flags); @@ -201,8 +202,12 @@ static void __dwc3_set_mode(struct work_struct *work) } else { if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); + if (dwc->dis_split_quirk) { reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); reg |= DWC3_GUCTL3_SPLITDISABLE; @@ -217,8 +222,8 @@ static void __dwc3_set_mode(struct work_struct *work) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) @@ -589,22 +594,14 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc) return ret; } -/** - * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core - * @dwc: Pointer to our controller context structure - * - * Returns 0 on success. The USB PHY interfaces are configured but not - * initialized. The PHY interfaces and the PHYs get initialized together with - * the core in dwc3_core_init. - */ -static int dwc3_phy_setup(struct dwc3 *dwc) +static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index) { unsigned int hw_mode; u32 reg; hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index)); /* * Make sure UX_EXIT_PX is cleared as that causes issues with some @@ -659,9 +656,19 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_del_phy_power_chg_quirk) reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg); + + return 0; +} + +static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) +{ + unsigned int hw_mode; + u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); /* Select the HS PHY interface */ switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { @@ -673,7 +680,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc) } else if (dwc->hsphy_interface && !strncmp(dwc->hsphy_interface, "ulpi", 4)) { reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); } else { /* Relying on default value. */ if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) @@ -740,7 +747,35 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->ulpi_ext_vbus_drv) reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + + return 0; +} + +/** + * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. + */ +static int dwc3_phy_setup(struct dwc3 *dwc) +{ + int i; + int ret; + + for (i = 0; i < dwc->num_usb3_ports; i++) { + ret = dwc3_ss_phy_setup(dwc, i); + if (ret) + return ret; + } + + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = dwc3_hs_phy_setup(dwc, i); + if (ret) + return ret; + } return 0; } @@ -748,23 +783,34 @@ static int dwc3_phy_setup(struct dwc3 *dwc) static int dwc3_phy_init(struct dwc3 *dwc) { int ret; + int i; + int j; usb_phy_init(dwc->usb2_phy); usb_phy_init(dwc->usb3_phy); - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) - goto err_shutdown_usb3_phy; + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = phy_init(dwc->usb2_generic_phy[i]); + if (ret < 0) + goto err_exit_usb2_phy; + } - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) - goto err_exit_usb2_phy; + for (j = 0; j < dwc->num_usb3_ports; j++) { + ret = phy_init(dwc->usb3_generic_phy[j]); + if (ret < 0) + goto err_exit_usb3_phy; + } return 0; +err_exit_usb3_phy: + while (--j >= 0) + phy_exit(dwc->usb3_generic_phy[j]); + err_exit_usb2_phy: - phy_exit(dwc->usb2_generic_phy); -err_shutdown_usb3_phy: + while (--i >= 0) + phy_exit(dwc->usb2_generic_phy[i]); + usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); @@ -773,8 +819,13 @@ static int dwc3_phy_init(struct dwc3 *dwc) static void dwc3_phy_exit(struct dwc3 *dwc) { - phy_exit(dwc->usb3_generic_phy); - phy_exit(dwc->usb2_generic_phy); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_exit(dwc->usb3_generic_phy[i]); + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_exit(dwc->usb2_generic_phy[i]); usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); @@ -783,23 +834,34 @@ static void dwc3_phy_exit(struct dwc3 *dwc) static int dwc3_phy_power_on(struct dwc3 *dwc) { int ret; + int i; + int j; usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) - goto err_suspend_usb3_phy; + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = phy_power_on(dwc->usb2_generic_phy[i]); + if (ret < 0) + goto err_power_off_usb2_phy; + } - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err_power_off_usb2_phy; + for (j = 0; j < dwc->num_usb3_ports; j++) { + ret = phy_power_on(dwc->usb3_generic_phy[j]); + if (ret < 0) + goto err_power_off_usb3_phy; + } return 0; +err_power_off_usb3_phy: + while (--j >= 0) + phy_power_off(dwc->usb3_generic_phy[j]); + err_power_off_usb2_phy: - phy_power_off(dwc->usb2_generic_phy); -err_suspend_usb3_phy: + while (--i >= 0) + phy_power_off(dwc->usb2_generic_phy[i]); + usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -808,8 +870,13 @@ static int dwc3_phy_power_on(struct dwc3 *dwc) static void dwc3_phy_power_off(struct dwc3 *dwc) { - phy_power_off(dwc->usb3_generic_phy); - phy_power_off(dwc->usb2_generic_phy); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_power_off(dwc->usb3_generic_phy[i]); + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_power_off(dwc->usb2_generic_phy[i]); usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -1201,6 +1268,7 @@ static int dwc3_core_init(struct dwc3 *dwc) unsigned int hw_mode; u32 reg; int ret; + int i; hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); @@ -1244,15 +1312,19 @@ static int dwc3_core_init(struct dwc3 *dwc) if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) { if (!dwc->dis_u3_susphy_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - reg |= DWC3_GUSB3PIPECTL_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + for (i = 0; i < dwc->num_usb3_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i)); + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg); + } } if (!dwc->dis_u2_susphy_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } } } @@ -1375,7 +1447,9 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) { struct device *dev = dwc->dev; struct device_node *node = dev->of_node; + char phy_name[9]; int ret; + int i; if (node) { dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); @@ -1401,22 +1475,38 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) return dev_err_probe(dev, ret, "no usb3 phy configured\n"); } - dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); - if (IS_ERR(dwc->usb2_generic_phy)) { - ret = PTR_ERR(dwc->usb2_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) - dwc->usb2_generic_phy = NULL; + for (i = 0; i < dwc->num_usb2_ports; i++) { + if (dwc->num_usb2_ports == 1) + snprintf(phy_name, sizeof(phy_name), "usb2-phy"); else - return dev_err_probe(dev, ret, "no usb2 phy configured\n"); + snprintf(phy_name, sizeof(phy_name), "usb2-%d", i); + + dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name); + if (IS_ERR(dwc->usb2_generic_phy[i])) { + ret = PTR_ERR(dwc->usb2_generic_phy[i]); + if (ret == -ENOSYS || ret == -ENODEV) + dwc->usb2_generic_phy[i] = NULL; + else + return dev_err_probe(dev, ret, "failed to lookup phy %s\n", + phy_name); + } } - dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); - if (IS_ERR(dwc->usb3_generic_phy)) { - ret = PTR_ERR(dwc->usb3_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) - dwc->usb3_generic_phy = NULL; + for (i = 0; i < dwc->num_usb3_ports; i++) { + if (dwc->num_usb3_ports == 1) + snprintf(phy_name, sizeof(phy_name), "usb3-phy"); else - return dev_err_probe(dev, ret, "no usb3 phy configured\n"); + snprintf(phy_name, sizeof(phy_name), "usb3-%d", i); + + dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name); + if (IS_ERR(dwc->usb3_generic_phy[i])) { + ret = PTR_ERR(dwc->usb3_generic_phy[i]); + if (ret == -ENOSYS || ret == -ENODEV) + dwc->usb3_generic_phy[i] = NULL; + else + return dev_err_probe(dev, ret, "failed to lookup phy %s\n", + phy_name); + } } return 0; @@ -1426,6 +1516,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) { struct device *dev = dwc->dev; int ret; + int i; switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: @@ -1433,8 +1524,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) @@ -1445,8 +1536,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); ret = dwc3_host_init(dwc); if (ret) @@ -1940,6 +2033,10 @@ static int dwc3_get_num_ports(struct dwc3 *dwc) iounmap(base); + if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS || + dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS) + return -EINVAL; + return 0; } @@ -2177,6 +2274,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) { unsigned long flags; u32 reg; + int i; switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: @@ -2195,17 +2293,21 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) /* Let controller to suspend HSPHY before PHY driver suspends */ if (dwc->dis_u2_susphy_quirk || dwc->dis_enblslpm_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= DWC3_GUSB2PHYCFG_ENBLSLPM | - DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg |= DWC3_GUSB2PHYCFG_ENBLSLPM | + DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } /* Give some time for USB2 PHY to suspend */ usleep_range(5000, 6000); } - phy_pm_runtime_put_sync(dwc->usb2_generic_phy); - phy_pm_runtime_put_sync(dwc->usb3_generic_phy); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]); break; case DWC3_GCTL_PRTCAP_OTG: /* do nothing during runtime_suspend */ @@ -2235,6 +2337,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) unsigned long flags; int ret; u32 reg; + int i; switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: @@ -2254,17 +2357,21 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) break; } /* Restore GUSB2PHYCFG bits that were modified in suspend */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (dwc->dis_u2_susphy_quirk) - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + if (dwc->dis_u2_susphy_quirk) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - if (dwc->dis_enblslpm_quirk) - reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + if (dwc->dis_enblslpm_quirk) + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } - phy_pm_runtime_get_sync(dwc->usb2_generic_phy); - phy_pm_runtime_get_sync(dwc->usb3_generic_phy); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]); break; case DWC3_GCTL_PRTCAP_OTG: /* nothing to do on runtime_resume */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 341e4c73cb2e..5cbc64883dbc 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -33,6 +33,13 @@ #include +/* + * DWC3 Multiport controllers support up to 15 High-Speed PHYs + * and 4 SuperSpeed PHYs. + */ +#define DWC3_USB2_MAX_PORTS 15 +#define DWC3_USB3_MAX_PORTS 4 + #define DWC3_MSG_MAX 500 /* Global constants */ @@ -1037,8 +1044,8 @@ struct dwc3_scratchpad_array { * @usb_psy: pointer to power supply interface. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY - * @usb2_generic_phy: pointer to USB2 PHY - * @usb3_generic_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to array of USB2 PHYs + * @usb3_generic_phy: pointer to array of USB3 PHYs * @num_usb2_ports: number of USB2 ports * @num_usb3_ports: number of USB3 ports * @phys_ready: flag to indicate that PHYs are ready @@ -1186,8 +1193,8 @@ struct dwc3 { struct usb_phy *usb2_phy; struct usb_phy *usb3_phy; - struct phy *usb2_generic_phy; - struct phy *usb3_generic_phy; + struct phy *usb2_generic_phy[DWC3_USB2_MAX_PORTS]; + struct phy *usb3_generic_phy[DWC3_USB3_MAX_PORTS]; u8 num_usb2_ports; u8 num_usb3_ports; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 57ddd2e43022..d76ae676783c 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -331,6 +331,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) u32 reg; int id; unsigned long flags; + int i; if (dwc->dr_mode != USB_DR_MODE_OTG) return; @@ -386,9 +387,12 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) } else { if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, - PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb2_ports; i++) { + if (dwc->usb2_generic_phy[i]) { + phy_set_mode(dwc->usb2_generic_phy[i], + PHY_MODE_USB_HOST); + } + } } break; case DWC3_OTG_ROLE_DEVICE: @@ -400,9 +404,8 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, - PHY_MODE_USB_DEVICE); + if (dwc->usb2_generic_phy[0]) + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) dev_err(dwc->dev, "failed to initialize peripheral\n"); -- 2.34.1