mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
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: Kishon Vijay Abraham I <kishon@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJZ8HRDAAoJEA5ceFyATYLZyFAP/R8kN02DtuvPTzXxgGYli1kC CNpFYBGwL4rq39C/zT7+8xBXamwCOpPWqQJ6M0HgkD3XX1Vg6htH0VdvND9nZsPk oSDenxPkDELyM6k5OqjGdWQCBEZdvoZqGHtuIsk4ZrZKVajvqBPAG2WI8tVBpiPL 27mwm/5QpXzXfNm1k8onXDqTpSBGmY1EcNvIgmon2Zl/qkWoKuNTpOv3w6JE/ktt YOxg9ScQCyw1LYx82hKF86aq0M9nQ5F+pN5o6c7oNFrotK5cE/Us3XTgKCVH+3sb wtbgHhDCCYeYShBr+msgPj8u8Jy596W6IcGFt2nuIm+ATL/qBZQyhSUtfzqPEf6k ShtJ05jTUHrM16jVDf+nDIkv9ENeSsUOoDJ7FKo4ht0miBE5fD3tmLcXP5NfOla6 PMJN4g+TF/5Ym6eB0mtlq41V5aBInbBr07wD+WFp4uNgdtBHVSRz0yRA2CNIZpww dhjoiAPjOeJlIeWMDJni7cvU6eAEV1hXRHB0+hPa0lQU3B7t4hZaROypzIwJd7Fu ZUetYfcKCmViRinr8O6Ym4DhMatlxPSLgD2R6VQhGYtThUEO57oLBPv5XMzAthwi PkigsXaO9fDCodECcNDO/PD0Zc6tUugRmiiD/UhVpSCQ+X1EsdYSrYBYtSwwMauC DqNvwPo3MExkMjFs68ZU =VvPn -----END PGP SIGNATURE----- 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: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
commit
6bd5bb1ede
@ -0,0 +1,43 @@
|
||||
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:
|
||||
This property is not applicable for "brcm,iproc-ns2-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 {
|
||||
compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3";
|
||||
reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>;
|
||||
|
@ -4,10 +4,13 @@ This file provides information on what the device node for the R-Car generation
|
||||
2 USB PHY contains.
|
||||
|
||||
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-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
|
||||
SoC-specific version corresponding to the platform first
|
||||
|
@ -8,6 +8,8 @@ Required properties:
|
||||
SoC.
|
||||
"renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
|
||||
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.
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
|
@ -2896,6 +2896,13 @@ S: Supported
|
||||
F: drivers/gpio/gpio-brcmstb.c
|
||||
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
|
||||
M: Florian Fainelli <f.fainelli@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
@ -594,6 +595,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
|
||||
int i;
|
||||
int phy_count;
|
||||
struct phy **phy;
|
||||
struct device_link **link;
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
struct dw_pcie *pci;
|
||||
@ -649,11 +651,21 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL);
|
||||
if (!link)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < phy_count; i++) {
|
||||
snprintf(name, sizeof(name), "pcie-phy%d", i);
|
||||
phy[i] = devm_phy_get(dev, name);
|
||||
if (IS_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;
|
||||
@ -732,6 +744,10 @@ err_get_sync:
|
||||
pm_runtime_disable(dev);
|
||||
dra7xx_pcie_disable_phy(dra7xx);
|
||||
|
||||
err_link:
|
||||
while (--i >= 0)
|
||||
device_link_del(link[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -926,6 +926,7 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
|
||||
.phyctl_offset = REG_PHYCTL_A33,
|
||||
.dedicated_clocks = true,
|
||||
.enable_pmu_unk1 = true,
|
||||
.phy0_dual_route = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
|
||||
|
@ -67,3 +67,16 @@ config PHY_BRCM_SATA
|
||||
help
|
||||
Enable this to support the Broadcom SATA PHY.
|
||||
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
|
||||
obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.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_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 {
|
||||
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 {
|
||||
int portnum;
|
||||
struct phy *phy;
|
||||
struct brcm_sata_phy *phy_priv;
|
||||
bool ssc_en;
|
||||
enum brcm_sata_phy_rxaeq_mode rxaeq_mode;
|
||||
u32 rxaeq_val;
|
||||
};
|
||||
|
||||
struct brcm_sata_phy {
|
||||
@ -93,6 +111,15 @@ enum sata_phy_regs {
|
||||
TX_ACTRL0 = 0x80,
|
||||
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,
|
||||
OOB1_REG_BANK = 0x160,
|
||||
OOB_CTRL1 = 0x80,
|
||||
@ -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_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);
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
@ -215,10 +242,47 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port)
|
||||
|
||||
brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
|
||||
~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;
|
||||
|
||||
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 */
|
||||
#define NS2_PLL1_ACTRL2_MAGIC 0x1df8
|
||||
#define NS2_PLL1_ACTRL3_MAGIC 0x2b00
|
||||
@ -463,6 +527,7 @@ MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
|
||||
|
||||
static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const char *rxaeq_mode;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
const struct of_device_id *of_id;
|
||||
@ -525,6 +590,13 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
port->portnum = id;
|
||||
port->phy_priv = priv;
|
||||
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");
|
||||
if (IS_ERR(port->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
1017
drivers/phy/broadcom/phy-brcm-usb-init.c
Normal file
1017
drivers/phy/broadcom/phy-brcm-usb-init.c
Normal file
File diff suppressed because it is too large
Load Diff
50
drivers/phy/broadcom/phy-brcm-usb-init.h
Normal file
50
drivers/phy/broadcom/phy-brcm-usb-init.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 */
|
459
drivers/phy/broadcom/phy-brcm-usb.c
Normal file
459
drivers/phy/broadcom/phy-brcm-usb.c
Normal file
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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 {
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
int modes[MVEBU_COMPHY_LANES];
|
||||
};
|
||||
|
||||
struct mvebu_comphy_lane {
|
||||
|
@ -96,9 +96,11 @@
|
||||
|
||||
#define U3P_U2PHYDTM1 0x06C
|
||||
#define P2C_RG_UART_EN BIT(16)
|
||||
#define P2C_FORCE_IDDIG BIT(9)
|
||||
#define P2C_RG_VBUSVALID BIT(5)
|
||||
#define P2C_RG_SESSEND BIT(4)
|
||||
#define P2C_RG_AVALID BIT(2)
|
||||
#define P2C_RG_IDDIG BIT(1)
|
||||
|
||||
#define U3P_U3_CHIP_GPIO_CTLD 0x0c
|
||||
#define P3C_REG_IP_SW_RST BIT(31)
|
||||
@ -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,
|
||||
struct mtk_phy_instance *instance)
|
||||
{
|
||||
@ -881,6 +908,17 @@ static int mtk_phy_exit(struct phy *phy)
|
||||
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,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
@ -931,6 +969,7 @@ static const struct phy_ops mtk_tphy_ops = {
|
||||
.exit = mtk_phy_exit,
|
||||
.power_on = mtk_phy_power_on,
|
||||
.power_off = mtk_phy_power_off,
|
||||
.set_mode = mtk_phy_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -372,6 +372,21 @@ int phy_reset(struct phy *phy)
|
||||
}
|
||||
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
|
||||
* @np: device_node for which to get the phy
|
||||
|
@ -114,14 +114,16 @@ struct ufs_qcom_phy {
|
||||
struct ufs_qcom_phy_calibration *cached_regs;
|
||||
int cached_regs_table_size;
|
||||
bool is_powered_on;
|
||||
bool is_started;
|
||||
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
|
||||
* specific implementation per phy. Each UFS phy, should implement
|
||||
* 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
|
||||
* @is_physical_coding_sublayer_ready: pointer to a function that
|
||||
* checks pcs readiness. returns 0 for success and non-zero for error.
|
||||
@ -130,7 +132,6 @@ struct ufs_qcom_phy {
|
||||
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
||||
*/
|
||||
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);
|
||||
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
|
||||
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)
|
||||
|
||||
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)
|
||||
@ -52,6 +64,19 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
|
||||
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
|
||||
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 = {
|
||||
.exit = ufs_qcom_phy_qmp_14nm_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
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,
|
||||
.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,
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
@ -71,6 +83,19 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
|
||||
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
|
||||
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 = {
|
||||
.exit = ufs_qcom_phy_qmp_20nm_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
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,
|
||||
.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,
|
||||
|
@ -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;
|
||||
|
||||
if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
|
||||
@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
|
||||
|
||||
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)
|
||||
{
|
||||
@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
|
||||
}
|
||||
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) {
|
||||
dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
|
||||
__func__);
|
||||
@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
|
||||
return ufs_qcom_phy->phy_spec_ops->
|
||||
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)
|
||||
{
|
||||
@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
|
||||
if (phy_common->is_powered_on)
|
||||
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);
|
||||
if (err) {
|
||||
dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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:
|
||||
* Copyright (C) 2014 Renesas Solutions Corp.
|
||||
@ -18,10 +18,12 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/******* USB2.0 Host registers (original offset is +0x200) *******/
|
||||
@ -79,6 +81,8 @@
|
||||
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
||||
#define USB2_ADPCTRL_DRVVBUS BIT(4)
|
||||
|
||||
#define RCAR_GEN3_PHY_HAS_DEDICATED_PINS 1
|
||||
|
||||
struct rcar_gen3_chan {
|
||||
void __iomem *base;
|
||||
struct extcon_dev *extcon;
|
||||
@ -86,7 +90,7 @@ struct rcar_gen3_chan {
|
||||
struct regulator *vbus;
|
||||
struct work_struct work;
|
||||
bool extcon_host;
|
||||
bool has_otg;
|
||||
bool has_otg_pins;
|
||||
};
|
||||
|
||||
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)
|
||||
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,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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")))
|
||||
new_mode_is_host = true;
|
||||
new_mode = PHY_MODE_USB_HOST;
|
||||
else if (!strncmp(buf, "peripheral", strlen("peripheral")))
|
||||
new_mode_is_host = false;
|
||||
new_mode = PHY_MODE_USB_DEVICE;
|
||||
else
|
||||
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 (is_host == new_mode_is_host)
|
||||
if (cur_mode == new_mode)
|
||||
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 */
|
||||
rcar_gen3_init_from_a_peri_to_a_host(ch);
|
||||
else /* B-Peripheral */
|
||||
@ -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);
|
||||
|
||||
if (!ch->has_otg || !ch->phy->init_count)
|
||||
if (!ch->has_otg_pins || !ch->phy->init_count)
|
||||
return -EIO;
|
||||
|
||||
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)
|
||||
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
|
||||
|
||||
/* Initialize otg part */
|
||||
if (channel->has_otg)
|
||||
if (channel->has_otg_pins)
|
||||
rcar_gen3_init_otg(channel);
|
||||
|
||||
return 0;
|
||||
@ -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[] = {
|
||||
{ .compatible = "renesas,usb2-phy-r8a7795" },
|
||||
{ .compatible = "renesas,usb2-phy-r8a7796" },
|
||||
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
|
||||
{
|
||||
.compatible = "renesas,usb2-phy-r8a7795",
|
||||
.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);
|
||||
@ -415,14 +434,17 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
/* call request_irq for OTG */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0) {
|
||||
int ret;
|
||||
|
||||
INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
|
||||
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
|
||||
IRQF_SHARED, dev_name(dev), channel);
|
||||
if (irq < 0)
|
||||
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,
|
||||
rcar_gen3_phy_cable);
|
||||
if (IS_ERR(channel->extcon))
|
||||
@ -464,7 +486,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "Failed to register PHY provider\n");
|
||||
ret = PTR_ERR(provider);
|
||||
goto error;
|
||||
} else if (channel->has_otg) {
|
||||
} else if (channel->has_otg_pins) {
|
||||
int ret;
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_role);
|
||||
@ -484,7 +506,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *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);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -102,9 +102,40 @@
|
||||
#define CMN_PLL1_SS_CTRL1 (0xb8 << 2)
|
||||
#define CMN_PLL1_SS_CTRL2 (0xb9 << 2)
|
||||
#define CMN_RXCAL_OVRD (0xd1 << 2)
|
||||
|
||||
#define CMN_TXPUCAL_CTRL (0xe0 << 2)
|
||||
#define CMN_TXPUCAL_OVRD (0xe1 << 2)
|
||||
#define CMN_TXPDCAL_CTRL (0xf0 << 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_FBL_OVRD (0x1c1 << 2)
|
||||
#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2)
|
||||
@ -138,6 +169,15 @@
|
||||
#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_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_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2)
|
||||
#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2)
|
||||
@ -150,10 +190,63 @@
|
||||
#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2)
|
||||
#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 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 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 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 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_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 TX_ANA_CTRL_REG_3 (0x5026 << 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)
|
||||
*/
|
||||
tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1);
|
||||
if (!tcphy->flip)
|
||||
tx_ana_ctrl_reg_1 |= BIT(12);
|
||||
tx_ana_ctrl_reg_1 |= AUXDA_POLARITY;
|
||||
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);
|
||||
}
|
||||
|
||||
static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
|
||||
{
|
||||
u16 val;
|
||||
u16 tx_ana_ctrl_reg_1;
|
||||
u16 rdata, rdata2, val;
|
||||
u16 tx_ana_ctrl_reg_2;
|
||||
s32 pu_calib_code, pd_calib_code;
|
||||
s32 pu_adj, pd_adj;
|
||||
u16 calib;
|
||||
|
||||
/*
|
||||
* Calculate calibration code as per docs: use an average of the
|
||||
* pull down and pull up. Then add in adjustments.
|
||||
*/
|
||||
val = readl(tcphy->base + CMN_TXPUCAL_CTRL);
|
||||
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;
|
||||
|
||||
/* disable txda_cal_latch_en for rewrite the calibration values */
|
||||
tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1);
|
||||
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);
|
||||
|
||||
/*
|
||||
* read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
|
||||
* write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
|
||||
* works.
|
||||
*/
|
||||
rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
|
||||
rdata = rdata & 0xffc0;
|
||||
|
||||
rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
|
||||
rdata2 = rdata2 & 0x3f;
|
||||
|
||||
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);
|
||||
usleep_range(1000, 1050);
|
||||
usleep_range(10000, 10050);
|
||||
|
||||
/*
|
||||
* Enable signal for latch that sample and holds calibration values.
|
||||
* Activate this signal for 1 clock cycle to sample new calibration
|
||||
* 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);
|
||||
usleep_range(150, 200);
|
||||
|
||||
/* set TX Voltage Level and TX Deemphasis to 0 */
|
||||
writel(0, tcphy->base + PHY_DP_TX_CTL);
|
||||
|
||||
/* re-enable decap */
|
||||
writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
|
||||
writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
|
||||
tx_ana_ctrl_reg_1 |= BIT(3);
|
||||
tx_ana_ctrl_reg_2 = XCVR_DECAP_EN;
|
||||
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
|
||||
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);
|
||||
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(0, tcphy->base + TX_ANA_CTRL_REG_5);
|
||||
@ -515,44 +626,66 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
|
||||
writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
/*
|
||||
* re-enables the transmitter pre-driver, driver data selection MUX,
|
||||
* and receiver detect circuits.
|
||||
*/
|
||||
writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
|
||||
writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
|
||||
tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN;
|
||||
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
|
||||
* undo the "re-enables Bandgap reference for LDO" above.
|
||||
* Do all the undocumented magic:
|
||||
* - 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 &= ~BIT(8);
|
||||
tx_ana_ctrl_reg_1 &= ~BIT(7);
|
||||
tx_ana_ctrl_reg_1 |= BIT(6);
|
||||
tx_ana_ctrl_reg_1 |= BIT(5);
|
||||
tx_ana_ctrl_reg_1 |= TXDA_DP_AUX_EN;
|
||||
tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN;
|
||||
tx_ana_ctrl_reg_1 &= ~TXDA_DRV_LDO_EN;
|
||||
tx_ana_ctrl_reg_1 &= ~TXDA_BGREF_EN;
|
||||
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(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
|
||||
* driver to 400mv. The values below are peak to peak (differential)
|
||||
* values.
|
||||
* Undo the work we did to set the LDO voltage.
|
||||
* This doesn't seem to help nor hurt, but it kinda goes with the
|
||||
* 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);
|
||||
|
||||
/* Init TXDA_CYA_AUXDA_CYA for unknown magic reasons */
|
||||
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 |= BIT(15);
|
||||
val |= TX_HIGH_Z_TM_EN;
|
||||
writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,40 @@
|
||||
#define PCIE_PCS_MASK 0xFF0000
|
||||
#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
|
||||
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
|
||||
@ -91,6 +125,8 @@ struct pipe3_dpll_map {
|
||||
|
||||
struct ti_pipe3 {
|
||||
void __iomem *pll_ctrl_base;
|
||||
void __iomem *phy_rx;
|
||||
void __iomem *phy_tx;
|
||||
struct device *dev;
|
||||
struct device *control_dev;
|
||||
struct clk *wkupclk;
|
||||
@ -261,6 +297,37 @@ static int ti_pipe3_dpll_program(struct ti_pipe3 *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)
|
||||
{
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
@ -282,7 +349,12 @@ static int ti_pipe3_init(struct phy *x)
|
||||
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
|
||||
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
|
||||
PCIE_PCS_MASK, val);
|
||||
return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ti_pipe3_calibrate(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bring it out of IDLE if it is IDLE */
|
||||
@ -513,6 +585,29 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
|
||||
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)
|
||||
{
|
||||
struct resource *res;
|
||||
@ -559,6 +654,10 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_pipe3_get_tx_rx_base(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_pipe3_get_sysctrl(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -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)
|
||||
? true : false;
|
||||
|
||||
if (is_rate_B)
|
||||
phy_set_mode(phy, PHY_MODE_UFS_HS_B);
|
||||
|
||||
/* Assert PHY reset and apply PHY calibration values */
|
||||
ufs_qcom_assert_reset(hba);
|
||||
/* provide 1ms delay to let the reset pulse propagate */
|
||||
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) {
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
@ -294,21 +297,22 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||
* voltage, current to settle down before starting serdes.
|
||||
*/
|
||||
usleep_range(1000, 1100);
|
||||
ret = ufs_qcom_phy_start_serdes(phy);
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "%s: ufs_qcom_phy_start_serdes() failed, ret = %d\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ufs_qcom_phy_is_pcs_ready(phy);
|
||||
if (ret)
|
||||
dev_err(hba->dev,
|
||||
"%s: is_physical_coding_sublayer_ready() failed, ret = %d\n",
|
||||
/* power on phy - start serdes and phy's power and clocks */
|
||||
ret = phy_power_on(phy);
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "%s: phy power on failed, ret = %d\n",
|
||||
__func__, ret);
|
||||
goto out_disable_phy;
|
||||
}
|
||||
|
||||
ufs_qcom_select_unipro_mode(host);
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_phy:
|
||||
ufs_qcom_assert_reset(hba);
|
||||
phy_exit(phy);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@ -1273,14 +1277,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
||||
ufs_qcom_phy_save_controller_version(host->generic_phy,
|
||||
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);
|
||||
if (err)
|
||||
goto out_disable_phy;
|
||||
goto out_variant_clear;
|
||||
|
||||
ufs_qcom_set_caps(hba);
|
||||
ufs_qcom_advertise_quirks(hba);
|
||||
@ -1301,10 +1300,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
||||
|
||||
goto out;
|
||||
|
||||
out_disable_phy:
|
||||
phy_power_off(host->generic_phy);
|
||||
out_unregister_bus:
|
||||
phy_exit(host->generic_phy);
|
||||
out_variant_clear:
|
||||
ufshcd_set_variant(hba, NULL);
|
||||
out:
|
||||
|
@ -40,6 +40,18 @@ bool soc_is_brcmstb(void)
|
||||
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[] = {
|
||||
{ .compatible = "brcm,bcm7125-sun-top-ctrl", },
|
||||
{ .compatible = "brcm,bcm7346-sun-top-ctrl", },
|
||||
|
@ -15,5 +15,6 @@
|
||||
#define PHY_TYPE_PCIE 2
|
||||
#define PHY_TYPE_USB2 3
|
||||
#define PHY_TYPE_USB3 4
|
||||
#define PHY_TYPE_UFS 5
|
||||
|
||||
#endif /* _DT_BINDINGS_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);
|
||||
|
||||
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_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,
|
||||
u8 major, u16 minor, u16 step);
|
||||
|
||||
|
@ -29,6 +29,8 @@ enum phy_mode {
|
||||
PHY_MODE_USB_OTG,
|
||||
PHY_MODE_SGMII,
|
||||
PHY_MODE_10GKR,
|
||||
PHY_MODE_UFS_HS_A,
|
||||
PHY_MODE_UFS_HS_B,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -39,6 +41,7 @@ enum phy_mode {
|
||||
* @power_off: powering off the phy
|
||||
* @set_mode: set the mode of the phy
|
||||
* @reset: resetting the phy
|
||||
* @calibrate: calibrate the phy
|
||||
* @owner: the module owner containing the ops
|
||||
*/
|
||||
struct phy_ops {
|
||||
@ -48,6 +51,7 @@ struct phy_ops {
|
||||
int (*power_off)(struct phy *phy);
|
||||
int (*set_mode)(struct phy *phy, enum phy_mode mode);
|
||||
int (*reset)(struct phy *phy);
|
||||
int (*calibrate)(struct phy *phy);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
@ -141,6 +145,7 @@ int phy_power_on(struct phy *phy);
|
||||
int phy_power_off(struct phy *phy);
|
||||
int phy_set_mode(struct phy *phy, enum phy_mode mode);
|
||||
int phy_reset(struct phy *phy);
|
||||
int phy_calibrate(struct phy *phy);
|
||||
static inline int phy_get_bus_width(struct phy *phy)
|
||||
{
|
||||
return phy->attrs.bus_width;
|
||||
@ -262,6 +267,13 @@ static inline int phy_reset(struct phy *phy)
|
||||
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)
|
||||
{
|
||||
return -ENOSYS;
|
||||
@ -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,
|
||||
const char *string)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct phy *devm_of_phy_get(struct device *dev,
|
||||
|
@ -1,10 +1,27 @@
|
||||
#ifndef __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,
|
||||
* before SMP is brought up, called by machine entry point.
|
||||
*/
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user