From patchwork Wed Jul 26 22:56:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jon Mason X-Patchwork-Id: 9866029 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 15C5A602B1 for ; Wed, 26 Jul 2017 23:49:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 03F622876F for ; Wed, 26 Jul 2017 23:49:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EB4C0287D4; Wed, 26 Jul 2017 23:49:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 1FEBE2876F for ; Wed, 26 Jul 2017 23:49:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=JWQGfvmO2Evg5Idg7bBsTamI78/7YcirEnY/dPDNxrw=; b=esfenrm3uGIiECQDXmaw3NBVV8 86dg1IUIBTPvc5Ww4xH78vyaishFHc13gRctWsGpaq1dzYcVUZYMIEpnDsdAMnNMYBMqFTcuqqO8V eqnpayHA+e9kDVvezrFO8Ltf0+GMPCRx394rNbsiGR1xmfFpNbyZQsR4iL8Fyo1KZp85SiyKWa/Yu xF1wAn3vPEoB+DfAYOSJXR4YB9fOabfcGOJv6Ox8D6O8OCcoBMU3aKyWLze/5V01rT0PUoKwmXq1y Ww5BjACEa7cjubWWJgV+EbCGJY9SE3BqBBnHLIkbV4DxnF7s5ocecVNzI+T/h7gyIpmqKEc+RAaeV QfZeNBsg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1daW3W-0000CU-I3; Wed, 26 Jul 2017 23:49:30 +0000 Received: from merlin.infradead.org ([2001:8b0:10b:1231::1]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1daW3T-0000CK-Ng for linux-arm-kernel@bombadil.infradead.org; Wed, 26 Jul 2017 23:49:28 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=merlin.20170209; h=References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=nIPQBymuYP0QDWKs+KKmgS9DFuE7F4ROkSQG+MwOKH0=; b=ilkQ6oCDXcPs2zEIUtp3a1gSl 654cgB8ez9CA+QyNLpK0GlT+rt9YC3iCn0kIUHi7bUl5AWpOqlfKoQoBnHMnv046r63UtvGX4+1nQ tn4nD7pOCtXvW0ir6BzQjHOvdut0s/LueonduJXL5648ed9by/lVuTdLVjgwTW4SrExtWNCtqVkIn DxAy/jjoo85hFsr9oQ5WwuBSBtI35dAIbKkFFM4gS2dnPTqXlesNIJH45XrPdfQkTVNfoYULCny01 IM40c8cZLS5vcxn7BOhgags2M9EmMqyYlosS8Xp+BirWxbqWCQenTf44GdwwrIzhQfmIwkmazZ31r 3XUvh0lcg==; Received: from mail-wm0-x22a.google.com ([2a00:1450:400c:c09::22a]) by merlin.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1daVF2-000658-RD for linux-arm-kernel@lists.infradead.org; Wed, 26 Jul 2017 22:57:22 +0000 Received: by mail-wm0-x22a.google.com with SMTP id c184so90722785wmd.0 for ; Wed, 26 Jul 2017 15:56:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=nIPQBymuYP0QDWKs+KKmgS9DFuE7F4ROkSQG+MwOKH0=; b=ghr0pJ6rJS7X8N9S6XAyHP28hBEGkHz5FVFeFib1xJUmJBbK6pt7iHd/SwiRAULypu zL6eRkU3YOyB5sUUdv+EZzFXOIXWvk8kIv9c3hFqablVAzftLgF20KV6BTBS0En1asPF V6DQi+XUzjA++FhY2gm5zzQAiUN/ZPOQHLEoQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=nIPQBymuYP0QDWKs+KKmgS9DFuE7F4ROkSQG+MwOKH0=; b=r/pTn1GyA2R3sxtR5oEijNyyIUNmKdyi0YtX+Je80fk3jpBBhMMHd6+ShaNN2FthUC r5nfPgBfT6tMc/SbhCXuX7xAvWT6hYMfiHqDfrkkIOQ+nRc6EoliaotqVa5Boroig5Jt IQ17LM6JHieotQJ2nF3/08g9eC6hqJRr/kK7Rv7df7+uo3yzEbBKHvrIm6KGhStqnJdw NDE5ceLIrGxIECYBAhUFGdUCQpJpiwwueFrG9HZmANRcf6VrtL83bQlEP3rMOQmbDKGO 4n1CXYMqwFR53FhHzAK77SDkklpgRiBZ2OdS5YK5+buGZxbgWIaB7ApV7v7hOPhXshYM fVNw== X-Gm-Message-State: AIVw111TOnt42VUgaFlQ+Zcj3q0Fg6zYFi2I3weqptcKCuA0ZzyTrfM9 ei/Q7RZn8sh3iyfO X-Received: by 10.28.113.221 with SMTP id d90mr1809434wmi.49.1501109816543; Wed, 26 Jul 2017 15:56:56 -0700 (PDT) Received: from venom ([192.19.231.250]) by smtp.gmail.com with ESMTPSA id u30sm17085365wru.88.2017.07.26.15.56.53 (version=TLS1 cipher=AES128-SHA bits=128/128); Wed, 26 Jul 2017 15:56:56 -0700 (PDT) Received: by venom (sSMTP sendmail emulation); Wed, 26 Jul 2017 18:56:51 -0400 From: Jon Mason To: Kishon Vijay Abraham I , Florian Fainelli , Rob Herring , Mark Rutland Subject: [PATCH v2 5/6] phy: Add USB3 PHY support for Broadcom NS2 SoC Date: Wed, 26 Jul 2017 18:56:26 -0400 Message-Id: <1501109787-15489-6-git-send-email-jon.mason@broadcom.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1501109787-15489-1-git-send-email-jon.mason@broadcom.com> References: <1501109787-15489-1-git-send-email-jon.mason@broadcom.com> X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, bcm-kernel-feedback-list@broadcom.com, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Yendapally Reddy Dhananjaya Reddy This patch adds support for Broadcom NS2 USB3 PHY Signed-off-by: Yendapally Reddy Dhananjaya Reddy Signed-off-by: Jon Mason --- drivers/phy/broadcom/Kconfig | 9 + drivers/phy/broadcom/Makefile | 1 + drivers/phy/broadcom/phy-bcm-ns2-usb3.c | 580 ++++++++++++++++++++++++++++++++ 3 files changed, 590 insertions(+) create mode 100644 drivers/phy/broadcom/phy-bcm-ns2-usb3.c diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index 37371b89b14f..e7d238efd539 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -45,6 +45,15 @@ config PHY_NS2_PCIE Enable this to support the Broadcom Northstar2 PCIe PHY. If unsure, say N. +config PHY_NS2_USB3 + tristate "Broadcom NorthStar2 USB3 PHY driver" + depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST) + select GENERIC_PHY + default ARCH_BCM_IPROC + help + Enable this to support the Broadcom Northstar2 USB3 PHY. + If unsure, say N. + config PHY_NS2_USB_DRD tristate "Broadcom Northstar2 USB DRD PHY support" depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST) diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index 4eb82ec8d491..a40e6e6f56b8 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o +obj-$(CONFIG_PHY_NS2_USB3) += phy-bcm-ns2-usb3.o obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usb3.c b/drivers/phy/broadcom/phy-bcm-ns2-usb3.c new file mode 100644 index 000000000000..7f272757434b --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-ns2-usb3.c @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NS2_USB3_PHY_MAX 0x02 + +#define NS2_USB3_PHY_CONFIG_CTRL_REG 0x10 +#define NS2_USB3_PHY_CONFIG_CTRL_MASK (BIT(3) | BIT(4) | BIT(5)) +#define NS2_USB3_PHY_CONFIG_CTRL_PLL_SEQ_START BIT(6) + +#define NS2_USB3_PHY_P0CTL_REG 0x14 +#define NS2_USB3_PHY_P1CTL_REG 0x18 +#define NS2_USB3_PHY_PXCTL_I_BIT BIT(1) + +#define NS2_USB3_PHY_MISC_STATUS_REG 0x20 + +#define NS2_IDM_RST_CTRL_P0_OFFSET 0x800 +#define NS2_IDM_RST_CTRL_P1_OFFSET 0x1800 +#define NS2_IDM_RESET_CONTROL_BIT BIT(0) + +#define NS2_IDM_IO_CTRL_P0_OFFSET 0x408 +#define NS2_IDM_IO_CTRL_P1_OFFSET 0x1408 +/* Bit 23 for PPC Polarity, Bit 24 for PPC NANDNOR select */ +#define NS2_IDM_IO_CTRL_PPC_CFG (BIT(23) | BIT(24)) + +#define NS2_PHY_RESET_BIT BIT(5) +#define NS2_PHY_PLL_RESET_BIT BIT(6) + +/* NS2 USB3 MDIO */ +#define NS2_USB3_MDIO_PLL30_ADDR 0x8000 +#define NS2_USB3_MDIO_BLK_ACCESS 0x1F +#define NS2_USB3_MDIO_PLL30_ANAPLL_CTRL 0x14 +#define NS2_USB3_MDIO_PLL30_ANAPLL_CTRL_VAL 0x23 +#define NS2_USB3_MDIO_PLL30_GEN_PLL 0xF +#define NS2_USB3_MDIO_PLL30_GEN_PLL_PCLK_SEL BIT(11) +#define NS2_USB3_MDIO_P0_AFE30_ADDR 0x8080 +#define NS2_USB3_MDIO_P1_AFE30_ADDR 0x9080 +#define NS2_USB3_MDIO_AFE30_RX_SIG_DETECT 0x5 +#define NS2_USB3_MDIO_AFE30_RX_SIG_DETECT_VAL 0xAC0D + +#define NS2_USB3_MDIO_P0_PIPE_BLK_ADDR 0x8060 +#define NS2_USB3_MDIO_P1_PIPE_BLK_ADDR 0x9060 +#define NS2_USB3_MDIO_PIPE_BLK_REG_1_OFFSET 0x1 +#define NS2_USB3_MDIO_PIPE_BLK_REG_1_VAL 0x207 + +#define NS2_USB3_MDIO_P0_AEQ_BLK_ADDR 0x80E0 +#define NS2_USB3_MDIO_P1_AEQ_BLK_ADDR 0x90E0 +#define NS2_USB3_MDIO_AEQ_BLK_REG_1_OFFSET 0x1 +#define NS2_USB3_MDIO_AEQ_BLK_REG_1_VAL 0x3000 + +enum ns2_phy_block { + PHY_RESET, + PHY_PLL_RESET, + PHY_SOFT_RESET, + PHY_PIPE_RESET, + PHY_REF_CLOCK, + PHY_PLL_SEQ_START, + PHY_PLL_STATUS, + PHY_VBUS_PPC, +}; + +enum ns2_reg_base { + NS2_USB3_CTRL = 1, + NS2_USB3_PHY_CFG, + NS2_USB3_RST_CTRL, + NS2_USB3_REG_BASE_MAX +}; + +struct ns2_usb3_phy { + void __iomem *reg_base[NS2_USB3_REG_BASE_MAX]; + struct ns2_usb3_phy_master *mphy; + struct phy *phy; + int port_no; +}; + +struct ns2_usb3_phy_master { + struct ns2_usb3_phy iphys[NS2_USB3_PHY_MAX]; + struct mdio_device *mdiodev; + struct mutex phy_mutex; + int init_count; /* PHY is dual port phy, so init once*/ +}; + +static int iproc_ns2_phy_action(struct ns2_usb3_phy *iphy, + enum ns2_phy_block block, bool assert) +{ + void __iomem *addr; + u32 data, count; + u32 offset = 0; + int ret = 0; + + switch (block) { + case PHY_RESET: + addr = iphy->reg_base[NS2_USB3_CTRL]; + + data = readl(addr + offset); + + if (assert) + data &= ~NS2_PHY_RESET_BIT; + else + data |= NS2_PHY_RESET_BIT; + + writel(data, addr + offset); + break; + + case PHY_PLL_RESET: + addr = iphy->reg_base[NS2_USB3_CTRL]; + + data = readl(addr + offset); + + if (assert) + data &= ~NS2_PHY_PLL_RESET_BIT; + else + data |= NS2_PHY_PLL_RESET_BIT; + + writel(data, addr + offset); + break; + + case PHY_SOFT_RESET: + addr = iphy->reg_base[NS2_USB3_PHY_CFG]; + offset = NS2_USB3_PHY_P0CTL_REG; + + data = readl(addr + offset); + + if (assert) + data &= ~NS2_USB3_PHY_PXCTL_I_BIT; + else + data |= NS2_USB3_PHY_PXCTL_I_BIT; + + writel(data, addr + offset); + + offset = NS2_USB3_PHY_P1CTL_REG; + + data = readl(addr + offset); + + if (assert) + data &= ~NS2_USB3_PHY_PXCTL_I_BIT; + else + data |= NS2_USB3_PHY_PXCTL_I_BIT; + + writel(data, addr + offset); + break; + + case PHY_PIPE_RESET: + addr = iphy->reg_base[NS2_USB3_RST_CTRL]; + offset = NS2_IDM_RST_CTRL_P0_OFFSET; + + data = readl(addr + offset); + + if (assert) + data |= NS2_IDM_RESET_CONTROL_BIT; + else + data &= ~NS2_IDM_RESET_CONTROL_BIT; + + writel(data, addr + offset); + + offset = NS2_IDM_RST_CTRL_P1_OFFSET; + data = readl(addr + offset); + + if (assert) + data |= NS2_IDM_RESET_CONTROL_BIT; + else + data &= ~NS2_IDM_RESET_CONTROL_BIT; + + writel(data, addr + offset); + break; + + case PHY_VBUS_PPC: + addr = iphy->reg_base[NS2_USB3_RST_CTRL]; + offset = NS2_IDM_IO_CTRL_P0_OFFSET; + + data = readl(addr + offset); + + if (assert) + data |= NS2_IDM_IO_CTRL_PPC_CFG; + else + data &= ~NS2_IDM_IO_CTRL_PPC_CFG; + + writel(data, addr + offset); + + offset = NS2_IDM_IO_CTRL_P1_OFFSET; + data = readl(addr + offset); + + if (assert) + data |= NS2_IDM_IO_CTRL_PPC_CFG; + else + data &= ~NS2_IDM_IO_CTRL_PPC_CFG; + + writel(data, addr + offset); + break; + + case PHY_REF_CLOCK: + addr = iphy->reg_base[NS2_USB3_PHY_CFG]; + offset = NS2_USB3_PHY_CONFIG_CTRL_REG; + + data = readl(addr + offset); + + data &= ~NS2_USB3_PHY_CONFIG_CTRL_MASK; + + writel(data, addr + offset); + break; + + case PHY_PLL_SEQ_START: + addr = iphy->reg_base[NS2_USB3_PHY_CFG]; + offset = NS2_USB3_PHY_CONFIG_CTRL_REG; + + data = readl(addr + offset); + + data |= NS2_USB3_PHY_CONFIG_CTRL_PLL_SEQ_START; + + writel(data, addr + offset); + break; + + case PHY_PLL_STATUS: + count = 2000; + addr = iphy->reg_base[NS2_USB3_PHY_CFG]; + offset = NS2_USB3_PHY_MISC_STATUS_REG; + + do { + udelay(1); + + data = readl(addr + offset); + if (data == 1) + break; + } while (--count); + + if (!count) + ret = -ETIMEDOUT; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int ns2_usb3_phy_exit(struct phy *phy) +{ + struct ns2_usb3_phy *iphy = phy_get_drvdata(phy); + int rc = 0; + + mutex_lock(&iphy->mphy->phy_mutex); + + if (iphy->mphy->init_count <= 0) { + mutex_unlock(&iphy->mphy->phy_mutex); + return 0; + } else if (iphy->mphy->init_count == 1) { + /* Only put in to reset for last port to exit */ + rc = iproc_ns2_phy_action(iphy, PHY_PLL_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_SOFT_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PIPE_RESET, true); + if (rc) + goto out; + } + +out: + iphy->mphy->init_count--; + mutex_unlock(&iphy->mphy->phy_mutex); + + return rc; +} + +static int ns2_usb3_phy_init(struct phy *phy) +{ + struct ns2_usb3_phy *iphy = phy_get_drvdata(phy); + u16 addr; + u16 reg_val; + int rc; + + mutex_lock(&iphy->mphy->phy_mutex); + + if (iphy->mphy->init_count) { + /* Use count to identify last port to call phy_exit. */ + iphy->mphy->init_count++; + mutex_unlock(&iphy->mphy->phy_mutex); + return 0; + } + + rc = iproc_ns2_phy_action(iphy, PHY_RESET, false); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_SOFT_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PIPE_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_REF_CLOCK, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PLL_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_RESET, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_RESET, false); + if (rc) + goto out; + + /* PLL programming */ + /* PHY PLL30 Block */ + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, + NS2_USB3_MDIO_PLL30_ADDR); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_PLL30_ANAPLL_CTRL, + NS2_USB3_MDIO_PLL30_ANAPLL_CTRL_VAL); + if (rc) + goto out; + + reg_val = (u16) mdiobus_read(iphy->mphy->mdiodev->bus, + iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_PLL30_GEN_PLL); + reg_val |= NS2_USB3_MDIO_PLL30_GEN_PLL_PCLK_SEL; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_PLL30_GEN_PLL, reg_val); + if (rc) + goto out; + + /* PHY AFE30 Block */ + addr = NS2_USB3_MDIO_P0_AFE30_ADDR; + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, addr); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_AFE30_RX_SIG_DETECT, + NS2_USB3_MDIO_AFE30_RX_SIG_DETECT_VAL); + if (rc) + goto out; + + addr = NS2_USB3_MDIO_P1_AFE30_ADDR; + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, addr); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_AFE30_RX_SIG_DETECT, + NS2_USB3_MDIO_AFE30_RX_SIG_DETECT_VAL); + if (rc) + goto out; + + /* PHY PIPE Block */ + addr = NS2_USB3_MDIO_P0_PIPE_BLK_ADDR; + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, addr); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_PIPE_BLK_REG_1_OFFSET, + NS2_USB3_MDIO_PIPE_BLK_REG_1_VAL); + if (rc) + goto out; + + addr = NS2_USB3_MDIO_P1_PIPE_BLK_ADDR; + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, addr); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_PIPE_BLK_REG_1_OFFSET, + NS2_USB3_MDIO_PIPE_BLK_REG_1_VAL); + if (rc) + goto out; + + /* AEQ Block */ + addr = NS2_USB3_MDIO_P0_AEQ_BLK_ADDR; + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, addr); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_AEQ_BLK_REG_1_OFFSET, + NS2_USB3_MDIO_AEQ_BLK_REG_1_VAL); + if (rc) + goto out; + + /* PHY PORT_1 */ + addr = NS2_USB3_MDIO_P1_AEQ_BLK_ADDR; + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_BLK_ACCESS, addr); + if (rc) + goto out; + + rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr, + NS2_USB3_MDIO_AEQ_BLK_REG_1_OFFSET, + NS2_USB3_MDIO_AEQ_BLK_REG_1_VAL); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PLL_SEQ_START, true); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PIPE_RESET, false); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_SOFT_RESET, false); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PLL_RESET, false); + if (rc) + goto out; + + rc = iproc_ns2_phy_action(iphy, PHY_PLL_STATUS, true); + if (rc) + goto out; + + /* Set USB3H VBUS PPC Polarity and NandNor select */ + rc = iproc_ns2_phy_action(iphy, PHY_VBUS_PPC, true); + +out: + iphy->mphy->init_count++; + mutex_unlock(&iphy->mphy->phy_mutex); + + return rc; +} + +static struct phy_ops ns2_usb3_phy_ops = { + .init = ns2_usb3_phy_init, + .exit = ns2_usb3_phy_exit, + .owner = THIS_MODULE, +}; + +static int ns2_usb3_phy_map_by_name(struct ns2_usb3_phy *iphy, + enum ns2_reg_base reg, const char *compat) +{ + struct device_node *temp; + void __iomem *base; + struct device *dev = &iphy->mphy->mdiodev->dev; + + temp = of_find_compatible_node(NULL, NULL, compat); + if (!temp) { + dev_err(dev, "Unable to find %s in Device Tree\n", compat); + return -EINVAL; + } + + base = of_iomap(temp, 0); + if (!base) { + dev_err(dev, "Unable to map %s\n", compat); + return -EINVAL; + } + iphy->reg_base[reg] = base; + + return 0; +} + +static int ns2_usb3_phy_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct device_node *dn = dev->of_node, *child; + struct ns2_usb3_phy_master *mphy; + struct phy_provider *provider; + int cnt; + + mphy = devm_kzalloc(dev, sizeof(*mphy), GFP_KERNEL); + if (!mphy) + return -ENOMEM; + mphy->mdiodev = mdiodev; + mutex_init(&mphy->phy_mutex); + mphy->init_count = 0; + + cnt = 0; + for_each_available_child_of_node(dn, child) { + struct ns2_usb3_phy *iphy; + unsigned int val; + int rc; + + iphy = &mphy->iphys[cnt]; + if (of_property_read_u32(child, "reg", &val)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + iphy->port_no = val; + iphy->mphy = mphy; + + rc = ns2_usb3_phy_map_by_name(iphy, NS2_USB3_CTRL, + "brcm,cdru-usb3-ctrl"); + if (rc) + return rc; + + rc = ns2_usb3_phy_map_by_name(iphy, NS2_USB3_PHY_CFG, + "brcm,icfg-usb"); + if (rc) + return rc; + + rc = ns2_usb3_phy_map_by_name(iphy, NS2_USB3_RST_CTRL, + "brcm,usb3-idm"); + if (rc) + return rc; + + iphy->phy = devm_phy_create(dev, child, &ns2_usb3_phy_ops); + if (IS_ERR(iphy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(iphy->phy); + } + + phy_set_drvdata(iphy->phy, iphy); + cnt++; + } + + dev_set_drvdata(dev, mphy); + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "could not register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "registered %d phy(s)\n", cnt); + return 0; +} + +static const struct of_device_id ns2_usb3_phy_of_match[] = { + {.compatible = "brcm,ns2-usb3-phy",}, + { /* sentinel */ } +}; + +static struct mdio_driver ns2_usb3_phy_driver = { + .mdiodrv = { + .driver = { + .name = "ns2-usb3-phy", + .of_match_table = ns2_usb3_phy_of_match, + }, + }, + .probe = ns2_usb3_phy_probe, +}; +mdio_module_driver(ns2_usb3_phy_driver); + +MODULE_DESCRIPTION("Broadcom NS2 USB3 PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Broadcom");