Commit 6bd5bb1e authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'phy-for-4.15_v1' of...

Merge tag 'phy-for-4.15_v1' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next

Kishon writes:

phy: for 4.15

 *) Add support in phy core to perform phy calibration
 *) Return NULL for optional PHY's even if CONFIG_GENERIC_PHY is not selected
 *) Add USB Phy driver for Broadcom STB SoCs
 *) Add support to force mediatek PHY with USB OTG function to enter
    a specific mode
 *) Calibrate rockchip-typec PHY according to docs
 *) Enable dual route feature for sun4i-usb in V3s SoC
 *) Use dr_mode dt property to enable otg capability in rcar-gen3-usb2
 *) Add driver data to specify dedicated otg pins in rcar-gen3-usb2 driver
 *) Configure the RX equalizer of brcm-sata PHY
 *) Update pcie phy settings for ti-pipe3 phy
 *) Add set_mode callback in qcom-ufs-qmp-14nm phy
 *) Use PHY callbacks in phy-qcom-ufs instead of export APIs
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parents c929d847 36914111
Broadcom STB USB PHY
Required properties:
- compatible: brcm,brcmstb-usb-phy
- reg: two offset and length pairs.
The first pair specifies a manditory set of memory mapped
registers used for general control of the PHY.
The second pair specifies optional registers used by some of
the SoCs that support USB 3.x
- #phy-cells: Shall be 1 as it expects one argument for setting
the type of the PHY. Possible values are:
- PHY_TYPE_USB2 for USB1.1/2.0 PHY
- PHY_TYPE_USB3 for USB3.x PHY
Optional Properties:
- clocks : clock phandles.
- clock-names: String, clock name.
- brcm,ipp: Boolean, Invert Port Power.
Possible values are: 0 (Don't invert), 1 (Invert)
- brcm,ioc: Boolean, Invert Over Current detection.
Possible values are: 0 (Don't invert), 1 (Invert)
NOTE: one or both of the following two properties must be set
- brcm,has-xhci: Boolean indicating the phy has an XHCI phy.
- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy.
- dr_mode: String, PHY Device mode.
Possible values are: "host", "peripheral ", "drd" or "typec-pd"
If this property is not defined, the phy will default to "host" mode.
Example:
usbphy_0: usb-phy@f0470200 {
reg = <0xf0470200 0xb8>,
<0xf0471940 0x6c0>;
compatible = "brcm,brcmstb-usb-phy";
#phy-cells = <1>;
dr_mode = "host"
brcm,ioc = <1>;
brcm,ipp = <1>;
brcm,has-xhci;
brcm,has-eohci;
clocks = <&usb20>, <&usb30>;
clock-names = "sw_usb", "sw_usb3";
};
...@@ -27,7 +27,16 @@ Sub-nodes optional properties: ...@@ -27,7 +27,16 @@ Sub-nodes optional properties:
This property is not applicable for "brcm,iproc-ns2-sata-phy", This property is not applicable for "brcm,iproc-ns2-sata-phy",
"brcm,iproc-nsp-sata-phy" and "brcm,iproc-sr-sata-phy". "brcm,iproc-nsp-sata-phy" and "brcm,iproc-sr-sata-phy".
Example: - brcm,rxaeq-mode: string that indicates the desired RX equalizer
mode, possible values are:
"off" (equivalent to not specifying the property)
"auto"
"manual" (brcm,rxaeq-value is used in that case)
- brcm,rxaeq-value: when 'rxaeq-mode' is set to "manual", provides the RX
equalizer value that should be used. Allowed range is 0..63.
Example
sata-phy@f0458100 { sata-phy@f0458100 {
compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3"; compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3";
reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>; reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>;
......
...@@ -4,10 +4,13 @@ This file provides information on what the device node for the R-Car generation ...@@ -4,10 +4,13 @@ This file provides information on what the device node for the R-Car generation
2 USB PHY contains. 2 USB PHY contains.
Required properties: Required properties:
- compatible: "renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC. - compatible: "renesas,usb-phy-r8a7743" if the device is a part of R8A7743 SoC.
"renesas,usb-phy-r8a7745" if the device is a part of R8A7745 SoC.
"renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC.
"renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC. "renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC.
"renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC. "renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC.
"renesas,rcar-gen2-usb-phy" for a generic R-Car Gen2 compatible device. "renesas,rcar-gen2-usb-phy" for a generic R-Car Gen2 or
RZ/G1 compatible device.
When compatible with the generic version, nodes must list the When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first SoC-specific version corresponding to the platform first
......
...@@ -8,6 +8,8 @@ Required properties: ...@@ -8,6 +8,8 @@ Required properties:
SoC. SoC.
"renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796 "renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
SoC. SoC.
"renesas,usb2-phy-r8a77995" if the device is a part of an
R8A77995 SoC.
"renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device. "renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.
When compatible with the generic version, nodes must list the When compatible with the generic version, nodes must list the
......
...@@ -2896,6 +2896,13 @@ S: Supported ...@@ -2896,6 +2896,13 @@ S: Supported
F: drivers/gpio/gpio-brcmstb.c F: drivers/gpio/gpio-brcmstb.c
F: Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt F: Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt
BROADCOM BRCMSTB USB2 and USB3 PHY DRIVER
M: Al Cooper <alcooperx@gmail.com>
L: linux-kernel@vger.kernel.org
L: bcm-kernel-feedback-list@broadcom.com
S: Maintained
F: drivers/phy/broadcom/phy-brcm-usb*
BROADCOM GENET ETHERNET DRIVER BROADCOM GENET ETHERNET DRIVER
M: Florian Fainelli <f.fainelli@gmail.com> M: Florian Fainelli <f.fainelli@gmail.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -594,6 +595,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) ...@@ -594,6 +595,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
int i; int i;
int phy_count; int phy_count;
struct phy **phy; struct phy **phy;
struct device_link **link;
void __iomem *base; void __iomem *base;
struct resource *res; struct resource *res;
struct dw_pcie *pci; struct dw_pcie *pci;
...@@ -649,11 +651,21 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) ...@@ -649,11 +651,21 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
if (!phy) if (!phy)
return -ENOMEM; return -ENOMEM;
link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL);
if (!link)
return -ENOMEM;
for (i = 0; i < phy_count; i++) { for (i = 0; i < phy_count; i++) {
snprintf(name, sizeof(name), "pcie-phy%d", i); snprintf(name, sizeof(name), "pcie-phy%d", i);
phy[i] = devm_phy_get(dev, name); phy[i] = devm_phy_get(dev, name);
if (IS_ERR(phy[i])) if (IS_ERR(phy[i]))
return PTR_ERR(phy[i]); return PTR_ERR(phy[i]);
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
if (!link[i]) {
ret = -EINVAL;
goto err_link;
}
} }
dra7xx->base = base; dra7xx->base = base;
...@@ -732,6 +744,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) ...@@ -732,6 +744,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
pm_runtime_disable(dev); pm_runtime_disable(dev);
dra7xx_pcie_disable_phy(dra7xx); dra7xx_pcie_disable_phy(dra7xx);
err_link:
while (--i >= 0)
device_link_del(link[i]);
return ret; return ret;
} }
......
...@@ -926,6 +926,7 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { ...@@ -926,6 +926,7 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
.phyctl_offset = REG_PHYCTL_A33, .phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true, .dedicated_clocks = true,
.enable_pmu_unk1 = true, .enable_pmu_unk1 = true,
.phy0_dual_route = true,
}; };
static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
......
...@@ -67,3 +67,16 @@ config PHY_BRCM_SATA ...@@ -67,3 +67,16 @@ config PHY_BRCM_SATA
help help
Enable this to support the Broadcom SATA PHY. Enable this to support the Broadcom SATA PHY.
If unsure, say N. If unsure, say N.
config PHY_BRCM_USB
tristate "Broadcom STB USB PHY driver"
depends on ARCH_BRCMSTB
depends on OF
select GENERIC_PHY
select SOC_BRCMSTB
default ARCH_BRCMSTB
help
Enable this to support the Broadcom STB USB PHY.
This driver is required by the USB XHCI, EHCI and OHCI
drivers.
If unsure, say N.
...@@ -5,3 +5,6 @@ obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o ...@@ -5,3 +5,6 @@ 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_PCIE) += phy-bcm-ns2-pcie.o
obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o
phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o
...@@ -49,11 +49,29 @@ enum brcm_sata_phy_version { ...@@ -49,11 +49,29 @@ enum brcm_sata_phy_version {
BRCM_SATA_PHY_IPROC_SR, BRCM_SATA_PHY_IPROC_SR,
}; };
enum brcm_sata_phy_rxaeq_mode {
RXAEQ_MODE_OFF = 0,
RXAEQ_MODE_AUTO,
RXAEQ_MODE_MANUAL,
};
static enum brcm_sata_phy_rxaeq_mode rxaeq_to_val(const char *m)
{
if (!strcmp(m, "auto"))
return RXAEQ_MODE_AUTO;
else if (!strcmp(m, "manual"))
return RXAEQ_MODE_MANUAL;
else
return RXAEQ_MODE_OFF;
}
struct brcm_sata_port { struct brcm_sata_port {
int portnum; int portnum;
struct phy *phy; struct phy *phy;
struct brcm_sata_phy *phy_priv; struct brcm_sata_phy *phy_priv;
bool ssc_en; bool ssc_en;
enum brcm_sata_phy_rxaeq_mode rxaeq_mode;
u32 rxaeq_val;
}; };
struct brcm_sata_phy { struct brcm_sata_phy {
...@@ -93,6 +111,15 @@ enum sata_phy_regs { ...@@ -93,6 +111,15 @@ enum sata_phy_regs {
TX_ACTRL0 = 0x80, TX_ACTRL0 = 0x80,
TX_ACTRL0_TXPOL_FLIP = BIT(6), TX_ACTRL0_TXPOL_FLIP = BIT(6),
AEQRX_REG_BANK_0 = 0xd0,
AEQ_CONTROL1 = 0x81,
AEQ_CONTROL1_ENABLE = BIT(2),
AEQ_CONTROL1_FREEZE = BIT(3),
AEQ_FRC_EQ = 0x83,
AEQ_FRC_EQ_FORCE = BIT(0),
AEQ_FRC_EQ_FORCE_VAL = BIT(1),
AEQRX_REG_BANK_1 = 0xe0,
OOB_REG_BANK = 0x150, OOB_REG_BANK = 0x150,
OOB1_REG_BANK = 0x160, OOB1_REG_BANK = 0x160,
OOB_CTRL1 = 0x80, OOB_CTRL1 = 0x80,
...@@ -190,7 +217,7 @@ static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) ...@@ -190,7 +217,7 @@ static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs)
#define STB_FMAX_VAL_DEFAULT 0x3df #define STB_FMAX_VAL_DEFAULT 0x3df
#define STB_FMAX_VAL_SSC 0x83 #define STB_FMAX_VAL_SSC 0x83
static int brcm_stb_sata_init(struct brcm_sata_port *port) static void brcm_stb_sata_ssc_init(struct brcm_sata_port *port)
{ {
void __iomem *base = brcm_sata_pcb_base(port); void __iomem *base = brcm_sata_pcb_base(port);
struct brcm_sata_phy *priv = port->phy_priv; struct brcm_sata_phy *priv = port->phy_priv;
...@@ -215,8 +242,45 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port) ...@@ -215,8 +242,45 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port)
brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp);
}
#define AEQ_FRC_EQ_VAL_SHIFT 2
#define AEQ_FRC_EQ_VAL_MASK 0x3f
static int brcm_stb_sata_rxaeq_init(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_pcb_base(port);
u32 tmp = 0, reg = 0;
switch (port->rxaeq_mode) {
case RXAEQ_MODE_OFF:
return 0; return 0;
case RXAEQ_MODE_AUTO:
reg = AEQ_CONTROL1;
tmp = AEQ_CONTROL1_ENABLE | AEQ_CONTROL1_FREEZE;
break;
case RXAEQ_MODE_MANUAL:
reg = AEQ_FRC_EQ;
tmp = AEQ_FRC_EQ_FORCE | AEQ_FRC_EQ_FORCE_VAL;
if (port->rxaeq_val > AEQ_FRC_EQ_VAL_MASK)
return -EINVAL;
tmp |= port->rxaeq_val << AEQ_FRC_EQ_VAL_SHIFT;
break;
}
brcm_sata_phy_wr(base, AEQRX_REG_BANK_0, reg, ~tmp, tmp);
brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, reg, ~tmp, tmp);
return 0;
}
static int brcm_stb_sata_init(struct brcm_sata_port *port)
{
brcm_stb_sata_ssc_init(port);
return brcm_stb_sata_rxaeq_init(port);
} }
/* NS2 SATA PLL1 defaults were characterized by H/W group */ /* NS2 SATA PLL1 defaults were characterized by H/W group */
...@@ -463,6 +527,7 @@ MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); ...@@ -463,6 +527,7 @@ MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
static int brcm_sata_phy_probe(struct platform_device *pdev) static int brcm_sata_phy_probe(struct platform_device *pdev)
{ {
const char *rxaeq_mode;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child; struct device_node *dn = dev->of_node, *child;
const struct of_device_id *of_id; const struct of_device_id *of_id;
...@@ -525,6 +590,13 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) ...@@ -525,6 +590,13 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
port->portnum = id; port->portnum = id;
port->phy_priv = priv; port->phy_priv = priv;
port->phy = devm_phy_create(dev, child, &phy_ops); port->phy = devm_phy_create(dev, child, &phy_ops);
port->rxaeq_mode = RXAEQ_MODE_OFF;
if (!of_property_read_string(child, "brcm,rxaeq-mode",
&rxaeq_mode))
port->rxaeq_mode = rxaeq_to_val(rxaeq_mode);
if (port->rxaeq_mode == RXAEQ_MODE_MANUAL)
of_property_read_u32(child, "brcm,rxaeq-value",
&port->rxaeq_val);
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
if (IS_ERR(port->phy)) { if (IS_ERR(port->phy)) {
dev_err(dev, "failed to create PHY\n"); dev_err(dev, "failed to create PHY\n");
......
/*
* phy-brcm-usb-init.c - Broadcom USB Phy chip specific init functions
*
* Copyright (C) 2014-2017 Broadcom
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* This module contains USB PHY initialization for power up and S3 resume
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include "phy-brcm-usb-init.h"
#define PHY_PORTS 2
#define PHY_PORT_SELECT_0 0
#define PHY_PORT_SELECT_1 0x1000
/* Register definitions for the USB CTRL block */
#define USB_CTRL_SETUP 0x00
#define USB_CTRL_SETUP_IOC_MASK 0x00000010
#define USB_CTRL_SETUP_IPP_MASK 0x00000020
#define USB_CTRL_SETUP_BABO_MASK 0x00000001
#define USB_CTRL_SETUP_FNHW_MASK 0x00000002
#define USB_CTRL_SETUP_FNBO_MASK 0x00000004
#define USB_CTRL_SETUP_WABO_MASK 0x00000008
#define USB_CTRL_SETUP_SCB_CLIENT_SWAP_MASK 0x00002000 /* option */
#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 /* option */
#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 /* option */
#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK 0X00020000 /* option */
#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK 0x00010000 /* option */
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 /* option */
#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK 0x04000000 /* option */
#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK 0x08000000 /* opt */
#define USB_CTRL_SETUP_OC3_DISABLE_MASK 0xc0000000 /* option */
#define USB_CTRL_PLL_CTL 0x04
#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK 0x08000000
#define USB_CTRL_PLL_CTL_PLL_RESETB_MASK 0x40000000
#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */
#define USB_CTRL_EBRIDGE 0x0c
#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */
#define USB_CTRL_MDIO 0x14
#define USB_CTRL_MDIO2 0x18
#define USB_CTRL_UTMI_CTL_1 0x2c
#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800
#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_P1_MASK 0x08000000
#define USB_CTRL_USB_PM 0x34
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 /* option */
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 /* option */
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK 0x40000000 /* option */
#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 /* option */
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */
#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */
#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */
#define USB_CTRL_USB30_CTL1 0x60
#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010
#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000
#define USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK 0x00020000 /* option */
#define USB_CTRL_USB30_CTL1_USB3_IOC_MASK 0x10000000 /* option */
#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK 0x20000000 /* option */
#define USB_CTRL_USB30_PCTL 0x70
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK 0x00000002
#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK 0x00020000
#define USB_CTRL_USB_DEVICE_CTL1 0x90
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 /* option */
/* Register definitions for the XHCI EC block */
#define USB_XHCI_EC_IRAADR 0x658
#define USB_XHCI_EC_IRADAT 0x65c
enum brcm_family_type {
BRCM_FAMILY_3390A0,
BRCM_FAMILY_7250B0,
BRCM_FAMILY_7271A0,
BRCM_FAMILY_7364A0,
BRCM_FAMILY_7366C0,
BRCM_FAMILY_74371A0,
BRCM_FAMILY_7439B0,
BRCM_FAMILY_7445D0,
BRCM_FAMILY_7260A0,
BRCM_FAMILY_7278A0,
BRCM_FAMILY_COUNT,
};
#define USB_BRCM_FAMILY(chip) \
[BRCM_FAMILY_##chip] = __stringify(chip)
static const char *family_names[BRCM_FAMILY_COUNT] = {
USB_BRCM_FAMILY(3390A0),
USB_BRCM_FAMILY(7250B0),
USB_BRCM_FAMILY(7271A0),
USB_BRCM_FAMILY(7364A0),
USB_BRCM_FAMILY(7366C0),
USB_BRCM_FAMILY(74371A0),
USB_BRCM_FAMILY(7439B0),
USB_BRCM_FAMILY(7445D0),
USB_BRCM_FAMILY(7260A0),
USB_BRCM_FAMILY(7278A0),
};
enum {
USB_CTRL_SETUP_SCB1_EN_SELECTOR,
USB_CTRL_SETUP_SCB2_EN_SELECTOR,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR,
USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR,
USB_CTRL_SETUP_OC3_DISABLE_SELECTOR,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR,
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_SELECTOR,
USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR,
USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR,
USB_CTRL_USB_PM_USB_PWRDN_SELECTOR,
USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_SELECTOR,
USB_CTRL_USB30_CTL1_USB3_IOC_SELECTOR,
USB_CTRL_USB30_CTL1_USB3_IPP_SELECTOR,
USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR,
USB_CTRL_USB_PM_SOFT_RESET_SELECTOR,
USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_SELECTOR,
USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_SELECTOR,
USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR,
USB_CTRL_SETUP_ENDIAN_SELECTOR,
USB_CTRL_SELECTOR_COUNT,
};
#define USB_CTRL_REG(base, reg) ((void *)base + USB_CTRL_##reg)
#define USB_XHCI_EC_REG(base, reg) ((void *)base + USB_XHCI_EC_##reg)
#define USB_CTRL_MASK(reg, field) \
USB_CTRL_##reg##_##field##_MASK
#define USB_CTRL_MASK_FAMILY(params, reg, field) \
(params->usb_reg_bits_map[USB_CTRL_##reg##_##field##_SELECTOR])
#define USB_CTRL_SET_FAMILY(params, reg, field) \
usb_ctrl_set_family(params, USB_CTRL_##reg, \
USB_CTRL_##reg##_##field##_SELECTOR)
#define USB_CTRL_UNSET_FAMILY(params, reg, field) \
usb_ctrl_unset_family(params, USB_CTRL_##reg, \
USB_CTRL_##reg##_##field##_SELECTOR)
#define USB_CTRL_SET(base, reg, field) \
usb_ctrl_set(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
#define USB_CTRL_UNSET(base, reg, field) \
usb_ctrl_unset(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
#define MDIO_USB2 0
#define MDIO_USB3 BIT(31)
#define USB_CTRL_SETUP_ENDIAN_BITS ( \
USB_CTRL_MASK(SETUP, BABO) | \
USB_CTRL_MASK(SETUP, FNHW) | \
USB_CTRL_MASK(SETUP, FNBO) | \
USB_CTRL_MASK(SETUP, WABO))
#ifdef __LITTLE_ENDIAN
#define ENDIAN_SETTINGS ( \
USB_CTRL_MASK(SETUP, BABO) | \
USB_CTRL_MASK(SETUP, FNHW))
#else
#define ENDIAN_SETTINGS ( \
USB_CTRL_MASK(SETUP, FNHW) | \
USB_CTRL_MASK(SETUP, FNBO) | \
USB_CTRL_MASK(SETUP, WABO))
#endif
struct id_to_type {
u32 id;
int type;
};
static const struct id_to_type id_to_type_table[] = {
{ 0x33900000, BRCM_FAMILY_3390A0 },
{ 0x72500010, BRCM_FAMILY_7250B0 },
{ 0x72600000, BRCM_FAMILY_7260A0 },
{ 0x72680000, BRCM_FAMILY_7271A0 },
{ 0x72710000, BRCM_FAMILY_7271A0 },
{ 0x73640000, BRCM_FAMILY_7364A0 },
{ 0x73660020, BRCM_FAMILY_7366C0 },
{ 0x07437100, BRCM_FAMILY_74371A0 },
{ 0x74390010, BRCM_FAMILY_7439B0 },
{ 0x74450030, BRCM_FAMILY_7445D0 },
{ 0x72780000, BRCM_FAMILY_7278A0 },
{ 0, BRCM_FAMILY_7271A0 }, /* default */
};
static const u32
usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
/* 3390B0 */
[BRCM_FAMILY_3390A0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_USB_PWRDN_MASK,
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK,
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7250b0 */
[BRCM_FAMILY_7250B0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
USB_CTRL_SETUP_OC3_DISABLE_MASK,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK,
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
USB_CTRL_USB_PM_USB20_HC_RESETB_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7271a0 */
[BRCM_FAMILY_7271A0] = {
0, /* USB_CTRL_SETUP_SCB1_EN_MASK */
0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_USB_PWRDN_MASK,
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK,
USB_CTRL_USB_PM_SOFT_RESET_MASK,
USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK,
USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK,
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7364a0 */
[BRCM_FAMILY_7364A0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
USB_CTRL_SETUP_OC3_DISABLE_MASK,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK,
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
USB_CTRL_USB_PM_USB20_HC_RESETB_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7366c0 */
[BRCM_FAMILY_7366C0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK,
USB_CTRL_USB_PM_USB_PWRDN_MASK,
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
USB_CTRL_USB_PM_USB20_HC_RESETB_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 74371A0 */
[BRCM_FAMILY_74371A0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK,
USB_CTRL_USB30_CTL1_USB3_IOC_MASK,
USB_CTRL_USB30_CTL1_USB3_IPP_MASK,
0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_MASK */
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7439B0 */
[BRCM_FAMILY_7439B0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_USB_PWRDN_MASK,
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK,
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7445d0 */
[BRCM_FAMILY_7445D0] = {
USB_CTRL_SETUP_SCB1_EN_MASK,
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
USB_CTRL_SETUP_OC3_DISABLE_MASK,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */
USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK,
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */
0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7260a0 */
[BRCM_FAMILY_7260A0] = {
0, /* USB_CTRL_SETUP_SCB1_EN_MASK */
0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_USB_PWRDN_MASK,
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK,
USB_CTRL_USB_PM_SOFT_RESET_MASK,
USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK,
USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK,
USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK,
ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */
},
/* 7278a0 */
[BRCM_FAMILY_7278A0] = {
0, /* USB_CTRL_SETUP_SCB1_EN_MASK */
0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
0, /*USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK,
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK,
USB_CTRL_USB_PM_USB_PWRDN_MASK,
0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */
0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */
USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK,
USB_CTRL_USB_PM_SOFT_RESET_MASK,
0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */
0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */
0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_MASK */
0, /* USB_CTRL_SETUP ENDIAN bits */
},
};
static inline u32 brcmusb_readl(void __iomem *addr)
{
return readl(addr);
}
static inline void brcmusb_writel(u32 val, void __iomem *addr)
{
writel(val, addr);
}
static inline
void usb_ctrl_unset_family(struct brcm_usb_init_params *params,
u32 reg_offset, u32 field)
{
u32 mask;
void *reg;
mask = params->usb_reg_bits_map[field];
reg = params->ctrl_regs + reg_offset;
brcmusb_writel(brcmusb_readl(reg) & ~mask, reg);
};
static inline
void usb_ctrl_set_family(struct brcm_usb_init_params *params,
u32 reg_offset, u32 field)
{
u32 mask;
void *reg;
mask = params->usb_reg_bits_map[field];
reg = params->ctrl_regs + reg_offset;
brcmusb_writel(brcmusb_readl(reg) | mask, reg);
};
static inline void usb_ctrl_set(void __iomem *reg, u32 field)
{
u32 value;
value = brcmusb_readl(reg);
brcmusb_writel(value | field, reg);
}
static inline void usb_ctrl_unset(void __iomem *reg, u32 field)
{
u32 value;
value = brcmusb_readl(reg);
brcmusb_writel(value & ~field, reg);
}
static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode)
{
u32 data;
data = (reg << 16) | mode;
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data |= (1 << 24);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data &= ~(1 << 24);
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
return brcmusb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
}
static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
u32 val, int mode)
{
u32 data;
data = (reg << 16) | val | mode;
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data |= (1 << 25);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data &= ~(1 << 25);
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
}
static void brcmusb_usb_phy_ldo_fix(void __iomem *ctrl_base)
{
/* first disable FSM but also leave it that way */
/* to allow normal suspend/resume */
USB_CTRL_UNSET(ctrl_base, UTMI_CTL_1, POWER_UP_FSM_EN);
USB_CTRL_UNSET(ctrl_base, UTMI_CTL_1, POWER_UP_FSM_EN_P1);
/* reset USB 2.0 PLL */
USB_CTRL_UNSET(ctrl_base, PLL_CTL, PLL_RESETB);
/* PLL reset period */
udelay(1);
USB_CTRL_SET(ctrl_base, PLL_CTL, PLL_RESETB);
/* Give PLL enough time to lock */
usleep_range(1000, 2000);
}
static void brcmusb_usb2_eye_fix(void __iomem *ctrl_base)
{
/* Increase USB 2.0 TX level to meet spec requirement */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x80a0, MDIO_USB2);
brcmusb_usb_mdio_write(ctrl_base, 0x0a, 0xc6a0, MDIO_USB2);
}
static void brcmusb_usb3_pll_fix(void __iomem *ctrl_base)
{
/* Set correct window for PLL lock detect */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8000, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x07, 0x1503, MDIO_USB3);
}
static void brcmusb_usb3_enable_pipe_reset(void __iomem *ctrl_base)
{
u32 val;
/* Re-enable USB 3.0 pipe reset */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8000, MDIO_USB3);
val = brcmusb_usb_mdio_read(ctrl_base, 0x0f, MDIO_USB3) | 0x200;
brcmusb_usb_mdio_write(ctrl_base, 0x0f, val, MDIO_USB3);
}
static void brcmusb_usb3_enable_sigdet(void __iomem *ctrl_base)
{
u32 val, ofs;
int ii;
ofs = 0;
for (ii = 0; ii < PHY_PORTS; ++ii) {
/* Set correct default for sigdet */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8080 + ofs),
MDIO_USB3);
val = brcmusb_usb_mdio_read(ctrl_base, 0x05, MDIO_USB3);
val = (val & ~0x800f) | 0x800d;
brcmusb_usb_mdio_write(ctrl_base, 0x05, val, MDIO_USB3);
ofs = PHY_PORT_SELECT_1;
}
}
static void brcmusb_usb3_enable_skip_align(void __iomem *ctrl_base)
{
u32 val, ofs;
int ii;
ofs = 0;
for (ii = 0; ii < PHY_PORTS; ++ii) {
/* Set correct default for SKIP align */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8060 + ofs),
MDIO_USB3);
val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3) | 0x200;
brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3);
ofs = PHY_PORT_SELECT_1;
}
}
static void brcmusb_usb3_unfreeze_aeq(void __iomem *ctrl_base)
{
u32 val, ofs;
int ii;
ofs = 0;
for (ii = 0; ii < PHY_PORTS; ++ii) {
/* Let EQ freeze after TSEQ */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x80e0 + ofs),
MDIO_USB3);
val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3);
val &= ~0x0008;
brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3);
ofs = PHY_PORT_SELECT_1;
}
}
static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params)
{
u32 ofs;
int ii;
void __iomem *ctrl_base = params->ctrl_regs;
/*
* On newer B53 based SoC's, the reference clock for the
* 3.0 PLL has been changed from 50MHz to 54MHz so the
* PLL needs to be reprogrammed.
* See SWLINUX-4006.
*
* On the 7364C0, the reference clock for the
* 3.0 PLL has been changed from 50MHz to 54MHz to
* work around a MOCA issue.
* See SWLINUX-4169.
*/
switch (params->selected_family) {
case BRCM_FAMILY_3390A0:
case BRCM_FAMILY_7250B0:
case BRCM_FAMILY_7366C0:
case BRCM_FAMILY_74371A0:
case BRCM_FAMILY_7439B0:
case BRCM_FAMILY_7445D0:
case BRCM_FAMILY_7260A0:
return;
case BRCM_FAMILY_7364A0:
if (BRCM_REV(params->family_id) < 0x20)
return;
break;
}
/* set USB 3.0 PLL to accept 54Mhz reference clock */
USB_CTRL_UNSET(ctrl_base, USB30_CTL1, PHY3_PLL_SEQ_START);
brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8000, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x10, 0x5784, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x11, 0x01d0, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x12, 0x1DE8, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x13, 0xAA80, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x14, 0x8826, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x15, 0x0044, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x16, 0x8000, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x17, 0x0851, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x18, 0x0000, MDIO_USB3);
/* both ports */
ofs = 0;
for (ii = 0; ii < PHY_PORTS; ++ii) {
brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8040 + ofs),
MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x03, 0x0090, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x04, 0x0134, MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8020 + ofs),
MDIO_USB3);
brcmusb_usb_mdio_write(ctrl_base, 0x01, 0x00e2, MDIO_USB3);
ofs = PHY_PORT_SELECT_1;
}
/* restart PLL sequence */
USB_CTRL_SET(ctrl_base, USB30_CTL1, PHY3_PLL_SEQ_START);
/* Give PLL enough time to lock */
usleep_range(1000, 2000);
}
static void brcmusb_usb3_ssc_enable(void __iomem *ctrl_base)
{
u32 val;
/* Enable USB 3.0 TX spread spectrum */
brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8040, MDIO_USB3);
val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3) | 0xf;
brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3);
/* Currently, USB 3.0 SSC is enabled via port 0 MDIO registers,
* which should have been adequate. However, due to a bug in the
* USB 3.0 PHY, it must be enabled via both ports (HWUSB3DVT-26).
*/
brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x9040, MDIO_USB3);
val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3) | 0xf;
brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3);
}
static void brcmusb_usb3_phy_workarounds(struct brcm_usb_init_params *params)
{
void __iomem *ctrl_base = params->ctrl_regs;
brcmusb_usb3_pll_fix(ctrl_base);
brcmusb_usb3_pll_54mhz(params);
brcmusb_usb3_ssc_enable(ctrl_base);
brcmusb_usb3_enable_pipe_reset(ctrl_base);
brcmusb_usb3_enable_sigdet(ctrl_base);
brcmusb_usb3_enable_skip_align(ctrl_base);
brcmusb_usb3_unfreeze_aeq(ctrl_base);
}
static void brcmusb_memc_fix(struct brcm_usb_init_params *params)
{
u32 prid;
if (params->selected_family != BRCM_FAMILY_7445D0)
return;
/*
* This is a workaround for HW7445-1869 where a DMA write ends up
* doing a read pre-fetch after the end of the DMA buffer. This
* causes a problem when the DMA buffer is at the end of physical
* memory, causing the pre-fetch read to access non-existent memory,
* and the chip bondout has MEMC2 disabled. When the pre-fetch read
* tries to use the disabled MEMC2, it hangs the bus. The workaround
* is to disable MEMC2 access in the usb controller which avoids
* the hang.
*/
prid = params->product_id & 0xfffff000;
switch (prid) {
case 0x72520000:
case 0x74480000:
case 0x74490000:
case 0x07252000:
case 0x07448000:
case 0x07449000:
USB_CTRL_UNSET_FAMILY(params, SETUP, SCB2_EN);
}
}
static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params)
{
void __iomem *xhci_ec_base = params->xhci_ec_regs;
u32 val;
if (params->family_id != 0x74371000 || xhci_ec_base == 0)
return;
brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
/* set cfg_pick_ss_lock */
val |= (1 << 27);
brcmusb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
/* Reset USB 3.0 PHY for workaround to take effect */
USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
USB_CTRL_SET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
}
static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
int on_off)
{
/* Assert reset */
if (on_off) {
if (USB_CTRL_MASK_FAMILY(params, USB_PM, XHC_SOFT_RESETB))
USB_CTRL_UNSET_FAMILY(params, USB_PM, XHC_SOFT_RESETB);
else
USB_CTRL_UNSET_FAMILY(params,
USB30_CTL1, XHC_SOFT_RESETB);
} else { /* De-assert reset */
if (USB_CTRL_MASK_FAMILY(params, USB_PM, XHC_SOFT_RESETB))
USB_CTRL_SET_FAMILY(params, USB_PM, XHC_SOFT_RESETB);
else
USB_CTRL_SET_FAMILY(params, USB30_CTL1,
XHC_SOFT_RESETB);
}
}
/*
* Return the best map table family. The order is:
* - exact match of chip and major rev
* - exact match of chip and closest older major rev
* - default chip/rev.
* NOTE: The minor rev is always ignored.
*/
static enum brcm_family_type brcmusb_get_family_type(
struct brcm_usb_init_params *params)
{
int last_type = -1;
u32 last_family = 0;
u32 family_no_major;
unsigned int x;
u32 family;
family = params->family_id & 0xfffffff0;
family_no_major = params->family_id & 0xffffff00;
for (x = 0; id_to_type_table[x].id; x++) {
if (family == id_to_type_table[x].id)
return id_to_type_table[x].type;
if (family_no_major == (id_to_type_table[x].id & 0xffffff00))
if (family > id_to_type_table[x].id &&
last_family < id_to_type_table[x].id) {
last_family = id_to_type_table[x].id;
last_type = id_to_type_table[x].type;
}
}
/* If no match, return the default family */
if (last_type == -1)
return id_to_type_table[x].type;
return last_type;
}
void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg;
u32 orig_reg;
/* Starting with the 7445d0, there are no longer separate 3.0
* versions of IOC and IPP.
*/
if (USB_CTRL_MASK_FAMILY(params, USB30_CTL1, USB3_IOC)) {
if (params->ioc)
USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IOC);
if (params->ipp == 1)
USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IPP);
}
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
orig_reg = reg;
if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL))
/* Never use the strap, it's going away. */
reg &= ~(USB_CTRL_MASK_FAMILY(params,
SETUP,
STRAP_CC_DRD_MODE_ENABLE_SEL));
if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL))
if (params->ipp != 2)
/* override ipp strap pin (if it exits) */
reg &= ~(USB_CTRL_MASK_FAMILY(params, SETUP,
STRAP_IPP_SEL));
/* Override the default OC and PP polarity */
reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
if (params->ioc)
reg |= USB_CTRL_MASK(SETUP, IOC);
if (params->ipp == 1 && ((reg & USB_CTRL_MASK(SETUP, IPP)) == 0))
reg |= USB_CTRL_MASK(SETUP, IPP);
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
/*
* If we're changing IPP, make sure power is off long enough
* to turn off any connected devices.
*/
if (reg != orig_reg)
msleep(50);
}
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg = 0;
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
}
return reg;
}
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
int mode)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg;
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
reg |= mode;
brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
}
void brcm_usb_init_common(struct brcm_usb_init_params *params)
{
u32 reg;
void __iomem *ctrl = params->ctrl_regs;
/* Take USB out of power down */
if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) {
USB_CTRL_UNSET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
/* 1 millisecond - for USB clocks to settle down */
usleep_range(1000, 2000);
}
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN)) {
USB_CTRL_UNSET_FAMILY(params, USB_PM, USB_PWRDN);
/* 1 millisecond - for USB clocks to settle down */
usleep_range(1000, 2000);
}
if (params->selected_family != BRCM_FAMILY_74371A0 &&
(BRCM_ID(params->family_id) != 0x7364))
/*
* HW7439-637: 7439a0 and its derivatives do not have large
* enough descriptor storage for this.
*/
USB_CTRL_SET_FAMILY(params, SETUP, SS_EHCI64BIT_EN);
/* Block auto PLL suspend by USB2 PHY (Sasi) */
USB_CTRL_SET(ctrl, PLL_CTL, PLL_SUSPEND_EN);
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
if (params->selected_family == BRCM_FAMILY_7364A0)
/* Suppress overcurrent indication from USB30 ports for A0 */
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, OC3_DISABLE);
brcmusb_usb_phy_ldo_fix(ctrl);
brcmusb_usb2_eye_fix(ctrl);
/*
* Make sure the the second and third memory controller
* interfaces are enabled if they exist.
*/
if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN))
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN);
if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN))
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN);
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
brcmusb_memc_fix(params);
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
reg |= params->mode;
brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) {
switch (params->mode) {
case USB_CTLR_MODE_HOST:
USB_CTRL_UNSET_FAMILY(params, USB_PM, BDC_SOFT_RESETB);
break;
default:
USB_CTRL_SET_FAMILY(params, USB_PM, BDC_SOFT_RESETB);
break;
}
}
if (USB_CTRL_MASK_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE)) {
if (params->mode == USB_CTLR_MODE_TYPEC_PD)
USB_CTRL_SET_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE);
else
USB_CTRL_UNSET_FAMILY(params, SETUP,
CC_DRD_MODE_ENABLE);
}
}
void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
{
u32 reg;
void __iomem *ctrl = params->ctrl_regs;
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
USB_CTRL_SET_FAMILY(params, USB_PM, USB20_HC_RESETB);
if (params->selected_family == BRCM_FAMILY_7366C0)
/*
* Don't enable this so the memory controller doesn't read
* into memory holes. NOTE: This bit is low true on 7366C0.
*/
USB_CTRL_SET_FAMILY(params, EBRIDGE, ESTOP_SCB_REQ);
/* Setup the endian bits */
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
reg &= ~USB_CTRL_SETUP_ENDIAN_BITS;
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN);
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
}
void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
if (BRCM_ID(params->family_id) == 0x7366) {
/*
* The PHY3_SOFT_RESETB bits default to the wrong state.
*/
USB_CTRL_SET(ctrl, USB30_PCTL, PHY3_SOFT_RESETB);
USB_CTRL_SET(ctrl, USB30_PCTL, PHY3_SOFT_RESETB_P1);
}
/*
* Kick start USB3 PHY
* Make sure it's low to insure a rising edge.
*/
USB_CTRL_UNSET(ctrl, USB30_CTL1, PHY3_PLL_SEQ_START);
USB_CTRL_SET(ctrl, USB30_CTL1, PHY3_PLL_SEQ_START);
brcmusb_usb3_phy_workarounds(params);
brcmusb_xhci_soft_reset(params, 0);
brcmusb_usb3_otp_fix(params);
}
void brcm_usb_uninit_common(struct brcm_usb_init_params *params)
{
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN))
USB_CTRL_SET_FAMILY(params, USB_PM, USB_PWRDN);
if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN))
USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
}
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params)
{
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
USB_CTRL_UNSET_FAMILY(params, USB_PM, USB20_HC_RESETB);
}
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params)
{
brcmusb_xhci_soft_reset(params, 1);
}
void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
{
int fam;
fam = brcmusb_get_family_type(params);
params->selected_family = fam;
params->usb_reg_bits_map =
&usb_reg_bits_map_table[fam][0];
params->family_name = family_names[fam];
}
/*
* Copyright (C) 2014-2017 Broadcom
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _USB_BRCM_COMMON_INIT_H
#define _USB_BRCM_COMMON_INIT_H
#define USB_CTLR_MODE_HOST 0
#define USB_CTLR_MODE_DEVICE 1
#define USB_CTLR_MODE_DRD 2
#define USB_CTLR_MODE_TYPEC_PD 3
struct brcm_usb_init_params;
struct brcm_usb_init_params {
void __iomem *ctrl_regs;
void __iomem *xhci_ec_regs;
int ioc;
int ipp;
int mode;
u32 family_id;
u32 product_id;
int selected_family;
const char *family_name;
const u32 *usb_reg_bits_map;
};
void brcm_usb_set_family_map(struct brcm_usb_init_params *params);
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params);
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
int mode);
void brcm_usb_init_ipp(struct brcm_usb_init_params *ini);
void brcm_usb_init_common(struct brcm_usb_init_params *ini);
void brcm_usb_init_eohci(struct brcm_usb_init_params *ini);
void brcm_usb_init_xhci(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
#endif /* _USB_BRCM_COMMON_INIT_H */
/*
* phy-brcm-usb.c - Broadcom USB Phy Driver
*
* Copyright (C) 2015-2017 Broadcom
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include <dt-bindings/phy/phy.h>
#include "phy-brcm-usb-init.h"
static DEFINE_MUTEX(sysfs_lock);
enum brcm_usb_phy_id {
BRCM_USB_PHY_2_0 = 0,
BRCM_USB_PHY_3_0,
BRCM_USB_PHY_ID_MAX
};
struct value_to_name_map {
int value;
const char *name;
};
static struct value_to_name_map brcm_dr_mode_to_name[] = {
{ USB_CTLR_MODE_HOST, "host" },
{ USB_CTLR_MODE_DEVICE, "peripheral" },
{ USB_CTLR_MODE_DRD, "drd" },
{ USB_CTLR_MODE_TYPEC_PD, "typec-pd" }
};
static struct value_to_name_map brcm_dual_mode_to_name[] = {
{ 0, "host" },
{ 1, "device" },
{ 2, "auto" },
};
struct brcm_usb_phy {
struct phy *phy;
unsigned int id;
bool inited;
};
struct brcm_usb_phy_data {
struct brcm_usb_init_params ini;
bool has_eohci;
bool has_xhci;
struct clk *usb_20_clk;
struct clk *usb_30_clk;
struct mutex mutex; /* serialize phy init */
int init_count;
struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
};
static int brcm_usb_phy_init(struct phy *gphy)
{
struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
struct brcm_usb_phy_data *priv =
container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
/*
* Use a lock to make sure a second caller waits until
* the base phy is inited before using it.
*/
mutex_lock(&priv->mutex);
if (priv->init_count++ == 0) {
clk_enable(priv->usb_20_clk);
clk_enable(priv->usb_30_clk);
brcm_usb_init_common(&priv->ini);
}
mutex_unlock(&priv->mutex);
if (phy->id == BRCM_USB_PHY_2_0)
brcm_usb_init_eohci(&priv->ini);
else if (phy->id == BRCM_USB_PHY_3_0)
brcm_usb_init_xhci(&priv->ini);
phy->inited = true;
dev_dbg(&gphy->dev, "INIT, id: %d, total: %d\n", phy->id,
priv->init_count);
return 0;
}
static int brcm_usb_phy_exit(struct phy *gphy)
{
struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
struct brcm_usb_phy_data *priv =
container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
dev_dbg(&gphy->dev, "EXIT\n");
if (phy->id == BRCM_USB_PHY_2_0)
brcm_usb_uninit_eohci(&priv->ini);
if (phy->id == BRCM_USB_PHY_3_0)
brcm_usb_uninit_xhci(&priv->ini);
/* If both xhci and eohci are gone, reset everything else */
mutex_lock(&priv->mutex);
if (--priv->init_count == 0) {
brcm_usb_uninit_common(&priv->ini);
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
}
mutex_unlock(&priv->mutex);
phy->inited = false;
return 0;
}
static struct phy_ops brcm_usb_phy_ops = {
.init = brcm_usb_phy_init,
.exit = brcm_usb_phy_exit,
.owner = THIS_MODULE,
};
static struct phy *brcm_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct brcm_usb_phy_data *data = dev_get_drvdata(dev);
/*
* values 0 and 1 are for backward compatibility with
* device tree nodes from older bootloaders.
*/
switch (args->args[0]) {
case 0:
case PHY_TYPE_USB2:
if (data->phys[BRCM_USB_PHY_2_0].phy)
return data->phys[BRCM_USB_PHY_2_0].phy;
dev_warn(dev, "Error, 2.0 Phy not found\n");
break;
case 1:
case PHY_TYPE_USB3:
if (data->phys[BRCM_USB_PHY_3_0].phy)
return data->phys[BRCM_USB_PHY_3_0].phy;
dev_warn(dev, "Error, 3.0 Phy not found\n");
break;
}
return ERR_PTR(-ENODEV);
}
static int name_to_value(struct value_to_name_map *table, int count,
const char *name, int *value)
{
int x;
*value = 0;
for (x = 0; x < count; x++) {
if (sysfs_streq(name, table[x].name)) {
*value = x;
return 0;
}
}
return -EINVAL;
}
static const char *value_to_name(struct value_to_name_map *table, int count,
int value)
{
if (value >= count)
return "unknown";
return table[value].name;
}
static ssize_t dr_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
return sprintf(buf, "%s\n",
value_to_name(&brcm_dr_mode_to_name[0],
ARRAY_SIZE(brcm_dr_mode_to_name),
priv->ini.mode));
}
static DEVICE_ATTR_RO(dr_mode);
static ssize_t dual_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
int value;
int res;
mutex_lock(&sysfs_lock);
res = name_to_value(&brcm_dual_mode_to_name[0],
ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
if (!res) {
brcm_usb_init_set_dual_select(&priv->ini, value);
res = len;
}
mutex_unlock(&sysfs_lock);
return res;
}
static ssize_t dual_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
int value;
mutex_lock(&sysfs_lock);
value = brcm_usb_init_get_dual_select(&priv->ini);
mutex_unlock(&sysfs_lock);
return sprintf(buf, "%s\n",
value_to_name(&brcm_dual_mode_to_name[0],
ARRAY_SIZE(brcm_dual_mode_to_name),
value));
}
static DEVICE_ATTR_RW(dual_select);
static struct attribute *brcm_usb_phy_attrs[] = {
&dev_attr_dr_mode.attr,
&dev_attr_dual_select.attr,
NULL
};
static const struct attribute_group brcm_usb_phy_group = {
.attrs = brcm_usb_phy_attrs,
};
static int brcm_usb_phy_dvr_init(struct device *dev,
struct brcm_usb_phy_data *priv,
struct device_node *dn)
{
struct phy *gphy;
int err;
priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
if (IS_ERR(priv->usb_20_clk)) {
dev_info(dev, "Clock not found in Device Tree\n");
priv->usb_20_clk = NULL;
}
err = clk_prepare_enable(priv->usb_20_clk);
if (err)
return err;
if (priv->has_eohci) {
gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops);
if (IS_ERR(gphy)) {
dev_err(dev, "failed to create EHCI/OHCI PHY\n");
return PTR_ERR(gphy);
}
priv->phys[BRCM_USB_PHY_2_0].phy = gphy;
priv->phys[BRCM_USB_PHY_2_0].id = BRCM_USB_PHY_2_0;
phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_2_0]);
}
if (priv->has_xhci) {
gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops);
if (IS_ERR(gphy)) {
dev_err(dev, "failed to create XHCI PHY\n");
return PTR_ERR(gphy);
}
priv->phys[BRCM_USB_PHY_3_0].phy = gphy;
priv->phys[BRCM_USB_PHY_3_0].id = BRCM_USB_PHY_3_0;
phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_3_0]);
priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3");
if (IS_ERR(priv->usb_30_clk)) {
dev_info(dev,
"USB3.0 clock not found in Device Tree\n");
priv->usb_30_clk = NULL;
}
err = clk_prepare_enable(priv->usb_30_clk);
if (err)
return err;
}
return 0;
}
static int brcm_usb_phy_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
struct brcm_usb_phy_data *priv;
struct phy_provider *phy_provider;
struct device_node *dn = pdev->dev.of_node;
int err;
const char *mode;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->ini.family_id = brcmstb_get_family_id();
priv->ini.product_id = brcmstb_get_product_id();
brcm_usb_set_family_map(&priv->ini);
dev_dbg(dev, "Best mapping table is for %s\n",
priv->ini.family_name);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "can't get USB_CTRL base address\n");
return -EINVAL;
}
priv->ini.ctrl_regs = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->ini.ctrl_regs)) {
dev_err(dev, "can't map CTRL register space\n");
return -EINVAL;
}
/* The XHCI EC registers are optional */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
priv->ini.xhci_ec_regs =
devm_ioremap_resource(dev, res);
if (IS_ERR(priv->ini.xhci_ec_regs)) {
dev_err(dev, "can't map XHCI EC register space\n");
return -EINVAL;
}
}
of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp);
of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc);
priv->ini.mode = USB_CTLR_MODE_HOST;
err = of_property_read_string(dn, "dr_mode", &mode);
if (err == 0) {
name_to_value(&brcm_dr_mode_to_name[0],
ARRAY_SIZE(brcm_dr_mode_to_name),
mode, &priv->ini.mode);
}
if (of_property_read_bool(dn, "brcm,has_xhci"))
priv->has_xhci = true;
if (of_property_read_bool(dn, "brcm,has_eohci"))
priv->has_eohci = true;
err = brcm_usb_phy_dvr_init(dev, priv, dn);
if (err)
return err;
mutex_init(&priv->mutex);
/* make sure invert settings are correct */
brcm_usb_init_ipp(&priv->ini);
/*
* Create sysfs entries for mode.
* Remove "dual_select" attribute if not in dual mode
*/
if (priv->ini.mode != USB_CTLR_MODE_DRD)
brcm_usb_phy_attrs[1] = NULL;
err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
if (err)
dev_warn(dev, "Error creating sysfs attributes\n");
/* start with everything off */
if (priv->has_xhci)
brcm_usb_uninit_xhci(&priv->ini);
if (priv->has_eohci)
brcm_usb_uninit_eohci(&priv->ini);
brcm_usb_uninit_common(&priv->ini);
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int brcm_usb_phy_suspend(struct device *dev)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
if (priv->init_count) {
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
}
return 0;
}
static int brcm_usb_phy_resume(struct device *dev)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
clk_enable(priv->usb_20_clk);
clk_enable(priv->usb_30_clk);
brcm_usb_init_ipp(&priv->ini);
/*
* Initialize anything that was previously initialized.
* Uninitialize anything that wasn't previously initialized.
*/
if (priv->init_count) {
brcm_usb_init_common(&priv->ini);
if (priv->phys[BRCM_USB_PHY_2_0].inited) {
brcm_usb_init_eohci(&priv->ini);
} else if (priv->has_eohci) {
brcm_usb_uninit_eohci(&priv->ini);
clk_disable(priv->usb_20_clk);
}
if (priv->phys[BRCM_USB_PHY_3_0].inited) {
brcm_usb_init_xhci(&priv->ini);
} else if (priv->has_xhci) {
brcm_usb_uninit_xhci(&priv->ini);
clk_disable(priv->usb_30_clk);
}
} else {
if (priv->has_xhci)
brcm_usb_uninit_xhci(&priv->ini);
if (priv->has_eohci)
brcm_usb_uninit_eohci(&priv->ini);
brcm_usb_uninit_common(&priv->ini);
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
}
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops brcm_usb_phy_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume)
};
static const struct of_device_id brcm_usb_dt_ids[] = {
{ .compatible = "brcm,brcmstb-usb-phy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids);
static struct platform_driver brcm_usb_driver = {
.probe = brcm_usb_phy_probe,
.driver = {
.name = "brcmstb-usb-phy",
.owner = THIS_MODULE,
.pm = &brcm_usb_phy_pm_ops,
.of_match_table = brcm_usb_dt_ids,
},
};
module_platform_driver(brcm_usb_driver);
MODULE_ALIAS("platform:brcmstb-usb-phy");
MODULE_AUTHOR("Al Cooper <acooper@broadcom.com>");
MODULE_DESCRIPTION("BRCM USB PHY driver");
MODULE_LICENSE("GPL v2");
...@@ -154,7 +154,6 @@ struct mvebu_comphy_priv { ...@@ -154,7 +154,6 @@ struct mvebu_comphy_priv {
void __iomem *base; void __iomem *base;
struct regmap *regmap; struct regmap *regmap;
struct device *dev; struct device *dev;
int modes[MVEBU_COMPHY_LANES];
}; };
struct mvebu_comphy_lane { struct mvebu_comphy_lane {
......
...@@ -96,9 +96,11 @@ ...@@ -96,9 +96,11 @@
#define U3P_U2PHYDTM1 0x06C #define U3P_U2PHYDTM1 0x06C
#define P2C_RG_UART_EN BIT(16) #define P2C_RG_UART_EN BIT(16)
#define P2C_FORCE_IDDIG BIT(9)
#define P2C_RG_VBUSVALID BIT(5) #define P2C_RG_VBUSVALID BIT(5)
#define P2C_RG_SESSEND BIT(4) #define P2C_RG_SESSEND BIT(4)
#define P2C_RG_AVALID BIT(2) #define P2C_RG_AVALID BIT(2)
#define P2C_RG_IDDIG BIT(1)
#define U3P_U3_CHIP_GPIO_CTLD 0x0c #define U3P_U3_CHIP_GPIO_CTLD 0x0c
#define P3C_REG_IP_SW_RST BIT(31) #define P3C_REG_IP_SW_RST BIT(31)
...@@ -585,6 +587,31 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy, ...@@ -585,6 +587,31 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy,
} }
} }
static void u2_phy_instance_set_mode(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance,
enum phy_mode mode)
{
struct u2phy_banks *u2_banks = &instance->u2_banks;
u32 tmp;
tmp = readl(u2_banks->com + U3P_U2PHYDTM1);
switch (mode) {
case PHY_MODE_USB_DEVICE:
tmp |= P2C_FORCE_IDDIG | P2C_RG_IDDIG;
break;
case PHY_MODE_USB_HOST:
tmp |= P2C_FORCE_IDDIG;
tmp &= ~P2C_RG_IDDIG;
break;
case PHY_MODE_USB_OTG:
tmp &= ~(P2C_FORCE_IDDIG | P2C_RG_IDDIG);
break;
default:
return;
}
writel(tmp, u2_banks->com + U3P_U2PHYDTM1);
}
static void pcie_phy_instance_init(struct mtk_tphy *tphy, static void pcie_phy_instance_init(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance) struct mtk_phy_instance *instance)
{ {
...@@ -881,6 +908,17 @@ static int mtk_phy_exit(struct phy *phy) ...@@ -881,6 +908,17 @@ static int mtk_phy_exit(struct phy *phy)
return 0; return 0;
} }
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent);
if (instance->type == PHY_TYPE_USB2)
u2_phy_instance_set_mode(tphy, instance, mode);
return 0;
}
static struct phy *mtk_phy_xlate(struct device *dev, static struct phy *mtk_phy_xlate(struct device *dev,
struct of_phandle_args *args) struct of_phandle_args *args)
{ {
...@@ -931,6 +969,7 @@ static const struct phy_ops mtk_tphy_ops = { ...@@ -931,6 +969,7 @@ static const struct phy_ops mtk_tphy_ops = {
.exit = mtk_phy_exit, .exit = mtk_phy_exit,
.power_on = mtk_phy_power_on, .power_on = mtk_phy_power_on,
.power_off = mtk_phy_power_off, .power_off = mtk_phy_power_off,
.set_mode = mtk_phy_set_mode,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
......
...@@ -372,6 +372,21 @@ int phy_reset(struct phy *phy) ...@@ -372,6 +372,21 @@ int phy_reset(struct phy *phy)
} }
EXPORT_SYMBOL_GPL(phy_reset); EXPORT_SYMBOL_GPL(phy_reset);
int phy_calibrate(struct phy *phy)
{
int ret;
if (!phy || !phy->ops->calibrate)
return 0;
mutex_lock(&phy->mutex);
ret = phy->ops->calibrate(phy);
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_calibrate);
/** /**
* _of_phy_get() - lookup and obtain a reference to a phy by phandle * _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy * @np: device_node for which to get the phy
......
...@@ -114,14 +114,16 @@ struct ufs_qcom_phy { ...@@ -114,14 +114,16 @@ struct ufs_qcom_phy {
struct ufs_qcom_phy_calibration *cached_regs; struct ufs_qcom_phy_calibration *cached_regs;
int cached_regs_table_size; int cached_regs_table_size;
bool is_powered_on; bool is_powered_on;
bool is_started;
struct ufs_qcom_phy_specific_ops *phy_spec_ops; struct ufs_qcom_phy_specific_ops *phy_spec_ops;
enum phy_mode mode;
}; };
/** /**
* struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
* specific implementation per phy. Each UFS phy, should implement * specific implementation per phy. Each UFS phy, should implement
* those functions according to its spec and requirements * those functions according to its spec and requirements
* @calibrate_phy: pointer to a function that calibrate the phy
* @start_serdes: pointer to a function that starts the serdes * @start_serdes: pointer to a function that starts the serdes
* @is_physical_coding_sublayer_ready: pointer to a function that * @is_physical_coding_sublayer_ready: pointer to a function that
* checks pcs readiness. returns 0 for success and non-zero for error. * checks pcs readiness. returns 0 for success and non-zero for error.
...@@ -130,7 +132,6 @@ struct ufs_qcom_phy { ...@@ -130,7 +132,6 @@ struct ufs_qcom_phy {
* and writes to QSERDES_RX_SIGDET_CNTRL attribute * and writes to QSERDES_RX_SIGDET_CNTRL attribute
*/ */
struct ufs_qcom_phy_specific_ops { struct ufs_qcom_phy_specific_ops {
int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
void (*start_serdes)(struct ufs_qcom_phy *phy); void (*start_serdes)(struct ufs_qcom_phy *phy);
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
......
...@@ -44,7 +44,19 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) ...@@ -44,7 +44,19 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
{ {
return 0; struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
bool is_rate_B = false;
int ret;
if (phy_common->mode == PHY_MODE_UFS_HS_B)
is_rate_B = true;
ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B);
if (!ret)
/* phy calibrated, but yet to be started */
phy_common->is_started = false;
return ret;
} }
static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
...@@ -52,6 +64,19 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) ...@@ -52,6 +64,19 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
return 0; return 0;
} }
static
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
{
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
phy_common->mode = PHY_MODE_INVALID;
if (mode > 0)
phy_common->mode = mode;
return 0;
}
static static
void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
{ {
...@@ -102,11 +127,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { ...@@ -102,11 +127,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
.exit = ufs_qcom_phy_qmp_14nm_exit, .exit = ufs_qcom_phy_qmp_14nm_exit,
.power_on = ufs_qcom_phy_power_on, .power_on = ufs_qcom_phy_power_on,
.power_off = ufs_qcom_phy_power_off, .power_off = ufs_qcom_phy_power_off,
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
.calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate,
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
......
...@@ -63,7 +63,19 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) ...@@ -63,7 +63,19 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
{ {
return 0; struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
bool is_rate_B = false;
int ret;
if (phy_common->mode == PHY_MODE_UFS_HS_B)
is_rate_B = true;
ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B);
if (!ret)
/* phy calibrated, but yet to be started */
phy_common->is_started = false;
return ret;
} }
static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
...@@ -71,6 +83,19 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) ...@@ -71,6 +83,19 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
return 0; return 0;
} }
static
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
{
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
phy_common->mode = PHY_MODE_INVALID;
if (mode > 0)
phy_common->mode = mode;
return 0;
}
static static
void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val)
{ {
...@@ -160,11 +185,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { ...@@ -160,11 +185,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
.exit = ufs_qcom_phy_qmp_20nm_exit, .exit = ufs_qcom_phy_qmp_20nm_exit,
.power_on = ufs_qcom_phy_power_on, .power_on = ufs_qcom_phy_power_on,
.power_off = ufs_qcom_phy_power_off, .power_off = ufs_qcom_phy_power_off,
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
.calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate,
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
......
...@@ -518,9 +518,8 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) ...@@ -518,9 +518,8 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
} }
} }
int ufs_qcom_phy_start_serdes(struct phy *generic_phy) static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy)
{ {
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
int ret = 0; int ret = 0;
if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
...@@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_phy) ...@@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes);
int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
{ {
...@@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, ...@@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version);
int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy)
{
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
int ret = 0;
if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) {
dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n",
__func__);
ret = -ENOTSUPP;
} else {
ret = ufs_qcom_phy->phy_spec_ops->
calibrate_phy(ufs_qcom_phy, is_rate_B);
if (ret)
dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n",
__func__, ret);
}
return ret;
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy);
int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
{ {
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
__func__); __func__);
...@@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) ...@@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
return ufs_qcom_phy->phy_spec_ops-> return ufs_qcom_phy->phy_spec_ops->
is_physical_coding_sublayer_ready(ufs_qcom_phy); is_physical_coding_sublayer_ready(ufs_qcom_phy);
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready);
int ufs_qcom_phy_power_on(struct phy *generic_phy) int ufs_qcom_phy_power_on(struct phy *generic_phy)
{ {
...@@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) ...@@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
if (phy_common->is_powered_on) if (phy_common->is_powered_on)
return 0; return 0;
if (!phy_common->is_started) {
err = ufs_qcom_phy_start_serdes(phy_common);
if (err)
return err;
err = ufs_qcom_phy_is_pcs_ready(phy_common);
if (err)
return err;
phy_common->is_started = true;
}
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
if (err) { if (err) {
dev_err(dev, "%s enable vdda_phy failed, err=%d\n", dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
......
/* /*
* Renesas R-Car Gen3 for USB2.0 PHY driver * Renesas R-Car Gen3 for USB2.0 PHY driver
* *
* Copyright (C) 2015 Renesas Electronics Corporation * Copyright (C) 2015-2017 Renesas Electronics Corporation
* *
* This is based on the phy-rcar-gen2 driver: * This is based on the phy-rcar-gen2 driver:
* Copyright (C) 2014 Renesas Solutions Corp. * Copyright (C) 2014 Renesas Solutions Corp.
...@@ -18,10 +18,12 @@ ...@@ -18,10 +18,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/usb/of.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
/******* USB2.0 Host registers (original offset is +0x200) *******/ /******* USB2.0 Host registers (original offset is +0x200) *******/
...@@ -79,6 +81,8 @@ ...@@ -79,6 +81,8 @@
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4) #define USB2_ADPCTRL_DRVVBUS BIT(4)
#define RCAR_GEN3_PHY_HAS_DEDICATED_PINS 1
struct rcar_gen3_chan { struct rcar_gen3_chan {
void __iomem *base; void __iomem *base;
struct extcon_dev *extcon; struct extcon_dev *extcon;
...@@ -86,7 +90,7 @@ struct rcar_gen3_chan { ...@@ -86,7 +90,7 @@ struct rcar_gen3_chan {
struct regulator *vbus; struct regulator *vbus;
struct work_struct work; struct work_struct work;
bool extcon_host; bool extcon_host;
bool has_otg; bool has_otg_pins;
}; };
static void rcar_gen3_phy_usb2_work(struct work_struct *work) static void rcar_gen3_phy_usb2_work(struct work_struct *work)
...@@ -218,33 +222,40 @@ static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) ...@@ -218,33 +222,40 @@ static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch)
return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI);
} }
static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch)
{
if (rcar_gen3_is_host(ch))
return PHY_MODE_USB_HOST;
return PHY_MODE_USB_DEVICE;
}
static ssize_t role_store(struct device *dev, struct device_attribute *attr, static ssize_t role_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct rcar_gen3_chan *ch = dev_get_drvdata(dev); struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
bool is_b_device, is_host, new_mode_is_host; bool is_b_device;
enum phy_mode cur_mode, new_mode;
if (!ch->has_otg || !ch->phy->init_count) if (!ch->has_otg_pins || !ch->phy->init_count)
return -EIO; return -EIO;
/*
* is_b_device: true is B-Device. false is A-Device.
* If {new_mode_}is_host: true is Host mode. false is Peripheral mode.
*/
is_b_device = rcar_gen3_check_id(ch);
is_host = rcar_gen3_is_host(ch);
if (!strncmp(buf, "host", strlen("host"))) if (!strncmp(buf, "host", strlen("host")))
new_mode_is_host = true; new_mode = PHY_MODE_USB_HOST;
else if (!strncmp(buf, "peripheral", strlen("peripheral"))) else if (!strncmp(buf, "peripheral", strlen("peripheral")))
new_mode_is_host = false; new_mode = PHY_MODE_USB_DEVICE;
else else
return -EINVAL; return -EINVAL;
/* is_b_device: true is B-Device. false is A-Device. */
is_b_device = rcar_gen3_check_id(ch);
cur_mode = rcar_gen3_get_phy_mode(ch);
/* If current and new mode is the same, this returns the error */ /* If current and new mode is the same, this returns the error */
if (is_host == new_mode_is_host) if (cur_mode == new_mode)
return -EINVAL; return -EINVAL;
if (new_mode_is_host) { /* And is_host must be false */ if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */
if (!is_b_device) /* A-Peripheral */ if (!is_b_device) /* A-Peripheral */
rcar_gen3_init_from_a_peri_to_a_host(ch); rcar_gen3_init_from_a_peri_to_a_host(ch);
else /* B-Peripheral */ else /* B-Peripheral */
...@@ -264,7 +275,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr, ...@@ -264,7 +275,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
{ {
struct rcar_gen3_chan *ch = dev_get_drvdata(dev); struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
if (!ch->has_otg || !ch->phy->init_count) if (!ch->has_otg_pins || !ch->phy->init_count)
return -EIO; return -EIO;
return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
...@@ -303,7 +314,7 @@ static int rcar_gen3_phy_usb2_init(struct phy *p) ...@@ -303,7 +314,7 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
/* Initialize otg part */ /* Initialize otg part */
if (channel->has_otg) if (channel->has_otg_pins)
rcar_gen3_init_otg(channel); rcar_gen3_init_otg(channel);
return 0; return 0;
...@@ -377,9 +388,17 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) ...@@ -377,9 +388,17 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
} }
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{ .compatible = "renesas,usb2-phy-r8a7795" }, {
{ .compatible = "renesas,usb2-phy-r8a7796" }, .compatible = "renesas,usb2-phy-r8a7795",
{ .compatible = "renesas,rcar-gen3-usb2-phy" }, .data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,usb2-phy-r8a7796",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,rcar-gen3-usb2-phy",
},
{ } { }
}; };
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
...@@ -415,14 +434,17 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) ...@@ -415,14 +434,17 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
/* call request_irq for OTG */ /* call request_irq for OTG */
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq >= 0) { if (irq >= 0) {
int ret;
INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
IRQF_SHARED, dev_name(dev), channel); IRQF_SHARED, dev_name(dev), channel);
if (irq < 0) if (irq < 0)
dev_err(dev, "No irq handler (%d)\n", irq); dev_err(dev, "No irq handler (%d)\n", irq);
channel->has_otg = true; }
if (of_usb_get_dr_mode_by_phy(dev->of_node, 0) == USB_DR_MODE_OTG) {
int ret;
channel->has_otg_pins = (uintptr_t)of_device_get_match_data(dev);
channel->extcon = devm_extcon_dev_allocate(dev, channel->extcon = devm_extcon_dev_allocate(dev,
rcar_gen3_phy_cable); rcar_gen3_phy_cable);
if (IS_ERR(channel->extcon)) if (IS_ERR(channel->extcon))
...@@ -464,7 +486,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) ...@@ -464,7 +486,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
dev_err(dev, "Failed to register PHY provider\n"); dev_err(dev, "Failed to register PHY provider\n");
ret = PTR_ERR(provider); ret = PTR_ERR(provider);
goto error; goto error;
} else if (channel->has_otg) { } else if (channel->has_otg_pins) {
int ret; int ret;
ret = device_create_file(dev, &dev_attr_role); ret = device_create_file(dev, &dev_attr_role);
...@@ -484,7 +506,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) ...@@ -484,7 +506,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
{ {
struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); struct rcar_gen3_chan *channel = platform_get_drvdata(pdev);
if (channel->has_otg) if (channel->has_otg_pins)
device_remove_file(&pdev->dev, &dev_attr_role); device_remove_file(&pdev->dev, &dev_attr_role);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
......
...@@ -102,9 +102,40 @@ ...@@ -102,9 +102,40 @@
#define CMN_PLL1_SS_CTRL1 (0xb8 << 2) #define CMN_PLL1_SS_CTRL1 (0xb8 << 2)
#define CMN_PLL1_SS_CTRL2 (0xb9 << 2) #define CMN_PLL1_SS_CTRL2 (0xb9 << 2)
#define CMN_RXCAL_OVRD (0xd1 << 2) #define CMN_RXCAL_OVRD (0xd1 << 2)
#define CMN_TXPUCAL_CTRL (0xe0 << 2) #define CMN_TXPUCAL_CTRL (0xe0 << 2)
#define CMN_TXPUCAL_OVRD (0xe1 << 2) #define CMN_TXPUCAL_OVRD (0xe1 << 2)
#define CMN_TXPDCAL_CTRL (0xf0 << 2)
#define CMN_TXPDCAL_OVRD (0xf1 << 2) #define CMN_TXPDCAL_OVRD (0xf1 << 2)
/* For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL */
#define CMN_TXPXCAL_START BIT(15)
#define CMN_TXPXCAL_DONE BIT(14)
#define CMN_TXPXCAL_NO_RESPONSE BIT(13)
#define CMN_TXPXCAL_CURRENT_RESPONSE BIT(12)
#define CMN_TXPU_ADJ_CTRL (0x108 << 2)
#define CMN_TXPD_ADJ_CTRL (0x10c << 2)
/*
* For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL,
* CMN_TXPU_ADJ_CTRL, CMN_TXPDCAL_CTRL
*
* NOTE: some of these registers are documented to be 2's complement
* signed numbers, but then documented to be always positive. Weird.
* In such a case, using CMN_CALIB_CODE_POS() avoids the unnecessary
* sign extension.
*/
#define CMN_CALIB_CODE_WIDTH 7
#define CMN_CALIB_CODE_OFFSET 0
#define CMN_CALIB_CODE_MASK GENMASK(CMN_CALIB_CODE_WIDTH, 0)
#define CMN_CALIB_CODE(x) \
sign_extend32((x) >> CMN_CALIB_CODE_OFFSET, CMN_CALIB_CODE_WIDTH)
#define CMN_CALIB_CODE_POS_MASK GENMASK(CMN_CALIB_CODE_WIDTH - 1, 0)
#define CMN_CALIB_CODE_POS(x) \
(((x) >> CMN_CALIB_CODE_OFFSET) & CMN_CALIB_CODE_POS_MASK)
#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) #define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2)
#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) #define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2)
#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) #define CMN_DIAG_PLL0_OVRD (0x1c2 << 2)
...@@ -138,6 +169,15 @@ ...@@ -138,6 +169,15 @@
#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2) #define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2)
#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2) #define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2)
#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2) #define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_000(n) ((0x4058 | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_001(n) ((0x4059 | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_010(n) ((0x405a | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_011(n) ((0x405b | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_100(n) ((0x405c | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_101(n) ((0x405d | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_110(n) ((0x405e | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_111(n) ((0x405f | ((n) << 9)) << 2)
#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2) #define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2)
#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2) #define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2)
#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2) #define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2)
...@@ -150,10 +190,63 @@ ...@@ -150,10 +190,63 @@
#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2) #define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2)
#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2) #define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2)
#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2) #define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2)
/* Use this for "n" in macros like "_MULT_XXX" to target the aux channel */
#define AUX_CH_LANE 8
#define TX_ANA_CTRL_REG_1 (0x5020 << 2) #define TX_ANA_CTRL_REG_1 (0x5020 << 2)
#define TXDA_DP_AUX_EN BIT(15)
#define AUXDA_SE_EN BIT(14)
#define TXDA_CAL_LATCH_EN BIT(13)
#define AUXDA_POLARITY BIT(12)
#define TXDA_DRV_POWER_ISOLATION_EN BIT(11)
#define TXDA_DRV_POWER_EN_PH_2_N BIT(10)
#define TXDA_DRV_POWER_EN_PH_1_N BIT(9)
#define TXDA_BGREF_EN BIT(8)
#define TXDA_DRV_LDO_EN BIT(7)
#define TXDA_DECAP_EN_DEL BIT(6)
#define TXDA_DECAP_EN BIT(5)
#define TXDA_UPHY_SUPPLY_EN_DEL BIT(4)
#define TXDA_UPHY_SUPPLY_EN BIT(3)
#define TXDA_LOW_LEAKAGE_EN BIT(2)
#define TXDA_DRV_IDLE_LOWI_EN BIT(1)
#define TXDA_DRV_CMN_MODE_EN BIT(0)
#define TX_ANA_CTRL_REG_2 (0x5021 << 2) #define TX_ANA_CTRL_REG_2 (0x5021 << 2)
#define AUXDA_DEBOUNCING_CLK BIT(15)
#define TXDA_LPBK_RECOVERED_CLK_EN BIT(14)
#define TXDA_LPBK_ISI_GEN_EN BIT(13)
#define TXDA_LPBK_SERIAL_EN BIT(12)
#define TXDA_LPBK_LINE_EN BIT(11)
#define TXDA_DRV_LDO_REDC_SINKIQ BIT(10)
#define XCVR_DECAP_EN_DEL BIT(9)
#define XCVR_DECAP_EN BIT(8)
#define TXDA_MPHY_ENABLE_HS_NT BIT(7)
#define TXDA_MPHY_SA_MODE BIT(6)
#define TXDA_DRV_LDO_RBYR_FB_EN BIT(5)
#define TXDA_DRV_RST_PULL_DOWN BIT(4)
#define TXDA_DRV_LDO_BG_FB_EN BIT(3)
#define TXDA_DRV_LDO_BG_REF_EN BIT(2)
#define TXDA_DRV_PREDRV_EN_DEL BIT(1)
#define TXDA_DRV_PREDRV_EN BIT(0)
#define TXDA_COEFF_CALC_CTRL (0x5022 << 2) #define TXDA_COEFF_CALC_CTRL (0x5022 << 2)
#define TX_HIGH_Z BIT(6)
#define TX_VMARGIN_OFFSET 3
#define TX_VMARGIN_MASK 0x7
#define LOW_POWER_SWING_EN BIT(2)
#define TX_FCM_DRV_MAIN_EN BIT(1)
#define TX_FCM_FULL_MARGIN BIT(0)
#define TX_DIG_CTRL_REG_2 (0x5024 << 2) #define TX_DIG_CTRL_REG_2 (0x5024 << 2)
#define TX_HIGH_Z_TM_EN BIT(15)
#define TX_RESCAL_CODE_OFFSET 0
#define TX_RESCAL_CODE_MASK 0x3f
#define TXDA_CYA_AUXDA_CYA (0x5025 << 2) #define TXDA_CYA_AUXDA_CYA (0x5025 << 2)
#define TX_ANA_CTRL_REG_3 (0x5026 << 2) #define TX_ANA_CTRL_REG_3 (0x5026 << 2)
#define TX_ANA_CTRL_REG_4 (0x5027 << 2) #define TX_ANA_CTRL_REG_4 (0x5027 << 2)
...@@ -456,54 +549,72 @@ static void tcphy_dp_aux_set_flip(struct rockchip_typec_phy *tcphy) ...@@ -456,54 +549,72 @@ static void tcphy_dp_aux_set_flip(struct rockchip_typec_phy *tcphy)
*/ */
tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1);
if (!tcphy->flip) if (!tcphy->flip)
tx_ana_ctrl_reg_1 |= BIT(12); tx_ana_ctrl_reg_1 |= AUXDA_POLARITY;
else else
tx_ana_ctrl_reg_1 &= ~BIT(12); tx_ana_ctrl_reg_1 &= ~AUXDA_POLARITY;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
} }
static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
{ {
u16 val;
u16 tx_ana_ctrl_reg_1; u16 tx_ana_ctrl_reg_1;
u16 rdata, rdata2, val; u16 tx_ana_ctrl_reg_2;
s32 pu_calib_code, pd_calib_code;
/* disable txda_cal_latch_en for rewrite the calibration values */ s32 pu_adj, pd_adj;
tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); u16 calib;
tx_ana_ctrl_reg_1 &= ~BIT(13);
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
/* /*
* read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and * Calculate calibration code as per docs: use an average of the
* write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it * pull down and pull up. Then add in adjustments.
* works.
*/ */
rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); val = readl(tcphy->base + CMN_TXPUCAL_CTRL);
rdata = rdata & 0xffc0; pu_calib_code = CMN_CALIB_CODE_POS(val);
val = readl(tcphy->base + CMN_TXPDCAL_CTRL);
pd_calib_code = CMN_CALIB_CODE_POS(val);
val = readl(tcphy->base + CMN_TXPU_ADJ_CTRL);
pu_adj = CMN_CALIB_CODE(val);
val = readl(tcphy->base + CMN_TXPD_ADJ_CTRL);
pd_adj = CMN_CALIB_CODE(val);
calib = (pu_calib_code + pd_calib_code) / 2 + pu_adj + pd_adj;
rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); /* disable txda_cal_latch_en for rewrite the calibration values */
rdata2 = rdata2 & 0x3f; tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1);
tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
val = rdata | rdata2; /* write the calibration, then delay 10 ms as sample in docs */
val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
val &= ~(TX_RESCAL_CODE_MASK << TX_RESCAL_CODE_OFFSET);
val |= calib << TX_RESCAL_CODE_OFFSET;
writel(val, tcphy->base + TX_DIG_CTRL_REG_2); writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
usleep_range(1000, 1050); usleep_range(10000, 10050);
/* /*
* Enable signal for latch that sample and holds calibration values. * Enable signal for latch that sample and holds calibration values.
* Activate this signal for 1 clock cycle to sample new calibration * Activate this signal for 1 clock cycle to sample new calibration
* values. * values.
*/ */
tx_ana_ctrl_reg_1 |= BIT(13); tx_ana_ctrl_reg_1 |= TXDA_CAL_LATCH_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
usleep_range(150, 200); usleep_range(150, 200);
/* set TX Voltage Level and TX Deemphasis to 0 */ /* set TX Voltage Level and TX Deemphasis to 0 */
writel(0, tcphy->base + PHY_DP_TX_CTL); writel(0, tcphy->base + PHY_DP_TX_CTL);
/* re-enable decap */ /* re-enable decap */
writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); tx_ana_ctrl_reg_2 = XCVR_DECAP_EN;
writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
tx_ana_ctrl_reg_1 |= BIT(3); udelay(1);
tx_ana_ctrl_reg_2 |= XCVR_DECAP_EN_DEL;
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
tx_ana_ctrl_reg_1 |= BIT(4); udelay(1);
tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN_DEL;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
writel(0, tcphy->base + TX_ANA_CTRL_REG_5); writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
...@@ -515,44 +626,66 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) ...@@ -515,44 +626,66 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
/* re-enables Bandgap reference for LDO */ /* re-enables Bandgap reference for LDO */
tx_ana_ctrl_reg_1 |= BIT(7); tx_ana_ctrl_reg_1 |= TXDA_DRV_LDO_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
tx_ana_ctrl_reg_1 |= BIT(8); udelay(5);
tx_ana_ctrl_reg_1 |= TXDA_BGREF_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
/* /*
* re-enables the transmitter pre-driver, driver data selection MUX, * re-enables the transmitter pre-driver, driver data selection MUX,
* and receiver detect circuits. * and receiver detect circuits.
*/ */
writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2); tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN;
writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
udelay(1);
tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN_DEL;
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
/* /*
* Do some magic undocumented stuff, some of which appears to * Do all the undocumented magic:
* undo the "re-enables Bandgap reference for LDO" above. * - Turn on TXDA_DP_AUX_EN, whatever that is, even though sample
* never shows this going on.
* - Turn on TXDA_DECAP_EN (and TXDA_DECAP_EN_DEL) even though
* docs say for aux it's always 0.
* - Turn off the LDO and BGREF, which we just spent time turning
* on above (???).
*
* Without this magic, things seem worse.
*/ */
tx_ana_ctrl_reg_1 |= BIT(15); tx_ana_ctrl_reg_1 |= TXDA_DP_AUX_EN;
tx_ana_ctrl_reg_1 &= ~BIT(8); tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN;
tx_ana_ctrl_reg_1 &= ~BIT(7); tx_ana_ctrl_reg_1 &= ~TXDA_DRV_LDO_EN;
tx_ana_ctrl_reg_1 |= BIT(6); tx_ana_ctrl_reg_1 &= ~TXDA_BGREF_EN;
tx_ana_ctrl_reg_1 |= BIT(5); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
udelay(1);
tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN_DEL;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
/* /*
* Controls low_power_swing_en, don't set the voltage swing of the * Undo the work we did to set the LDO voltage.
* driver to 400mv. The values below are peak to peak (differential) * This doesn't seem to help nor hurt, but it kinda goes with the
* values. * undocumented magic above.
*/ */
writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
/* Don't set voltage swing to 400 mV peak to peak (differential) */
writel(0, tcphy->base + TXDA_COEFF_CALC_CTRL); writel(0, tcphy->base + TXDA_COEFF_CALC_CTRL);
/* Init TXDA_CYA_AUXDA_CYA for unknown magic reasons */
writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
/* Controls tx_high_z_tm_en */ /*
* More undocumented magic, presumably the goal of which is to
* make the "auxda_source_aux_oen" be ignored and instead to decide
* about "high impedance state" based on what software puts in the
* register TXDA_COEFF_CALC_CTRL (see TX_HIGH_Z). Since we only
* program that register once and we don't set the bit TX_HIGH_Z,
* presumably the goal here is that we should never put the analog
* driver in high impedance state.
*/
val = readl(tcphy->base + TX_DIG_CTRL_REG_2); val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
val |= BIT(15); val |= TX_HIGH_Z_TM_EN;
writel(val, tcphy->base + TX_DIG_CTRL_REG_2); writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
} }
......
...@@ -68,6 +68,40 @@ ...@@ -68,6 +68,40 @@
#define PCIE_PCS_MASK 0xFF0000 #define PCIE_PCS_MASK 0xFF0000
#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 #define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
#define PCIEPHYRX_ANA_PROGRAMMABILITY 0x0000000C
#define INTERFACE_MASK GENMASK(31, 27)
#define INTERFACE_SHIFT 27
#define LOSD_MASK GENMASK(17, 14)
#define LOSD_SHIFT 14
#define MEM_PLLDIV GENMASK(6, 5)
#define PCIEPHYRX_TRIM 0x0000001C
#define MEM_DLL_TRIM_SEL GENMASK(31, 30)
#define MEM_DLL_TRIM_SHIFT 30
#define PCIEPHYRX_DLL 0x00000024
#define MEM_DLL_PHINT_RATE GENMASK(31, 30)
#define PCIEPHYRX_DIGITAL_MODES 0x00000028
#define MEM_CDR_FASTLOCK BIT(23)
#define MEM_CDR_LBW GENMASK(22, 21)
#define MEM_CDR_STEPCNT GENMASK(20, 19)
#define MEM_CDR_STL_MASK GENMASK(18, 16)
#define MEM_CDR_STL_SHIFT 16
#define MEM_CDR_THR_MASK GENMASK(15, 13)
#define MEM_CDR_THR_SHIFT 13
#define MEM_CDR_THR_MODE BIT(12)
#define MEM_CDR_CDR_2NDO_SDM_MODE BIT(11)
#define MEM_OVRD_HS_RATE BIT(26)
#define PCIEPHYRX_EQUALIZER 0x00000038
#define MEM_EQLEV GENMASK(31, 16)
#define MEM_EQFTC GENMASK(15, 11)
#define MEM_EQCTL GENMASK(10, 7)
#define MEM_EQCTL_SHIFT 7
#define MEM_OVRD_EQLEV BIT(2)
#define MEM_OVRD_EQFTC BIT(1)
/* /*
* This is an Empirical value that works, need to confirm the actual * This is an Empirical value that works, need to confirm the actual
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
...@@ -91,6 +125,8 @@ struct pipe3_dpll_map { ...@@ -91,6 +125,8 @@ struct pipe3_dpll_map {
struct ti_pipe3 { struct ti_pipe3 {
void __iomem *pll_ctrl_base; void __iomem *pll_ctrl_base;
void __iomem *phy_rx;
void __iomem *phy_tx;
struct device *dev; struct device *dev;
struct device *control_dev; struct device *control_dev;
struct clk *wkupclk; struct clk *wkupclk;
...@@ -261,6 +297,37 @@ static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) ...@@ -261,6 +297,37 @@ static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
return ti_pipe3_dpll_wait_lock(phy); return ti_pipe3_dpll_wait_lock(phy);
} }
static void ti_pipe3_calibrate(struct ti_pipe3 *phy)
{
u32 val;
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY);
val &= ~(INTERFACE_MASK | LOSD_MASK | MEM_PLLDIV);
val = (0x1 << INTERFACE_SHIFT | 0xA << LOSD_SHIFT);
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES);
val &= ~(MEM_CDR_STEPCNT | MEM_CDR_STL_MASK | MEM_CDR_THR_MASK |
MEM_CDR_CDR_2NDO_SDM_MODE | MEM_OVRD_HS_RATE);
val |= (MEM_CDR_FASTLOCK | MEM_CDR_LBW | 0x3 << MEM_CDR_STL_SHIFT |
0x1 << MEM_CDR_THR_SHIFT | MEM_CDR_THR_MODE);
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_TRIM);
val &= ~MEM_DLL_TRIM_SEL;
val |= 0x2 << MEM_DLL_TRIM_SHIFT;
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_TRIM, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DLL);
val |= MEM_DLL_PHINT_RATE;
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DLL, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_EQUALIZER);
val &= ~(MEM_EQLEV | MEM_EQCTL | MEM_OVRD_EQLEV | MEM_OVRD_EQFTC);
val |= MEM_EQFTC | 0x1 << MEM_EQCTL_SHIFT;
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_EQUALIZER, val);
}
static int ti_pipe3_init(struct phy *x) static int ti_pipe3_init(struct phy *x)
{ {
struct ti_pipe3 *phy = phy_get_drvdata(x); struct ti_pipe3 *phy = phy_get_drvdata(x);
...@@ -282,7 +349,12 @@ static int ti_pipe3_init(struct phy *x) ...@@ -282,7 +349,12 @@ static int ti_pipe3_init(struct phy *x)
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
PCIE_PCS_MASK, val); PCIE_PCS_MASK, val);
if (ret)
return ret; return ret;
ti_pipe3_calibrate(phy);
return 0;
} }
/* Bring it out of IDLE if it is IDLE */ /* Bring it out of IDLE if it is IDLE */
...@@ -513,6 +585,29 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) ...@@ -513,6 +585,29 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
return 0; return 0;
} }
static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy)
{
struct resource *res;
struct device *dev = phy->dev;
struct device_node *node = dev->of_node;
struct platform_device *pdev = to_platform_device(dev);
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
return 0;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"phy_rx");
phy->phy_rx = devm_ioremap_resource(dev, res);
if (IS_ERR(phy->phy_rx))
return PTR_ERR(phy->phy_rx);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"phy_tx");
phy->phy_tx = devm_ioremap_resource(dev, res);
return PTR_ERR_OR_ZERO(phy->phy_tx);
}
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
{ {
struct resource *res; struct resource *res;
...@@ -559,6 +654,10 @@ static int ti_pipe3_probe(struct platform_device *pdev) ...@@ -559,6 +654,10 @@ static int ti_pipe3_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
ret = ti_pipe3_get_tx_rx_base(phy);
if (ret)
return ret;
ret = ti_pipe3_get_sysctrl(phy); ret = ti_pipe3_get_sysctrl(phy);
if (ret) if (ret)
return ret; return ret;
......
...@@ -273,15 +273,18 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) ...@@ -273,15 +273,18 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B) bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B)
? true : false; ? true : false;
if (is_rate_B)
phy_set_mode(phy, PHY_MODE_UFS_HS_B);
/* Assert PHY reset and apply PHY calibration values */ /* Assert PHY reset and apply PHY calibration values */
ufs_qcom_assert_reset(hba); ufs_qcom_assert_reset(hba);
/* provide 1ms delay to let the reset pulse propagate */ /* provide 1ms delay to let the reset pulse propagate */
usleep_range(1000, 1100); usleep_range(1000, 1100);
ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B); /* phy initialization - calibrate the phy */
ret = phy_init(phy);
if (ret) { if (ret) {
dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret = %d\n", dev_err(hba->dev, "%s: phy init failed, ret = %d\n",
__func__, ret); __func__, ret);
goto out; goto out;
} }
...@@ -294,21 +297,22 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) ...@@ -294,21 +297,22 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
* voltage, current to settle down before starting serdes. * voltage, current to settle down before starting serdes.
*/ */
usleep_range(1000, 1100); usleep_range(1000, 1100);
ret = ufs_qcom_phy_start_serdes(phy);
/* power on phy - start serdes and phy's power and clocks */
ret = phy_power_on(phy);
if (ret) { if (ret) {
dev_err(hba->dev, "%s: ufs_qcom_phy_start_serdes() failed, ret = %d\n", dev_err(hba->dev, "%s: phy power on failed, ret = %d\n",
__func__, ret); __func__, ret);
goto out; goto out_disable_phy;
} }
ret = ufs_qcom_phy_is_pcs_ready(phy);
if (ret)
dev_err(hba->dev,
"%s: is_physical_coding_sublayer_ready() failed, ret = %d\n",
__func__, ret);
ufs_qcom_select_unipro_mode(host); ufs_qcom_select_unipro_mode(host);
return 0;
out_disable_phy:
ufs_qcom_assert_reset(hba);
phy_exit(phy);
out: out:
return ret; return ret;
} }
...@@ -1273,14 +1277,9 @@ static int ufs_qcom_init(struct ufs_hba *hba) ...@@ -1273,14 +1277,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)
ufs_qcom_phy_save_controller_version(host->generic_phy, ufs_qcom_phy_save_controller_version(host->generic_phy,
host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step);
phy_init(host->generic_phy);
err = phy_power_on(host->generic_phy);
if (err)
goto out_unregister_bus;
err = ufs_qcom_init_lane_clks(host); err = ufs_qcom_init_lane_clks(host);
if (err) if (err)
goto out_disable_phy; goto out_variant_clear;
ufs_qcom_set_caps(hba); ufs_qcom_set_caps(hba);
ufs_qcom_advertise_quirks(hba); ufs_qcom_advertise_quirks(hba);
...@@ -1301,10 +1300,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) ...@@ -1301,10 +1300,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
goto out; goto out;
out_disable_phy:
phy_power_off(host->generic_phy);
out_unregister_bus:
phy_exit(host->generic_phy);
out_variant_clear: out_variant_clear:
ufshcd_set_variant(hba, NULL); ufshcd_set_variant(hba, NULL);
out: out:
......
...@@ -40,6 +40,18 @@ bool soc_is_brcmstb(void) ...@@ -40,6 +40,18 @@ bool soc_is_brcmstb(void)
return of_match_node(brcmstb_machine_match, root) != NULL; return of_match_node(brcmstb_machine_match, root) != NULL;
} }
u32 brcmstb_get_family_id(void)
{
return family_id;
}
EXPORT_SYMBOL(brcmstb_get_family_id);
u32 brcmstb_get_product_id(void)
{
return product_id;
}
EXPORT_SYMBOL(brcmstb_get_product_id);
static const struct of_device_id sun_top_ctrl_match[] = { static const struct of_device_id sun_top_ctrl_match[] = {
{ .compatible = "brcm,bcm7125-sun-top-ctrl", }, { .compatible = "brcm,bcm7125-sun-top-ctrl", },
{ .compatible = "brcm,bcm7346-sun-top-ctrl", }, { .compatible = "brcm,bcm7346-sun-top-ctrl", },
......
...@@ -15,5 +15,6 @@ ...@@ -15,5 +15,6 @@
#define PHY_TYPE_PCIE 2 #define PHY_TYPE_PCIE 2
#define PHY_TYPE_USB2 3 #define PHY_TYPE_USB2 3
#define PHY_TYPE_USB3 4 #define PHY_TYPE_USB3 4
#define PHY_TYPE_UFS 5
#endif /* _DT_BINDINGS_PHY */ #endif /* _DT_BINDINGS_PHY */
...@@ -31,10 +31,7 @@ void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy); ...@@ -31,10 +31,7 @@ void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy);
*/ */
void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy); void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy);
int ufs_qcom_phy_start_serdes(struct phy *phy);
int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes); int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes);
int ufs_qcom_phy_calibrate_phy(struct phy *phy, bool is_rate_B);
int ufs_qcom_phy_is_pcs_ready(struct phy *phy);
void ufs_qcom_phy_save_controller_version(struct phy *phy, void ufs_qcom_phy_save_controller_version(struct phy *phy,
u8 major, u16 minor, u16 step); u8 major, u16 minor, u16 step);
......
...@@ -29,6 +29,8 @@ enum phy_mode { ...@@ -29,6 +29,8 @@ enum phy_mode {
PHY_MODE_USB_OTG, PHY_MODE_USB_OTG,
PHY_MODE_SGMII, PHY_MODE_SGMII,
PHY_MODE_10GKR, PHY_MODE_10GKR,
PHY_MODE_UFS_HS_A,
PHY_MODE_UFS_HS_B,
}; };
/** /**
...@@ -39,6 +41,7 @@ enum phy_mode { ...@@ -39,6 +41,7 @@ enum phy_mode {
* @power_off: powering off the phy * @power_off: powering off the phy
* @set_mode: set the mode of the phy * @set_mode: set the mode of the phy
* @reset: resetting the phy * @reset: resetting the phy
* @calibrate: calibrate the phy
* @owner: the module owner containing the ops * @owner: the module owner containing the ops
*/ */
struct phy_ops { struct phy_ops {
...@@ -48,6 +51,7 @@ struct phy_ops { ...@@ -48,6 +51,7 @@ struct phy_ops {
int (*power_off)(struct phy *phy); int (*power_off)(struct phy *phy);
int (*set_mode)(struct phy *phy, enum phy_mode mode); int (*set_mode)(struct phy *phy, enum phy_mode mode);
int (*reset)(struct phy *phy); int (*reset)(struct phy *phy);
int (*calibrate)(struct phy *phy);
struct module *owner; struct module *owner;
}; };
...@@ -141,6 +145,7 @@ int phy_power_on(struct phy *phy); ...@@ -141,6 +145,7 @@ int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy); int phy_power_off(struct phy *phy);
int phy_set_mode(struct phy *phy, enum phy_mode mode); int phy_set_mode(struct phy *phy, enum phy_mode mode);
int phy_reset(struct phy *phy); int phy_reset(struct phy *phy);
int phy_calibrate(struct phy *phy);
static inline int phy_get_bus_width(struct phy *phy) static inline int phy_get_bus_width(struct phy *phy)
{ {
return phy->attrs.bus_width; return phy->attrs.bus_width;
...@@ -262,6 +267,13 @@ static inline int phy_reset(struct phy *phy) ...@@ -262,6 +267,13 @@ static inline int phy_reset(struct phy *phy)
return -ENOSYS; return -ENOSYS;
} }
static inline int phy_calibrate(struct phy *phy)
{
if (!phy)
return 0;
return -ENOSYS;
}
static inline int phy_get_bus_width(struct phy *phy) static inline int phy_get_bus_width(struct phy *phy)
{ {
return -ENOSYS; return -ENOSYS;
...@@ -291,7 +303,7 @@ static inline struct phy *devm_phy_get(struct device *dev, const char *string) ...@@ -291,7 +303,7 @@ static inline struct phy *devm_phy_get(struct device *dev, const char *string)
static inline struct phy *devm_phy_optional_get(struct device *dev, static inline struct phy *devm_phy_optional_get(struct device *dev,
const char *string) const char *string)
{ {
return ERR_PTR(-ENOSYS); return NULL;
} }
static inline struct phy *devm_of_phy_get(struct device *dev, static inline struct phy *devm_of_phy_get(struct device *dev,
......
#ifndef __BRCMSTB_SOC_H #ifndef __BRCMSTB_SOC_H
#define __BRCMSTB_SOC_H #define __BRCMSTB_SOC_H
static inline u32 BRCM_ID(u32 reg)
{
return reg >> 28 ? reg >> 16 : reg >> 8;
}
static inline u32 BRCM_REV(u32 reg)
{
return reg & 0xff;
}
/* /*
* Bus Interface Unit control register setup, must happen early during boot, * Bus Interface Unit control register setup, must happen early during boot,
* before SMP is brought up, called by machine entry point. * before SMP is brought up, called by machine entry point.
*/ */
void brcmstb_biuctrl_init(void); void brcmstb_biuctrl_init(void);
/*
* Helper functions for getting family or product id from the
* SoC driver.
*/
u32 brcmstb_get_family_id(void);
u32 brcmstb_get_product_id(void);
#endif /* __BRCMSTB_SOC_H */ #endif /* __BRCMSTB_SOC_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment