
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); + + /* 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