704 lines
19 KiB
Diff
704 lines
19 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Marvin Wewer <mwewer37@proton.me>
|
|
Date: Sat, 25 Oct 2025 20:32:35 +0000
|
|
Subject: phy: allwinner: Add SUN55I INNO combo PHY driver for PCIe/USB3
|
|
|
|
Signed-off-by: Marvin Wewer <mwewer37@proton.me>
|
|
---
|
|
drivers/phy/allwinner/Kconfig | 8 +
|
|
drivers/phy/allwinner/Makefile | 1 +
|
|
drivers/phy/allwinner/phy-sun55i-pcie-usb3.c | 656 ++++++++++
|
|
3 files changed, 665 insertions(+)
|
|
|
|
diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/phy/allwinner/Kconfig
|
|
+++ b/drivers/phy/allwinner/Kconfig
|
|
@@ -33,3 +33,11 @@ config PHY_SUN50I_USB3
|
|
help
|
|
Enable this to support the USB3 transceiver that is part of
|
|
Allwinner sun50i SoCs.
|
|
+
|
|
+config PHY_SUN55I_PCIE_USB3
|
|
+ tristate "Allwinner SUN55I INNO COMBO PHY Driver"
|
|
+ depends on ARCH_SUNXI
|
|
+ select PHY
|
|
+ help
|
|
+ Enable this to support the Allwinner sun55i PCIe/USB3.0 combo PHY
|
|
+ with INNOSILICON IP block.
|
|
diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/phy/allwinner/Makefile
|
|
+++ b/drivers/phy/allwinner/Makefile
|
|
@@ -5,3 +5,4 @@
|
|
|
|
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
|
obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o
|
|
+obj-$(CONFIG_PHY_SUN55I_PCIE_USB3) += phy-sun55i-pcie-usb3.o
|
|
\ No newline at end of file
|
|
diff --git a/drivers/phy/allwinner/phy-sun55i-pcie-usb3.c b/drivers/phy/allwinner/phy-sun55i-pcie-usb3.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/phy/allwinner/phy-sun55i-pcie-usb3.c
|
|
@@ -0,0 +1,656 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Allwinner PIPE USB3.0 PCIE Combo Phy driver
|
|
+ *
|
|
+ * Copyright(c) 2020 - 2024 Allwinner Technology Co.,Ltd. All rights reserved.
|
|
+ *
|
|
+ * sun55i-inno-combophy.c: chenhuaqiang <chenhuaqiang@allwinnertech.com>
|
|
+ */
|
|
+
|
|
+
|
|
+#include <clk-uclass.h>
|
|
+#include <dm.h>
|
|
+#include <log.h>
|
|
+#include <dm/device.h>
|
|
+#include <dm/device_compat.h>
|
|
+#include <dm/lists.h>
|
|
+#include <dt-bindings/phy/phy.h>
|
|
+#include <generic-phy.h>
|
|
+#include <asm/io.h>
|
|
+#include <power-domain.h>
|
|
+#include <regmap.h>
|
|
+#include <syscon.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/err.h>
|
|
+#include <power/regulator.h>
|
|
+#include <reset.h>
|
|
+
|
|
+
|
|
+#define sun55i_COMBOPHY_CTL_BASE 0x04f00000
|
|
+#define sun55i_COMBOPHY_CLK_BASE 0x04f80000
|
|
+
|
|
+/* PCIE USB3 Sub-System Registers */
|
|
+/* Sub-System Version Reset Register */
|
|
+#define PCIE_USB3_SYS_VER 0x00
|
|
+
|
|
+/* CCMU Base Address */
|
|
+#define SUNXI_CCMU_BASE 0x02001000
|
|
+#define SUNXI_CCM_BASE (SUNXI_CCMU_BASE)
|
|
+
|
|
+/* Sub-System PCIE Bus Gating Reset Register */
|
|
+#define PCIE_BRG_REG_RST 16
|
|
+#define PCIE_BGR_REG (SUNXI_CCM_BASE + 0x0aac)
|
|
+
|
|
+/* Sub-System PCIE Bus Gating Reset Register */
|
|
+#define PCIE_COMBO_PHY_BGR 0x04
|
|
+#define PCIE_SLV_ACLK_EN BIT(18)
|
|
+#define PCIE_ACLK_EN BIT(17)
|
|
+#define PCIE_HCLK_EN BIT(16)
|
|
+#define PCIE_PERSTN BIT(1)
|
|
+#define PCIE_PW_UP_RSTN BIT(0)
|
|
+
|
|
+/* Sub-System USB3 Bus Gating Reset Register */
|
|
+#define USB3_COMBO_PHY_BGR 0x08
|
|
+#define USB3_ACLK_EN BIT(17)
|
|
+#define USB3_HCLK_EN BIT(16)
|
|
+#define USB3_U2_PHY_RSTN BIT(4)
|
|
+#define USB3_U2_PHY_MUX_EN BIT(3)
|
|
+#define USB3_U2_PHY_MUX_SEL BIT(0)
|
|
+#define USB3_RESETN BIT(0)
|
|
+
|
|
+/* Sub-System PCIE PHY Control Register */
|
|
+#define PCIE_COMBO_PHY_CTL 0x10
|
|
+#define PHY_USE_SEL BIT(31) /* 0:PCIE; 1:USB3 */
|
|
+#define PHY_CLK_SEL BIT(30) /* 0:internal clk; 1:external clk */
|
|
+#define PHY_BIST_EN BIT(16)
|
|
+#define PHY_PIPE_SW BIT(9)
|
|
+#define PHY_PIPE_SEL BIT(8) /* 0:rstn by PCIE or USB3; 1:rstn by PHY_PIPE_SW */
|
|
+#define PHY_PIPE_CLK_INVERT BIT(4)
|
|
+#define PHY_FPGA_SYS_RSTN BIT(1) /* for FPGA */
|
|
+#define PHY_RSTN BIT(0)
|
|
+
|
|
+/* PHY CLK Registers (offset from sun55i_COMBOPHY_CLK_BASE) */
|
|
+#define PCIE_REF_CLK_REG_PCIE_REF_CLK_GATING_CLEAR_MASK 0x80000000
|
|
+
|
|
+/* Registers */
|
|
+#define COMBO_REG_SYSVER(comb_base_addr) ((comb_base_addr) \
|
|
+ + PCIE_USB3_SYS_VER)
|
|
+#define COMBO_REG_PCIEBGR(comb_base_addr) ((comb_base_addr) \
|
|
+ + PCIE_COMBO_PHY_BGR)
|
|
+#define COMBO_REG_USB3BGR(comb_base_addr) ((comb_base_addr) \
|
|
+ + USB3_COMBO_PHY_BGR)
|
|
+#define COMBO_REG_PHYCTRL(comb_base_addr) ((comb_base_addr) \
|
|
+ + PCIE_COMBO_PHY_CTL)
|
|
+
|
|
+/* Sub-System Version Number */
|
|
+#define COMBO_VERSION_01 (0x10000)
|
|
+#define COMBO_VERSION_ANY (0x0)
|
|
+
|
|
+enum phy_use_sel {
|
|
+ PHY_USE_BY_PCIE = 0, /* PHY used by PCIE */
|
|
+ PHY_USE_BY_USB3, /* PHY used by USB3 */
|
|
+ PHY_USE_BY_PCIE_USB3_U2,/* PHY used by PCIE & USB3_U2 */
|
|
+};
|
|
+
|
|
+enum phy_refclk_sel {
|
|
+ INTER_SIG_REF_CLK = 0, /* PHY use internal single end reference clock */
|
|
+ EXTER_DIF_REF_CLK, /* PHY use external single end reference clock */
|
|
+};
|
|
+
|
|
+struct sun55i_combophy_of_data {
|
|
+ bool has_cfg_clk;
|
|
+ bool has_slv_clk;
|
|
+ bool has_phy_mbus_clk;
|
|
+ bool has_phy_ahb_clk;
|
|
+ bool has_pcie_axi_clk;
|
|
+ bool has_u2_phy_mux;
|
|
+ bool need_noppu_rst;
|
|
+ bool has_u3_phy_data_quirk;
|
|
+ bool need_optimize_jitter;
|
|
+};
|
|
+
|
|
+struct sun55i_combphy {
|
|
+ struct device *dev;
|
|
+ struct phy *phy;
|
|
+ void __iomem *phy_ctl; /* parse dts, control the phy mode, reset and power */
|
|
+ void __iomem *phy_clk; /* parse dts, set the phy clock */
|
|
+
|
|
+ struct reset_ctl reset;
|
|
+ struct reset_ctl noppu_reset;
|
|
+
|
|
+ struct clk *phyclk_ref;
|
|
+ struct clk *refclk_par;
|
|
+ struct clk *phyclk_cfg;
|
|
+ struct clk *cfgclk_par;
|
|
+ struct clk *phy_mclk;
|
|
+ struct clk *phy_hclk;
|
|
+ struct clk *phy_axi;
|
|
+ struct clk *phy_axi_par;
|
|
+ __u8 mode;
|
|
+ __u32 vernum; /* version number */
|
|
+ enum phy_use_sel user;
|
|
+ enum phy_refclk_sel ref;
|
|
+ const struct sun55i_combophy_of_data *drvdata;
|
|
+
|
|
+ struct udevice *select3v3_supply;
|
|
+ bool initialized;
|
|
+};
|
|
+
|
|
+static void sun55i_combphy_usb3_phy_set(struct sun55i_combphy *combphy, bool enable)
|
|
+{
|
|
+ u32 val, tmp = 0;
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1418);
|
|
+ tmp = GENMASK(17, 16);
|
|
+ if (enable) {
|
|
+ val &= ~tmp;
|
|
+ val |= BIT(25);
|
|
+ } else {
|
|
+ val |= tmp;
|
|
+ val &= ~BIT(25);
|
|
+ }
|
|
+ writel(val, combphy->phy_clk + 0x1418);
|
|
+
|
|
+ /* reg_rx_eq_bypass[3]=1, rx_ctle_res_cal_bypass */
|
|
+ val = readl(combphy->phy_clk + 0x0674);
|
|
+ if (enable)
|
|
+ val |= BIT(3);
|
|
+ else
|
|
+ val &= ~BIT(3);
|
|
+ writel(val, combphy->phy_clk + 0x0674);
|
|
+
|
|
+ /* rx_ctle_res_cal=0xf, 0x4->0xf */
|
|
+ val = readl(combphy->phy_clk + 0x0704);
|
|
+ tmp = GENMASK(9, 8) | BIT(11);
|
|
+ if (enable)
|
|
+ val |= tmp;
|
|
+ else
|
|
+ val &= ~tmp;
|
|
+ writel(val, combphy->phy_clk + 0x0704);
|
|
+
|
|
+ /* CDR_div_fin_gain1 */
|
|
+ val = readl(combphy->phy_clk + 0x0400);
|
|
+ if (enable)
|
|
+ val |= BIT(4);
|
|
+ else
|
|
+ val &= ~BIT(4);
|
|
+ writel(val, combphy->phy_clk + 0x0400);
|
|
+
|
|
+ /* CDR_div1_fin_gain1 */
|
|
+ val = readl(combphy->phy_clk + 0x0404);
|
|
+ tmp = GENMASK(3, 0) | BIT(5);
|
|
+ if (enable)
|
|
+ val |= tmp;
|
|
+ else
|
|
+ val &= ~tmp;
|
|
+ writel(val, combphy->phy_clk + 0x0404);
|
|
+
|
|
+ /* CDR_div3_fin_gain1 */
|
|
+ val = readl(combphy->phy_clk + 0x0408);
|
|
+ if (enable)
|
|
+ val |= BIT(5);
|
|
+ else
|
|
+ val &= ~BIT(5);
|
|
+ writel(val, combphy->phy_clk + 0x0408);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x109c);
|
|
+ if (enable)
|
|
+ val |= BIT(1);
|
|
+ else
|
|
+ val &= ~BIT(1);
|
|
+ writel(val, combphy->phy_clk + 0x109c);
|
|
+
|
|
+ /* balance parm configure */
|
|
+ if (combphy->drvdata->has_u3_phy_data_quirk) {
|
|
+ val = readl(combphy->phy_clk + 0x0804);
|
|
+ if (enable)
|
|
+ val |= (0x6<<4);
|
|
+ else
|
|
+ val &= ~(0xf<<4);
|
|
+ writel(val, combphy->phy_clk + 0x0804);
|
|
+ }
|
|
+
|
|
+ /* SSC configure */
|
|
+ val = readl(combphy->phy_clk + 0x107c);
|
|
+ tmp = 0x3f << 12;
|
|
+ val = val & (~tmp);
|
|
+ val |= ((0x1 << 12) & tmp); /* div_N */
|
|
+ writel(val, combphy->phy_clk + 0x107c);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1020);
|
|
+ tmp = 0x1f << 0;
|
|
+ val = val & (~tmp);
|
|
+ val |= ((0x6 << 0) & tmp); /* modulation freq div */
|
|
+ writel(val, combphy->phy_clk + 0x1020);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1034);
|
|
+ tmp = 0x7f << 16;
|
|
+ val = val & (~tmp);
|
|
+ val |= ((0x9 << 16) & tmp); /* spread[6:0], 400*9=4410ppm ssc */
|
|
+ writel(val, combphy->phy_clk + 0x1034);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x101c);
|
|
+ tmp = 0x1 << 27;
|
|
+ val = val & (~tmp);
|
|
+ val |= ((0x1 << 27) & tmp); /* choose downspread */
|
|
+
|
|
+ tmp = 0x1 << 28;
|
|
+ val = val & (~tmp);
|
|
+ if (enable)
|
|
+ val |= ((0x0 << 28) & tmp); /* don't disable ssc = 0 */
|
|
+ else
|
|
+ val |= ((0x1 << 28) & tmp); /* don't enable ssc = 1 */
|
|
+ writel(val, combphy->phy_clk + 0x101c);
|
|
+
|
|
+#ifdef SUN55i_INNO_COMMBOPHY_DEBUG
|
|
+ /* TX Eye configure bypass_en */
|
|
+ val = readl(combphy->phy_clk + 0x0ddc);
|
|
+ if (enable)
|
|
+ val |= BIT(4); /* 0x0ddc[4]=1 */
|
|
+ else
|
|
+ val &= ~BIT(4);
|
|
+ writel(val, combphy->phy_clk + 0x0ddc);
|
|
+
|
|
+ /* Leg_cur[6:0] - 7'd84 */
|
|
+ val = readl(combphy->phy_clk + 0x0ddc);
|
|
+ val |= ((0x54 & BIT(6)) >> 3); /* 0x0ddc[3] */
|
|
+ writel(val, combphy->phy_clk + 0x0ddc);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x0de0);
|
|
+ val |= ((0x54 & GENMASK(5, 0)) << 2); /* 0x0de0[7:2] */
|
|
+ writel(val, combphy->phy_clk + 0x0de0);
|
|
+
|
|
+ /* Leg_curb[5:0] - 6'd18 */
|
|
+ val = readl(combphy->phy_clk + 0x0de4);
|
|
+ val |= ((0x12 & GENMASK(5, 1)) >> 1); /* 0x0de4[4:0] */
|
|
+ writel(val, combphy->phy_clk + 0x0de4);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x0de8);
|
|
+ val |= ((0x12 & BIT(0)) << 7); /* 0x0de8[7] */
|
|
+ writel(val, combphy->phy_clk + 0x0de8);
|
|
+
|
|
+ /* Exswing_isel */
|
|
+ val = readl(combphy->phy_clk + 0x0028);
|
|
+ val |= (0x4 << 28); /* 0x28[30:28] */
|
|
+ writel(val, combphy->phy_clk + 0x0028);
|
|
+
|
|
+ /* Exswing_en */
|
|
+ val = readl(combphy->phy_clk + 0x0028);
|
|
+ if (enable)
|
|
+ val |= BIT(31); /* 0x28[31]=1 */
|
|
+ else
|
|
+ val &= ~BIT(31);
|
|
+ writel(val, combphy->phy_clk + 0x0028);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static int sun55i_combphy_usb3_init(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ sun55i_combphy_usb3_phy_set(combphy, true);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+// static int sun55i_combphy_usb3_exit(struct sun55i_combphy *combphy)
|
|
+// {
|
|
+// sun55i_combphy_usb3_phy_set(combphy, false);
|
|
+
|
|
+// return 0;
|
|
+// }
|
|
+
|
|
+static void sun55i_combphy_pcie_phy_enable(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ /* set the phy:
|
|
+ * bit(18): slv aclk enable
|
|
+ * bit(17): aclk enable
|
|
+ * bit(16): hclk enbale
|
|
+ * bit(1) : pcie_presetn
|
|
+ * bit(0) : pcie_power_up_rstn
|
|
+ */
|
|
+ val = readl(combphy->phy_ctl + PCIE_COMBO_PHY_BGR);
|
|
+ val &= (~(0x03<<0));
|
|
+ val &= (~(0x03<<16));
|
|
+ val |= (0x03<<0);
|
|
+ if (combphy->drvdata->has_slv_clk)
|
|
+ val |= (0x07<<16);
|
|
+ else
|
|
+ val |= (0x03<<16);
|
|
+ writel(val, combphy->phy_ctl + PCIE_COMBO_PHY_BGR);
|
|
+
|
|
+
|
|
+ /* select phy mode, phy assert */
|
|
+ val = readl(combphy->phy_ctl + PCIE_COMBO_PHY_CTL);
|
|
+ val &= (~PHY_USE_SEL);
|
|
+ val &= (~(0x03<<8));
|
|
+ val &= (~PHY_FPGA_SYS_RSTN);
|
|
+ val &= (~PHY_RSTN);
|
|
+ writel(val, combphy->phy_ctl + PCIE_COMBO_PHY_CTL);
|
|
+
|
|
+ /* phy De-assert */
|
|
+ val = readl(combphy->phy_ctl + PCIE_COMBO_PHY_CTL);
|
|
+ val &= (~PHY_CLK_SEL);
|
|
+ val &= (~(0x03<<8));
|
|
+ val &= (~PHY_FPGA_SYS_RSTN);
|
|
+ val &= (~PHY_RSTN);
|
|
+ val |= PHY_RSTN;
|
|
+ writel(val, combphy->phy_ctl + PCIE_COMBO_PHY_CTL);
|
|
+
|
|
+ val = readl(combphy->phy_ctl + PCIE_COMBO_PHY_CTL);
|
|
+ val &= (~PHY_CLK_SEL);
|
|
+ val &= (~(0x03<<8));
|
|
+ val &= (~PHY_FPGA_SYS_RSTN);
|
|
+ val &= (~PHY_RSTN);
|
|
+ val |= PHY_RSTN;
|
|
+ val |= (PHY_FPGA_SYS_RSTN);
|
|
+ writel(val, combphy->phy_ctl + PCIE_COMBO_PHY_CTL);
|
|
+
|
|
+}
|
|
+
|
|
+static void sun55i_combphy_pcie_phy_100M(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1004);
|
|
+ val &= ~(0x3<<3);
|
|
+ val &= ~(0x1<<0);
|
|
+ val |= (0x1<<0);
|
|
+ val |= (0x1<<2);
|
|
+ val |= (0x1<<4);
|
|
+ writel(val, combphy->phy_clk + 0x1004);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1018);
|
|
+ val &= ~(0x3<<4);
|
|
+ val |= (0x3<<4);
|
|
+ writel(val, combphy->phy_clk + 0x1018);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x101c);
|
|
+ val &= ~(0x0fffffff);
|
|
+ writel(val, combphy->phy_clk + 0x101c);
|
|
+
|
|
+ /* if need optimize jitter parm*/
|
|
+ if (combphy->drvdata->need_optimize_jitter) {
|
|
+ val = readl(combphy->phy_clk + 0x107c);
|
|
+ val &= ~(0x3ffff);
|
|
+ val |= (0x4<<12);
|
|
+ val |= 0x64;
|
|
+ writel(val, combphy->phy_clk + 0x107c);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1030);
|
|
+ val &= ~(0x3<<20);
|
|
+ writel(val, combphy->phy_clk + 0x1030);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1050);
|
|
+ val &= ~(0x7<<0);
|
|
+ val &= ~(0x7<<5);
|
|
+ val &= ~(0x3<<3);
|
|
+ val |= (0x3<<3);
|
|
+ writel(val, combphy->phy_clk + 0x1050);
|
|
+ } else {
|
|
+ val = readl(combphy->phy_clk + 0x107c);
|
|
+ val &= ~(0x3ffff);
|
|
+ val |= (0x2<<12);
|
|
+ val |= 0x32;
|
|
+ writel(val, combphy->phy_clk + 0x107c);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1030);
|
|
+ val &= ~(0x3<<20);
|
|
+ writel(val, combphy->phy_clk + 0x1030);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1050);
|
|
+ val &= ~(0x7<<5);
|
|
+ val |= (0x1<<5);
|
|
+ writel(val, combphy->phy_clk + 0x1050);
|
|
+ }
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x1054);
|
|
+ val &= ~(0x7<<5);
|
|
+ val |= (0x1<<5);
|
|
+ writel(val, combphy->phy_clk + 0x1054);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x0804);
|
|
+ val &= ~(0xf<<4);
|
|
+ val |= (0xc<<4);
|
|
+ writel(val, combphy->phy_clk + 0x0804);
|
|
+
|
|
+ val = readl(combphy->phy_clk + 0x109c);
|
|
+ val &= ~(0x3<<8);
|
|
+ val |= (0x1<<1);
|
|
+ writel(val, combphy->phy_clk + 0x109c);
|
|
+
|
|
+ writel(0x80540a0a, combphy->phy_clk + 0x1418);
|
|
+}
|
|
+
|
|
+static int sun55i_combphy_pcie_init(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ sun55i_combphy_pcie_phy_100M(combphy);
|
|
+
|
|
+ sun55i_combphy_pcie_phy_enable(combphy);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sun55i_combphy_set_mode(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ switch (combphy->mode) {
|
|
+ case PHY_TYPE_PCIE:
|
|
+ sun55i_combphy_pcie_init(combphy);
|
|
+ break;
|
|
+ case PHY_TYPE_USB3:
|
|
+ if (combphy->user == PHY_USE_BY_PCIE_USB3_U2) {
|
|
+ sun55i_combphy_pcie_init(combphy);
|
|
+ } else if (combphy->user == PHY_USE_BY_USB3) {
|
|
+ sun55i_combphy_usb3_init(combphy);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ printf("incompatible PHY type\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void combo_phy_mode_set(struct sun55i_combphy *combphy, bool enable)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = readl(COMBO_REG_PHYCTRL(combphy->phy_ctl));
|
|
+
|
|
+ /* Set PHY_USE_SEL based on user (default: clear for PCIE/PCIE_USB3_U2, set for USB3) */
|
|
+ val &= ~PHY_USE_SEL; /* Default: clear (PCIE mode) */
|
|
+ if (combphy->user == PHY_USE_BY_USB3)
|
|
+ val |= PHY_USE_SEL;
|
|
+
|
|
+ /* Set PHY_CLK_SEL based on ref (default: clear for internal, set for external) */
|
|
+ val &= ~PHY_CLK_SEL; /* Default: clear (internal clock) */
|
|
+ if (combphy->ref == EXTER_DIF_REF_CLK)
|
|
+ val |= PHY_CLK_SEL;
|
|
+
|
|
+ /* Set or clear PHY_RSTN based on enable */
|
|
+ if (enable)
|
|
+ val |= PHY_RSTN;
|
|
+ else
|
|
+ val &= ~PHY_RSTN;
|
|
+
|
|
+ writel(val, COMBO_REG_PHYCTRL(combphy->phy_ctl));
|
|
+}
|
|
+
|
|
+/* PCIE USB3 Sub-system Application */
|
|
+static void combo_pcie_clk_set(struct sun55i_combphy *combphy, bool enable)
|
|
+{
|
|
+ u32 val, tmp = 0;
|
|
+
|
|
+ val = readl(COMBO_REG_PCIEBGR(combphy->phy_ctl));
|
|
+ if (combphy->drvdata->has_slv_clk)
|
|
+ tmp = PCIE_SLV_ACLK_EN | PCIE_ACLK_EN | PCIE_HCLK_EN | PCIE_PERSTN | PCIE_PW_UP_RSTN;
|
|
+ else
|
|
+ tmp = PCIE_ACLK_EN | PCIE_HCLK_EN | PCIE_PERSTN | PCIE_PW_UP_RSTN;
|
|
+ if (enable)
|
|
+ val |= tmp;
|
|
+ else
|
|
+ val &= ~tmp;
|
|
+ writel(val, COMBO_REG_PCIEBGR(combphy->phy_ctl));
|
|
+}
|
|
+
|
|
+static void combo_usb3_clk_set(struct sun55i_combphy *combphy, bool enable)
|
|
+{
|
|
+ u32 val, tmp = 0;
|
|
+
|
|
+ val = readl(COMBO_REG_USB3BGR(combphy->phy_ctl));
|
|
+ if (combphy->drvdata->has_u2_phy_mux)
|
|
+ tmp = USB3_ACLK_EN | USB3_HCLK_EN | USB3_U2_PHY_MUX_SEL | USB3_U2_PHY_RSTN | USB3_U2_PHY_MUX_EN;
|
|
+ else
|
|
+ tmp = USB3_ACLK_EN | USB3_HCLK_EN | USB3_RESETN;
|
|
+ if (enable)
|
|
+ val |= tmp;
|
|
+ else
|
|
+ val &= ~tmp;
|
|
+ writel(val, COMBO_REG_USB3BGR(combphy->phy_ctl));
|
|
+}
|
|
+
|
|
+static u32 combo_sysver_get(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ u32 reg;
|
|
+
|
|
+ reg = readl(COMBO_REG_SYSVER(combphy->phy_ctl));
|
|
+
|
|
+ return reg;
|
|
+}
|
|
+
|
|
+static void pcie_usb3_sub_system_enable(struct sun55i_combphy *combphy)
|
|
+{
|
|
+ combo_phy_mode_set(combphy, true);
|
|
+
|
|
+ if (combphy->user == PHY_USE_BY_PCIE)
|
|
+ combo_pcie_clk_set(combphy, true);
|
|
+ else if (combphy->user == PHY_USE_BY_USB3)
|
|
+ combo_usb3_clk_set(combphy, true);
|
|
+ else if (combphy->user == PHY_USE_BY_PCIE_USB3_U2) {
|
|
+ combo_pcie_clk_set(combphy, true);
|
|
+ combo_usb3_clk_set(combphy, true);
|
|
+ }
|
|
+
|
|
+ combphy->vernum = combo_sysver_get(combphy);
|
|
+}
|
|
+
|
|
+static int pcie_usb3_sub_system_init(void *phy)
|
|
+{
|
|
+ struct sun55i_combphy *combphy = (struct sun55i_combphy *)(phy);
|
|
+ unsigned long reg_value = 0;
|
|
+
|
|
+ if (combphy->initialized)
|
|
+ return 0;
|
|
+
|
|
+ reg_value = readl(PCIE_BGR_REG);
|
|
+ reg_value |= (1 << PCIE_BRG_REG_RST);
|
|
+ writel(reg_value, PCIE_BGR_REG);
|
|
+
|
|
+ reg_value = 0x81000001;
|
|
+ writel(reg_value, 0x2001a84);
|
|
+
|
|
+ pcie_usb3_sub_system_enable(combphy);
|
|
+
|
|
+ combphy->initialized = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int sun55i_combphy_init(struct phy *phy)
|
|
+{
|
|
+ struct sun55i_combphy *combphy = dev_get_priv(phy->dev);
|
|
+ int ret;
|
|
+
|
|
+ if (!combphy) {
|
|
+ printf("PHY drvdata not set!\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (combphy->select3v3_supply) {
|
|
+ ret = regulator_set_enable_if_allowed(combphy->select3v3_supply, true);
|
|
+ if (ret && ret != -EALREADY) {
|
|
+ printf("PHY: Failed to enable 3.3V supply: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = reset_deassert(&combphy->reset);
|
|
+ if (ret) {
|
|
+ printf("PHY: Failed to deassert reset: %d\n", ret);
|
|
+ regulator_set_enable(combphy->select3v3_supply, false);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = pcie_usb3_sub_system_init(combphy);
|
|
+ if (ret) {
|
|
+ printf("PHY: failed to init sub system\n");
|
|
+ reset_assert(&combphy->reset);
|
|
+ regulator_set_enable(combphy->select3v3_supply, false);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = sun55i_combphy_set_mode(combphy);
|
|
+ if (ret) {
|
|
+ printf("PHY: invalid number of arguments\n");
|
|
+ reset_assert(&combphy->reset);
|
|
+ regulator_set_enable(combphy->select3v3_supply, false);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct sun55i_combophy_of_data sun55i_inno_v1_of_data = {
|
|
+ .has_cfg_clk = false,
|
|
+};
|
|
+
|
|
+static int sun55i_inno_phy_probe(struct udevice *dev)
|
|
+{
|
|
+ struct sun55i_combphy *combphy = dev_get_priv(dev);
|
|
+ int ret;
|
|
+
|
|
+ combphy->phy_ctl = dev_read_addr_name_ptr(dev, "phy-ctl");
|
|
+ if (!combphy->phy_ctl)
|
|
+ return -ENODEV;
|
|
+
|
|
+ combphy->phy_clk = dev_read_addr_name_ptr(dev, "phy-clk");
|
|
+ if (!combphy->phy_clk)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = reset_get_by_index(dev, 0, &combphy->reset);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = device_get_supply_regulator(dev, "select3v3-supply", &combphy->select3v3_supply);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ combphy->drvdata = (const struct sun55i_combophy_of_data *)dev_get_driver_data(dev);
|
|
+ combphy->user = PHY_USE_BY_PCIE;
|
|
+ combphy->mode = PHY_TYPE_PCIE;
|
|
+ combphy->ref = EXTER_DIF_REF_CLK;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct phy_ops sun55i_inno_phy_ops = {
|
|
+ .init = sun55i_combphy_init,
|
|
+};
|
|
+
|
|
+static const struct udevice_id sun55i_inno_phy_of_match_table[] = {
|
|
+ {
|
|
+ .compatible = "allwinner,inno-combphy",
|
|
+ .data = (ulong)&sun55i_inno_v1_of_data,
|
|
+ },
|
|
+};
|
|
+
|
|
+U_BOOT_DRIVER(sun55i_inno_combophy) = {
|
|
+ .name = "sun55i_inno_combophy",
|
|
+ .id = UCLASS_PHY,
|
|
+ .of_match = sun55i_inno_phy_of_match_table,
|
|
+ .ops = &sun55i_inno_phy_ops,
|
|
+ .probe = sun55i_inno_phy_probe,
|
|
+ .priv_auto = sizeof(struct sun55i_combphy),
|
|
+};
|
|
--
|
|
Armbian
|
|
|