| From a2eaa93a5887ddd20af0373244481139627d0d77 Mon Sep 17 00:00:00 2001 |
| From: Zhanyong Wang <zhanyong.wang@mediatek.com> |
| Date: Mon, 8 Nov 2021 14:51:38 +0800 |
| Subject: [PATCH 2/8] phy: phy-mtk-tphy: add support efuse setting |
| |
| Due to some SoCs have a bit shift issue that will drop a bit for usb3 |
| phy or pcie phy, fix it by adding software efuse reading and setting, |
| but only support it optionally for versoin. |
| |
| Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com> |
| Signed-off-by: Zhanyong Wang <zhanyong.wang@mediatek.com> |
| Change-Id: Ibf88868668b3889f18c7930531981400cac732f1 |
| --- |
| drivers/phy/mediatek/phy-mtk-tphy.c | 195 ++++++++++++++++++++++++++++ |
| 1 file changed, 195 insertions(+) |
| |
| diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c |
| index a59fe65f69e5..ce2731b2f5ff 100644 |
| --- a/drivers/phy/mediatek/phy-mtk-tphy.c |
| +++ b/drivers/phy/mediatek/phy-mtk-tphy.c |
| @@ -12,6 +12,7 @@ |
| #include <linux/iopoll.h> |
| #include <linux/mfd/syscon.h> |
| #include <linux/module.h> |
| +#include <linux/nvmem-consumer.h> |
| #include <linux/of_address.h> |
| #include <linux/of_device.h> |
| #include <linux/phy/phy.h> |
| @@ -39,11 +40,16 @@ |
| #define SSUSB_SIFSLV_V2_U3PHYD 0x200 |
| #define SSUSB_SIFSLV_V2_U3PHYA 0x400 |
| |
| +#define U3P_MISC_REG1 0x04 |
| +#define MR1_EFUSE_AUTO_LOAD_DIS BIT(6) |
| + |
| #define U3P_USBPHYACR0 0x000 |
| #define PA0_RG_U2PLL_FORCE_ON BIT(15) |
| #define PA0_RG_USB20_INTR_EN BIT(5) |
| |
| #define U3P_USBPHYACR1 0x004 |
| +#define PA1_RG_INTR_CAL GENMASK(23, 19) |
| +#define PA1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) |
| #define PA1_RG_VRT_SEL GENMASK(14, 12) |
| #define PA1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) |
| #define PA1_RG_TERM_SEL GENMASK(10, 8) |
| @@ -115,6 +121,8 @@ |
| #define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) |
| |
| #define U3P_U3_PHYA_REG0 0x000 |
| +#define P3A_RG_IEXT_INTR GENMASK(15, 10) |
| +#define P3A_RG_IEXT_INTR_VAL(x) ((0x3f & (x)) << 10) |
| #define P3A_RG_CLKDRV_OFF GENMASK(3, 2) |
| #define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) |
| |
| @@ -169,6 +177,25 @@ |
| #define P3D_RG_FWAKE_TH GENMASK(21, 16) |
| #define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16) |
| |
| +#define U3P_U3_PHYD_IMPCAL0 0x010 |
| +#define P3D_RG_FORCE_TX_IMPEL BIT(31) |
| +#define P3D_RG_TX_IMPEL GENMASK(28, 24) |
| +#define P3D_RG_TX_IMPEL_VAL(x) ((0x1f & (x)) << 24) |
| + |
| +#define U3P_U3_PHYD_IMPCAL1 0x014 |
| +#define P3D_RG_FORCE_RX_IMPEL BIT(31) |
| +#define P3D_RG_RX_IMPEL GENMASK(28, 24) |
| +#define P3D_RG_RX_IMPEL_VAL(x) ((0x1f & (x)) << 24) |
| + |
| +#define U3P_U3_PHYD_RX0 0x02c |
| + |
| +#define U3P_U3_PHYD_T2RLB 0x030 |
| + |
| +#define U3P_U3_PHYD_PIPE0 0x040 |
| + |
| +#define U3P_U3_PHYD_RSV 0x054 |
| +#define P3D_RG_EFUSE_AUTO_LOAD_DIS BIT(12) |
| + |
| #define U3P_U3_PHYD_CDR1 0x05c |
| #define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) |
| #define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24) |
| @@ -275,11 +302,23 @@ |
| enum mtk_phy_version { |
| MTK_PHY_V1 = 1, |
| MTK_PHY_V2, |
| + MTK_PHY_V3, |
| }; |
| |
| struct mtk_phy_pdata { |
| /* avoid RX sensitivity level degradation only for mt8173 */ |
| bool avoid_rx_sen_degradation; |
| + /* |
| + * u2phy should use integer mode instead of fractional mode of |
| + * 48M PLL, fix it by switching PLL to 26M from default 48M |
| + * for mt8195 |
| + */ |
| + bool sx_pll_48m_to_26m; |
| + /* |
| + * Some SoCs (e.g. mt8195) drop a bit when use auto load efuse, |
| + * support sw way, also support it for v2/v3 optionally. |
| + */ |
| + bool sw_efuse_supported; |
| enum mtk_phy_version version; |
| }; |
| |
| @@ -304,6 +343,10 @@ struct mtk_phy_instance { |
| struct u3phy_banks u3_banks; |
| }; |
| struct clk *ref_clk; /* reference clock of anolog phy */ |
| + u32 efuse_sw_en; |
| + u32 efuse_intr; |
| + u32 efuse_tx_imp; |
| + u32 efuse_rx_imp; |
| u32 index; |
| u32 type; |
| struct regmap *type_sw; |
| @@ -960,6 +1003,139 @@ static int phy_type_set(struct mtk_phy_instance *instance) |
| return 0; |
| } |
| |
| +static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) |
| +{ |
| + struct device *dev = &instance->phy->dev; |
| + int ret = 0; |
| + |
| + dev_err(dev, "try to get sw efuse\n"); |
| + |
| + /* tphy v1 doesn't support sw efuse, skip it */ |
| + if (!tphy->pdata->sw_efuse_supported) { |
| + instance->efuse_sw_en = 0; |
| + return 0; |
| + } |
| + |
| + /* software efuse is optional */ |
| + instance->efuse_sw_en = device_property_read_bool(dev, "nvmem-cells"); |
| + if (!instance->efuse_sw_en) |
| + return 0; |
| + |
| + dev_err(dev, "try to get sw efuse+\n"); |
| + |
| + switch (instance->type) { |
| + case PHY_TYPE_USB2: |
| + ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); |
| + if (ret) { |
| + dev_err(dev, "fail to get u2 intr efuse, %d\n", ret); |
| + break; |
| + } |
| + |
| + /* no efuse, ignore it */ |
| + if (!instance->efuse_intr) { |
| + dev_warn(dev, "no u2 intr efuse, but dts enable it\n"); |
| + instance->efuse_sw_en = 0; |
| + break; |
| + } |
| + |
| + dev_info(dev, "u2 efuse - intr %x\n", instance->efuse_intr); |
| + break; |
| + case PHY_TYPE_USB3: |
| + case PHY_TYPE_PCIE: |
| + ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); |
| + if (ret) { |
| + dev_err(dev, "fail to get u3 intr efuse, %d\n", ret); |
| + break; |
| + } |
| + |
| + ret = nvmem_cell_read_variable_le_u32(dev, "rx_imp", &instance->efuse_rx_imp); |
| + if (ret) { |
| + dev_err(dev, "fail to get u3 rx_imp efuse, %d\n", ret); |
| + break; |
| + } |
| + |
| + ret = nvmem_cell_read_variable_le_u32(dev, "tx_imp", &instance->efuse_tx_imp); |
| + if (ret) { |
| + dev_err(dev, "fail to get u3 tx_imp efuse, %d\n", ret); |
| + break; |
| + } |
| + |
| + /* no efuse, ignore it */ |
| + if (!instance->efuse_intr && |
| + !instance->efuse_rx_imp && |
| + !instance->efuse_tx_imp) { |
| + dev_warn(dev, "no u3 intr efuse, but dts enable it\n"); |
| + instance->efuse_sw_en = 0; |
| + break; |
| + } |
| + |
| + dev_info(dev, "u3 efuse - intr %x, rx_imp %x, tx_imp %x\n", |
| + instance->efuse_intr, instance->efuse_rx_imp, |
| + instance->efuse_tx_imp); |
| + break; |
| + default: |
| + dev_err(dev, "no sw efuse for type %d\n", instance->type); |
| + ret = -EINVAL; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +static void phy_efuse_set(struct mtk_phy_instance *instance) |
| +{ |
| + struct device *dev = &instance->phy->dev; |
| + struct u2phy_banks *u2_banks = &instance->u2_banks; |
| + struct u3phy_banks *u3_banks = &instance->u3_banks; |
| + u32 tmp; |
| + |
| + if (!instance->efuse_sw_en) |
| + return; |
| + |
| + switch (instance->type) { |
| + case PHY_TYPE_USB2: |
| + tmp = readl(u2_banks->misc + U3P_MISC_REG1); |
| + tmp |= MR1_EFUSE_AUTO_LOAD_DIS; |
| + writel(tmp, u2_banks->misc + U3P_MISC_REG1); |
| + |
| + tmp = readl(u2_banks->com + U3P_USBPHYACR1); |
| + tmp &= ~PA1_RG_INTR_CAL; |
| + tmp |= PA1_RG_INTR_CAL_VAL(instance->efuse_intr); |
| + writel(tmp, u2_banks->com + U3P_USBPHYACR1); |
| + pr_err("%s set efuse intr %x\n", __func__, instance->efuse_intr); |
| + |
| + break; |
| + case PHY_TYPE_USB3: |
| + case PHY_TYPE_PCIE: |
| + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); |
| + tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; |
| + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); |
| + |
| + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); |
| + tmp &= ~P3D_RG_TX_IMPEL; |
| + tmp |= P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp); |
| + tmp |= P3D_RG_FORCE_TX_IMPEL; |
| + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); |
| + |
| + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); |
| + tmp &= ~P3D_RG_RX_IMPEL; |
| + tmp |= P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp); |
| + tmp |= P3D_RG_FORCE_RX_IMPEL; |
| + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); |
| + |
| + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); |
| + tmp &= ~P3A_RG_IEXT_INTR; |
| + tmp |= P3A_RG_IEXT_INTR_VAL(instance->efuse_intr); |
| + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); |
| + pr_err("%s set efuse, tx_imp %x, rx_imp %x intr %x\n", |
| + __func__, instance->efuse_tx_imp, |
| + instance->efuse_rx_imp, instance->efuse_intr); |
| + break; |
| + default: |
| + dev_warn(dev, "no sw efuse for type %d\n", instance->type); |
| + } |
| +} |
| + |
| + |
| static int mtk_phy_init(struct phy *phy) |
| { |
| struct mtk_phy_instance *instance = phy_get_drvdata(phy); |
| @@ -978,6 +1154,8 @@ static int mtk_phy_init(struct phy *phy) |
| return ret; |
| } |
| |
| + phy_efuse_set(instance); |
| + |
| switch (instance->type) { |
| case PHY_TYPE_USB2: |
| u2_phy_instance_init(tphy, instance); |
| @@ -1062,6 +1240,7 @@ static struct phy *mtk_phy_xlate(struct device *dev, |
| struct mtk_phy_instance *instance = NULL; |
| struct device_node *phy_np = args->np; |
| int index; |
| + int ret; |
| |
| if (args->args_count != 1) { |
| dev_err(dev, "invalid number of cells in 'phy' property\n"); |
| @@ -1098,6 +1277,10 @@ static struct phy *mtk_phy_xlate(struct device *dev, |
| return ERR_PTR(-EINVAL); |
| } |
| |
| + ret = phy_efuse_get(tphy, instance); |
| + if (ret) |
| + return ERR_PTR(ret); |
| + |
| phy_parse_property(tphy, instance); |
| phy_type_set(instance); |
| |
| @@ -1120,14 +1303,26 @@ static const struct mtk_phy_pdata tphy_v1_pdata = { |
| |
| static const struct mtk_phy_pdata tphy_v2_pdata = { |
| .avoid_rx_sen_degradation = false, |
| + .sw_efuse_supported = true, |
| .version = MTK_PHY_V2, |
| }; |
| |
| +static const struct mtk_phy_pdata tphy_v3_pdata = { |
| + .sw_efuse_supported = true, |
| + .version = MTK_PHY_V3, |
| +}; |
| + |
| static const struct mtk_phy_pdata mt8173_pdata = { |
| .avoid_rx_sen_degradation = true, |
| .version = MTK_PHY_V1, |
| }; |
| |
| +static const struct mtk_phy_pdata mt8195_pdata = { |
| + .sx_pll_48m_to_26m = true, |
| + .sw_efuse_supported = true, |
| + .version = MTK_PHY_V3, |
| +}; |
| + |
| static const struct of_device_id mtk_tphy_id_table[] = { |
| { .compatible = "mediatek,mt2701-u3phy", .data = &tphy_v1_pdata }, |
| { .compatible = "mediatek,mt2712-u3phy", .data = &tphy_v2_pdata }, |
| -- |
| 2.18.0 |
| |