Received: by 2002:ac0:950c:0:0:0:0:0 with SMTP id f12csp2212069imc; Tue, 12 Mar 2019 09:08:22 -0700 (PDT) X-Google-Smtp-Source: APXvYqyyNNUWLTxJJFM/L2eMyPAxDQ3eIi0V/UsipRntYpi/bw3BGlNz7HEJLQ/ppdXuFymVCyqS X-Received: by 2002:a17:902:121:: with SMTP id 30mr36286315plb.315.1552406902459; Tue, 12 Mar 2019 09:08:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1552406902; cv=none; d=google.com; s=arc-20160816; b=snauTAiml8rV8KcKZtJbLLMYNeqOoScKid1E19+uqmgijIoM57145RvCm3LwUJYIk4 aLEmNRVmsMVR26dqFSzr4CtCaW+Wa2KrcYq1YfmUSwX/83YTtiLeL9luwd186J0zC7S3 rb8Kcg2Yj3VBY+OAbJmV0qPO+R5Oapuo3PcAmJDKLmTbeGU2b+GpADPQU3YXg/6BMSrm VyrVknq7R2gPf3Ao8Ov701ddKEkHXa1gQu/xPp3HtjZKL8vlbU3gUexUHLXPGRUeo9uI OYfati8yntzritc8x8O8FsOT4GYtOu++xrHrlzGLX/jvU4qVy0pvKz2MxzWwgsxk0zBl MpXg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=BAVjFbYQqOCyFiPKzVWBEQCPnB4L2xdCg9ZolylfjRw=; b=XC7gqpKEOBFued7F/f56fPm2JuVCEbmoQoNIdF7mmru8TcLAF2UyRD5CbRu4ERU3u5 /734OGA3YO6dlHFrq2cHsUq4KoIGiJLNE4LvyCLEWszj09eRUB63A3MlujIybaCyVQ0M +B8yOTM8CP5vcDTr+Ow8OODD1cLbYTBjZhQ4S8tQ5zJiDSKTMNXlw+leGmReILifq6PH F0vRCtHtFAcABqAY7FosXkgZyQ1zH4qRJJOGuIr5P/RkOn5+GQLgSN9GSLIMYzu5+ZYj CDb162Ahzu4fJMTx2soBChH8gwZ+wKuGEhJ9RFSbfjdc6KnWPUfBCok5+JAaVTPE1CiD 327g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@broadcom.com header.s=google header.b=hRTfPa11; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=broadcom.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 37si6246077plc.233.2019.03.12.09.08.06; Tue, 12 Mar 2019 09:08:22 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@broadcom.com header.s=google header.b=hRTfPa11; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=broadcom.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726833AbfCLQH0 (ORCPT + 99 others); Tue, 12 Mar 2019 12:07:26 -0400 Received: from mail-pg1-f195.google.com ([209.85.215.195]:43387 "EHLO mail-pg1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726762AbfCLQHZ (ORCPT ); Tue, 12 Mar 2019 12:07:25 -0400 Received: by mail-pg1-f195.google.com with SMTP id l11so2148837pgq.10 for ; Tue, 12 Mar 2019 09:07:24 -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=BAVjFbYQqOCyFiPKzVWBEQCPnB4L2xdCg9ZolylfjRw=; b=hRTfPa11jr38pdqTZs2uJELCQOtY0jIZJak3RvBGgAiVNewn8CcTzsLQ+fgJH7uwCE pYqQnFyvhaBdBunMuSdzQ0gErvRrYvsiKj1ieCGd+xOTCfpgUjXrWeNRpu2peEg4tMzS hmiyPJgogVfvr9SDI5OwnsHhr4GfK4a13DSYo= 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=BAVjFbYQqOCyFiPKzVWBEQCPnB4L2xdCg9ZolylfjRw=; b=pO19bdFcPFR2VeS/NCQOhQYlF0M8Bn5guwt05TPbKvSPUlxWQjlaAZJi7cFLxIh54H 7NXYGztcY5NjljHARY5NLzOIJD7t0i05SjBaotDUIs5vSGAma60ptT0x/2ZqJ+7or4Jp rSknFRtbX9B29bvQpfT1ks2E5Mn3tzAK1cjnnWpBgCxCScL9miJWAT04fU0fm11T1evp IHTEKFjby/BEnB0VfSUBEl7hsAd/T/rNfZdP7DnleYLnH/yLCZ/A7Jwz9uB9WRtT3+8e aqWCThDeo9+WP15ebj5Xa3cDq2efPTBnR6+h1AjOWWuNNlMkm8brOw1t0qdP+EfwCnJe xWlA== X-Gm-Message-State: APjAAAXSM3qPpkcFfU97a6432C6mt5V+XUMr+V8J5tq9/s7axBu49GFW BQ0NkaJ5lGt1H0fYzUEWp/SC1w== X-Received: by 2002:a63:ff0f:: with SMTP id k15mr8077120pgi.301.1552406843872; Tue, 12 Mar 2019 09:07:23 -0700 (PDT) Received: from mannams-OptiPlex-7010.dhcp.broadcom.net ([192.19.234.250]) by smtp.gmail.com with ESMTPSA id z12sm12875854pgv.0.2019.03.12.09.07.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 12 Mar 2019 09:07:23 -0700 (PDT) From: Srinath Mannam To: Rob Herring , Mark Rutland , Kishon Vijay Abraham I Cc: Tejun Heo , Jayachandran C , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, bcm-kernel-feedback-list@broadcom.com, Srinath Mannam Subject: [PATCH v5 2/3] phy: sr-usb: Add Stingray USB PHY driver Date: Tue, 12 Mar 2019 21:36:20 +0530 Message-Id: <1552406781-5180-3-git-send-email-srinath.mannam@broadcom.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1552406781-5180-1-git-send-email-srinath.mannam@broadcom.com> References: <1552406781-5180-1-git-send-email-srinath.mannam@broadcom.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org USB PHY driver supports two types of stingray USB PHYs - Type 1 is a combo PHY contains two PHYs, one SS and one HS. - Type 2 is a single HS PHY. These two PHY versons support both Generic xHCI host controller driver and BDC Broadcom device controller driver. Signed-off-by: Srinath Mannam --- drivers/phy/broadcom/Kconfig | 11 + drivers/phy/broadcom/Makefile | 1 + drivers/phy/broadcom/phy-bcm-sr-usb.c | 393 ++++++++++++++++++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 drivers/phy/broadcom/phy-bcm-sr-usb.c diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index aa917a6..f30f481 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -10,6 +10,17 @@ config PHY_CYGNUS_PCIE Enable this to support the Broadcom Cygnus PCIe PHY. If unsure, say N. +config PHY_BCM_SR_USB + tristate "Broadcom Stingray USB PHY driver" + depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST) + select GENERIC_PHY + default ARCH_BCM_IPROC + help + Enable this to support the Broadcom Stingray USB PHY + driver. It supports all versions of Superspeed and + Highspeed PHYs. + If unsure, say N. + config BCM_KONA_USB2_PHY tristate "Broadcom Kona USB2 PHY Driver" depends on HAS_IOMEM diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index 0f60184..f453c7d 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o +obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o diff --git a/drivers/phy/broadcom/phy-bcm-sr-usb.c b/drivers/phy/broadcom/phy-bcm-sr-usb.c new file mode 100644 index 0000000..bfe8237 --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-sr-usb.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Broadcom + */ + +#include +#include +#include +#include +#include +#include + +enum bcm_usb_phy_version { + BCM_SR_USB_COMBO_PHY, + BCM_SR_USB_HS_PHY, +}; + +enum bcm_usb_phy_reg { + PLL_NDIV_FRAC, + PLL_NDIV_INT, + PLL_CTRL, + PHY_CTRL, + PHY_PLL_CTRL, +}; + +/* USB PHY registers */ + +static const u8 bcm_usb_combo_phy_ss[] = { + [PLL_CTRL] = 0x18, + [PHY_CTRL] = 0x14, +}; + +static const u8 bcm_usb_combo_phy_hs[] = { + [PLL_NDIV_FRAC] = 0x04, + [PLL_NDIV_INT] = 0x08, + [PLL_CTRL] = 0x0c, + [PHY_CTRL] = 0x10, +}; + +#define HSPLL_NDIV_INT_VAL 0x13 +#define HSPLL_NDIV_FRAC_VAL 0x1005 + +static const u8 bcm_usb_hs_phy[] = { + [PLL_NDIV_FRAC] = 0x0, + [PLL_NDIV_INT] = 0x4, + [PLL_CTRL] = 0x8, + [PHY_CTRL] = 0xc, +}; + +enum pll_ctrl_bits { + PLL_RESETB, + SSPLL_SUSPEND_EN, + PLL_SEQ_START, + PLL_LOCK, + PLL_PDIV, +}; + +static const u8 u3pll_ctrl[] = { + [PLL_RESETB] = 0, + [SSPLL_SUSPEND_EN] = 1, + [PLL_SEQ_START] = 2, + [PLL_LOCK] = 3, +}; + +#define HSPLL_PDIV_MASK 0xF +#define HSPLL_PDIV_VAL 0x1 + +static const u8 u2pll_ctrl[] = { + [PLL_PDIV] = 1, + [PLL_RESETB] = 5, + [PLL_LOCK] = 6, +}; + +enum bcm_usb_phy_ctrl_bits { + CORERDY, + AFE_LDO_PWRDWNB, + AFE_PLL_PWRDWNB, + AFE_BG_PWRDWNB, + PHY_ISO, + PHY_RESETB, + PHY_PCTL, +}; + +#define PHY_PCTL_MASK 0xffff +/* + * 0x0806 of PCTL_VAL has below bits set + * BIT-8 : refclk divider 1 + * BIT-3:2: device mode; mode is not effect + * BIT-1: soft reset active low + */ +#define HSPHY_PCTL_VAL 0x0806 +#define SSPHY_PCTL_VAL 0x0006 + +static const u8 u3phy_ctrl[] = { + [PHY_RESETB] = 1, + [PHY_PCTL] = 2, +}; + +static const u8 u2phy_ctrl[] = { + [CORERDY] = 0, + [AFE_LDO_PWRDWNB] = 1, + [AFE_PLL_PWRDWNB] = 2, + [AFE_BG_PWRDWNB] = 3, + [PHY_ISO] = 4, + [PHY_RESETB] = 5, + [PHY_PCTL] = 6, +}; + +struct bcm_usb_phy_cfg { + uint32_t type; + uint32_t version; + void __iomem *regs; + struct phy *phy; + const u8 *offset; +}; + +#define PLL_LOCK_RETRY_COUNT 1000 + +enum bcm_usb_phy_type { + USB_HS_PHY, + USB_SS_PHY, +}; + +#define NUM_BCM_SR_USB_COMBO_PHYS 2 + +static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear) +{ + writel(readl(addr) & ~clear, addr); +} + +static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set) +{ + writel(readl(addr) | set, addr); +} + +static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit) +{ + int retry; + u32 rd_data; + + retry = PLL_LOCK_RETRY_COUNT; + do { + rd_data = readl(addr); + if (rd_data & bit) + return 0; + udelay(1); + } while (--retry > 0); + + pr_err("%s: FAIL\n", __func__); + return -ETIMEDOUT; +} + +static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg) +{ + int ret = 0; + void __iomem *regs = phy_cfg->regs; + const u8 *offset; + u32 rd_data; + + offset = phy_cfg->offset; + + /* Set pctl with mode and soft reset */ + rd_data = readl(regs + offset[PHY_CTRL]); + rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]); + rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]); + writel(rd_data, regs + offset[PHY_CTRL]); + + bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL], + BIT(u3pll_ctrl[SSPLL_SUSPEND_EN])); + bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], + BIT(u3pll_ctrl[PLL_SEQ_START])); + bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], + BIT(u3pll_ctrl[PLL_RESETB])); + + /* Maximum timeout for PLL reset done */ + msleep(30); + + ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL], + BIT(u3pll_ctrl[PLL_LOCK])); + + return ret; +} + +static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg) +{ + int ret = 0; + void __iomem *regs = phy_cfg->regs; + const u8 *offset; + u32 rd_data; + + offset = phy_cfg->offset; + + writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]); + writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]); + + rd_data = readl(regs + offset[PLL_CTRL]); + rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]); + rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]); + writel(rd_data, regs + offset[PLL_CTRL]); + + /* Set Core Ready high */ + bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], + BIT(u2phy_ctrl[CORERDY])); + + /* Maximum timeout for Core Ready done */ + msleep(30); + + bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], + BIT(u2pll_ctrl[PLL_RESETB])); + bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], + BIT(u2phy_ctrl[PHY_RESETB])); + + + rd_data = readl(regs + offset[PHY_CTRL]); + rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]); + rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]); + writel(rd_data, regs + offset[PHY_CTRL]); + + /* Maximum timeout for PLL reset done */ + msleep(30); + + ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL], + BIT(u2pll_ctrl[PLL_LOCK])); + + return ret; +} + +static int bcm_usb_phy_reset(struct phy *phy) +{ + struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy); + void __iomem *regs = phy_cfg->regs; + const u8 *offset; + + offset = phy_cfg->offset; + + if (phy_cfg->type == USB_HS_PHY) { + bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL], + BIT(u2phy_ctrl[CORERDY])); + bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], + BIT(u2phy_ctrl[CORERDY])); + } + + return 0; +} + +static int bcm_usb_phy_init(struct phy *phy) +{ + struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy); + int ret = -EINVAL; + + if (phy_cfg->type == USB_SS_PHY) + ret = bcm_usb_ss_phy_init(phy_cfg); + else if (phy_cfg->type == USB_HS_PHY) + ret = bcm_usb_hs_phy_init(phy_cfg); + + return ret; +} + +static struct phy_ops sr_phy_ops = { + .init = bcm_usb_phy_init, + .reset = bcm_usb_phy_reset, + .owner = THIS_MODULE, +}; + +static struct phy *bcm_usb_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct bcm_usb_phy_cfg *phy_cfg; + int phy_idx; + + phy_cfg = dev_get_drvdata(dev); + if (!phy_cfg) + return ERR_PTR(-EINVAL); + + if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) { + phy_idx = args->args[0]; + + if (WARN_ON(phy_idx > 1)) + return ERR_PTR(-ENODEV); + + return phy_cfg[phy_idx].phy; + } else + return phy_cfg->phy; +} + +static int bcm_usb_phy_create(struct device *dev, struct device_node *node, + void __iomem *regs, uint32_t version) +{ + struct bcm_usb_phy_cfg *phy_cfg; + int idx; + + if (version == BCM_SR_USB_COMBO_PHY) { + phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS * + sizeof(struct bcm_usb_phy_cfg), + GFP_KERNEL); + if (!phy_cfg) + return -ENOMEM; + + for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) { + phy_cfg[idx].regs = regs; + phy_cfg[idx].version = version; + if (idx == 0) { + phy_cfg[idx].offset = bcm_usb_combo_phy_hs; + phy_cfg[idx].type = USB_HS_PHY; + } else { + phy_cfg[idx].offset = bcm_usb_combo_phy_ss; + phy_cfg[idx].type = USB_SS_PHY; + } + phy_cfg[idx].phy = devm_phy_create(dev, node, + &sr_phy_ops); + if (IS_ERR(phy_cfg[idx].phy)) + return PTR_ERR(phy_cfg[idx].phy); + + phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]); + } + } else if (version == BCM_SR_USB_HS_PHY) { + phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg), + GFP_KERNEL); + if (!phy_cfg) + return -ENOMEM; + + phy_cfg->regs = regs; + phy_cfg->version = version; + phy_cfg->offset = bcm_usb_hs_phy; + phy_cfg->type = USB_HS_PHY; + phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops); + if (IS_ERR(phy_cfg->phy)) + return PTR_ERR(phy_cfg->phy); + + phy_set_drvdata(phy_cfg->phy, phy_cfg); + } + + dev_set_drvdata(dev, phy_cfg); + + return 0; +} + +static const struct of_device_id bcm_usb_phy_of_match[] = { + { + .compatible = "brcm,sr-usb-combo-phy", + .data = (void *)BCM_SR_USB_COMBO_PHY, + }, + { + .compatible = "brcm,sr-usb-hs-phy", + .data = (void *)BCM_SR_USB_HS_PHY, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match); + +static int bcm_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; + const struct of_device_id *of_id; + struct resource *res; + void __iomem *regs; + int ret; + enum bcm_usb_phy_version version; + struct phy_provider *phy_provider; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + of_id = of_match_node(bcm_usb_phy_of_match, dn); + if (of_id) + version = (enum bcm_usb_phy_version)of_id->data; + else + return -ENODEV; + + ret = bcm_usb_phy_create(dev, dn, regs, version); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver bcm_usb_phy_driver = { + .driver = { + .name = "phy-bcm-sr-usb", + .of_match_table = bcm_usb_phy_of_match, + }, + .probe = bcm_usb_phy_probe, +}; +module_platform_driver(bcm_usb_phy_driver); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("Broadcom stingray USB Phy driver"); +MODULE_LICENSE("GPL v2"); -- 2.7.4