Message ID | 20190804172342.5225-3-frank-w@public-files.de |
---|---|
State | Superseded |
Delegated to: | Tom Rini |
Headers | show |
Series | U-boot: add PCIe, its PHY and AHCI support | expand |
On Sun, Aug 4, 2019 at 8:27 PM Frank Wunderlich <frank-w@public-files.de> wrote: > > From: Ryder Lee <ryder.lee@mediatek.com> > > The driver provides PHY for USB2, USB3.0, PCIe and SATA, and now > we just enable PCIe. As for the other functionalities will be > added gradually in upcoming days. > > Tested-by: Frank Wunderlich <frank-w@public-files.de> > Signed-off-by: Frank Wunderlich <frank-w@public-files.de> > Signed-off-by: Ryder Lee <ryder.lee@mediatek.com> > --- > drivers/phy/Kconfig | 11 ++ > drivers/phy/Makefile | 1 + > drivers/phy/phy-mtk-tphy.c | 388 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 400 insertions(+) > create mode 100644 drivers/phy/phy-mtk-tphy.c > > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig > index 957efb3984..2099dd9547 100644 > --- a/drivers/phy/Kconfig > +++ b/drivers/phy/Kconfig > @@ -190,4 +190,15 @@ config MT76X8_USB_PHY > > This PHY is found on MT76x8 devices supporting USB. > > +config PHY_MTK_TPHY > + bool "MediaTek T-PHY Driver" > + depends on PHY > + depends on ARCH_MEDIATEK > + help > + MediaTek T-PHY driver supports usb2.0, usb3.0 ports, PCIe and > + SATA, and meanwhile supports two version T-PHY which have > + different banks layout, the T-PHY with shared banks between > + multi-ports is first version, otherwise is second veriosn, > + so you can easily distinguish them by banks layout. > + > endmenu > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index 90646ca55b..15b4d58a2d 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -21,3 +21,4 @@ obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o > obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o > obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o > obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o > +obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o > diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c > new file mode 100644 > index 0000000000..422e727c22 > --- /dev/null > +++ b/drivers/phy/phy-mtk-tphy.c > @@ -0,0 +1,388 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2015 - 2019 MediaTek Inc. > + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> > + * Ryder Lee <ryder.lee@mediatek.com> > + */ > + > +#include <common.h> > +#include <clk.h> > +#include <dm.h> > +#include <generic-phy.h> > +#include <mapmem.h> > +#include <asm/io.h> > + > +#include <dt-bindings/phy/phy.h> > + > +/* version V1 sub-banks offset base address */ > +/* banks shared by multiple phys */ > +#define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ > +#define SSUSB_SIFSLV_V1_CHIP 0x300 /* shared by u3 phys */ > +/* u3/pcie/sata phy banks */ > +#define SSUSB_SIFSLV_V1_U3PHYD 0x000 > +#define SSUSB_SIFSLV_V1_U3PHYA 0x200 > + > +#define U3P_U3_CHIP_GPIO_CTLD 0x0c > +#define P3C_REG_IP_SW_RST BIT(31) > +#define P3C_MCU_BUS_CK_GATE_EN BIT(30) > +#define P3C_FORCE_IP_SW_RST BIT(29) > + > +#define U3P_U3_CHIP_GPIO_CTLE 0x10 > +#define P3C_RG_SWRST_U3_PHYD BIT(25) > +#define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) > + > +#define U3P_U3_PHYA_REG0 0x000 > +#define P3A_RG_CLKDRV_OFF GENMASK(3, 2) > +#define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) > + > +#define U3P_U3_PHYA_REG1 0x004 > +#define P3A_RG_CLKDRV_AMP GENMASK(31, 29) > +#define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29) > + > +#define U3P_U3_PHYA_DA_REG0 0x100 > +#define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16) > +#define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16) > +#define P3A_RG_XTAL_EXT_PE1H GENMASK(13, 12) > +#define P3A_RG_XTAL_EXT_PE1H_VAL(x) ((0x3 & (x)) << 12) > +#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10) > +#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10) > + > +#define U3P_U3_PHYA_DA_REG4 0x108 > +#define P3A_RG_PLL_DIVEN_PE2H GENMASK(21, 19) > +#define P3A_RG_PLL_BC_PE2H GENMASK(7, 6) > +#define P3A_RG_PLL_BC_PE2H_VAL(x) ((0x3 & (x)) << 6) > + > +#define U3P_U3_PHYA_DA_REG5 0x10c > +#define P3A_RG_PLL_BR_PE2H GENMASK(29, 28) > +#define P3A_RG_PLL_BR_PE2H_VAL(x) ((0x3 & (x)) << 28) > +#define P3A_RG_PLL_IC_PE2H GENMASK(15, 12) > +#define P3A_RG_PLL_IC_PE2H_VAL(x) ((0xf & (x)) << 12) > + > +#define U3P_U3_PHYA_DA_REG6 0x110 > +#define P3A_RG_PLL_IR_PE2H GENMASK(19, 16) > +#define P3A_RG_PLL_IR_PE2H_VAL(x) ((0xf & (x)) << 16) > + > +#define U3P_U3_PHYA_DA_REG7 0x114 > +#define P3A_RG_PLL_BP_PE2H GENMASK(19, 16) > +#define P3A_RG_PLL_BP_PE2H_VAL(x) ((0xf & (x)) << 16) > + > +#define U3P_U3_PHYA_DA_REG20 0x13c > +#define P3A_RG_PLL_DELTA1_PE2H GENMASK(31, 16) > +#define P3A_RG_PLL_DELTA1_PE2H_VAL(x) ((0xffff & (x)) << 16) > + > +#define U3P_U3_PHYA_DA_REG25 0x148 > +#define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0) > +#define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x)) > + > +#define U3P_U3_PHYD_RXDET1 0x128 > +#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) > +#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9) > + > +#define U3P_U3_PHYD_RXDET2 0x12c > +#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) > +#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x)) > + > +struct u3phy_banks { > + void __iomem *spllc; > + void __iomem *chip; > + void __iomem *phyd; /* include u3phyd_bank2 */ > + void __iomem *phya; /* include u3phya_da */ > +}; > + > +struct mtk_phy_instance { > + void __iomem *port_base; > + const struct device_node *np; > + > + struct u3phy_banks u3_banks; > + > + /* reference clock of anolog phy */ > + struct clk ref_clk; > + u32 index; > + u8 type; > +}; > + > +struct mtk_tphy { > + void __iomem *sif_base; > + struct mtk_phy_instance **phys; > + int nphys; > +}; > + > +static void pcie_phy_instance_init(struct mtk_tphy *tphy, > + struct mtk_phy_instance *instance) > +{ > + struct u3phy_banks *u3_banks = &instance->u3_banks; > + u32 tmp; > + > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0); > + tmp &= ~(P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H); > + tmp |= P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0); clrsetbits_le32 is easier on the eyes. > + > + /* ref clk drive */ > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG1); > + tmp &= ~P3A_RG_CLKDRV_AMP; > + tmp |= P3A_RG_CLKDRV_AMP_VAL(0x4); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG1); > + > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); > + tmp &= ~P3A_RG_CLKDRV_OFF; > + tmp |= P3A_RG_CLKDRV_OFF_VAL(0x1); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); > + > + /* SSC delta -5000ppm */ > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG20); > + tmp &= ~P3A_RG_PLL_DELTA1_PE2H; > + tmp |= P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG20); > + > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG25); > + tmp &= ~P3A_RG_PLL_DELTA_PE2H; > + tmp |= P3A_RG_PLL_DELTA_PE2H_VAL(0x36); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG25); > + > + /* change pll BW 0.6M */ > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG5); > + tmp &= ~(P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H); > + tmp |= P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG5); > + > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG4); > + tmp &= ~(P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H); > + tmp |= P3A_RG_PLL_BC_PE2H_VAL(0x3); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG4); > + > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG6); > + tmp &= ~P3A_RG_PLL_IR_PE2H; > + tmp |= P3A_RG_PLL_IR_PE2H_VAL(0x2); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG6); > + > + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG7); > + tmp &= ~P3A_RG_PLL_BP_PE2H; > + tmp |= P3A_RG_PLL_BP_PE2H_VAL(0xa); > + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG7); > + > + /* Tx Detect Rx Timing: 10us -> 5us */ > + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1); > + tmp &= ~P3D_RG_RXDET_STB2_SET; > + tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10); > + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1); > + > + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2); > + tmp &= ~P3D_RG_RXDET_STB2_SET_P3; > + tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10); > + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2); > + > + /* wait for PCIe subsys register to active */ > + udelay(3000); > +} > + > +static void pcie_phy_instance_power_on(struct mtk_tphy *tphy, > + struct mtk_phy_instance *instance) > +{ > + struct u3phy_banks *bank = &instance->u3_banks; > + u32 tmp; > + > + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); > + tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); > + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); > + > + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); > + tmp &= ~(P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); > + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); > +} > + > +static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, > + struct mtk_phy_instance *instance) > + > +{ > + struct u3phy_banks *bank = &instance->u3_banks; > + u32 tmp; > + > + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); > + tmp |= P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST; > + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); > + > + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); > + tmp |= P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD; > + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); > +} > + > +static void phy_v1_banks_init(struct mtk_tphy *tphy, > + struct mtk_phy_instance *instance) > +{ > + struct u3phy_banks *u3_banks = &instance->u3_banks; > + > + switch (instance->type) { > + case PHY_TYPE_PCIE: > + u3_banks->spllc = tphy->sif_base + SSUSB_SIFSLV_V1_SPLLC; > + u3_banks->chip = tphy->sif_base + SSUSB_SIFSLV_V1_CHIP; > + u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V1_U3PHYD; > + u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA; > + break; > + default: > + return; > + } > +} > + > +static int mtk_phy_init(struct phy *phy) > +{ > + struct mtk_tphy *tphy = dev_get_priv(phy->dev); > + struct mtk_phy_instance *instance = tphy->phys[phy->id]; > + int ret; > + > + /* we may use a fixed-clock here */ > + ret = clk_enable(&instance->ref_clk); > + if (ret && ret != -ENOSYS) > + return ret; > + > + switch (instance->type) { > + case PHY_TYPE_PCIE: > + pcie_phy_instance_init(tphy, instance); > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int mtk_phy_power_on(struct phy *phy) > +{ > + struct mtk_tphy *tphy = dev_get_priv(phy->dev); > + struct mtk_phy_instance *instance = tphy->phys[phy->id]; > + > + pcie_phy_instance_power_on(tphy, instance); > + > + return 0; > +} > + > +static int mtk_phy_power_off(struct phy *phy) > +{ > + struct mtk_tphy *tphy = dev_get_priv(phy->dev); > + struct mtk_phy_instance *instance = tphy->phys[phy->id]; > + > + pcie_phy_instance_power_off(tphy, instance); > + > + return 0; > +} > + > +static int mtk_phy_exit(struct phy *phy) > +{ > + struct mtk_tphy *tphy = dev_get_priv(phy->dev); > + struct mtk_phy_instance *instance = tphy->phys[phy->id]; > + > + clk_disable(&instance->ref_clk); > + > + return 0; > +} > + > +static int mtk_phy_xlate(struct phy *phy, > + struct ofnode_phandle_args *args) > +{ > + struct mtk_tphy *tphy = dev_get_priv(phy->dev); > + struct mtk_phy_instance *instance = NULL; > + const struct device_node *phy_np = ofnode_to_np(args->node); > + u32 index; > + > + if (!phy_np) { > + dev_err(phy->dev, "null pointer phy node\n"); > + return -EINVAL; > + } > + > + if (args->args_count < 1) { > + dev_err(phy->dev, "invalid number of cells in 'phy' property\n"); > + return -EINVAL; > + } > + > + for (index = 0; index < tphy->nphys; index++) > + if (phy_np == tphy->phys[index]->np) { > + instance = tphy->phys[index]; > + break; > + } > + > + if (!instance) { > + dev_err(phy->dev, "failed to find appropriate phy\n"); > + return -EINVAL; > + } > + > + phy->id = index; > + instance->type = args->args[1]; > + if (!(instance->type == PHY_TYPE_USB2 || > + instance->type == PHY_TYPE_USB3 || > + instance->type == PHY_TYPE_PCIE || > + instance->type == PHY_TYPE_SATA)) { > + dev_err(phy->dev, "unsupported device type\n"); > + return -EINVAL; > + } > + > + phy_v1_banks_init(tphy, instance); > + > + return 0; > +} > + > +static const struct phy_ops mtk_tphy_ops = { > + .init = mtk_phy_init, > + .exit = mtk_phy_exit, > + .power_on = mtk_phy_power_on, > + .power_off = mtk_phy_power_off, > + .of_xlate = mtk_phy_xlate, > +}; > + > +static int mtk_tphy_probe(struct udevice *dev) > +{ > + struct mtk_tphy *tphy = dev_get_priv(dev); > + ofnode subnode; > + int index = 0; > + > + dev_for_each_subnode(subnode, dev) > + tphy->nphys++; > + > + tphy->phys = devm_kcalloc(dev, tphy->nphys, sizeof(*tphy->phys), > + GFP_KERNEL); > + if (!tphy->phys) > + return -ENOMEM; > + > + tphy->sif_base = dev_read_addr_ptr(dev); > + if (!tphy->sif_base) > + return -ENOENT; > + > + dev_for_each_subnode(subnode, dev) { > + struct mtk_phy_instance *instance; > + fdt_addr_t addr; > + int err; > + > + instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); > + if (!instance) > + return -ENOMEM; > + > + addr = ofnode_get_addr(subnode); > + if (addr == FDT_ADDR_T_NONE) > + return -ENOMEM; > + > + instance->port_base = map_sysmem(addr, 0); > + instance->index = index; > + instance->np = ofnode_to_np(subnode); > + tphy->phys[index] = instance; > + index++; > + > + err = clk_get_by_index_nodev(subnode, 0, &instance->ref_clk); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +static const struct udevice_id mtk_tphy_id_table[] = { > + { .compatible = "mediatek,generic-tphy-v1", }, > + { } > +}; > + > +U_BOOT_DRIVER(mtk_tphy) = { > + .name = "mtk-tphy", > + .id = UCLASS_PHY, > + .of_match = mtk_tphy_id_table, > + .ops = &mtk_tphy_ops, > + .probe = mtk_tphy_probe, > + .priv_auto_alloc_size = sizeof(struct mtk_tphy), > +}; > -- > 2.17.1 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > https://lists.denx.de/listinfo/u-boot
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 957efb3984..2099dd9547 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -190,4 +190,15 @@ config MT76X8_USB_PHY This PHY is found on MT76x8 devices supporting USB. +config PHY_MTK_TPHY + bool "MediaTek T-PHY Driver" + depends on PHY + depends on ARCH_MEDIATEK + help + MediaTek T-PHY driver supports usb2.0, usb3.0 ports, PCIe and + SATA, and meanwhile supports two version T-PHY which have + different banks layout, the T-PHY with shared banks between + multi-ports is first version, otherwise is second veriosn, + so you can easily distinguish them by banks layout. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 90646ca55b..15b4d58a2d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o +obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c new file mode 100644 index 0000000000..422e727c22 --- /dev/null +++ b/drivers/phy/phy-mtk-tphy.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 - 2019 MediaTek Inc. + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + * Ryder Lee <ryder.lee@mediatek.com> + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <mapmem.h> +#include <asm/io.h> + +#include <dt-bindings/phy/phy.h> + +/* version V1 sub-banks offset base address */ +/* banks shared by multiple phys */ +#define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ +#define SSUSB_SIFSLV_V1_CHIP 0x300 /* shared by u3 phys */ +/* u3/pcie/sata phy banks */ +#define SSUSB_SIFSLV_V1_U3PHYD 0x000 +#define SSUSB_SIFSLV_V1_U3PHYA 0x200 + +#define U3P_U3_CHIP_GPIO_CTLD 0x0c +#define P3C_REG_IP_SW_RST BIT(31) +#define P3C_MCU_BUS_CK_GATE_EN BIT(30) +#define P3C_FORCE_IP_SW_RST BIT(29) + +#define U3P_U3_CHIP_GPIO_CTLE 0x10 +#define P3C_RG_SWRST_U3_PHYD BIT(25) +#define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) + +#define U3P_U3_PHYA_REG0 0x000 +#define P3A_RG_CLKDRV_OFF GENMASK(3, 2) +#define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) + +#define U3P_U3_PHYA_REG1 0x004 +#define P3A_RG_CLKDRV_AMP GENMASK(31, 29) +#define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29) + +#define U3P_U3_PHYA_DA_REG0 0x100 +#define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16) +#define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16) +#define P3A_RG_XTAL_EXT_PE1H GENMASK(13, 12) +#define P3A_RG_XTAL_EXT_PE1H_VAL(x) ((0x3 & (x)) << 12) +#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10) +#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10) + +#define U3P_U3_PHYA_DA_REG4 0x108 +#define P3A_RG_PLL_DIVEN_PE2H GENMASK(21, 19) +#define P3A_RG_PLL_BC_PE2H GENMASK(7, 6) +#define P3A_RG_PLL_BC_PE2H_VAL(x) ((0x3 & (x)) << 6) + +#define U3P_U3_PHYA_DA_REG5 0x10c +#define P3A_RG_PLL_BR_PE2H GENMASK(29, 28) +#define P3A_RG_PLL_BR_PE2H_VAL(x) ((0x3 & (x)) << 28) +#define P3A_RG_PLL_IC_PE2H GENMASK(15, 12) +#define P3A_RG_PLL_IC_PE2H_VAL(x) ((0xf & (x)) << 12) + +#define U3P_U3_PHYA_DA_REG6 0x110 +#define P3A_RG_PLL_IR_PE2H GENMASK(19, 16) +#define P3A_RG_PLL_IR_PE2H_VAL(x) ((0xf & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG7 0x114 +#define P3A_RG_PLL_BP_PE2H GENMASK(19, 16) +#define P3A_RG_PLL_BP_PE2H_VAL(x) ((0xf & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG20 0x13c +#define P3A_RG_PLL_DELTA1_PE2H GENMASK(31, 16) +#define P3A_RG_PLL_DELTA1_PE2H_VAL(x) ((0xffff & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG25 0x148 +#define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0) +#define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x)) + +#define U3P_U3_PHYD_RXDET1 0x128 +#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) +#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9) + +#define U3P_U3_PHYD_RXDET2 0x12c +#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) +#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x)) + +struct u3phy_banks { + void __iomem *spllc; + void __iomem *chip; + void __iomem *phyd; /* include u3phyd_bank2 */ + void __iomem *phya; /* include u3phya_da */ +}; + +struct mtk_phy_instance { + void __iomem *port_base; + const struct device_node *np; + + struct u3phy_banks u3_banks; + + /* reference clock of anolog phy */ + struct clk ref_clk; + u32 index; + u8 type; +}; + +struct mtk_tphy { + void __iomem *sif_base; + struct mtk_phy_instance **phys; + int nphys; +}; + +static void pcie_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + u32 tmp; + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0); + tmp &= ~(P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H); + tmp |= P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0); + + /* ref clk drive */ + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG1); + tmp &= ~P3A_RG_CLKDRV_AMP; + tmp |= P3A_RG_CLKDRV_AMP_VAL(0x4); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG1); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); + tmp &= ~P3A_RG_CLKDRV_OFF; + tmp |= P3A_RG_CLKDRV_OFF_VAL(0x1); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); + + /* SSC delta -5000ppm */ + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG20); + tmp &= ~P3A_RG_PLL_DELTA1_PE2H; + tmp |= P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG20); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG25); + tmp &= ~P3A_RG_PLL_DELTA_PE2H; + tmp |= P3A_RG_PLL_DELTA_PE2H_VAL(0x36); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG25); + + /* change pll BW 0.6M */ + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG5); + tmp &= ~(P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H); + tmp |= P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG5); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG4); + tmp &= ~(P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H); + tmp |= P3A_RG_PLL_BC_PE2H_VAL(0x3); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG4); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG6); + tmp &= ~P3A_RG_PLL_IR_PE2H; + tmp |= P3A_RG_PLL_IR_PE2H_VAL(0x2); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG6); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG7); + tmp &= ~P3A_RG_PLL_BP_PE2H; + tmp |= P3A_RG_PLL_BP_PE2H_VAL(0xa); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG7); + + /* Tx Detect Rx Timing: 10us -> 5us */ + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1); + tmp &= ~P3D_RG_RXDET_STB2_SET; + tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10); + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1); + + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2); + tmp &= ~P3D_RG_RXDET_STB2_SET_P3; + tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10); + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2); + + /* wait for PCIe subsys register to active */ + udelay(3000); +} + +static void pcie_phy_instance_power_on(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *bank = &instance->u3_banks; + u32 tmp; + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); + tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); + tmp &= ~(P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); +} + +static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) + +{ + struct u3phy_banks *bank = &instance->u3_banks; + u32 tmp; + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); + tmp |= P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST; + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); + + tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); + tmp |= P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD; + writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); +} + +static void phy_v1_banks_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + + switch (instance->type) { + case PHY_TYPE_PCIE: + u3_banks->spllc = tphy->sif_base + SSUSB_SIFSLV_V1_SPLLC; + u3_banks->chip = tphy->sif_base + SSUSB_SIFSLV_V1_CHIP; + u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V1_U3PHYD; + u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA; + break; + default: + return; + } +} + +static int mtk_phy_init(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + int ret; + + /* we may use a fixed-clock here */ + ret = clk_enable(&instance->ref_clk); + if (ret && ret != -ENOSYS) + return ret; + + switch (instance->type) { + case PHY_TYPE_PCIE: + pcie_phy_instance_init(tphy, instance); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_phy_power_on(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + pcie_phy_instance_power_on(tphy, instance); + + return 0; +} + +static int mtk_phy_power_off(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + pcie_phy_instance_power_off(tphy, instance); + + return 0; +} + +static int mtk_phy_exit(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + clk_disable(&instance->ref_clk); + + return 0; +} + +static int mtk_phy_xlate(struct phy *phy, + struct ofnode_phandle_args *args) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = NULL; + const struct device_node *phy_np = ofnode_to_np(args->node); + u32 index; + + if (!phy_np) { + dev_err(phy->dev, "null pointer phy node\n"); + return -EINVAL; + } + + if (args->args_count < 1) { + dev_err(phy->dev, "invalid number of cells in 'phy' property\n"); + return -EINVAL; + } + + for (index = 0; index < tphy->nphys; index++) + if (phy_np == tphy->phys[index]->np) { + instance = tphy->phys[index]; + break; + } + + if (!instance) { + dev_err(phy->dev, "failed to find appropriate phy\n"); + return -EINVAL; + } + + phy->id = index; + instance->type = args->args[1]; + if (!(instance->type == PHY_TYPE_USB2 || + instance->type == PHY_TYPE_USB3 || + instance->type == PHY_TYPE_PCIE || + instance->type == PHY_TYPE_SATA)) { + dev_err(phy->dev, "unsupported device type\n"); + return -EINVAL; + } + + phy_v1_banks_init(tphy, instance); + + return 0; +} + +static const struct phy_ops mtk_tphy_ops = { + .init = mtk_phy_init, + .exit = mtk_phy_exit, + .power_on = mtk_phy_power_on, + .power_off = mtk_phy_power_off, + .of_xlate = mtk_phy_xlate, +}; + +static int mtk_tphy_probe(struct udevice *dev) +{ + struct mtk_tphy *tphy = dev_get_priv(dev); + ofnode subnode; + int index = 0; + + dev_for_each_subnode(subnode, dev) + tphy->nphys++; + + tphy->phys = devm_kcalloc(dev, tphy->nphys, sizeof(*tphy->phys), + GFP_KERNEL); + if (!tphy->phys) + return -ENOMEM; + + tphy->sif_base = dev_read_addr_ptr(dev); + if (!tphy->sif_base) + return -ENOENT; + + dev_for_each_subnode(subnode, dev) { + struct mtk_phy_instance *instance; + fdt_addr_t addr; + int err; + + instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return -ENOMEM; + + addr = ofnode_get_addr(subnode); + if (addr == FDT_ADDR_T_NONE) + return -ENOMEM; + + instance->port_base = map_sysmem(addr, 0); + instance->index = index; + instance->np = ofnode_to_np(subnode); + tphy->phys[index] = instance; + index++; + + err = clk_get_by_index_nodev(subnode, 0, &instance->ref_clk); + if (err) + return err; + } + + return 0; +} + +static const struct udevice_id mtk_tphy_id_table[] = { + { .compatible = "mediatek,generic-tphy-v1", }, + { } +}; + +U_BOOT_DRIVER(mtk_tphy) = { + .name = "mtk-tphy", + .id = UCLASS_PHY, + .of_match = mtk_tphy_id_table, + .ops = &mtk_tphy_ops, + .probe = mtk_tphy_probe, + .priv_auto_alloc_size = sizeof(struct mtk_tphy), +};