diff options
Diffstat (limited to 'drivers/phy')
237 files changed, 53279 insertions, 8086 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b3ed94b98d9b..7bd00a11d074 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -37,7 +37,7 @@ config PHY_LPC18XX_USB_OTG config PHY_PISTACHIO_USB tristate "IMG Pistachio USB2.0 PHY driver" - depends on MACH_PISTACHIO + depends on MIPS || COMPILE_TEST select GENERIC_PHY help Enable this to support the USB2.0 PHY on the IMG Pistachio SoC. @@ -49,15 +49,39 @@ config PHY_XGENE help This option enables support for APM X-Gene SoC multi-purpose PHY. +config USB_LGM_PHY + tristate "INTEL Lightning Mountain USB PHY Driver" + depends on USB_SUPPORT + depends on X86 || COMPILE_TEST + select USB_PHY + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + help + Enable this to support Intel DWC3 PHY USB phy. This driver provides + interface to interact with USB GEN-II and USB 3.x PHY that is part + of the Intel network SOC. + +config PHY_CAN_TRANSCEIVER + tristate "CAN transceiver PHY" + select GENERIC_PHY + select MULTIPLEXER + help + This option enables support for CAN transceivers as a PHY. This + driver provides function for putting the transceivers in various + functional modes using gpios and sets the attribute max link + rate, for CAN drivers. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" source "drivers/phy/freescale/Kconfig" source "drivers/phy/hisilicon/Kconfig" +source "drivers/phy/ingenic/Kconfig" source "drivers/phy/lantiq/Kconfig" source "drivers/phy/marvell/Kconfig" source "drivers/phy/mediatek/Kconfig" +source "drivers/phy/microchip/Kconfig" source "drivers/phy/motorola/Kconfig" source "drivers/phy/mscc/Kconfig" source "drivers/phy/qualcomm/Kconfig" @@ -67,8 +91,10 @@ source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" source "drivers/phy/socionext/Kconfig" source "drivers/phy/st/Kconfig" +source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/intel/Kconfig" +source "drivers/phy/xilinx/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 310c149a9df5..54f312c10a40 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -5,27 +5,33 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o +obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o -obj-$(CONFIG_ARCH_SUNXI) += allwinner/ -obj-$(CONFIG_ARCH_MESON) += amlogic/ -obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ -obj-$(CONFIG_ARCH_RENESAS) += renesas/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ -obj-$(CONFIG_ARCH_TEGRA) += tegra/ -obj-y += broadcom/ \ +obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o +obj-y += allwinner/ \ + amlogic/ \ + broadcom/ \ cadence/ \ freescale/ \ hisilicon/ \ + ingenic/ \ intel/ \ lantiq/ \ marvell/ \ + mediatek/ \ + microchip/ \ motorola/ \ mscc/ \ qualcomm/ \ ralink/ \ + renesas/ \ + rockchip/ \ samsung/ \ socionext/ \ st/ \ - ti/ + sunplus/ \ + tegra/ \ + ti/ \ + xilinx/ diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index e760d89d3976..fb584518b2d0 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -22,7 +22,7 @@ config PHY_SUN4I_USB config PHY_SUN6I_MIPI_DPHY tristate "Allwinner A31 MIPI D-PHY Support" depends on ARCH_SUNXI || COMPILE_TEST - depends on HAS_IOMEM + depends on HAS_IOMEM && COMMON_CLK depends on RESET_CONTROLLER select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 856927382248..3a3831f6059a 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -7,7 +7,7 @@ * Based on code from * Allwinner Technology Co., Ltd. <www.allwinnertech.com> * - * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Modelled after: Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> */ @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/extcon-provider.h> +#include <linux/gpio/consumer.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -42,7 +43,7 @@ #define REG_PHYCTL_A33 0x10 #define REG_PHY_OTGCTL 0x20 -#define REG_PMU_UNK1 0x10 +#define REG_HCI_PHY_CTL 0x10 #define PHYCTL_DATA BIT(7) @@ -81,6 +82,7 @@ /* A83T specific control bits for PHY0 */ #define PHY_CTL_VBUSVLDEXT BIT(5) #define PHY_CTL_SIDDQ BIT(3) +#define PHY_CTL_H3_SIDDQ BIT(1) /* A83T specific control bits for PHY2 HSIC */ #define SUNXI_EHCI_HS_FORCE BIT(20) @@ -114,9 +116,9 @@ struct sun4i_usb_phy_cfg { int hsic_index; enum sun4i_usb_phy_type type; u32 disc_thresh; + u32 hci_phy_ctl_clear; u8 phyctl_offset; bool dedicated_clocks; - bool enable_pmu_unk1; bool phy0_dual_route; int missing_phys; }; @@ -287,6 +289,12 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } + if (phy->pmu && data->cfg->hci_phy_ctl_clear) { + val = readl(phy->pmu + REG_HCI_PHY_CTL); + val &= ~data->cfg->hci_phy_ctl_clear; + writel(val, phy->pmu + REG_HCI_PHY_CTL); + } + if (data->cfg->type == sun8i_a83t_phy || data->cfg->type == sun50i_h6_phy) { if (phy->index == 0) { @@ -296,11 +304,6 @@ static int sun4i_usb_phy_init(struct phy *_phy) writel(val, data->base + data->cfg->phyctl_offset); } } else { - if (phy->pmu && data->cfg->enable_pmu_unk1) { - val = readl(phy->pmu + REG_PMU_UNK1); - writel(val & ~2, phy->pmu + REG_PMU_UNK1); - } - /* Enable USB 45 Ohm resistor calibration */ if (phy->index == 0) sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); @@ -545,13 +548,14 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) struct sun4i_usb_phy_data *data = container_of(work, struct sun4i_usb_phy_data, detect.work); struct phy *phy0 = data->phys[0].phy; - struct sun4i_usb_phy *phy = phy_get_drvdata(phy0); + struct sun4i_usb_phy *phy; bool force_session_end, id_notify = false, vbus_notify = false; int id_det, vbus_det; - if (phy0 == NULL) + if (!phy0) return; + phy = phy_get_drvdata(phy0); id_det = sun4i_usb_phy0_get_id_det(data); vbus_det = sun4i_usb_phy0_get_vbus_det(data); @@ -684,7 +688,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct phy_provider *phy_provider; - struct resource *res; int i, ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -698,8 +701,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (!data->cfg) return -EINVAL; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); - data->base = devm_ioremap_resource(dev, res); + data->base = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl"); if (IS_ERR(data->base)) return PTR_ERR(data->base); @@ -766,7 +768,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (data->cfg->dedicated_clocks) snprintf(name, sizeof(name), "usb%d_phy", i); else - strlcpy(name, "usb_phy", sizeof(name)); + strscpy(name, "usb_phy", sizeof(name)); phy->clk = devm_clk_get(dev, name); if (IS_ERR(phy->clk)) { @@ -794,9 +796,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ snprintf(name, sizeof(name), "pmu%d", i); - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, name); - phy->pmu = devm_ioremap_resource(dev, res); + phy->pmu = devm_platform_ioremap_resource_byname(pdev, name); if (IS_ERR(phy->pmu)) return PTR_ERR(phy->pmu); } @@ -865,7 +865,6 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { @@ -874,7 +873,6 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { @@ -883,7 +881,6 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { @@ -892,7 +889,6 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { @@ -901,7 +897,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { @@ -910,7 +905,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { @@ -927,7 +921,7 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; @@ -937,7 +931,7 @@ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; @@ -947,7 +941,16 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, + .phy0_dual_route = true, +}; + +static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = { + .num_phys = 2, + .type = sun50i_h6_phy, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .hci_phy_ctl_clear = PHY_CTL_SIDDQ, .phy0_dual_route = true, }; @@ -957,17 +960,15 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { .num_phys = 4, .type = sun50i_h6_phy, - .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, .phy0_dual_route = true, .missing_phys = BIT(1) | BIT(2), }; @@ -983,6 +984,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, + { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = &sun50i_a64_cfg}, { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg }, diff --git a/drivers/phy/allwinner/phy-sun50i-usb3.c b/drivers/phy/allwinner/phy-sun50i-usb3.c index b1c04f71a31d..84055b720016 100644 --- a/drivers/phy/allwinner/phy-sun50i-usb3.c +++ b/drivers/phy/allwinner/phy-sun50i-usb3.c @@ -134,7 +134,6 @@ static int sun50i_usb3_phy_probe(struct platform_device *pdev) struct sun50i_usb3_phy *phy; struct device *dev = &pdev->dev; struct phy_provider *phy_provider; - struct resource *res; phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) @@ -153,8 +152,7 @@ static int sun50i_usb3_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->reset); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->regs = devm_ioremap_resource(dev, res); + phy->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->regs)) return PTR_ERR(phy->regs); diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c index 79c8af5c7c1d..3900f1650851 100644 --- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c @@ -24,6 +24,14 @@ #define SUN6I_DPHY_TX_CTL_REG 0x04 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28) +#define SUN6I_DPHY_RX_CTL_REG 0x08 +#define SUN6I_DPHY_RX_CTL_EN_DBC BIT(31) +#define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE BIT(24) +#define SUN6I_DPHY_RX_CTL_RX_D3_FORCE BIT(23) +#define SUN6I_DPHY_RX_CTL_RX_D2_FORCE BIT(22) +#define SUN6I_DPHY_RX_CTL_RX_D1_FORCE BIT(21) +#define SUN6I_DPHY_RX_CTL_RX_D0_FORCE BIT(20) + #define SUN6I_DPHY_TX_TIME0_REG 0x10 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24) #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16) @@ -44,12 +52,29 @@ #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8) #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff) +#define SUN6I_DPHY_RX_TIME0_REG 0x30 +#define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n) (((n) & 0xff) << 24) +#define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n) (((n) & 0xff) << 16) +#define SUN6I_DPHY_RX_TIME0_LP_RX(n) (((n) & 0xff) << 8) + +#define SUN6I_DPHY_RX_TIME1_REG 0x34 +#define SUN6I_DPHY_RX_TIME1_RX_DLY(n) (((n) & 0xfff) << 20) +#define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n) ((n) & 0xfffff) + +#define SUN6I_DPHY_RX_TIME2_REG 0x38 +#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n) (((n) & 0xff) << 8) +#define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n) ((n) & 0xff) + +#define SUN6I_DPHY_RX_TIME3_REG 0x40 +#define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n) (((n) & 0xffff) << 16) + #define SUN6I_DPHY_ANA0_REG 0x4c #define SUN6I_DPHY_ANA0_REG_PWS BIT(31) #define SUN6I_DPHY_ANA0_REG_DMPC BIT(28) #define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24) #define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12) #define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8) +#define SUN6I_DPHY_ANA0_REG_SFB(n) (((n) & 3) << 2) #define SUN6I_DPHY_ANA1_REG 0x50 #define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31) @@ -84,6 +109,11 @@ #define SUN6I_DPHY_DBG5_REG 0xf4 +enum sun6i_dphy_direction { + SUN6I_DPHY_DIRECTION_TX, + SUN6I_DPHY_DIRECTION_RX, +}; + struct sun6i_dphy { struct clk *bus_clk; struct clk *mod_clk; @@ -92,6 +122,8 @@ struct sun6i_dphy { struct phy *phy; struct phy_configure_opts_mipi_dphy config; + + enum sun6i_dphy_direction direction; }; static int sun6i_dphy_init(struct phy *phy) @@ -119,9 +151,8 @@ static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts) return 0; } -static int sun6i_dphy_power_on(struct phy *phy) +static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy) { - struct sun6i_dphy *dphy = phy_get_drvdata(phy); u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG, @@ -211,12 +242,129 @@ static int sun6i_dphy_power_on(struct phy *phy) return 0; } +static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy) +{ + /* Physical clock rate is actually half of symbol rate with DDR. */ + unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate; + unsigned long dphy_clk_rate; + unsigned int rx_dly; + unsigned int lprst_dly; + u32 value; + + dphy_clk_rate = clk_get_rate(dphy->mod_clk); + if (!dphy_clk_rate) + return -EINVAL; + + /* Hardcoded timing parameters from the Allwinner BSP. */ + regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG, + SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) | + SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) | + SUN6I_DPHY_RX_TIME0_LP_RX(255)); + + /* + * Formula from the Allwinner BSP, with hardcoded coefficients + * (probably internal divider/multiplier). + */ + rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8)); + + /* + * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP: + * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000 + * but does not use it and hardcodes 255 instead. + */ + regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG, + SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) | + SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255)); + + /* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */ + regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG, + SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4)); + + /* + * Formula from the Allwinner BSP, with hardcoded coefficients + * (probably internal divider/multiplier). + */ + lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2)); + + regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG, + SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly)); + + /* Analog parameters are hardcoded in the Allwinner BSP. */ + regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, + SUN6I_DPHY_ANA0_REG_PWS | + SUN6I_DPHY_ANA0_REG_SLV(7) | + SUN6I_DPHY_ANA0_REG_SFB(2)); + + regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, + SUN6I_DPHY_ANA1_REG_SVTT(4)); + + regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, + SUN6I_DPHY_ANA4_REG_DMPLVC | + SUN6I_DPHY_ANA4_REG_DMPLVD(1)); + + regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, + SUN6I_DPHY_ANA2_REG_ENIB); + + regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, + SUN6I_DPHY_ANA3_EN_LDOR | + SUN6I_DPHY_ANA3_EN_LDOC | + SUN6I_DPHY_ANA3_EN_LDOD); + + /* + * Delay comes from the Allwinner BSP, likely for internal regulator + * ramp-up. + */ + udelay(3); + + value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE; + + /* + * Rx data lane force-enable bits are used as regular RX enable by the + * Allwinner BSP. + */ + if (dphy->config.lanes >= 1) + value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE; + if (dphy->config.lanes >= 2) + value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE; + if (dphy->config.lanes >= 3) + value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE; + if (dphy->config.lanes == 4) + value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE; + + regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value); + + regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, + SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | + SUN6I_DPHY_GCTL_EN); + + return 0; +} + +static int sun6i_dphy_power_on(struct phy *phy) +{ + struct sun6i_dphy *dphy = phy_get_drvdata(phy); + + switch (dphy->direction) { + case SUN6I_DPHY_DIRECTION_TX: + return sun6i_dphy_tx_power_on(dphy); + case SUN6I_DPHY_DIRECTION_RX: + return sun6i_dphy_rx_power_on(dphy); + default: + return -EINVAL; + } +} + static int sun6i_dphy_power_off(struct phy *phy) { struct sun6i_dphy *dphy = phy_get_drvdata(phy); - regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG, - SUN6I_DPHY_ANA1_REG_VTTMODE, 0); + regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0); + + regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0); + regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0); + regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0); + regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0); + regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0); return 0; } @@ -233,7 +381,7 @@ static int sun6i_dphy_exit(struct phy *phy) } -static struct phy_ops sun6i_dphy_ops = { +static const struct phy_ops sun6i_dphy_ops = { .configure = sun6i_dphy_configure, .power_on = sun6i_dphy_power_on, .power_off = sun6i_dphy_power_off, @@ -241,7 +389,7 @@ static struct phy_ops sun6i_dphy_ops = { .exit = sun6i_dphy_exit, }; -static struct regmap_config sun6i_dphy_regmap_config = { +static const struct regmap_config sun6i_dphy_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, @@ -253,15 +401,15 @@ static int sun6i_dphy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct sun6i_dphy *dphy; - struct resource *res; + const char *direction; void __iomem *regs; + int ret; dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); if (!dphy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n"); return PTR_ERR(regs); @@ -292,6 +440,14 @@ static int sun6i_dphy_probe(struct platform_device *pdev) return PTR_ERR(dphy->phy); } + dphy->direction = SUN6I_DPHY_DIRECTION_TX; + + ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction", + &direction); + + if (!ret && !strncmp(direction, "rx", 2)) + dphy->direction = SUN6I_DPHY_DIRECTION_RX; + phy_set_drvdata(dphy->phy, dphy); phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); diff --git a/drivers/phy/allwinner/phy-sun9i-usb.c b/drivers/phy/allwinner/phy-sun9i-usb.c index fc6784dd7fa0..2f9e60c188b8 100644 --- a/drivers/phy/allwinner/phy-sun9i-usb.c +++ b/drivers/phy/allwinner/phy-sun9i-usb.c @@ -117,7 +117,6 @@ static int sun9i_usb_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct phy_provider *phy_provider; - struct resource *res; phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) @@ -156,8 +155,7 @@ static int sun9i_usb_phy_probe(struct platform_device *pdev) } } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->pmu = devm_ioremap_resource(dev, res); + phy->pmu = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->pmu)) return PTR_ERR(phy->pmu); diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig index af774ac2b934..ce7ba3eb2a8e 100644 --- a/drivers/phy/amlogic/Kconfig +++ b/drivers/phy/amlogic/Kconfig @@ -2,13 +2,24 @@ # # Phy drivers for Amlogic platforms # +config PHY_MESON8_HDMI_TX + tristate "Meson8, Meson8b and Meson8m2 HDMI TX PHY driver" + depends on (ARCH_MESON && ARM) || COMPILE_TEST + depends on OF + select MFD_SYSCON + help + Enable this to support the HDMI TX PHYs found in Meson8, + Meson8b and Meson8m2 SoCs. + If unsure, say N. + config PHY_MESON8B_USB2 - tristate "Meson8, Meson8b and GXBB USB2 PHY driver" + tristate "Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver" default ARCH_MESON depends on OF && (ARCH_MESON || COMPILE_TEST) depends on USB_SUPPORT select USB_COMMON select GENERIC_PHY + select REGMAP_MMIO help Enable this to support the Meson USB2 PHYs found in Meson8, Meson8b and GXBB SoCs. @@ -26,16 +37,16 @@ config PHY_MESON_GXL_USB2 GXL and GXM SoCs. If unsure, say N. -config PHY_MESON_GXL_USB3 - tristate "Meson GXL and GXM USB3 PHY drivers" +config PHY_MESON_G12A_MIPI_DPHY_ANALOG + tristate "Meson G12A MIPI Analog DPHY driver" default ARCH_MESON depends on OF && (ARCH_MESON || COMPILE_TEST) - depends on USB_SUPPORT select GENERIC_PHY - select REGMAP_MMIO + select MFD_SYSCON + select GENERIC_PHY_MIPI_DPHY help - Enable this to support the Meson USB3 PHY and OTG detection - IP block found in Meson GXL and GXM SoCs. + Enable this to support the Meson MIPI Analog DPHY found in Meson G12A + SoCs. If unsure, say N. config PHY_MESON_G12A_USB2 @@ -59,3 +70,38 @@ config PHY_MESON_G12A_USB3_PCIE Enable this to support the Meson USB3 + PCIE Combo PHY found in Meson G12A SoCs. If unsure, say N. + +config PHY_MESON_AXG_PCIE + tristate "Meson AXG PCIE PHY driver" + default ARCH_MESON + depends on OF && (ARCH_MESON || COMPILE_TEST) + select GENERIC_PHY + select REGMAP_MMIO + help + Enable this to support the Meson MIPI + PCIE PHY found + in Meson AXG SoCs. + If unsure, say N. + +config PHY_MESON_AXG_MIPI_PCIE_ANALOG + tristate "Meson AXG MIPI + PCIE analog PHY driver" + default ARCH_MESON + depends on OF && (ARCH_MESON || COMPILE_TEST) + select GENERIC_PHY + select REGMAP_MMIO + select GENERIC_PHY_MIPI_DPHY + help + Enable this to support the Meson MIPI + PCIE analog PHY + found in Meson AXG SoCs. + If unsure, say N. + +config PHY_MESON_AXG_MIPI_DPHY + tristate "Meson AXG MIPI DPHY driver" + default ARCH_MESON + depends on OF && (ARCH_MESON || COMPILE_TEST) + select GENERIC_PHY + select REGMAP_MMIO + select GENERIC_PHY_MIPI_DPHY + help + Enable this to support the Meson MIPI DPHY found in Meson AXG + SoCs. + If unsure, say N. diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile index 11d1c42ac2be..91e3b9790c03 100644 --- a/drivers/phy/amlogic/Makefile +++ b/drivers/phy/amlogic/Makefile @@ -1,6 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o -obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o -obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o -obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o -obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o +obj-$(CONFIG_PHY_MESON8_HDMI_TX) += phy-meson8-hdmi-tx.o +obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o +obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o +obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o +obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o +obj-$(CONFIG_PHY_MESON_G12A_MIPI_DPHY_ANALOG) += phy-meson-g12a-mipi-dphy-analog.o +obj-$(CONFIG_PHY_MESON_AXG_PCIE) += phy-meson-axg-pcie.o +obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o +obj-$(CONFIG_PHY_MESON_AXG_MIPI_DPHY) += phy-meson-axg-mipi-dphy.o diff --git a/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c b/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c new file mode 100644 index 000000000000..32d1ff09befb --- /dev/null +++ b/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Meson AXG MIPI DPHY driver + * + * Copyright (C) 2018 Amlogic, Inc. All rights reserved + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +/* [31] soft reset for the phy. + * 1: reset. 0: dessert the reset. + * [30] clock lane soft reset. + * [29] data byte lane 3 soft reset. + * [28] data byte lane 2 soft reset. + * [27] data byte lane 1 soft reset. + * [26] data byte lane 0 soft reset. + * [25] mipi dsi pll clock selection. + * 1: clock from fixed 850Mhz clock source. 0: from VID2 PLL. + * [12] mipi HSbyteclk enable. + * [11] mipi divider clk selection. + * 1: select the mipi DDRCLKHS from clock divider. + * 0: from PLL clock. + * [10] mipi clock divider control. + * 1: /4. 0: /2. + * [9] mipi divider output enable. + * [8] mipi divider counter enable. + * [7] PLL clock enable. + * [5] LPDT data endian. + * 1 = transfer the high bit first. 0 : transfer the low bit first. + * [4] HS data endian. + * [3] force data byte lane in stop mode. + * [2] force data byte lane 0 in receiver mode. + * [1] write 1 to sync the txclkesc input. the internal logic have to + * use txclkesc to decide Txvalid and Txready. + * [0] enalbe the MIPI DPHY TxDDRClk. + */ +#define MIPI_DSI_PHY_CTRL 0x0 + +/* [31] clk lane tx_hs_en control selection. + * 1: from register. 0: use clk lane state machine. + * [30] register bit for clock lane tx_hs_en. + * [29] clk lane tx_lp_en contrl selection. + * 1: from register. 0: from clk lane state machine. + * [28] register bit for clock lane tx_lp_en. + * [27] chan0 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [26] register bit for chan0 tx_hs_en. + * [25] chan0 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [24] register bit from chan0 tx_lp_en. + * [23] chan0 rx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [22] register bit from chan0 rx_lp_en. + * [21] chan0 contention detection enable control selection. + * 1: from register. 0: from chan0 state machine. + * [20] register bit from chan0 contention dectection enable. + * [19] chan1 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [18] register bit for chan1 tx_hs_en. + * [17] chan1 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [16] register bit from chan1 tx_lp_en. + * [15] chan2 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [14] register bit for chan2 tx_hs_en. + * [13] chan2 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [12] register bit from chan2 tx_lp_en. + * [11] chan3 tx_hs_en control selection. + * 1: from register. 0: from chan0 state machine. + * [10] register bit for chan3 tx_hs_en. + * [9] chan3 tx_lp_en control selection. + * 1: from register. 0: from chan0 state machine. + * [8] register bit from chan3 tx_lp_en. + * [4] clk chan power down. this bit is also used as the power down + * of the whole MIPI_DSI_PHY. + * [3] chan3 power down. + * [2] chan2 power down. + * [1] chan1 power down. + * [0] chan0 power down. + */ +#define MIPI_DSI_CHAN_CTRL 0x4 + +/* [24] rx turn watch dog triggered. + * [23] rx esc watchdog triggered. + * [22] mbias ready. + * [21] txclkesc synced and ready. + * [20:17] clk lane state. {mbias_ready, tx_stop, tx_ulps, tx_hs_active} + * [16:13] chan3 state{0, tx_stop, tx_ulps, tx_hs_active} + * [12:9] chan2 state.{0, tx_stop, tx_ulps, tx_hs_active} + * [8:5] chan1 state. {0, tx_stop, tx_ulps, tx_hs_active} + * [4:0] chan0 state. {TX_STOP, tx_ULPS, hs_active, direction, rxulpsesc} + */ +#define MIPI_DSI_CHAN_STS 0x8 + +/* [31:24] TCLK_PREPARE. + * [23:16] TCLK_ZERO. + * [15:8] TCLK_POST. + * [7:0] TCLK_TRAIL. + */ +#define MIPI_DSI_CLK_TIM 0xc + +/* [31:24] THS_PREPARE. + * [23:16] THS_ZERO. + * [15:8] THS_TRAIL. + * [7:0] THS_EXIT. + */ +#define MIPI_DSI_HS_TIM 0x10 + +/* [31:24] tTA_GET. + * [23:16] tTA_GO. + * [15:8] tTA_SURE. + * [7:0] tLPX. + */ +#define MIPI_DSI_LP_TIM 0x14 + +/* wait time to MIPI DIS analog ready. */ +#define MIPI_DSI_ANA_UP_TIM 0x18 + +/* TINIT. */ +#define MIPI_DSI_INIT_TIM 0x1c + +/* TWAKEUP. */ +#define MIPI_DSI_WAKEUP_TIM 0x20 + +/* when in RxULPS check state, after the logic enable the analog, + * how long we should wait to check the lP state . + */ +#define MIPI_DSI_LPOK_TIM 0x24 + +/* Watchdog for RX low power state no finished. */ +#define MIPI_DSI_LP_WCHDOG 0x28 + +/* tMBIAS, after send power up signals to analog, + * how long we should wait for analog powered up. + */ +#define MIPI_DSI_ANA_CTRL 0x2c + +/* [31:8] reserved for future. + * [7:0] tCLK_PRE. + */ +#define MIPI_DSI_CLK_TIM1 0x30 + +/* watchdog for turn around waiting time. */ +#define MIPI_DSI_TURN_WCHDOG 0x34 + +/* When in RxULPS state, how frequency we should to check + * if the TX side out of ULPS state. + */ +#define MIPI_DSI_ULPS_CHECK 0x38 +#define MIPI_DSI_TEST_CTRL0 0x3c +#define MIPI_DSI_TEST_CTRL1 0x40 + +struct phy_meson_axg_mipi_dphy_priv { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *analog; + struct phy_configure_opts_mipi_dphy config; +}; + +static const struct regmap_config phy_meson_axg_mipi_dphy_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MIPI_DSI_TEST_CTRL1, +}; + +static int phy_meson_axg_mipi_dphy_init(struct phy *phy) +{ + struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_init(priv->analog); + if (ret) + return ret; + + ret = reset_control_reset(priv->reset); + if (ret) + return ret; + + return 0; +} + +static int phy_meson_axg_mipi_dphy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); + if (ret) + return ret; + + ret = phy_configure(priv->analog, opts); + if (ret) + return ret; + + memcpy(&priv->config, opts, sizeof(priv->config)); + + return 0; +} + +static int phy_meson_axg_mipi_dphy_power_on(struct phy *phy) +{ + struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy); + int ret; + unsigned long temp; + + ret = phy_power_on(priv->analog); + if (ret) + return ret; + + /* enable phy clock */ + regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, 0x1); + regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, + BIT(0) | /* enable the DSI PLL clock . */ + BIT(7) | /* enable pll clock which connected to DDR clock path */ + BIT(8)); /* enable the clock divider counter */ + + /* enable the divider clock out */ + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(9), BIT(9)); + + /* enable the byte clock generation. */ + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(12), BIT(12)); + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), BIT(31)); + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), 0); + + /* Calculate lanebyteclk period in ps */ + temp = (1000000 * 100) / (priv->config.hs_clk_rate / 1000); + temp = temp * 8 * 10; + + regmap_write(priv->regmap, MIPI_DSI_CLK_TIM, + DIV_ROUND_UP(priv->config.clk_trail, temp) | + (DIV_ROUND_UP(priv->config.clk_post + + priv->config.hs_trail, temp) << 8) | + (DIV_ROUND_UP(priv->config.clk_zero, temp) << 16) | + (DIV_ROUND_UP(priv->config.clk_prepare, temp) << 24)); + regmap_write(priv->regmap, MIPI_DSI_CLK_TIM1, + DIV_ROUND_UP(priv->config.clk_pre, BITS_PER_BYTE)); + + regmap_write(priv->regmap, MIPI_DSI_HS_TIM, + DIV_ROUND_UP(priv->config.hs_exit, temp) | + (DIV_ROUND_UP(priv->config.hs_trail, temp) << 8) | + (DIV_ROUND_UP(priv->config.hs_zero, temp) << 16) | + (DIV_ROUND_UP(priv->config.hs_prepare, temp) << 24)); + + regmap_write(priv->regmap, MIPI_DSI_LP_TIM, + DIV_ROUND_UP(priv->config.lpx, temp) | + (DIV_ROUND_UP(priv->config.ta_sure, temp) << 8) | + (DIV_ROUND_UP(priv->config.ta_go, temp) << 16) | + (DIV_ROUND_UP(priv->config.ta_get, temp) << 24)); + + regmap_write(priv->regmap, MIPI_DSI_ANA_UP_TIM, 0x0100); + regmap_write(priv->regmap, MIPI_DSI_INIT_TIM, + DIV_ROUND_UP(priv->config.init * NSEC_PER_MSEC, temp)); + regmap_write(priv->regmap, MIPI_DSI_WAKEUP_TIM, + DIV_ROUND_UP(priv->config.wakeup * NSEC_PER_MSEC, temp)); + regmap_write(priv->regmap, MIPI_DSI_LPOK_TIM, 0x7C); + regmap_write(priv->regmap, MIPI_DSI_ULPS_CHECK, 0x927C); + regmap_write(priv->regmap, MIPI_DSI_LP_WCHDOG, 0x1000); + regmap_write(priv->regmap, MIPI_DSI_TURN_WCHDOG, 0x1000); + + /* Powerup the analog circuit */ + switch (priv->config.lanes) { + case 1: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xe); + break; + case 2: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xc); + break; + case 3: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0x8); + break; + case 4: + default: + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0); + break; + } + + /* Trigger a sync active for esc_clk */ + regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(1), BIT(1)); + + return 0; +} + +static int phy_meson_axg_mipi_dphy_power_off(struct phy *phy) +{ + struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy); + + regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xf); + regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31)); + + phy_power_off(priv->analog); + + return 0; +} + +static int phy_meson_axg_mipi_dphy_exit(struct phy *phy) +{ + struct phy_meson_axg_mipi_dphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_exit(priv->analog); + if (ret) + return ret; + + return reset_control_reset(priv->reset); +} + +static const struct phy_ops phy_meson_axg_mipi_dphy_ops = { + .configure = phy_meson_axg_mipi_dphy_configure, + .init = phy_meson_axg_mipi_dphy_init, + .exit = phy_meson_axg_mipi_dphy_exit, + .power_on = phy_meson_axg_mipi_dphy_power_on, + .power_off = phy_meson_axg_mipi_dphy_power_off, + .owner = THIS_MODULE, +}; + +static int phy_meson_axg_mipi_dphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct resource *res; + struct phy_meson_axg_mipi_dphy_priv *priv; + struct phy *phy; + void __iomem *base; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + platform_set_drvdata(pdev, priv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_meson_axg_mipi_dphy_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->clk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->reset = devm_reset_control_get(dev, "phy"); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + + priv->analog = devm_phy_get(dev, "analog"); + if (IS_ERR(priv->analog)) + return PTR_ERR(priv->analog); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + ret = reset_control_deassert(priv->reset); + if (ret) + return ret; + + phy = devm_phy_create(dev, NULL, &phy_meson_axg_mipi_dphy_ops); + if (IS_ERR(phy)) { + ret = PTR_ERR(phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to create PHY\n"); + + return ret; + } + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_meson_axg_mipi_dphy_of_match[] = { + { .compatible = "amlogic,axg-mipi-dphy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_meson_axg_mipi_dphy_of_match); + +static struct platform_driver phy_meson_axg_mipi_dphy_driver = { + .probe = phy_meson_axg_mipi_dphy_probe, + .driver = { + .name = "phy-meson-axg-mipi-dphy", + .of_match_table = phy_meson_axg_mipi_dphy_of_match, + }, +}; +module_platform_driver(phy_meson_axg_mipi_dphy_driver); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION("Meson AXG MIPI DPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c b/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c new file mode 100644 index 000000000000..a3e1108b736d --- /dev/null +++ b/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic AXG MIPI + PCIE analog PHY driver + * + * Copyright (C) 2019 Remi Pommarel <repk@triplefau.lt> + */ +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <dt-bindings/phy/phy.h> + +#define HHI_MIPI_CNTL0 0x00 +#define HHI_MIPI_CNTL0_COMMON_BLOCK GENMASK(31, 28) +#define HHI_MIPI_CNTL0_ENABLE BIT(29) +#define HHI_MIPI_CNTL0_BANDGAP BIT(26) +#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(25, 16) +#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0) + +#define HHI_MIPI_CNTL1 0x04 +#define HHI_MIPI_CNTL1_CH0_CML_PDR_EN BIT(12) +#define HHI_MIPI_CNTL1_LP_ABILITY GENMASK(5, 4) +#define HHI_MIPI_CNTL1_LP_RESISTER BIT(3) +#define HHI_MIPI_CNTL1_INPUT_SETTING BIT(2) +#define HHI_MIPI_CNTL1_INPUT_SEL BIT(1) +#define HHI_MIPI_CNTL1_PRBS7_EN BIT(0) + +#define HHI_MIPI_CNTL2 0x08 +#define HHI_MIPI_CNTL2_CH_PU GENMASK(31, 25) +#define HHI_MIPI_CNTL2_CH_CTL GENMASK(24, 19) +#define HHI_MIPI_CNTL2_CH0_DIGDR_EN BIT(18) +#define HHI_MIPI_CNTL2_CH_DIGDR_EN BIT(17) +#define HHI_MIPI_CNTL2_LPULPS_EN BIT(16) +#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11) +#define HHI_MIPI_CNTL2_CH0_LP_CTL GENMASK(10, 1) + +#define DSI_LANE_0 BIT(4) +#define DSI_LANE_1 BIT(3) +#define DSI_LANE_CLK BIT(2) +#define DSI_LANE_2 BIT(1) +#define DSI_LANE_3 BIT(0) + +struct phy_axg_mipi_pcie_analog_priv { + struct phy *phy; + struct regmap *regmap; + bool dsi_configured; + bool dsi_enabled; + bool powered; + struct phy_configure_opts_mipi_dphy config; +}; + +static void phy_bandgap_enable(struct phy_axg_mipi_pcie_analog_priv *priv) +{ + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_BANDGAP, HHI_MIPI_CNTL0_BANDGAP); + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_ENABLE, HHI_MIPI_CNTL0_ENABLE); +} + +static void phy_bandgap_disable(struct phy_axg_mipi_pcie_analog_priv *priv) +{ + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_BANDGAP, 0); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_ENABLE, 0); +} + +static void phy_dsi_analog_enable(struct phy_axg_mipi_pcie_analog_priv *priv) +{ + u32 reg; + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL1, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0x1b8)); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + BIT(31), BIT(31)); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL0, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8)); + + regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x001e); + + regmap_write(priv->regmap, HHI_MIPI_CNTL2, + (0x26e0 << 16) | (0x459 << 0)); + + reg = DSI_LANE_CLK; + switch (priv->config.lanes) { + case 4: + reg |= DSI_LANE_3; + fallthrough; + case 3: + reg |= DSI_LANE_2; + fallthrough; + case 2: + reg |= DSI_LANE_1; + fallthrough; + case 1: + reg |= DSI_LANE_0; + break; + default: + reg = 0; + } + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2, + HHI_MIPI_CNTL2_CH_EN, + FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg)); + + priv->dsi_enabled = true; +} + +static void phy_dsi_analog_disable(struct phy_axg_mipi_pcie_analog_priv *priv) +{ + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL1, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0)); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, BIT(31), 0); + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, + HHI_MIPI_CNTL0_DIF_REF_CTL1, 0); + + regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x6); + + regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0x00200000); + + priv->dsi_enabled = false; +} + +static int phy_axg_mipi_pcie_analog_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); + if (ret) + return ret; + + memcpy(&priv->config, opts, sizeof(priv->config)); + + priv->dsi_configured = true; + + /* If PHY was already powered on, setup the DSI analog part */ + if (priv->powered) { + /* If reconfiguring, disable & reconfigure */ + if (priv->dsi_enabled) + phy_dsi_analog_disable(priv); + + usleep_range(100, 200); + + phy_dsi_analog_enable(priv); + } + + return 0; +} + +static int phy_axg_mipi_pcie_analog_power_on(struct phy *phy) +{ + struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy); + + phy_bandgap_enable(priv); + + if (priv->dsi_configured) + phy_dsi_analog_enable(priv); + + priv->powered = true; + + return 0; +} + +static int phy_axg_mipi_pcie_analog_power_off(struct phy *phy) +{ + struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy); + + phy_bandgap_disable(priv); + + if (priv->dsi_enabled) + phy_dsi_analog_disable(priv); + + priv->powered = false; + + return 0; +} + +static const struct phy_ops phy_axg_mipi_pcie_analog_ops = { + .configure = phy_axg_mipi_pcie_analog_configure, + .power_on = phy_axg_mipi_pcie_analog_power_on, + .power_off = phy_axg_mipi_pcie_analog_power_off, + .owner = THIS_MODULE, +}; + +static int phy_axg_mipi_pcie_analog_probe(struct platform_device *pdev) +{ + struct phy_provider *phy; + struct device *dev = &pdev->dev; + struct phy_axg_mipi_pcie_analog_priv *priv; + struct device_node *np = dev->of_node, *parent_np; + struct regmap *map; + int ret; + + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Get the hhi system controller node */ + parent_np = of_get_parent(dev->of_node); + map = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + if (IS_ERR(map)) { + dev_err(dev, + "failed to get HHI regmap\n"); + return PTR_ERR(map); + } + + priv->regmap = map; + + priv->phy = devm_phy_create(dev, np, &phy_axg_mipi_pcie_analog_ops); + if (IS_ERR(priv->phy)) { + ret = PTR_ERR(priv->phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to create PHY\n"); + return ret; + } + + phy_set_drvdata(priv->phy, priv); + dev_set_drvdata(dev, priv); + + phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy); +} + +static const struct of_device_id phy_axg_mipi_pcie_analog_of_match[] = { + { + .compatible = "amlogic,axg-mipi-pcie-analog-phy", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_axg_mipi_pcie_analog_of_match); + +static struct platform_driver phy_axg_mipi_pcie_analog_driver = { + .probe = phy_axg_mipi_pcie_analog_probe, + .driver = { + .name = "phy-axg-mipi-pcie-analog", + .of_match_table = phy_axg_mipi_pcie_analog_of_match, + }, +}; +module_platform_driver(phy_axg_mipi_pcie_analog_driver); + +MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>"); +MODULE_DESCRIPTION("Amlogic AXG MIPI + PCIE analog PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson-axg-pcie.c b/drivers/phy/amlogic/phy-meson-axg-pcie.c new file mode 100644 index 000000000000..2299bab38e05 --- /dev/null +++ b/drivers/phy/amlogic/phy-meson-axg-pcie.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic AXG PCIE PHY driver + * + * Copyright (C) 2020 Remi Pommarel <repk@triplefau.lt> + */ +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/platform_device.h> +#include <linux/bitfield.h> +#include <dt-bindings/phy/phy.h> + +#define MESON_PCIE_REG0 0x00 +#define MESON_PCIE_COMMON_CLK BIT(4) +#define MESON_PCIE_PORT_SEL GENMASK(3, 2) +#define MESON_PCIE_CLK BIT(1) +#define MESON_PCIE_POWERDOWN BIT(0) + +#define MESON_PCIE_TWO_X1 FIELD_PREP(MESON_PCIE_PORT_SEL, 0x3) +#define MESON_PCIE_COMMON_REF_CLK FIELD_PREP(MESON_PCIE_COMMON_CLK, 0x1) +#define MESON_PCIE_PHY_INIT (MESON_PCIE_TWO_X1 | \ + MESON_PCIE_COMMON_REF_CLK) +#define MESON_PCIE_RESET_DELAY 500 + +struct phy_axg_pcie_priv { + struct phy *phy; + struct phy *analog; + struct regmap *regmap; + struct reset_control *reset; +}; + +static const struct regmap_config phy_axg_pcie_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_PCIE_REG0, +}; + +static int phy_axg_pcie_power_on(struct phy *phy) +{ + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_power_on(priv->analog); + if (ret != 0) + return ret; + + regmap_update_bits(priv->regmap, MESON_PCIE_REG0, + MESON_PCIE_POWERDOWN, 0); + return 0; +} + +static int phy_axg_pcie_power_off(struct phy *phy) +{ + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_power_off(priv->analog); + if (ret != 0) + return ret; + + regmap_update_bits(priv->regmap, MESON_PCIE_REG0, + MESON_PCIE_POWERDOWN, 1); + return 0; +} + +static int phy_axg_pcie_init(struct phy *phy) +{ + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_init(priv->analog); + if (ret != 0) + return ret; + + regmap_write(priv->regmap, MESON_PCIE_REG0, MESON_PCIE_PHY_INIT); + return reset_control_reset(priv->reset); +} + +static int phy_axg_pcie_exit(struct phy *phy) +{ + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_exit(priv->analog); + if (ret != 0) + return ret; + + return reset_control_reset(priv->reset); +} + +static int phy_axg_pcie_reset(struct phy *phy) +{ + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + ret = phy_reset(priv->analog); + if (ret != 0) + goto out; + + ret = reset_control_assert(priv->reset); + if (ret != 0) + goto out; + udelay(MESON_PCIE_RESET_DELAY); + + ret = reset_control_deassert(priv->reset); + if (ret != 0) + goto out; + udelay(MESON_PCIE_RESET_DELAY); + +out: + return ret; +} + +static const struct phy_ops phy_axg_pcie_ops = { + .init = phy_axg_pcie_init, + .exit = phy_axg_pcie_exit, + .power_on = phy_axg_pcie_power_on, + .power_off = phy_axg_pcie_power_off, + .reset = phy_axg_pcie_reset, + .owner = THIS_MODULE, +}; + +static int phy_axg_pcie_probe(struct platform_device *pdev) +{ + struct phy_provider *pphy; + struct device *dev = &pdev->dev; + struct phy_axg_pcie_priv *priv; + struct device_node *np = dev->of_node; + void __iomem *base; + int ret; + + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->phy = devm_phy_create(dev, np, &phy_axg_pcie_ops); + if (IS_ERR(priv->phy)) { + ret = PTR_ERR(priv->phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to create PHY\n"); + return ret; + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_axg_pcie_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->reset = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + + priv->analog = devm_phy_get(dev, "analog"); + if (IS_ERR(priv->analog)) + return PTR_ERR(priv->analog); + + phy_set_drvdata(priv->phy, priv); + dev_set_drvdata(dev, priv); + pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(pphy); +} + +static const struct of_device_id phy_axg_pcie_of_match[] = { + { + .compatible = "amlogic,axg-pcie-phy", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_axg_pcie_of_match); + +static struct platform_driver phy_axg_pcie_driver = { + .probe = phy_axg_pcie_probe, + .driver = { + .name = "phy-axg-pcie", + .of_match_table = phy_axg_pcie_of_match, + }, +}; +module_platform_driver(phy_axg_pcie_driver); + +MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>"); +MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c new file mode 100644 index 000000000000..c14089fa7db4 --- /dev/null +++ b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Meson G12A MIPI DSI Analog PHY + * + * Copyright (C) 2018 Amlogic, Inc. All rights reserved + * Copyright (C) 2022 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <dt-bindings/phy/phy.h> + +#define HHI_MIPI_CNTL0 0x00 +#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(31, 16) +#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0) + +#define HHI_MIPI_CNTL1 0x04 +#define HHI_MIPI_CNTL1_BANDGAP BIT(16) +#define HHI_MIPI_CNTL2_DIF_REF_CTL2 GENMASK(15, 0) + +#define HHI_MIPI_CNTL2 0x08 +#define HHI_MIPI_CNTL2_DIF_TX_CTL1 GENMASK(31, 16) +#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11) +#define HHI_MIPI_CNTL2_DIF_TX_CTL0 GENMASK(10, 0) + +#define DSI_LANE_0 BIT(4) +#define DSI_LANE_1 BIT(3) +#define DSI_LANE_CLK BIT(2) +#define DSI_LANE_2 BIT(1) +#define DSI_LANE_3 BIT(0) + +struct phy_g12a_mipi_dphy_analog_priv { + struct phy *phy; + struct regmap *regmap; + struct phy_configure_opts_mipi_dphy config; +}; + +static int phy_g12a_mipi_dphy_analog_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); + if (ret) + return ret; + + memcpy(&priv->config, opts, sizeof(priv->config)); + + return 0; +} + +static int phy_g12a_mipi_dphy_analog_power_on(struct phy *phy) +{ + struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy); + unsigned int reg; + + regmap_write(priv->regmap, HHI_MIPI_CNTL0, + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8) | + FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0xa487)); + + regmap_write(priv->regmap, HHI_MIPI_CNTL1, + FIELD_PREP(HHI_MIPI_CNTL2_DIF_REF_CTL2, 0x2e) | + HHI_MIPI_CNTL1_BANDGAP); + + regmap_write(priv->regmap, HHI_MIPI_CNTL2, + FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL0, 0x459) | + FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL1, 0x2680)); + + reg = DSI_LANE_CLK; + switch (priv->config.lanes) { + case 4: + reg |= DSI_LANE_3; + fallthrough; + case 3: + reg |= DSI_LANE_2; + fallthrough; + case 2: + reg |= DSI_LANE_1; + fallthrough; + case 1: + reg |= DSI_LANE_0; + break; + default: + reg = 0; + } + + regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2, + HHI_MIPI_CNTL2_CH_EN, + FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg)); + + return 0; +} + +static int phy_g12a_mipi_dphy_analog_power_off(struct phy *phy) +{ + struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy); + + regmap_write(priv->regmap, HHI_MIPI_CNTL0, 0); + regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0); + regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0); + + return 0; +} + +static const struct phy_ops phy_g12a_mipi_dphy_analog_ops = { + .configure = phy_g12a_mipi_dphy_analog_configure, + .power_on = phy_g12a_mipi_dphy_analog_power_on, + .power_off = phy_g12a_mipi_dphy_analog_power_off, + .owner = THIS_MODULE, +}; + +static int phy_g12a_mipi_dphy_analog_probe(struct platform_device *pdev) +{ + struct phy_provider *phy; + struct device *dev = &pdev->dev; + struct phy_g12a_mipi_dphy_analog_priv *priv; + struct device_node *np = dev->of_node, *parent_np; + struct regmap *map; + + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Get the hhi system controller node */ + parent_np = of_get_parent(np); + map = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), "failed to get HHI regmap\n"); + + priv->regmap = map; + + priv->phy = devm_phy_create(dev, np, &phy_g12a_mipi_dphy_analog_ops); + if (IS_ERR(priv->phy)) + return dev_err_probe(dev, PTR_ERR(priv->phy), "failed to create PHY\n"); + + phy_set_drvdata(priv->phy, priv); + dev_set_drvdata(dev, priv); + + phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy); +} + +static const struct of_device_id phy_g12a_mipi_dphy_analog_of_match[] = { + { + .compatible = "amlogic,g12a-mipi-dphy-analog", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phy_g12a_mipi_dphy_analog_of_match); + +static struct platform_driver phy_g12a_mipi_dphy_analog_driver = { + .probe = phy_g12a_mipi_dphy_analog_probe, + .driver = { + .name = "phy-meson-g12a-mipi-dphy-analog", + .of_match_table = phy_g12a_mipi_dphy_analog_of_match, + }, +}; +module_platform_driver(phy_g12a_mipi_dphy_analog_driver); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION("Meson G12A MIPI Analog D-PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c index 9065ffc85eb4..9d1efa0d9394 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-usb2.c +++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c @@ -66,7 +66,7 @@ #define PHY_CTRL_R14 0x38 #define PHY_CTRL_R14_I_RDP_EN BIT(0) #define PHY_CTRL_R14_I_RPU_SW1_EN BIT(1) - #define PHY_CTRL_R14_I_RPU_SW2_EN GENMASK(2, 3) + #define PHY_CTRL_R14_I_RPU_SW2_EN GENMASK(3, 2) #define PHY_CTRL_R14_PG_RSTN BIT(4) #define PHY_CTRL_R14_I_C2L_DATA_16_8 BIT(5) #define PHY_CTRL_R14_I_C2L_ASSERT_SINGLE_EN_ZERO BIT(6) @@ -146,11 +146,17 @@ #define RESET_COMPLETE_TIME 1000 #define PLL_RESET_COMPLETE_TIME 100 +enum meson_soc_id { + MESON_SOC_G12A = 0, + MESON_SOC_A1, +}; + struct phy_meson_g12a_usb2_priv { struct device *dev; struct regmap *regmap; struct clk *clk; struct reset_control *reset; + int soc_id; }; static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = { @@ -164,6 +170,7 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) { struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy); int ret; + unsigned int value; ret = reset_control_reset(priv->reset); if (ret) @@ -192,18 +199,22 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) | FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9)); - regmap_write(priv->regmap, PHY_CTRL_R18, - FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) | - FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) | - FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) | - FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) | - FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) | - FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) | - FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) | - FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) | - FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) | - FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) | - PHY_CTRL_R18_MPLL_ACG_RANGE); + value = FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) | + FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) | + FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) | + FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) | + FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) | + FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) | + FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) | + PHY_CTRL_R18_MPLL_ACG_RANGE; + + if (priv->soc_id == MESON_SOC_A1) + value |= PHY_CTRL_R18_MPLL_DCO_CLK_SEL; + + regmap_write(priv->regmap, PHY_CTRL_R18, value); udelay(PLL_RESET_COMPLETE_TIME); @@ -227,13 +238,24 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) | FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0)); - regmap_write(priv->regmap, PHY_CTRL_R4, - FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) | - FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) | - FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) | - PHY_CTRL_R4_TEST_BYPASS_MODE_EN | - FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) | - FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0)); + if (priv->soc_id == MESON_SOC_G12A) + regmap_write(priv->regmap, PHY_CTRL_R4, + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) | + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) | + FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) | + PHY_CTRL_R4_TEST_BYPASS_MODE_EN | + FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) | + FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0)); + else if (priv->soc_id == MESON_SOC_A1) { + regmap_write(priv->regmap, PHY_CTRL_R21, + PHY_CTRL_R21_USB2_CAL_ACK_EN | + PHY_CTRL_R21_USB2_TX_STRG_PD | + FIELD_PREP(PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0, 2)); + + /* Analog Settings */ + regmap_write(priv->regmap, PHY_CTRL_R13, + FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + } /* Tuning Disconnect Threshold */ regmap_write(priv->regmap, PHY_CTRL_R3, @@ -241,11 +263,13 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) | FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3)); - /* Analog Settings */ - regmap_write(priv->regmap, PHY_CTRL_R14, 0); - regmap_write(priv->regmap, PHY_CTRL_R13, - PHY_CTRL_R13_UPDATE_PMA_SIGNALS | - FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + if (priv->soc_id == MESON_SOC_G12A) { + /* Analog Settings */ + regmap_write(priv->regmap, PHY_CTRL_R14, 0); + regmap_write(priv->regmap, PHY_CTRL_R13, + PHY_CTRL_R13_UPDATE_PMA_SIGNALS | + FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7)); + } return 0; } @@ -268,7 +292,6 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct phy_provider *phy_provider; - struct resource *res; struct phy_meson_g12a_usb2_priv *priv; struct phy *phy; void __iomem *base; @@ -281,11 +304,12 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev) priv->dev = dev; platform_set_drvdata(pdev, priv); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); + priv->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); + priv->regmap = devm_regmap_init_mmio(dev, base, &phy_meson_g12a_usb2_regmap_conf); if (IS_ERR(priv->regmap)) @@ -321,8 +345,15 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev) } static const struct of_device_id phy_meson_g12a_usb2_of_match[] = { - { .compatible = "amlogic,g12a-usb2-phy", }, - { }, + { + .compatible = "amlogic,g12a-usb2-phy", + .data = (void *)MESON_SOC_G12A, + }, + { + .compatible = "amlogic,a1-usb2-phy", + .data = (void *)MESON_SOC_A1, + }, + { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, phy_meson_g12a_usb2_of_match); diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c index 08e322789e59..d2a1da8d9e58 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c +++ b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c @@ -386,17 +386,14 @@ static int phy_g12a_usb3_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct phy_g12a_usb3_pcie_priv *priv; - struct resource *res; struct phy_provider *phy_provider; void __iomem *base; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -410,39 +407,24 @@ static int phy_g12a_usb3_pcie_probe(struct platform_device *pdev) if (IS_ERR(priv->regmap_cr)) return PTR_ERR(priv->regmap_cr); - priv->clk_ref = devm_clk_get(dev, "ref_clk"); + priv->clk_ref = devm_clk_get_enabled(dev, "ref_clk"); if (IS_ERR(priv->clk_ref)) return PTR_ERR(priv->clk_ref); - ret = clk_prepare_enable(priv->clk_ref); - if (ret) - goto err_disable_clk_ref; - - priv->reset = devm_reset_control_array_get(dev, false, false); + priv->reset = devm_reset_control_array_get_exclusive(dev); if (IS_ERR(priv->reset)) return PTR_ERR(priv->reset); priv->phy = devm_phy_create(dev, np, &phy_g12a_usb3_pcie_ops); - if (IS_ERR(priv->phy)) { - ret = PTR_ERR(priv->phy); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to create PHY\n"); - - return ret; - } + if (IS_ERR(priv->phy)) + return dev_err_probe(dev, PTR_ERR(priv->phy), "failed to create PHY\n"); phy_set_drvdata(priv->phy, priv); dev_set_drvdata(dev, priv); phy_provider = devm_of_phy_provider_register(dev, phy_g12a_usb3_pcie_xlate); - return PTR_ERR_OR_ZERO(phy_provider); - -err_disable_clk_ref: - clk_disable_unprepare(priv->clk_ref); - - return ret; } static const struct of_device_id phy_g12a_usb3_pcie_of_match[] = { diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c index 43ec9bf24abf..db17c3448bfe 100644 --- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c +++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c @@ -114,8 +114,10 @@ static int phy_meson_gxl_usb2_init(struct phy *phy) return ret; ret = clk_prepare_enable(priv->clk); - if (ret) + if (ret) { + reset_control_rearm(priv->reset); return ret; + } return 0; } @@ -125,6 +127,7 @@ static int phy_meson_gxl_usb2_exit(struct phy *phy) struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy); clk_disable_unprepare(priv->clk); + reset_control_rearm(priv->reset); return 0; } @@ -158,7 +161,8 @@ static int phy_meson_gxl_usb2_set_mode(struct phy *phy, U2P_R0_DM_PULLDOWN); regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN, U2P_R0_DP_PULLDOWN); - regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0); + regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, + U2P_R0_ID_PULLUP); break; case PHY_MODE_USB_DEVICE: @@ -230,7 +234,6 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct phy_provider *phy_provider; - struct resource *res; struct phy_meson_gxl_usb2_priv *priv; struct phy *phy; void __iomem *base; @@ -242,8 +245,7 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c deleted file mode 100644 index c0e9e4c16149..000000000000 --- a/drivers/phy/amlogic/phy-meson-gxl-usb3.c +++ /dev/null @@ -1,283 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Meson GXL USB3 PHY and OTG mode detection driver - * - * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> - */ - -#include <linux/bitfield.h> -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/phy/phy.h> -#include <linux/regmap.h> -#include <linux/reset.h> -#include <linux/platform_device.h> - -#define USB_R0 0x00 - #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) - #define USB_R0_P30_PHY_RESET BIT(6) - #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) - #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) - #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) - #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) - #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) - #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) - #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) - #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) - #define USB_R0_U2D_ACT BIT(31) - -#define USB_R1 0x04 - #define USB_R1_U3H_BIGENDIAN_GS BIT(0) - #define USB_R1_U3H_PME_ENABLE BIT(1) - #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) - #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) - #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) - #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) - #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) - #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) - #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) - #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) - -#define USB_R2 0x08 - #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) - #define USB_R2_P30_CR_READ BIT(16) - #define USB_R2_P30_CR_WRITE BIT(17) - #define USB_R2_P30_CR_CAP_ADDR BIT(18) - #define USB_R2_P30_CR_CAP_DATA BIT(19) - #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) - #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) - -#define USB_R3 0x0c - #define USB_R3_P30_SSC_ENABLE BIT(0) - #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) - #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) - #define USB_R3_P30_REF_SSP_EN BIT(13) - #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) - #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) - #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) - -#define USB_R4 0x10 - #define USB_R4_P21_PORT_RESET_0 BIT(0) - #define USB_R4_P21_SLEEP_M0 BIT(1) - #define USB_R4_MEM_PD_MASK GENMASK(3, 2) - #define USB_R4_P21_ONLY BIT(4) - -#define USB_R5 0x14 - #define USB_R5_ID_DIG_SYNC BIT(0) - #define USB_R5_ID_DIG_REG BIT(1) - #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) - #define USB_R5_ID_DIG_EN_0 BIT(4) - #define USB_R5_ID_DIG_EN_1 BIT(5) - #define USB_R5_ID_DIG_CURR BIT(6) - #define USB_R5_ID_DIG_IRQ BIT(7) - #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) - #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) - -/* read-only register */ -#define USB_R6 0x18 - #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) - #define USB_R6_P30_CR_ACK BIT(16) - -struct phy_meson_gxl_usb3_priv { - struct regmap *regmap; - enum phy_mode mode; - struct clk *clk_phy; - struct clk *clk_peripheral; - struct reset_control *reset; -}; - -static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = { - .reg_bits = 8, - .val_bits = 32, - .reg_stride = 4, - .max_register = USB_R6, -}; - -static int phy_meson_gxl_usb3_power_on(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, - USB_R5_ID_DIG_EN_0); - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, - USB_R5_ID_DIG_EN_1); - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK, - FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); - - return 0; -} - -static int phy_meson_gxl_usb3_power_off(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0); - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0); - - return 0; -} - -static int phy_meson_gxl_usb3_set_mode(struct phy *phy, - enum phy_mode mode, int submode) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - switch (mode) { - case PHY_MODE_USB_HOST: - regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0); - regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0, - 0); - break; - - case PHY_MODE_USB_DEVICE: - regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, - USB_R0_U2D_ACT); - regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0, - USB_R4_P21_SLEEP_M0); - break; - - default: - dev_err(&phy->dev, "unsupported PHY mode %d\n", mode); - return -EINVAL; - } - - priv->mode = mode; - - return 0; -} - -static int phy_meson_gxl_usb3_init(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - int ret; - - ret = reset_control_reset(priv->reset); - if (ret) - goto err; - - ret = clk_prepare_enable(priv->clk_phy); - if (ret) - goto err; - - ret = clk_prepare_enable(priv->clk_peripheral); - if (ret) - goto err_disable_clk_phy; - - ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0); - if (ret) - goto err_disable_clk_peripheral; - - regmap_update_bits(priv->regmap, USB_R1, - USB_R1_U3H_FLADJ_30MHZ_REG_MASK, - FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); - - return 0; - -err_disable_clk_peripheral: - clk_disable_unprepare(priv->clk_peripheral); -err_disable_clk_phy: - clk_disable_unprepare(priv->clk_phy); -err: - return ret; -} - -static int phy_meson_gxl_usb3_exit(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - clk_disable_unprepare(priv->clk_peripheral); - clk_disable_unprepare(priv->clk_phy); - - return 0; -} - -static const struct phy_ops phy_meson_gxl_usb3_ops = { - .power_on = phy_meson_gxl_usb3_power_on, - .power_off = phy_meson_gxl_usb3_power_off, - .set_mode = phy_meson_gxl_usb3_set_mode, - .init = phy_meson_gxl_usb3_init, - .exit = phy_meson_gxl_usb3_exit, - .owner = THIS_MODULE, -}; - -static int phy_meson_gxl_usb3_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_meson_gxl_usb3_priv *priv; - struct resource *res; - struct phy *phy; - struct phy_provider *phy_provider; - void __iomem *base; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - priv->regmap = devm_regmap_init_mmio(dev, base, - &phy_meson_gxl_usb3_regmap_conf); - if (IS_ERR(priv->regmap)) - return PTR_ERR(priv->regmap); - - priv->clk_phy = devm_clk_get(dev, "phy"); - if (IS_ERR(priv->clk_phy)) - return PTR_ERR(priv->clk_phy); - - priv->clk_peripheral = devm_clk_get(dev, "peripheral"); - if (IS_ERR(priv->clk_peripheral)) - return PTR_ERR(priv->clk_peripheral); - - priv->reset = devm_reset_control_array_get_shared(dev); - if (IS_ERR(priv->reset)) - return PTR_ERR(priv->reset); - - /* - * default to host mode as hardware defaults and/or boot-loader - * behavior can result in this PHY starting up in device mode. this - * default and the initialization in phy_meson_gxl_usb3_init ensure - * that we reproducibly start in a known mode on all devices. - */ - priv->mode = PHY_MODE_USB_HOST; - - phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops); - if (IS_ERR(phy)) { - ret = PTR_ERR(phy); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to create PHY\n"); - - return ret; - } - - phy_set_drvdata(phy, priv); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id phy_meson_gxl_usb3_of_match[] = { - { .compatible = "amlogic,meson-gxl-usb3-phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match); - -static struct platform_driver phy_meson_gxl_usb3_driver = { - .probe = phy_meson_gxl_usb3_probe, - .driver = { - .name = "phy-meson-gxl-usb3", - .of_match_table = phy_meson_gxl_usb3_of_match, - }, -}; -module_platform_driver(phy_meson_gxl_usb3_driver); - -MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); -MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson8-hdmi-tx.c b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c new file mode 100644 index 000000000000..f9a6572c27d8 --- /dev/null +++ b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Meson8, Meson8b and Meson8m2 HDMI TX PHY. + * + * Copyright (C) 2021 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> + +/* + * Unfortunately there is no detailed documentation available for the + * HHI_HDMI_PHY_CNTL0 register. CTL0 and CTL1 is all we know about. + * Magic register values in the driver below are taken from the vendor + * BSP / kernel. + */ +#define HHI_HDMI_PHY_CNTL0 0x3a0 + #define HHI_HDMI_PHY_CNTL0_HDMI_CTL1 GENMASK(31, 16) + #define HHI_HDMI_PHY_CNTL0_HDMI_CTL0 GENMASK(15, 0) + +#define HHI_HDMI_PHY_CNTL1 0x3a4 + #define HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE BIT(1) + #define HHI_HDMI_PHY_CNTL1_SOFT_RESET BIT(0) + +#define HHI_HDMI_PHY_CNTL2 0x3a8 + +struct phy_meson8_hdmi_tx_priv { + struct regmap *hhi; + struct clk *tmds_clk; +}; + +static int phy_meson8_hdmi_tx_init(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + + return clk_prepare_enable(priv->tmds_clk); +} + +static int phy_meson8_hdmi_tx_exit(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->tmds_clk); + + return 0; +} + +static int phy_meson8_hdmi_tx_power_on(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + unsigned int i; + u16 hdmi_ctl0; + + if (clk_get_rate(priv->tmds_clk) >= 2970UL * 1000 * 1000) + hdmi_ctl0 = 0x1e8b; + else + hdmi_ctl0 = 0x4d0b; + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x08c3) | + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, hdmi_ctl0)); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, 0x0); + + /* Reset three times, just like the vendor driver does */ + for (i = 0; i < 3; i++) { + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, + HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE | + HHI_HDMI_PHY_CNTL1_SOFT_RESET); + usleep_range(1000, 2000); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, + HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE); + usleep_range(1000, 2000); + } + + return 0; +} + +static int phy_meson8_hdmi_tx_power_off(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x0841) | + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, 0x8d00)); + + return 0; +} + +static const struct phy_ops phy_meson8_hdmi_tx_ops = { + .init = phy_meson8_hdmi_tx_init, + .exit = phy_meson8_hdmi_tx_exit, + .power_on = phy_meson8_hdmi_tx_power_on, + .power_off = phy_meson8_hdmi_tx_power_off, + .owner = THIS_MODULE, +}; + +static int phy_meson8_hdmi_tx_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct phy_meson8_hdmi_tx_priv *priv; + struct phy_provider *phy_provider; + struct resource *res; + struct phy *phy; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hhi = syscon_node_to_regmap(np->parent); + if (IS_ERR(priv->hhi)) + return PTR_ERR(priv->hhi); + + priv->tmds_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->tmds_clk)) + return PTR_ERR(priv->tmds_clk); + + phy = devm_phy_create(&pdev->dev, np, &phy_meson8_hdmi_tx_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_meson8_hdmi_tx_of_match[] = { + { .compatible = "amlogic,meson8-hdmi-tx-phy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phy_meson8_hdmi_tx_of_match); + +static struct platform_driver phy_meson8_hdmi_tx_driver = { + .probe = phy_meson8_hdmi_tx_probe, + .driver = { + .name = "phy-meson8-hdmi-tx", + .of_match_table = phy_meson8_hdmi_tx_of_match, + }, +}; +module_platform_driver(phy_meson8_hdmi_tx_driver); + +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); +MODULE_DESCRIPTION("Meson8, Meson8b and Meson8m2 HDMI TX PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c index bd66bd723e4a..dd96763911b8 100644 --- a/drivers/phy/amlogic/phy-meson8b-usb2.c +++ b/drivers/phy/amlogic/phy-meson8b-usb2.c @@ -10,6 +10,8 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/property.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -76,6 +78,17 @@ #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) #define REG_DBG_UART 0x10 + #define REG_DBG_UART_BYPASS_SEL BIT(0) + #define REG_DBG_UART_BYPASS_DM_EN BIT(1) + #define REG_DBG_UART_BYPASS_DP_EN BIT(2) + #define REG_DBG_UART_BYPASS_DM_DATA BIT(3) + #define REG_DBG_UART_BYPASS_DP_DATA BIT(4) + #define REG_DBG_UART_FSV_MINUS BIT(5) + #define REG_DBG_UART_FSV_PLUS BIT(6) + #define REG_DBG_UART_FSV_BURN_IN_TEST BIT(7) + #define REG_DBG_UART_LOOPBACK_EN_B BIT(8) + #define REG_DBG_UART_SET_IDDQ BIT(9) + #define REG_DBG_UART_ATE_RESET BIT(10) #define REG_TEST 0x14 #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) @@ -104,35 +117,30 @@ #define RESET_COMPLETE_TIME 500 #define ACA_ENABLE_COMPLETE_TIME 50 -struct phy_meson8b_usb2_priv { - void __iomem *regs; - enum usb_dr_mode dr_mode; - struct clk *clk_usb_general; - struct clk *clk_usb; - struct reset_control *reset; +struct phy_meson8b_usb2_match_data { + bool host_enable_aca; }; -static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, - u32 reg) -{ - return readl(phy_priv->regs + reg); -} - -static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, - u32 reg, u32 mask, u32 value) -{ - u32 data; - - data = phy_meson8b_usb2_read(phy_priv, reg); - data &= ~mask; - data |= (value & mask); +struct phy_meson8b_usb2_priv { + struct regmap *regmap; + enum usb_dr_mode dr_mode; + struct clk *clk_usb_general; + struct clk *clk_usb; + struct reset_control *reset; + const struct phy_meson8b_usb2_match_data *match; +}; - writel(data, phy_priv->regs + reg); -} +static const struct regmap_config phy_meson8b_usb2_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_TUNE, +}; static int phy_meson8b_usb2_power_on(struct phy *phy) { struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + u32 reg; int ret; if (!IS_ERR_OR_NULL(priv->reset)) { @@ -146,6 +154,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy) ret = clk_prepare_enable(priv->clk_usb_general); if (ret) { dev_err(&phy->dev, "Failed to enable USB general clock\n"); + reset_control_rearm(priv->reset); return ret; } @@ -153,41 +162,48 @@ static int phy_meson8b_usb2_power_on(struct phy *phy) if (ret) { dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); clk_disable_unprepare(priv->clk_usb_general); + reset_control_rearm(priv->reset); return ret; } - phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, - REG_CONFIG_CLK_32k_ALTSEL); + regmap_update_bits(priv->regmap, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, + REG_CONFIG_CLK_32k_ALTSEL); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, - 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, + 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, - 0x5 << REG_CTRL_FSEL_SHIFT); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_FSEL_MASK, + 0x5 << REG_CTRL_FSEL_SHIFT); /* reset the PHY */ - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, - REG_CTRL_POWER_ON_RESET); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, + REG_CTRL_POWER_ON_RESET); udelay(RESET_COMPLETE_TIME); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); udelay(RESET_COMPLETE_TIME); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, - REG_CTRL_SOF_TOGGLE_OUT); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, + REG_CTRL_SOF_TOGGLE_OUT); if (priv->dr_mode == USB_DR_MODE_HOST) { - phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, + regmap_update_bits(priv->regmap, REG_DBG_UART, + REG_DBG_UART_SET_IDDQ, 0); + + if (priv->match->host_enable_aca) { + regmap_update_bits(priv->regmap, REG_ADP_BC, REG_ADP_BC_ACA_ENABLE, REG_ADP_BC_ACA_ENABLE); - udelay(ACA_ENABLE_COMPLETE_TIME); + udelay(ACA_ENABLE_COMPLETE_TIME); - if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & - REG_ADP_BC_ACA_PIN_FLOAT) { - dev_warn(&phy->dev, "USB ID detect failed!\n"); - clk_disable_unprepare(priv->clk_usb); - clk_disable_unprepare(priv->clk_usb_general); - return -EINVAL; + regmap_read(priv->regmap, REG_ADP_BC, ®); + if (reg & REG_ADP_BC_ACA_PIN_FLOAT) { + dev_warn(&phy->dev, "USB ID detect failed!\n"); + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + reset_control_rearm(priv->reset); + return -EINVAL; + } } } @@ -198,8 +214,18 @@ static int phy_meson8b_usb2_power_off(struct phy *phy) { struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + if (priv->dr_mode == USB_DR_MODE_HOST) + regmap_update_bits(priv->regmap, REG_DBG_UART, + REG_DBG_UART_SET_IDDQ, + REG_DBG_UART_SET_IDDQ); + clk_disable_unprepare(priv->clk_usb); clk_disable_unprepare(priv->clk_usb_general); + reset_control_rearm(priv->reset); + + /* power off the PHY by putting it into reset mode */ + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, + REG_CTRL_POWER_ON_RESET); return 0; } @@ -213,18 +239,26 @@ static const struct phy_ops phy_meson8b_usb2_ops = { static int phy_meson8b_usb2_probe(struct platform_device *pdev) { struct phy_meson8b_usb2_priv *priv; - struct resource *res; struct phy *phy; struct phy_provider *phy_provider; + void __iomem *base; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->regs)) - return PTR_ERR(priv->regs); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->match = device_get_match_data(&pdev->dev); + if (!priv->match) + return -ENODEV; + + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &phy_meson8b_usb2_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); if (IS_ERR(priv->clk_usb_general)) @@ -235,8 +269,9 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev) return PTR_ERR(priv->clk_usb); priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); - if (PTR_ERR(priv->reset) == -EPROBE_DEFER) - return PTR_ERR(priv->reset); + if (IS_ERR(priv->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset), + "Failed to get the reset line"); priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { @@ -247,8 +282,8 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev) phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); if (IS_ERR(phy)) { - dev_err(&pdev->dev, "failed to create PHY\n"); - return PTR_ERR(phy); + return dev_err_probe(&pdev->dev, PTR_ERR(phy), + "failed to create PHY\n"); } phy_set_drvdata(phy, priv); @@ -259,11 +294,32 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } +static const struct phy_meson8b_usb2_match_data phy_meson8_usb2_match_data = { + .host_enable_aca = false, +}; + +static const struct phy_meson8b_usb2_match_data phy_meson8b_usb2_match_data = { + .host_enable_aca = true, +}; + static const struct of_device_id phy_meson8b_usb2_of_match[] = { - { .compatible = "amlogic,meson8-usb2-phy", }, - { .compatible = "amlogic,meson8b-usb2-phy", }, - { .compatible = "amlogic,meson-gxbb-usb2-phy", }, - { }, + { + .compatible = "amlogic,meson8-usb2-phy", + .data = &phy_meson8_usb2_match_data + }, + { + .compatible = "amlogic,meson8b-usb2-phy", + .data = &phy_meson8b_usb2_match_data + }, + { + .compatible = "amlogic,meson8m2-usb2-phy", + .data = &phy_meson8b_usb2_match_data + }, + { + .compatible = "amlogic,meson-gxbb-usb2-phy", + .data = &phy_meson8b_usb2_match_data + }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); @@ -277,5 +333,5 @@ static struct platform_driver phy_meson8b_usb2_driver = { module_platform_driver(phy_meson8b_usb2_driver); MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); -MODULE_DESCRIPTION("Meson8, Meson8b and GXBB USB2 PHY driver"); +MODULE_DESCRIPTION("Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index b29f11c19155..1d89a2fd9b79 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -2,6 +2,16 @@ # # Phy drivers for Broadcom platforms # +menu "PHY drivers for Broadcom platforms" + +config PHY_BCM63XX_USBH + tristate "BCM63xx USBH PHY driver" + depends on BMIPS_GENERIC || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support the BCM63xx USBH PHY driver. + If unsure, say N. + config PHY_CYGNUS_PCIE tristate "Broadcom Cygnus PCIe PHY driver" depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) @@ -73,7 +83,7 @@ config PHY_NS2_USB_DRD config PHY_BRCM_SATA tristate "Broadcom SATA PHY driver" depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || \ - ARCH_BCM_63XX || COMPILE_TEST + ARCH_BCMBCA || COMPILE_TEST depends on OF select GENERIC_PHY default ARCH_BCM_IPROC @@ -83,11 +93,11 @@ config PHY_BRCM_SATA config PHY_BRCM_USB tristate "Broadcom STB USB PHY driver" - depends on ARCH_BRCMSTB || COMPILE_TEST + depends on ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST depends on OF select GENERIC_PHY - select SOC_BRCMSTB - default ARCH_BRCMSTB + select SOC_BRCMSTB if ARCH_BRCMSTB + default ARCH_BCMBCA || ARCH_BRCMSTB help Enable this to support the Broadcom STB USB PHY. This driver is required by the USB XHCI, EHCI and OHCI @@ -103,3 +113,5 @@ config PHY_BCM_SR_PCIE help Enable this to support the Broadcom Stingray PCIe PHY If unsure, say N. + +endmenu diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index c78de546135c..7024127f86ad 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_BCM63XX_USBH) += phy-bcm63xx-usbh.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o diff --git a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c index b074682d9dd8..cc29b08e49eb 100644 --- a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c +++ b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015 Broadcom Corporation #include <linux/delay.h> #include <linux/io.h> @@ -126,7 +116,6 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev) struct device_node *node = dev->of_node, *child; struct cygnus_pcie_phy_core *core; struct phy_provider *provider; - struct resource *res; unsigned cnt = 0; int ret; @@ -141,8 +130,7 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev) core->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - core->base = devm_ioremap_resource(dev, res); + core->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(core->base)) return PTR_ERR(core->base); diff --git a/drivers/phy/broadcom/phy-bcm-kona-usb2.c b/drivers/phy/broadcom/phy-bcm-kona-usb2.c index 6459296d9bf9..e9cc5f2cb89a 100644 --- a/drivers/phy/broadcom/phy-bcm-kona-usb2.c +++ b/drivers/phy/broadcom/phy-bcm-kona-usb2.c @@ -94,7 +94,6 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct bcm_kona_usb *phy; - struct resource *res; struct phy *gphy; struct phy_provider *phy_provider; @@ -102,8 +101,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) if (!phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->regs = devm_ioremap_resource(&pdev->dev, res); + phy->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->regs)) return PTR_ERR(phy->regs); diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c index 9f2f84d65dcd..6a36e187d100 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb2.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c @@ -9,17 +9,23 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.h> struct bcm_ns_usb2 { struct device *dev; struct clk *ref_clk; struct phy *phy; + struct regmap *clkset; + void __iomem *base; + + /* Deprecated binding */ void __iomem *dmu; }; @@ -27,7 +33,6 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) { struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); struct device *dev = usb2->dev; - void __iomem *dmu = usb2->dmu; u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; int err = 0; @@ -44,7 +49,10 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) goto err_clk_off; } - usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); + if (usb2->base) + usb2ctl = readl(usb2->base); + else + usb2ctl = readl(usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL); if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { usb_pll_pdiv = usb2ctl; @@ -58,15 +66,24 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; /* Unlock DMU PLL settings with some magic value */ - writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); + if (usb2->clkset) + regmap_write(usb2->clkset, 0, 0x0000ea68); + else + writel(0x0000ea68, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY); /* Write USB 2.0 PLL control setting */ usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; - writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); + if (usb2->base) + writel(usb2ctl, usb2->base); + else + writel(usb2ctl, usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL); /* Lock DMU PLL settings */ - writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); + if (usb2->clkset) + regmap_write(usb2->clkset, 0, 0x00000000); + else + writel(0x00000000, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY); err_clk_off: clk_disable_unprepare(usb2->ref_clk); @@ -83,7 +100,6 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct bcm_ns_usb2 *usb2; - struct resource *res; struct phy_provider *phy_provider; usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); @@ -91,16 +107,32 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev) return -ENOMEM; usb2->dev = dev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); - usb2->dmu = devm_ioremap_resource(dev, res); - if (IS_ERR(usb2->dmu)) { - dev_err(dev, "Failed to map DMU regs\n"); - return PTR_ERR(usb2->dmu); + if (of_find_property(dev->of_node, "brcm,syscon-clkset", NULL)) { + usb2->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usb2->base)) { + dev_err(dev, "Failed to map control reg\n"); + return PTR_ERR(usb2->base); + } + + usb2->clkset = syscon_regmap_lookup_by_phandle(dev->of_node, + "brcm,syscon-clkset"); + if (IS_ERR(usb2->clkset)) { + dev_err(dev, "Failed to lookup clkset regmap\n"); + return PTR_ERR(usb2->clkset); + } + } else { + usb2->dmu = devm_platform_ioremap_resource_byname(pdev, "dmu"); + if (IS_ERR(usb2->dmu)) { + dev_err(dev, "Failed to map DMU regs\n"); + return PTR_ERR(usb2->dmu); + } + + dev_warn(dev, "using deprecated DT binding\n"); } usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); if (IS_ERR(usb2->ref_clk)) { - dev_err(dev, "Clock not defined\n"); + dev_err_probe(dev, PTR_ERR(usb2->ref_clk), "failed to get ref clk\n"); return PTR_ERR(usb2->ref_clk); } diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c index 14f45bc35cc5..bbfad209c890 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb3.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c @@ -13,6 +13,7 @@ #include <linux/bcma/bcma.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/iopoll.h> #include <linux/mdio.h> #include <linux/module.h> #include <linux/of_address.h> @@ -21,8 +22,6 @@ #include <linux/phy/phy.h> #include <linux/slab.h> -#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */ - #define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f #define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 #define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 @@ -50,11 +49,8 @@ struct bcm_ns_usb3 { struct device *dev; enum bcm_ns_family family; void __iomem *dmp; - void __iomem *ccb_mii; struct mdio_device *mdiodev; struct phy *phy; - - int (*phy_write)(struct bcm_ns_usb3 *usb3, u16 reg, u16 value); }; static const struct of_device_id bcm_ns_usb3_id_table[] = { @@ -68,13 +64,9 @@ static const struct of_device_id bcm_ns_usb3_id_table[] = { }, {}, }; -MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, - u16 value) -{ - return usb3->phy_write(usb3, reg, value); -} + u16 value); static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) { @@ -186,12 +178,12 @@ static const struct phy_ops ops = { * MDIO driver code **************************************************/ -static int bcm_ns_usb3_mdiodev_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, - u16 value) +static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, + u16 value) { struct mdio_device *mdiodev = usb3->mdiodev; - return mdiobus_write(mdiodev->bus, mdiodev->addr, reg, value); + return mdiodev_write(mdiodev, reg, value); } static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) @@ -223,12 +215,8 @@ static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) return err; usb3->dmp = devm_ioremap_resource(dev, &res); - if (IS_ERR(usb3->dmp)) { - dev_err(dev, "Failed to map DMP regs\n"); + if (IS_ERR(usb3->dmp)) return PTR_ERR(usb3->dmp); - } - - usb3->phy_write = bcm_ns_usb3_mdiodev_phy_write; usb3->phy = devm_phy_create(dev, NULL, &ops); if (IS_ERR(usb3->phy)) { @@ -253,150 +241,7 @@ static struct mdio_driver bcm_ns_usb3_mdio_driver = { .probe = bcm_ns_usb3_mdio_probe, }; -/************************************************** - * Platform driver code - **************************************************/ - -static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, - u32 mask, u32 value, unsigned long timeout) -{ - unsigned long deadline = jiffies + timeout; - u32 val; - - do { - val = readl(addr); - if ((val & mask) == value) - return 0; - cpu_relax(); - udelay(10); - } while (!time_after_eq(jiffies, deadline)); - - dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); - - return -EBUSY; -} - -static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3) -{ - return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL, - 0x0100, 0x0000, - usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US)); -} - -static int bcm_ns_usb3_platform_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, - u16 value) -{ - u32 tmp = 0; - int err; - - err = bcm_ns_usb3_mii_mng_wait_idle(usb3); - if (err < 0) { - dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value); - return err; - } - - /* TODO: Use a proper MDIO bus layer */ - tmp |= 0x58020000; /* Magic value for MDIO PHY write */ - tmp |= reg << 18; - tmp |= value; - writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA); - - return bcm_ns_usb3_mii_mng_wait_idle(usb3); -} - -static int bcm_ns_usb3_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct of_device_id *of_id; - struct bcm_ns_usb3 *usb3; - struct resource *res; - struct phy_provider *phy_provider; - - usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); - if (!usb3) - return -ENOMEM; - - usb3->dev = dev; - - of_id = of_match_device(bcm_ns_usb3_id_table, dev); - if (!of_id) - return -EINVAL; - usb3->family = (enum bcm_ns_family)of_id->data; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp"); - usb3->dmp = devm_ioremap_resource(dev, res); - if (IS_ERR(usb3->dmp)) { - dev_err(dev, "Failed to map DMP regs\n"); - return PTR_ERR(usb3->dmp); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii"); - usb3->ccb_mii = devm_ioremap_resource(dev, res); - if (IS_ERR(usb3->ccb_mii)) { - dev_err(dev, "Failed to map ChipCommon B MII regs\n"); - return PTR_ERR(usb3->ccb_mii); - } - - /* Enable MDIO. Setting MDCDIV as 26 */ - writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); - - /* Wait for MDIO? */ - udelay(2); - - usb3->phy_write = bcm_ns_usb3_platform_phy_write; - - usb3->phy = devm_phy_create(dev, NULL, &ops); - if (IS_ERR(usb3->phy)) { - dev_err(dev, "Failed to create PHY\n"); - return PTR_ERR(usb3->phy); - } - - phy_set_drvdata(usb3->phy, usb3); - platform_set_drvdata(pdev, usb3); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (!IS_ERR(phy_provider)) - dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n"); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver bcm_ns_usb3_driver = { - .probe = bcm_ns_usb3_probe, - .driver = { - .name = "bcm_ns_usb3", - .of_match_table = bcm_ns_usb3_id_table, - }, -}; - -static int __init bcm_ns_usb3_module_init(void) -{ - int err; - - /* - * For backward compatibility we register as MDIO and platform driver. - * After getting MDIO binding commonly used (e.g. switching all DT files - * to use it) we should deprecate the old binding and eventually drop - * support for it. - */ - - err = mdio_driver_register(&bcm_ns_usb3_mdio_driver); - if (err) - return err; - - err = platform_driver_register(&bcm_ns_usb3_driver); - if (err) - mdio_driver_unregister(&bcm_ns_usb3_mdio_driver); - - return err; -} -module_init(bcm_ns_usb3_module_init); - -static void __exit bcm_ns_usb3_module_exit(void) -{ - platform_driver_unregister(&bcm_ns_usb3_driver); - mdio_driver_unregister(&bcm_ns_usb3_mdio_driver); -} -module_exit(bcm_ns_usb3_module_exit) +mdio_module_driver(bcm_ns_usb3_mdio_driver); MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); diff --git a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c index 4c7d11d2b378..2eaa41f8fc70 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include <linux/device.h> #include <linux/module.h> @@ -29,14 +19,12 @@ static int ns2_pci_phy_init(struct phy *p) int rc; /* select the AFE 100MHz block page */ - rc = mdiobus_write(mdiodev->bus, mdiodev->addr, - BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); + rc = mdiodev_write(mdiodev, BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); if (rc) goto err; /* set the 100 MHz reference clock amplitude to 2.05 v */ - rc = mdiobus_write(mdiodev->bus, mdiodev->addr, - PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); + rc = mdiodev_write(mdiodev, PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); if (rc) goto err; diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c index 7ceea5ae2704..36ad02c33ac5 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2017 Broadcom #include <linux/delay.h> #include <linux/extcon-provider.h> @@ -18,6 +8,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/irq.h> #include <linux/mfd/syscon.h> #include <linux/module.h> @@ -87,17 +78,11 @@ static const unsigned int usb_extcon_cable[] = { static inline int pll_lock_stat(u32 usb_reg, int reg_mask, struct ns2_phy_driver *driver) { - int retry = PLL_LOCK_RETRY; u32 val; - do { - udelay(1); - val = readl(driver->icfgdrd_regs + usb_reg); - if (val & reg_mask) - return 0; - } while (--retry > 0); - - return -EBUSY; + return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg, + val, (val & reg_mask), 1, + PLL_LOCK_RETRY); } static int ns2_drd_phy_init(struct phy *phy) @@ -279,7 +264,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .init = ns2_drd_phy_init, .power_on = ns2_drd_phy_poweron, .power_off = ns2_drd_phy_poweroff, @@ -298,7 +283,6 @@ static int ns2_drd_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ns2_phy_driver *driver; struct ns2_phy_data *data; - struct resource *res; int ret; u32 val; @@ -312,23 +296,19 @@ static int ns2_drd_phy_probe(struct platform_device *pdev) if (!driver->data) return -ENOMEM; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icfg"); - driver->icfgdrd_regs = devm_ioremap_resource(dev, res); + driver->icfgdrd_regs = devm_platform_ioremap_resource_byname(pdev, "icfg"); if (IS_ERR(driver->icfgdrd_regs)) return PTR_ERR(driver->icfgdrd_regs); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rst-ctrl"); - driver->idmdrd_rst_ctrl = devm_ioremap_resource(dev, res); + driver->idmdrd_rst_ctrl = devm_platform_ioremap_resource_byname(pdev, "rst-ctrl"); if (IS_ERR(driver->idmdrd_rst_ctrl)) return PTR_ERR(driver->idmdrd_rst_ctrl); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crmu-ctrl"); - driver->crmu_usb2_ctrl = devm_ioremap_resource(dev, res); + driver->crmu_usb2_ctrl = devm_platform_ioremap_resource_byname(pdev, "crmu-ctrl"); if (IS_ERR(driver->crmu_usb2_ctrl)) return PTR_ERR(driver->crmu_usb2_ctrl); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2-strap"); - driver->usb2h_strap_reg = devm_ioremap_resource(dev, res); + driver->usb2h_strap_reg = devm_platform_ioremap_resource_byname(pdev, "usb2-strap"); if (IS_ERR(driver->usb2h_strap_reg)) return PTR_ERR(driver->usb2h_strap_reg); diff --git a/drivers/phy/broadcom/phy-bcm-sr-pcie.c b/drivers/phy/broadcom/phy-bcm-sr-pcie.c index 96a3af126a78..8a4aadf166cf 100644 --- a/drivers/phy/broadcom/phy-bcm-sr-pcie.c +++ b/drivers/phy/broadcom/phy-bcm-sr-pcie.c @@ -217,7 +217,6 @@ static int sr_pcie_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct sr_pcie_phy_core *core; - struct resource *res; struct phy_provider *provider; unsigned int phy_idx = 0; @@ -226,9 +225,7 @@ static int sr_pcie_phy_probe(struct platform_device *pdev) return -ENOMEM; core->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - core->base = devm_ioremap_resource(core->dev, res); + core->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(core->base)) return PTR_ERR(core->base); diff --git a/drivers/phy/broadcom/phy-bcm-sr-usb.c b/drivers/phy/broadcom/phy-bcm-sr-usb.c index fe6c58910e4c..0002da3b5b5d 100644 --- a/drivers/phy/broadcom/phy-bcm-sr-usb.c +++ b/drivers/phy/broadcom/phy-bcm-sr-usb.c @@ -5,6 +5,7 @@ #include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> #include <linux/phy/phy.h> @@ -16,8 +17,6 @@ enum bcm_usb_phy_version { }; enum bcm_usb_phy_reg { - PLL_NDIV_FRAC, - PLL_NDIV_INT, PLL_CTRL, PHY_CTRL, PHY_PLL_CTRL, @@ -31,18 +30,11 @@ static const u8 bcm_usb_combo_phy_ss[] = { }; static const u8 bcm_usb_combo_phy_hs[] = { - [PLL_NDIV_FRAC] = 0x04, - [PLL_NDIV_INT] = 0x08, [PLL_CTRL] = 0x0c, [PHY_CTRL] = 0x10, }; -#define HSPLL_NDIV_INT_VAL 0x13 -#define HSPLL_NDIV_FRAC_VAL 0x1005 - static const u8 bcm_usb_hs_phy[] = { - [PLL_NDIV_FRAC] = 0x0, - [PLL_NDIV_INT] = 0x4, [PLL_CTRL] = 0x8, [PHY_CTRL] = 0xc, }; @@ -52,7 +44,6 @@ enum pll_ctrl_bits { SSPLL_SUSPEND_EN, PLL_SEQ_START, PLL_LOCK, - PLL_PDIV, }; static const u8 u3pll_ctrl[] = { @@ -66,29 +57,17 @@ static const u8 u3pll_ctrl[] = { #define HSPLL_PDIV_VAL 0x1 static const u8 u2pll_ctrl[] = { - [PLL_PDIV] = 1, [PLL_RESETB] = 5, [PLL_LOCK] = 6, }; enum bcm_usb_phy_ctrl_bits { CORERDY, - AFE_LDO_PWRDWNB, - AFE_PLL_PWRDWNB, - AFE_BG_PWRDWNB, - PHY_ISO, PHY_RESETB, PHY_PCTL, }; #define PHY_PCTL_MASK 0xffff -/* - * 0x0806 of PCTL_VAL has below bits set - * BIT-8 : refclk divider 1 - * BIT-3:2: device mode; mode is not effect - * BIT-1: soft reset active low - */ -#define HSPHY_PCTL_VAL 0x0806 #define SSPHY_PCTL_VAL 0x0006 static const u8 u3phy_ctrl[] = { @@ -98,10 +77,6 @@ static const u8 u3phy_ctrl[] = { static const u8 u2phy_ctrl[] = { [CORERDY] = 0, - [AFE_LDO_PWRDWNB] = 1, - [AFE_PLL_PWRDWNB] = 2, - [AFE_BG_PWRDWNB] = 3, - [PHY_ISO] = 4, [PHY_RESETB] = 5, [PHY_PCTL] = 6, }; @@ -135,19 +110,15 @@ static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set) static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit) { - int retry; - u32 rd_data; + u32 data; + int ret; - retry = PLL_LOCK_RETRY_COUNT; - do { - rd_data = readl(addr); - if (rd_data & bit) - return 0; - udelay(1); - } while (--retry > 0); + ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1, + PLL_LOCK_RETRY_COUNT); + if (ret) + pr_err("%s: FAIL\n", __func__); - pr_err("%s: FAIL\n", __func__); - return -ETIMEDOUT; + return ret; } static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg) @@ -186,38 +157,13 @@ static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg) int ret = 0; void __iomem *regs = phy_cfg->regs; const u8 *offset; - u32 rd_data; offset = phy_cfg->offset; - writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]); - writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]); - - rd_data = readl(regs + offset[PLL_CTRL]); - rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]); - rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]); - writel(rd_data, regs + offset[PLL_CTRL]); - - /* Set Core Ready high */ - bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], - BIT(u2phy_ctrl[CORERDY])); - - /* Maximum timeout for Core Ready done */ - msleep(30); - + bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL], + BIT(u2pll_ctrl[PLL_RESETB])); bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], BIT(u2pll_ctrl[PLL_RESETB])); - bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], - BIT(u2phy_ctrl[PHY_RESETB])); - - - rd_data = readl(regs + offset[PHY_CTRL]); - rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]); - rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]); - writel(rd_data, regs + offset[PHY_CTRL]); - - /* Maximum timeout for PLL reset done */ - msleep(30); ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL], BIT(u2pll_ctrl[PLL_LOCK])); @@ -256,7 +202,7 @@ static int bcm_usb_phy_init(struct phy *phy) return ret; } -static struct phy_ops sr_phy_ops = { +static const struct phy_ops sr_phy_ops = { .init = bcm_usb_phy_init, .reset = bcm_usb_phy_reset, .owner = THIS_MODULE, @@ -354,14 +300,12 @@ static int bcm_usb_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; const struct of_device_id *of_id; - struct resource *res; void __iomem *regs; int ret; enum bcm_usb_phy_version version; struct phy_provider *phy_provider; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); diff --git a/drivers/phy/broadcom/phy-bcm63xx-usbh.c b/drivers/phy/broadcom/phy-bcm63xx-usbh.c new file mode 100644 index 000000000000..6c05ba8b08be --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm63xx-usbh.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BCM6328 USBH PHY Controller Driver + * + * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com> + * Copyright (C) 2015 Simon Arlott + * + * Derived from bcm963xx_4.12L.06B_consumer/kernel/linux/arch/mips/bcm963xx/setup.c: + * Copyright (C) 2002 Broadcom Corporation + * + * Derived from OpenWrt patches: + * Copyright (C) 2013 Jonas Gorski <jonas.gorski@gmail.com> + * Copyright (C) 2013 Florian Fainelli <f.fainelli@gmail.com> + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +/* USBH control register offsets */ +enum usbh_regs { + USBH_BRT_CONTROL1 = 0, + USBH_BRT_CONTROL2, + USBH_BRT_STATUS1, + USBH_BRT_STATUS2, + USBH_UTMI_CONTROL1, +#define USBH_UC1_DEV_MODE_SEL BIT(0) + USBH_TEST_PORT_CONTROL, + USBH_PLL_CONTROL1, +#define USBH_PLLC_REFCLKSEL_SHIFT 0 +#define USBH_PLLC_REFCLKSEL_MASK (0x3 << USBH_PLLC_REFCLKSEL_SHIFT) +#define USBH_PLLC_CLKSEL_SHIFT 2 +#define USBH_PLLC_CLKSEL_MASK (0x3 << USBH_PLLC_CLKSEL_MASK) +#define USBH_PLLC_XTAL_PWRDWNB BIT(4) +#define USBH_PLLC_PLL_PWRDWNB BIT(5) +#define USBH_PLLC_PLL_CALEN BIT(6) +#define USBH_PLLC_PHYPLL_BYP BIT(7) +#define USBH_PLLC_PLL_RESET BIT(8) +#define USBH_PLLC_PLL_IDDQ_PWRDN BIT(9) +#define USBH_PLLC_PLL_PWRDN_DELAY BIT(10) +#define USBH_6318_PLLC_PLL_SUSPEND_EN BIT(27) +#define USBH_6318_PLLC_PHYPLL_BYP BIT(29) +#define USBH_6318_PLLC_PLL_RESET BIT(30) +#define USBH_6318_PLLC_PLL_IDDQ_PWRDN BIT(31) + USBH_SWAP_CONTROL, +#define USBH_SC_OHCI_DATA_SWAP BIT(0) +#define USBH_SC_OHCI_ENDIAN_SWAP BIT(1) +#define USBH_SC_OHCI_LOGICAL_ADDR_EN BIT(2) +#define USBH_SC_EHCI_DATA_SWAP BIT(3) +#define USBH_SC_EHCI_ENDIAN_SWAP BIT(4) +#define USBH_SC_EHCI_LOGICAL_ADDR_EN BIT(5) +#define USBH_SC_USB_DEVICE_SEL BIT(6) + USBH_GENERIC_CONTROL, +#define USBH_GC_PLL_SUSPEND_EN BIT(1) + USBH_FRAME_ADJUST_VALUE, + USBH_SETUP, +#define USBH_S_IOC BIT(4) +#define USBH_S_IPP BIT(5) + USBH_MDIO, + USBH_MDIO32, + USBH_USB_SIM_CONTROL, +#define USBH_USC_LADDR_SEL BIT(5) + + __USBH_ENUM_SIZE +}; + +struct bcm63xx_usbh_phy_variant { + /* Registers */ + long regs[__USBH_ENUM_SIZE]; + + /* PLLC bits to set/clear for power on */ + u32 power_pllc_clr; + u32 power_pllc_set; + + /* Setup bits to set/clear for power on */ + u32 setup_clr; + u32 setup_set; + + /* Swap Control bits to set */ + u32 swapctl_dev_set; + + /* Test Port Control value to set if non-zero */ + u32 tpc_val; + + /* USB Sim Control bits to set */ + u32 usc_set; + + /* UTMI Control 1 bits to set */ + u32 utmictl1_dev_set; +}; + +struct bcm63xx_usbh_phy { + void __iomem *base; + struct clk *usbh_clk; + struct clk *usb_ref_clk; + struct reset_control *reset; + const struct bcm63xx_usbh_phy_variant *variant; + bool device_mode; +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = { + .regs = { + [USBH_BRT_CONTROL1] = -1, + [USBH_BRT_CONTROL2] = -1, + [USBH_BRT_STATUS1] = -1, + [USBH_BRT_STATUS2] = -1, + [USBH_UTMI_CONTROL1] = 0x2c, + [USBH_TEST_PORT_CONTROL] = 0x1c, + [USBH_PLL_CONTROL1] = 0x04, + [USBH_SWAP_CONTROL] = 0x0c, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = 0x08, + [USBH_SETUP] = 0x00, + [USBH_MDIO] = 0x14, + [USBH_MDIO32] = 0x18, + [USBH_USB_SIM_CONTROL] = 0x20, + }, + .power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN, + .power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .usc_set = USBH_USC_LADDR_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = 0x20, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = { + .regs = { + [USBH_BRT_CONTROL1] = -1, + [USBH_BRT_CONTROL2] = -1, + [USBH_BRT_STATUS1] = -1, + [USBH_BRT_STATUS2] = -1, + [USBH_UTMI_CONTROL1] = -1, + [USBH_TEST_PORT_CONTROL] = 0x24, + [USBH_PLL_CONTROL1] = -1, + [USBH_SWAP_CONTROL] = 0x00, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = -1, + [USBH_SETUP] = -1, + [USBH_MDIO] = -1, + [USBH_MDIO32] = -1, + [USBH_USB_SIM_CONTROL] = -1, + }, + /* + * The magic value comes for the original vendor BSP + * and is needed for USB to work. Datasheet does not + * help, so the magic value is used as-is. + */ + .tpc_val = 0x1c0020, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = 0x20, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY, + .setup_clr = USBH_S_IPP, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg) +{ + return (usbh->variant->regs[reg] >= 0); +} + +static inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg) +{ + return __raw_readl(usbh->base + usbh->variant->regs[reg]); +} + +static inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg, + u32 value) +{ + __raw_writel(value, usbh->base + usbh->variant->regs[reg]); +} + +static int bcm63xx_usbh_phy_init(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(usbh->usbh_clk); + if (ret) { + dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(usbh->usb_ref_clk); + if (ret) { + dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret); + clk_disable_unprepare(usbh->usbh_clk); + return ret; + } + + ret = reset_control_reset(usbh->reset); + if (ret) { + dev_err(&phy->dev, "unable to reset device: %d\n", ret); + clk_disable_unprepare(usbh->usb_ref_clk); + clk_disable_unprepare(usbh->usbh_clk); + return ret; + } + + /* Configure to work in native CPU endian */ + if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) { + u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL); + + val |= USBH_SC_EHCI_DATA_SWAP; + val &= ~USBH_SC_EHCI_ENDIAN_SWAP; + + val |= USBH_SC_OHCI_DATA_SWAP; + val &= ~USBH_SC_OHCI_ENDIAN_SWAP; + + if (usbh->device_mode && usbh->variant->swapctl_dev_set) + val |= usbh->variant->swapctl_dev_set; + + usbh_writel(usbh, USBH_SWAP_CONTROL, val); + } + + if (usbh_has_reg(usbh, USBH_SETUP)) { + u32 val = usbh_readl(usbh, USBH_SETUP); + + val |= usbh->variant->setup_set; + val &= ~usbh->variant->setup_clr; + + usbh_writel(usbh, USBH_SETUP, val); + } + + if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) { + u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL); + + val |= usbh->variant->usc_set; + + usbh_writel(usbh, USBH_USB_SIM_CONTROL, val); + } + + if (usbh->variant->tpc_val && + usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL)) + usbh_writel(usbh, USBH_TEST_PORT_CONTROL, + usbh->variant->tpc_val); + + if (usbh->device_mode && + usbh_has_reg(usbh, USBH_UTMI_CONTROL1) && + usbh->variant->utmictl1_dev_set) { + u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1); + + val |= usbh->variant->utmictl1_dev_set; + + usbh_writel(usbh, USBH_UTMI_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_power_on(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) { + u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1); + + val |= usbh->variant->power_pllc_set; + val &= ~usbh->variant->power_pllc_clr; + + usbh_writel(usbh, USBH_PLL_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_power_off(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) { + u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1); + + val &= ~usbh->variant->power_pllc_set; + val |= usbh->variant->power_pllc_clr; + + usbh_writel(usbh, USBH_PLL_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_exit(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + clk_disable_unprepare(usbh->usbh_clk); + clk_disable_unprepare(usbh->usb_ref_clk); + + return 0; +} + +static const struct phy_ops bcm63xx_usbh_phy_ops = { + .exit = bcm63xx_usbh_phy_exit, + .init = bcm63xx_usbh_phy_init, + .power_off = bcm63xx_usbh_phy_power_off, + .power_on = bcm63xx_usbh_phy_power_on, + .owner = THIS_MODULE, +}; + +static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev); + + usbh->device_mode = !!args->args[0]; + + return of_phy_simple_xlate(dev, args); +} + +static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm63xx_usbh_phy *usbh; + const struct bcm63xx_usbh_phy_variant *variant; + struct phy *phy; + struct phy_provider *phy_provider; + + usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL); + if (!usbh) + return -ENOMEM; + + variant = device_get_match_data(dev); + if (!variant) + return -EINVAL; + usbh->variant = variant; + + usbh->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usbh->base)) + return PTR_ERR(usbh->base); + + usbh->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(usbh->reset)) { + if (PTR_ERR(usbh->reset) != -EPROBE_DEFER) + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(usbh->reset); + } + + usbh->usbh_clk = devm_clk_get_optional(dev, "usbh"); + if (IS_ERR(usbh->usbh_clk)) + return PTR_ERR(usbh->usbh_clk); + + usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref"); + if (IS_ERR(usbh->usb_ref_clk)) + return PTR_ERR(usbh->usb_ref_clk); + + phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + platform_set_drvdata(pdev, usbh); + phy_set_drvdata(phy, usbh); + + phy_provider = devm_of_phy_provider_register(dev, + bcm63xx_usbh_phy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(phy_provider); + } + + dev_dbg(dev, "Registered BCM63xx USB PHY driver\n"); + + return 0; +} + +static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = { + { .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 }, + { .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 }, + { .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 }, + { .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 }, + { .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 }, + { .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids); + +static struct platform_driver bcm63xx_usbh_phy_driver __refdata = { + .driver = { + .name = "bcm63xx-usbh-phy", + .of_match_table = bcm63xx_usbh_phy_ids, + }, + .probe = bcm63xx_usbh_phy_probe, +}; +module_platform_driver(bcm63xx_usbh_phy_driver); + +MODULE_DESCRIPTION("BCM63xx USBH PHY driver"); +MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>"); +MODULE_AUTHOR("Simon Arlott"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index 18251f232172..769c707d9b71 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -65,6 +65,7 @@ struct brcm_sata_port { bool ssc_en; enum brcm_sata_phy_rxaeq_mode rxaeq_mode; u32 rxaeq_val; + u32 tx_amplitude_val; }; struct brcm_sata_phy { @@ -84,6 +85,10 @@ enum sata_phy_regs { BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, + BLOCK1_REG_BANK = 0x10, + BLOCK1_TEST_TX = 0x83, + BLOCK1_TEST_TX_AMP_SHIFT = 12, + PLL_REG_BANK_0 = 0x050, PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), @@ -379,6 +384,29 @@ static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port) brcm_sata_phy_wr(port, RXPMD_REG_BANK, RXPMD_RX_FREQ_MON_CONTROL1, ~tmp, RXPMD_MON_CORRECT_EN | value); + tmp = GENMASK(15, 12); + switch (port->tx_amplitude_val) { + case 400: + value = BIT(12) | BIT(13); + break; + case 500: + value = BIT(13); + break; + case 600: + value = BIT(12); + break; + case 800: + value = 0; + break; + default: + value = tmp; + break; + } + + if (value != tmp) + brcm_sata_phy_wr(port, BLOCK1_REG_BANK, BLOCK1_TEST_TX, ~tmp, + value); + /* Turn on/off SSC */ brcm_sata_phy_wr(port, TX_REG_BANK, TX_ACTRL5, ~TX_ACTRL5_SSC_EN, port->ssc_en ? TX_ACTRL5_SSC_EN : 0); @@ -623,7 +651,7 @@ static int brcm_dsl_sata_init(struct brcm_sata_port *port) break; msleep(20); try--; - }; + } if (!try) { /* PLL did not lock; give up */ @@ -726,7 +754,6 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) struct device_node *dn = dev->of_node, *child; const struct of_device_id *of_id; struct brcm_sata_phy *priv; - struct resource *res; struct phy_provider *provider; int ret, count = 0; @@ -739,8 +766,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); priv->dev = dev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - priv->phy_base = devm_ioremap_resource(dev, res); + priv->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy"); if (IS_ERR(priv->phy_base)) return PTR_ERR(priv->phy_base); @@ -751,9 +777,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) priv->version = BRCM_SATA_PHY_STB_28NM; if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "phy-ctrl"); - priv->ctrl_base = devm_ioremap_resource(dev, res); + priv->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "phy-ctrl"); if (IS_ERR(priv->ctrl_base)) return PTR_ERR(priv->ctrl_base); } @@ -791,6 +815,10 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) if (port->rxaeq_mode == RXAEQ_MODE_MANUAL) of_property_read_u32(child, "brcm,rxaeq-value", &port->rxaeq_val); + + of_property_read_u32(child, "brcm,tx-amplitude-millivolt", + &port->tx_amplitude_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"); diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index 456dc4a100c2..d2524b70ea16 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -47,6 +47,8 @@ #define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 #define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 #define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 +#define USB_CTRL_USB_PM_XHC_PME_EN_MASK 0x00000010 +#define USB_CTRL_USB_PM_XHC_S2_CLK_SWITCH_EN_MASK 0x00000008 #define USB_CTRL_USB_PM_STATUS 0x08 #define USB_CTRL_USB_DEVICE_CTL1 0x10 #define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 @@ -190,10 +192,6 @@ static void usb_init_common(struct brcm_usb_init_params *params) pr_debug("%s\n", __func__); - USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); - /* 1 millisecond - for USB clocks to settle down */ - usleep_range(1000, 2000); - if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) { reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); @@ -222,6 +220,17 @@ static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params, USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en); } +static void usb_wake_enable_7216(struct brcm_usb_init_params *params, + bool enable) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + if (enable) + USB_CTRL_SET(ctrl, USB_PM, XHC_PME_EN); + else + USB_CTRL_UNSET(ctrl, USB_PM, XHC_PME_EN); +} + static void usb_init_common_7211b0(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; @@ -270,11 +279,6 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT; brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1); - /* Fix the incorrect default */ - reg = brcm_usb_readl(ctrl + USB_CTRL_SETUP); - reg &= ~USB_CTRL_SETUP_tca_drv_sel_MASK; - brcm_usb_writel(reg, ctrl + USB_CTRL_SETUP); - usb_init_common(params); /* @@ -300,6 +304,20 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) usb2_eye_fix_7211b0(params); } +static void usb_init_common_7216(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + USB_CTRL_UNSET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN); + USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); + + /* 1 millisecond - for USB clocks to settle down */ + usleep_range(1000, 2000); + + usb_wake_enable_7216(params, false); + usb_init_common(params); +} + static void usb_init_xhci(struct brcm_usb_init_params *params) { pr_debug("%s\n", __func__); @@ -307,14 +325,20 @@ static void usb_init_xhci(struct brcm_usb_init_params *params) xhci_soft_reset(params, 0); } -static void usb_uninit_common(struct brcm_usb_init_params *params) +static void usb_uninit_common_7216(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; pr_debug("%s\n", __func__); - USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + if (!params->wake_enabled) { + USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + /* Switch to using slower clock during suspend to save power */ + USB_CTRL_SET(ctrl, USB_PM, XHC_S2_CLK_SWITCH_EN); + } else { + usb_wake_enable_7216(params, true); + } } static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params) @@ -376,9 +400,9 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) static const struct brcm_usb_init_ops bcm7216_ops = { .init_ipp = usb_init_ipp, - .init_common = usb_init_common, + .init_common = usb_init_common_7216, .init_xhci = usb_init_xhci, - .uninit_common = usb_uninit_common, + .uninit_common = usb_uninit_common_7216, .uninit_xhci = usb_uninit_xhci, .get_dual_select = usb_get_dual_select, .set_dual_select = usb_set_dual_select, @@ -401,6 +425,7 @@ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) params->family_name = "7216"; params->ops = &bcm7216_ops; + params->suspend_with_clocks = true; } void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 9391ab42a12b..dddcbd3cd5f3 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -79,6 +79,7 @@ enum brcm_family_type { BRCM_FAMILY_3390A0, + BRCM_FAMILY_4908, BRCM_FAMILY_7250B0, BRCM_FAMILY_7271A0, BRCM_FAMILY_7364A0, @@ -96,6 +97,7 @@ enum brcm_family_type { static const char *family_names[BRCM_FAMILY_COUNT] = { USB_BRCM_FAMILY(3390A0), + USB_BRCM_FAMILY(4908), USB_BRCM_FAMILY(7250B0), USB_BRCM_FAMILY(7271A0), USB_BRCM_FAMILY(7364A0), @@ -203,6 +205,27 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ }, + /* 4908 */ + [BRCM_FAMILY_4908] = { + 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ + 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ + 0, /* USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK */ + 0, /* USB_CTRL_SETUP ENDIAN bits */ + }, /* 7250b0 */ [BRCM_FAMILY_7250B0] = { USB_CTRL_SETUP_SCB1_EN_MASK, @@ -559,6 +582,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params) */ switch (params->selected_family) { case BRCM_FAMILY_3390A0: + case BRCM_FAMILY_4908: case BRCM_FAMILY_7250B0: case BRCM_FAMILY_7366C0: case BRCM_FAMILY_74371A0: @@ -837,7 +861,7 @@ static void usb_init_common(struct brcm_usb_init_params *params) brcmusb_usb2_eye_fix(ctrl); /* - * Make sure the the second and third memory controller + * Make sure the second and third memory controller * interfaces are enabled if they exist. */ if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN)) @@ -1004,6 +1028,18 @@ static const struct brcm_usb_init_ops bcm7445_ops = { .set_dual_select = usb_set_dual_select, }; +void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params) +{ + int fam; + + fam = BRCM_FAMILY_4908; + params->selected_family = fam; + params->usb_reg_bits_map = + &usb_reg_bits_map_table[fam][0]; + params->family_name = family_names[fam]; + params->ops = &bcm7445_ops; +} + void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params) { int fam; diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index 899b9eb43fad..1ccb5ddab865 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -64,6 +64,7 @@ struct brcm_usb_init_params { bool suspend_with_clocks; }; +void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params); @@ -78,7 +79,7 @@ static inline u32 brcm_usb_readl(void __iomem *addr) * Other architectures (e.g., ARM) either do not support big endian, or * else leave I/O in little endian mode. */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) return __raw_readl(addr); else return readl_relaxed(addr); @@ -87,7 +88,7 @@ static inline u32 brcm_usb_readl(void __iomem *addr) static inline void brcm_usb_writel(u32 val, void __iomem *addr) { /* See brcmnand_readl() comments */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) __raw_writel(val, addr); else writel_relaxed(val, addr); diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 491bbd46c5b3..2cb3779fcdf8 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -11,12 +11,14 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.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 <linux/mfd/syscon.h> +#include <linux/suspend.h> #include "phy-brcm-usb-init.h" @@ -34,19 +36,19 @@ struct value_to_name_map { }; struct match_chip_info { - void *init_func; + void (*init_func)(struct brcm_usb_init_params *params); u8 required_regs[BRCM_REGS_MAX + 1]; u8 optional_reg; }; -static struct value_to_name_map brcm_dr_mode_to_name[] = { +static const 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[] = { +static const struct value_to_name_map brcm_dual_mode_to_name[] = { { 0, "host" }, { 1, "device" }, { 2, "auto" }, @@ -69,12 +71,35 @@ struct brcm_usb_phy_data { int init_count; int wake_irq; struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; + struct notifier_block pm_notifier; + bool pm_active; }; static s8 *node_reg_names[BRCM_REGS_MAX] = { "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" }; +static int brcm_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, + void *unused) +{ + struct brcm_usb_phy_data *priv = + container_of(notifier, struct brcm_usb_phy_data, pm_notifier); + + switch (pm_event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + priv->pm_active = true; + break; + case PM_POST_RESTORE: + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + priv->pm_active = false; + break; + } + return NOTIFY_DONE; +} + static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) { struct phy *gphy = dev_id; @@ -90,6 +115,9 @@ static int brcm_usb_phy_init(struct phy *gphy) struct brcm_usb_phy_data *priv = container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); + if (priv->pm_active) + return 0; + /* * Use a lock to make sure a second caller waits until * the base phy is inited before using it. @@ -119,6 +147,9 @@ static int brcm_usb_phy_exit(struct phy *gphy) struct brcm_usb_phy_data *priv = container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); + if (priv->pm_active) + return 0; + dev_dbg(&gphy->dev, "EXIT\n"); if (phy->id == BRCM_USB_PHY_2_0) brcm_usb_uninit_eohci(&priv->ini); @@ -138,7 +169,7 @@ static int brcm_usb_phy_exit(struct phy *gphy) return 0; } -static struct phy_ops brcm_usb_phy_ops = { +static const struct phy_ops brcm_usb_phy_ops = { .init = brcm_usb_phy_init, .exit = brcm_usb_phy_exit, .owner = THIS_MODULE, @@ -170,7 +201,7 @@ static struct phy *brcm_usb_phy_xlate(struct device *dev, return ERR_PTR(-ENODEV); } -static int name_to_value(struct value_to_name_map *table, int count, +static int name_to_value(const struct value_to_name_map *table, int count, const char *name, int *value) { int x; @@ -185,7 +216,7 @@ static int name_to_value(struct value_to_name_map *table, int count, return -EINVAL; } -static const char *value_to_name(struct value_to_name_map *table, int count, +static const char *value_to_name(const struct value_to_name_map *table, int count, int value) { if (value >= count) @@ -252,7 +283,16 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; -static struct match_chip_info chip_info_7216 = { +static const struct match_chip_info chip_info_4908 = { + .init_func = &brcm_usb_dvr_init_4908, + .required_regs = { + BRCM_REGS_CTRL, + BRCM_REGS_XHCI_EC, + -1, + }, +}; + +static const struct match_chip_info chip_info_7216 = { .init_func = &brcm_usb_dvr_init_7216, .required_regs = { BRCM_REGS_CTRL, @@ -262,7 +302,7 @@ static struct match_chip_info chip_info_7216 = { }, }; -static struct match_chip_info chip_info_7211b0 = { +static const struct match_chip_info chip_info_7211b0 = { .init_func = &brcm_usb_dvr_init_7211b0, .required_regs = { BRCM_REGS_CTRL, @@ -275,7 +315,7 @@ static struct match_chip_info chip_info_7211b0 = { .optional_reg = BRCM_REGS_BDC_EC, }; -static struct match_chip_info chip_info_7445 = { +static const struct match_chip_info chip_info_7445 = { .init_func = &brcm_usb_dvr_init_7445, .required_regs = { BRCM_REGS_CTRL, @@ -286,6 +326,10 @@ static struct match_chip_info chip_info_7445 = { static const struct of_device_id brcm_usb_dt_ids[] = { { + .compatible = "brcm,bcm4908-usb-phy", + .data = &chip_info_4908, + }, + { .compatible = "brcm,bcm7216-usb-phy", .data = &chip_info_7216, }, @@ -427,8 +471,6 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) struct device_node *dn = pdev->dev.of_node; int err; const char *mode; - const struct of_device_id *match; - void (*dvr_init)(struct brcm_usb_init_params *params); const struct match_chip_info *info; struct regmap *rmap; int x; @@ -441,10 +483,11 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) priv->ini.family_id = brcmstb_get_family_id(); priv->ini.product_id = brcmstb_get_product_id(); - match = of_match_node(brcm_usb_dt_ids, dev->of_node); - info = match->data; - dvr_init = info->init_func; - (*dvr_init)(&priv->ini); + info = of_device_get_match_data(&pdev->dev); + if (!info) + return -ENOENT; + + info->init_func(&priv->ini); dev_dbg(dev, "Best mapping table is for %s\n", priv->ini.family_name); @@ -484,6 +527,9 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) if (err) return err; + priv->pm_notifier.notifier_call = brcm_pm_notifier; + register_pm_notifier(&priv->pm_notifier); + mutex_init(&priv->mutex); /* make sure invert settings are correct */ @@ -524,7 +570,10 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) static int brcm_usb_phy_remove(struct platform_device *pdev) { + struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev); + sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group); + unregister_pm_notifier(&priv->pm_notifier); return 0; } @@ -535,6 +584,7 @@ static int brcm_usb_phy_suspend(struct device *dev) struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); if (priv->init_count) { + dev_dbg(dev, "SUSPEND\n"); priv->ini.wake_enabled = device_may_wakeup(dev); if (priv->phys[BRCM_USB_PHY_3_0].inited) brcm_usb_uninit_xhci(&priv->ini); @@ -574,6 +624,7 @@ static int brcm_usb_phy_resume(struct device *dev) * Uninitialize anything that wasn't previously initialized. */ if (priv->init_count) { + dev_dbg(dev, "RESUME\n"); if (priv->wake_irq >= 0) disable_irq_wake(priv->wake_irq); brcm_usb_init_common(&priv->ini); diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig index b2db916db64b..1adde2d99ae7 100644 --- a/drivers/phy/cadence/Kconfig +++ b/drivers/phy/cadence/Kconfig @@ -3,13 +3,14 @@ # Phy drivers for Cadence PHYs # -config PHY_CADENCE_DP - tristate "Cadence MHDP DisplayPort PHY driver" +config PHY_CADENCE_TORRENT + tristate "Cadence Torrent PHY driver" depends on OF depends on HAS_IOMEM + depends on COMMON_CLK select GENERIC_PHY help - Support for Cadence MHDP DisplayPort PHY. + Support for Cadence Torrent PHY. config PHY_CADENCE_DPHY tristate "Cadence D-PHY Support" @@ -21,9 +22,27 @@ config PHY_CADENCE_DPHY system. If M is selected, the module will be called cdns-dphy. +config PHY_CADENCE_DPHY_RX + tristate "Cadence D-PHY Rx Support" + depends on HAS_IOMEM && OF + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Support for Cadence D-PHY in Rx configuration. + config PHY_CADENCE_SIERRA tristate "Cadence Sierra PHY Driver" depends on OF && HAS_IOMEM && RESET_CONTROLLER + depends on COMMON_CLK select GENERIC_PHY help Enable this to support the Cadence Sierra PHY driver + +config PHY_CADENCE_SALVO + tristate "Cadence Salvo PHY Driver" + depends on OF && HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the Cadence SALVO PHY driver, + this PHY is a legacy PHY, and only are used for USB3 + and USB2. diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index 8f89560f1711..e17f035ddece 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o +obj-$(CONFIG_PHY_CADENCE_TORRENT) += phy-cadence-torrent.o obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o +obj-$(CONFIG_PHY_CADENCE_DPHY_RX) += cdns-dphy-rx.o obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o +obj-$(CONFIG_PHY_CADENCE_SALVO) += phy-cadence-salvo.o diff --git a/drivers/phy/cadence/cdns-dphy-rx.c b/drivers/phy/cadence/cdns-dphy-rx.c new file mode 100644 index 000000000000..572c70089a94 --- /dev/null +++ b/drivers/phy/cadence/cdns-dphy-rx.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> + +#define DPHY_PMA_CMN(reg) (reg) +#define DPHY_PCS(reg) (0xb00 + (reg)) +#define DPHY_ISO(reg) (0xc00 + (reg)) + +#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) +#define DPHY_CMN_RX_MODE_EN BIT(10) +#define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1) +#define DPHY_CMN_SSM_EN BIT(0) + +#define DPHY_CMN_RX_BANDGAP_TIMER 0x14 + +#define DPHY_BAND_CFG DPHY_PCS(0x0) +#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) +#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) + +#define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8) +#define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa + +#define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc) +#define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa + +#define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10) +#define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14) +#define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20) +#define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30) +#define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c) + +#define DPHY_ISO_LANE_READY_BIT 0 +#define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL + +#define DPHY_LANES_MIN 1 +#define DPHY_LANES_MAX 4 + +struct cdns_dphy_rx { + void __iomem *regs; + struct device *dev; + struct phy *phy; +}; + +struct cdns_dphy_rx_band { + /* Rates are in Mbps. */ + unsigned int min_rate; + unsigned int max_rate; +}; + +/* Order of bands is important since the index is the band number. */ +static const struct cdns_dphy_rx_band bands[] = { + { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 }, + { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 }, + { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 }, + { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 }, + { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 } +}; + +static int cdns_dphy_rx_power_on(struct phy *phy) +{ + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); + + /* Start RX state machine. */ + writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN | + FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK, + DPHY_CMN_RX_BANDGAP_TIMER), + dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_rx_power_off(struct phy *phy) +{ + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); + + writel(0, dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate) +{ + unsigned int rate, i; + + rate = hs_clk_rate / 1000000UL; + /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */ + rate *= 2; + + if (rate < bands[0].min_rate) + return -EOPNOTSUPP; + + for (i = 0; i < ARRAY_SIZE(bands); i++) + if (rate < bands[i].max_rate) + return i; + + return -EOPNOTSUPP; +} + +static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr, + unsigned int bit) +{ + u32 val; + + return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10, + DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000); +} + +static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy, + unsigned int lanes) +{ + static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0, + DPHY_ISO_DL_CTRL_L1, + DPHY_ISO_DL_CTRL_L2, + DPHY_ISO_DL_CTRL_L3}; + void __iomem *reg = dphy->regs; + unsigned int i; + int ret; + + /* Clock lane */ + ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L, + DPHY_ISO_LANE_READY_BIT); + if (ret) + return ret; + + for (i = 0; i < lanes; i++) { + ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i], + DPHY_ISO_LANE_READY_BIT); + if (ret) + return ret; + } + + return 0; +} + +static int cdns_dphy_rx_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); + unsigned int reg, lanes = opts->mipi_dphy.lanes; + int band_ctrl, ret; + + /* Data lanes. Minimum one lane is mandatory. */ + if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX) + return -EINVAL; + + band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (band_ctrl < 0) + return band_ctrl; + + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); + writel(reg, dphy->regs + DPHY_BAND_CFG); + + /* + * Set the required power island phase 2 time. This is mandated by DPHY + * specs. + */ + reg = DPHY_POWER_ISLAND_EN_DATA_VAL; + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA); + reg = DPHY_POWER_ISLAND_EN_CLK_VAL; + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK); + + ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes); + if (ret) { + dev_err(dphy->dev, "DPHY wait for lane ready timeout\n"); + return ret; + } + + return 0; +} + +static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode, + int submode, union phy_configure_opts *opts) +{ + int ret; + + if (mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; + + ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (ret < 0) + return ret; + + return phy_mipi_dphy_config_validate(&opts->mipi_dphy); +} + +static const struct phy_ops cdns_dphy_rx_ops = { + .power_on = cdns_dphy_rx_power_on, + .power_off = cdns_dphy_rx_power_off, + .configure = cdns_dphy_rx_configure, + .validate = cdns_dphy_rx_validate, +}; + +static int cdns_dphy_rx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct cdns_dphy_rx *dphy; + + dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + + dev_set_drvdata(dev, dphy); + dphy->dev = dev; + + dphy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops); + if (IS_ERR(dphy->phy)) { + dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy)); + return PTR_ERR(dphy->phy); + } + + phy_set_drvdata(dphy->phy, dphy); + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "Failed to register PHY provider: %ld\n", + PTR_ERR(provider)); + return PTR_ERR(provider); + } + + return 0; +} + +static const struct of_device_id cdns_dphy_rx_of_match[] = { + { .compatible = "cdns,dphy-rx" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match); + +static struct platform_driver cdns_dphy_rx_platform_driver = { + .probe = cdns_dphy_rx_probe, + .driver = { + .name = "cdns-mipi-dphy-rx", + .of_match_table = cdns_dphy_rx_of_match, + }, +}; +module_platform_driver(cdns_dphy_rx_platform_driver); + +MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>"); +MODULE_DESCRIPTION("Cadence D-PHY Rx Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index 90c4e9b5aac8..3dfdfb33cd0a 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -3,9 +3,11 @@ * Copyright: 2017-2018 Cadence Design Systems, Inc. */ +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_device.h> @@ -17,6 +19,7 @@ #define REG_WAKEUP_TIME_NS 800 #define DPHY_PLL_RATE_HZ 108000000 +#define POLL_TIMEOUT_US 1000 /* DPHY registers */ #define DPHY_PMA_CMN(reg) (reg) @@ -45,6 +48,10 @@ #define DPHY_CMN_OPDIV_FROM_REG BIT(6) #define DPHY_CMN_OPDIV(x) ((x) << 7) +#define DPHY_BAND_CFG DPHY_PCS(0x0) +#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) +#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) + #define DPHY_PSM_CFG DPHY_PCS(0x4) #define DPHY_PSM_CFG_FROM_REG BIT(0) #define DPHY_PSM_CLK_DIV(x) ((x) << 1) @@ -57,6 +64,18 @@ #define DSI_NULL_FRAME_OVERHEAD 6 #define DSI_EOT_PKT_SIZE 4 +#define DPHY_TX_J721E_WIZ_PLL_CTRL 0xF04 +#define DPHY_TX_J721E_WIZ_STATUS 0xF08 +#define DPHY_TX_J721E_WIZ_RST_CTRL 0xF0C +#define DPHY_TX_J721E_WIZ_PSM_FREQ 0xF10 + +#define DPHY_TX_J721E_WIZ_IPDIV GENMASK(4, 0) +#define DPHY_TX_J721E_WIZ_OPDIV GENMASK(13, 8) +#define DPHY_TX_J721E_WIZ_FBDIV GENMASK(25, 16) +#define DPHY_TX_J721E_WIZ_LANE_RSTB BIT(31) +#define DPHY_TX_WIZ_PLL_LOCK BIT(31) +#define DPHY_TX_WIZ_O_CMN_READY BIT(31) + struct cdns_dphy_cfg { u8 pll_ipdiv; u8 pll_opdiv; @@ -92,6 +111,12 @@ struct cdns_dphy { struct phy *phy; }; +/* Order of bands is important since the index is the band number. */ +static const unsigned int tx_bands[] = { + 80, 100, 120, 160, 200, 240, 320, 390, 450, 510, 560, 640, 690, 770, + 870, 950, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2500 +}; + static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, struct cdns_dphy_cfg *cfg, struct phy_configure_opts_mipi_dphy *opts, @@ -199,6 +224,46 @@ static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div) dphy->regs + DPHY_PSM_CFG); } +static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy) +{ + /* Minimum wakeup time as per MIPI D-PHY spec v1.2 */ + return 1000000; +} + +static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy, + const struct cdns_dphy_cfg *cfg) +{ + u32 status; + + /* + * set the PWM and PLL Byteclk divider settings to recommended values + * which is same as that of in ref ops + */ + writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) | + DPHY_CMN_PWM_DIV(0x8), + dphy->regs + DPHY_CMN_PWM); + + writel((FIELD_PREP(DPHY_TX_J721E_WIZ_IPDIV, cfg->pll_ipdiv) | + FIELD_PREP(DPHY_TX_J721E_WIZ_OPDIV, cfg->pll_opdiv) | + FIELD_PREP(DPHY_TX_J721E_WIZ_FBDIV, cfg->pll_fbdiv)), + dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL); + + writel(DPHY_TX_J721E_WIZ_LANE_RSTB, + dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL); + + readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, + (status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US); + + readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, + (status & DPHY_TX_WIZ_O_CMN_READY), 0, + POLL_TIMEOUT_US); +} + +static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div) +{ + writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ); +} + /* * This is the reference implementation of DPHY hooks. Specific integration of * this IP may have to re-implement some of them depending on how they decided @@ -210,6 +275,12 @@ static const struct cdns_dphy_ops ref_dphy_ops = { .set_psm_div = cdns_dphy_ref_set_psm_div, }; +static const struct cdns_dphy_ops j721e_dphy_ops = { + .get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns, + .set_pll_cfg = cdns_dphy_j721e_set_pll_cfg, + .set_psm_div = cdns_dphy_j721e_set_psm_div, +}; + static int cdns_dphy_config_from_opts(struct phy *phy, struct phy_configure_opts_mipi_dphy *opts, struct cdns_dphy_cfg *cfg) @@ -232,6 +303,24 @@ static int cdns_dphy_config_from_opts(struct phy *phy, return 0; } +static int cdns_dphy_tx_get_band_ctrl(unsigned long hs_clk_rate) +{ + unsigned int rate; + int i; + + rate = hs_clk_rate / 1000000UL; + + if (rate < tx_bands[0]) + return -EOPNOTSUPP; + + for (i = 0; i < ARRAY_SIZE(tx_bands) - 1; i++) { + if (rate >= tx_bands[i] && rate < tx_bands[i + 1]) + return i; + } + + return -EOPNOTSUPP; +} + static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts) { @@ -247,7 +336,8 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) { struct cdns_dphy *dphy = phy_get_drvdata(phy); struct cdns_dphy_cfg cfg = { 0 }; - int ret; + int ret, band_ctrl; + unsigned int reg; ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); if (ret) @@ -276,6 +366,14 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) */ cdns_dphy_set_pll_cfg(dphy, &cfg); + band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); + if (band_ctrl < 0) + return band_ctrl; + + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); + writel(reg, dphy->regs + DPHY_BAND_CFG); + return 0; } @@ -314,7 +412,6 @@ static int cdns_dphy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct cdns_dphy *dphy; - struct resource *res; int ret; dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); @@ -326,8 +423,7 @@ static int cdns_dphy_probe(struct platform_device *pdev) if (!dphy->ops) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dphy->regs = devm_ioremap_resource(&pdev->dev, res); + dphy->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dphy->regs)) return PTR_ERR(dphy->regs); @@ -372,6 +468,7 @@ static int cdns_dphy_remove(struct platform_device *pdev) static const struct of_device_id cdns_dphy_of_match[] = { { .compatible = "cdns,dphy", .data = &ref_dphy_ops }, + { .compatible = "ti,j721e-dphy", .data = &j721e_dphy_ops }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, cdns_dphy_of_match); diff --git a/drivers/phy/cadence/phy-cadence-dp.c b/drivers/phy/cadence/phy-cadence-dp.c deleted file mode 100644 index bc10cb264b7a..000000000000 --- a/drivers/phy/cadence/phy-cadence-dp.c +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Cadence MHDP DisplayPort SD0801 PHY driver. - * - * Copyright 2018 Cadence Design Systems, Inc. - * - */ - -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#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> - -#define DEFAULT_NUM_LANES 2 -#define MAX_NUM_LANES 4 -#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */ - -#define POLL_TIMEOUT_US 2000 -#define LANE_MASK 0x7 - -/* - * register offsets from DPTX PHY register block base (i.e MHDP - * register base + 0x30a00) - */ -#define PHY_AUX_CONFIG 0x00 -#define PHY_AUX_CTRL 0x04 -#define PHY_RESET 0x20 -#define PHY_PMA_XCVR_PLLCLK_EN 0x24 -#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28 -#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c -#define PHY_POWER_STATE_LN_0 0x0000 -#define PHY_POWER_STATE_LN_1 0x0008 -#define PHY_POWER_STATE_LN_2 0x0010 -#define PHY_POWER_STATE_LN_3 0x0018 -#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30 -#define PHY_PMA_CMN_READY 0x34 -#define PHY_PMA_XCVR_TX_VMARGIN 0x38 -#define PHY_PMA_XCVR_TX_DEEMPH 0x3c - -/* - * register offsets from SD0801 PHY register block base (i.e MHDP - * register base + 0x500000) - */ -#define CMN_SSM_BANDGAP_TMR 0x00084 -#define CMN_SSM_BIAS_TMR 0x00088 -#define CMN_PLLSM0_PLLPRE_TMR 0x000a8 -#define CMN_PLLSM0_PLLLOCK_TMR 0x000b0 -#define CMN_PLLSM1_PLLPRE_TMR 0x000c8 -#define CMN_PLLSM1_PLLLOCK_TMR 0x000d0 -#define CMN_BGCAL_INIT_TMR 0x00190 -#define CMN_BGCAL_ITER_TMR 0x00194 -#define CMN_IBCAL_INIT_TMR 0x001d0 -#define CMN_PLL0_VCOCAL_INIT_TMR 0x00210 -#define CMN_PLL0_VCOCAL_ITER_TMR 0x00214 -#define CMN_PLL0_VCOCAL_REFTIM_START 0x00218 -#define CMN_PLL0_VCOCAL_PLLCNT_START 0x00220 -#define CMN_PLL0_INTDIV_M0 0x00240 -#define CMN_PLL0_FRACDIVL_M0 0x00244 -#define CMN_PLL0_FRACDIVH_M0 0x00248 -#define CMN_PLL0_HIGH_THR_M0 0x0024c -#define CMN_PLL0_DSM_DIAG_M0 0x00250 -#define CMN_PLL0_LOCK_PLLCNT_START 0x00278 -#define CMN_PLL1_VCOCAL_INIT_TMR 0x00310 -#define CMN_PLL1_VCOCAL_ITER_TMR 0x00314 -#define CMN_PLL1_DSM_DIAG_M0 0x00350 -#define CMN_TXPUCAL_INIT_TMR 0x00410 -#define CMN_TXPUCAL_ITER_TMR 0x00414 -#define CMN_TXPDCAL_INIT_TMR 0x00430 -#define CMN_TXPDCAL_ITER_TMR 0x00434 -#define CMN_RXCAL_INIT_TMR 0x00450 -#define CMN_RXCAL_ITER_TMR 0x00454 -#define CMN_SD_CAL_INIT_TMR 0x00490 -#define CMN_SD_CAL_ITER_TMR 0x00494 -#define CMN_SD_CAL_REFTIM_START 0x00498 -#define CMN_SD_CAL_PLLCNT_START 0x004a0 -#define CMN_PDIAG_PLL0_CTRL_M0 0x00680 -#define CMN_PDIAG_PLL0_CLK_SEL_M0 0x00684 -#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x00690 -#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x00694 -#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x00698 -#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x006d0 -#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x006d4 -#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x00704 -#define XCVR_DIAG_PLLDRC_CTRL 0x10394 -#define XCVR_DIAG_HSCLK_SEL 0x10398 -#define XCVR_DIAG_HSCLK_DIV 0x1039c -#define TX_PSC_A0 0x10400 -#define TX_PSC_A1 0x10404 -#define TX_PSC_A2 0x10408 -#define TX_PSC_A3 0x1040c -#define RX_PSC_A0 0x20000 -#define RX_PSC_A1 0x20004 -#define RX_PSC_A2 0x20008 -#define RX_PSC_A3 0x2000c -#define PHY_PLL_CFG 0x30038 - -struct cdns_dp_phy { - void __iomem *base; /* DPTX registers base */ - void __iomem *sd_base; /* SD0801 registers base */ - u32 num_lanes; /* Number of lanes to use */ - u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */ - struct device *dev; -}; - -static int cdns_dp_phy_init(struct phy *phy); -static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy); -static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy); -static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy); -static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy); -static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy, - unsigned int lane); -static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy); -static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy); -static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy, - unsigned int offset, - unsigned char start_bit, - unsigned char num_bits, - unsigned int val); - -static const struct phy_ops cdns_dp_phy_ops = { - .init = cdns_dp_phy_init, - .owner = THIS_MODULE, -}; - -static int cdns_dp_phy_init(struct phy *phy) -{ - unsigned char lane_bits; - - struct cdns_dp_phy *cdns_phy = phy_get_drvdata(phy); - - writel(0x0003, cdns_phy->base + PHY_AUX_CTRL); /* enable AUX */ - - /* PHY PMA registers configuration function */ - cdns_dp_phy_pma_cfg(cdns_phy); - - /* - * Set lines power state to A0 - * Set lines pll clk enable to 0 - */ - - cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ, - PHY_POWER_STATE_LN_0, 6, 0x0000); - - if (cdns_phy->num_lanes >= 2) { - cdns_dp_phy_write_field(cdns_phy, - PHY_PMA_XCVR_POWER_STATE_REQ, - PHY_POWER_STATE_LN_1, 6, 0x0000); - - if (cdns_phy->num_lanes == 4) { - cdns_dp_phy_write_field(cdns_phy, - PHY_PMA_XCVR_POWER_STATE_REQ, - PHY_POWER_STATE_LN_2, 6, 0); - cdns_dp_phy_write_field(cdns_phy, - PHY_PMA_XCVR_POWER_STATE_REQ, - PHY_POWER_STATE_LN_3, 6, 0); - } - } - - cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN, - 0, 1, 0x0000); - - if (cdns_phy->num_lanes >= 2) { - cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN, - 1, 1, 0x0000); - if (cdns_phy->num_lanes == 4) { - cdns_dp_phy_write_field(cdns_phy, - PHY_PMA_XCVR_PLLCLK_EN, - 2, 1, 0x0000); - cdns_dp_phy_write_field(cdns_phy, - PHY_PMA_XCVR_PLLCLK_EN, - 3, 1, 0x0000); - } - } - - /* - * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on - * used lanes - */ - lane_bits = (1 << cdns_phy->num_lanes) - 1; - writel(((0xF & ~lane_bits) << 4) | (0xF & lane_bits), - cdns_phy->base + PHY_RESET); - - /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */ - writel(0x0001, cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN); - - /* PHY PMA registers configuration functions */ - cdns_dp_phy_pma_cmn_vco_cfg_25mhz(cdns_phy); - cdns_dp_phy_pma_cmn_rate(cdns_phy); - - /* take out of reset */ - cdns_dp_phy_write_field(cdns_phy, PHY_RESET, 8, 1, 1); - cdns_dp_phy_wait_pma_cmn_ready(cdns_phy); - cdns_dp_phy_run(cdns_phy); - - return 0; -} - -static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy) -{ - unsigned int reg; - int ret; - - ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_CMN_READY, reg, - reg & 1, 0, 500); - if (ret == -ETIMEDOUT) - dev_err(cdns_phy->dev, - "timeout waiting for PMA common ready\n"); -} - -static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy) -{ - unsigned int i; - - /* PMA common configuration */ - cdns_dp_phy_pma_cmn_cfg_25mhz(cdns_phy); - - /* PMA lane configuration to deal with multi-link operation */ - for (i = 0; i < cdns_phy->num_lanes; i++) - cdns_dp_phy_pma_lane_cfg(cdns_phy, i); -} - -static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy) -{ - /* refclock registers - assumes 25 MHz refclock */ - writel(0x0019, cdns_phy->sd_base + CMN_SSM_BIAS_TMR); - writel(0x0032, cdns_phy->sd_base + CMN_PLLSM0_PLLPRE_TMR); - writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM0_PLLLOCK_TMR); - writel(0x0032, cdns_phy->sd_base + CMN_PLLSM1_PLLPRE_TMR); - writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM1_PLLLOCK_TMR); - writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_INIT_TMR); - writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_ITER_TMR); - writel(0x0019, cdns_phy->sd_base + CMN_IBCAL_INIT_TMR); - writel(0x001E, cdns_phy->sd_base + CMN_TXPUCAL_INIT_TMR); - writel(0x0006, cdns_phy->sd_base + CMN_TXPUCAL_ITER_TMR); - writel(0x001E, cdns_phy->sd_base + CMN_TXPDCAL_INIT_TMR); - writel(0x0006, cdns_phy->sd_base + CMN_TXPDCAL_ITER_TMR); - writel(0x02EE, cdns_phy->sd_base + CMN_RXCAL_INIT_TMR); - writel(0x0006, cdns_phy->sd_base + CMN_RXCAL_ITER_TMR); - writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_INIT_TMR); - writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_ITER_TMR); - writel(0x000E, cdns_phy->sd_base + CMN_SD_CAL_REFTIM_START); - writel(0x012B, cdns_phy->sd_base + CMN_SD_CAL_PLLCNT_START); - /* PLL registers */ - writel(0x0409, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_PADJ_M0); - writel(0x1001, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_IADJ_M0); - writel(0x0F08, cdns_phy->sd_base + CMN_PDIAG_PLL0_FILT_PADJ_M0); - writel(0x0004, cdns_phy->sd_base + CMN_PLL0_DSM_DIAG_M0); - writel(0x00FA, cdns_phy->sd_base + CMN_PLL0_VCOCAL_INIT_TMR); - writel(0x0004, cdns_phy->sd_base + CMN_PLL0_VCOCAL_ITER_TMR); - writel(0x00FA, cdns_phy->sd_base + CMN_PLL1_VCOCAL_INIT_TMR); - writel(0x0004, cdns_phy->sd_base + CMN_PLL1_VCOCAL_ITER_TMR); - writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_REFTIM_START); -} - -static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy) -{ - /* Assumes 25 MHz refclock */ - switch (cdns_phy->max_bit_rate) { - /* Setting VCO for 10.8GHz */ - case 2700: - case 5400: - writel(0x01B0, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0); - writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0); - writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0); - writel(0x0120, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0); - break; - /* Setting VCO for 9.72GHz */ - case 2430: - case 3240: - writel(0x0184, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0); - writel(0xCCCD, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0); - writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0); - writel(0x0104, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0); - break; - /* Setting VCO for 8.64GHz */ - case 2160: - case 4320: - writel(0x0159, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0); - writel(0x999A, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0); - writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0); - writel(0x00E7, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0); - break; - /* Setting VCO for 8.1GHz */ - case 8100: - writel(0x0144, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0); - writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0); - writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0); - writel(0x00D8, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0); - break; - } - - writel(0x0002, cdns_phy->sd_base + CMN_PDIAG_PLL0_CTRL_M0); - writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_PLLCNT_START); -} - -static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy) -{ - unsigned int clk_sel_val = 0; - unsigned int hsclk_div_val = 0; - unsigned int i; - - /* 16'h0000 for single DP link configuration */ - writel(0x0000, cdns_phy->sd_base + PHY_PLL_CFG); - - switch (cdns_phy->max_bit_rate) { - case 1620: - clk_sel_val = 0x0f01; - hsclk_div_val = 2; - break; - case 2160: - case 2430: - case 2700: - clk_sel_val = 0x0701; - hsclk_div_val = 1; - break; - case 3240: - clk_sel_val = 0x0b00; - hsclk_div_val = 2; - break; - case 4320: - case 5400: - clk_sel_val = 0x0301; - hsclk_div_val = 0; - break; - case 8100: - clk_sel_val = 0x0200; - hsclk_div_val = 0; - break; - } - - writel(clk_sel_val, cdns_phy->sd_base + CMN_PDIAG_PLL0_CLK_SEL_M0); - - /* PMA lane configuration to deal with multi-link operation */ - for (i = 0; i < cdns_phy->num_lanes; i++) { - writel(hsclk_div_val, - cdns_phy->sd_base + (XCVR_DIAG_HSCLK_DIV | (i<<11))); - } -} - -static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy, - unsigned int lane) -{ - unsigned int lane_bits = (lane & LANE_MASK) << 11; - - /* Writing Tx/Rx Power State Controllers registers */ - writel(0x00FB, cdns_phy->sd_base + (TX_PSC_A0 | lane_bits)); - writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A2 | lane_bits)); - writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A3 | lane_bits)); - writel(0x0000, cdns_phy->sd_base + (RX_PSC_A0 | lane_bits)); - writel(0x0000, cdns_phy->sd_base + (RX_PSC_A2 | lane_bits)); - writel(0x0000, cdns_phy->sd_base + (RX_PSC_A3 | lane_bits)); - - writel(0x0001, cdns_phy->sd_base + (XCVR_DIAG_PLLDRC_CTRL | lane_bits)); - writel(0x0000, cdns_phy->sd_base + (XCVR_DIAG_HSCLK_SEL | lane_bits)); -} - -static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy) -{ - unsigned int read_val; - u32 write_val1 = 0; - u32 write_val2 = 0; - u32 mask = 0; - int ret; - - /* - * waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the - * master lane - */ - ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN_ACK, - read_val, read_val & 1, 0, POLL_TIMEOUT_US); - if (ret == -ETIMEDOUT) - dev_err(cdns_phy->dev, - "timeout waiting for link PLL clock enable ack\n"); - - ndelay(100); - - switch (cdns_phy->num_lanes) { - - case 1: /* lane 0 */ - write_val1 = 0x00000004; - write_val2 = 0x00000001; - mask = 0x0000003f; - break; - case 2: /* lane 0-1 */ - write_val1 = 0x00000404; - write_val2 = 0x00000101; - mask = 0x00003f3f; - break; - case 4: /* lane 0-3 */ - write_val1 = 0x04040404; - write_val2 = 0x01010101; - mask = 0x3f3f3f3f; - break; - } - - writel(write_val1, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ); - - ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK, - read_val, (read_val & mask) == write_val1, 0, - POLL_TIMEOUT_US); - if (ret == -ETIMEDOUT) - dev_err(cdns_phy->dev, - "timeout waiting for link power state ack\n"); - - writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ); - ndelay(100); - - writel(write_val2, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ); - - ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK, - read_val, (read_val & mask) == write_val2, 0, - POLL_TIMEOUT_US); - if (ret == -ETIMEDOUT) - dev_err(cdns_phy->dev, - "timeout waiting for link power state ack\n"); - - writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ); - ndelay(100); -} - -static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy, - unsigned int offset, - unsigned char start_bit, - unsigned char num_bits, - unsigned int val) -{ - unsigned int read_val; - - read_val = readl(cdns_phy->base + offset); - writel(((val << start_bit) | (read_val & ~(((1 << num_bits) - 1) << - start_bit))), cdns_phy->base + offset); -} - -static int cdns_dp_phy_probe(struct platform_device *pdev) -{ - struct resource *regs; - struct cdns_dp_phy *cdns_phy; - struct device *dev = &pdev->dev; - struct phy_provider *phy_provider; - struct phy *phy; - int err; - - cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL); - if (!cdns_phy) - return -ENOMEM; - - cdns_phy->dev = &pdev->dev; - - phy = devm_phy_create(dev, NULL, &cdns_dp_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create DisplayPort PHY\n"); - return PTR_ERR(phy); - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs); - if (IS_ERR(cdns_phy->base)) - return PTR_ERR(cdns_phy->base); - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); - cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs); - if (IS_ERR(cdns_phy->sd_base)) - return PTR_ERR(cdns_phy->sd_base); - - err = device_property_read_u32(dev, "num_lanes", - &(cdns_phy->num_lanes)); - if (err) - cdns_phy->num_lanes = DEFAULT_NUM_LANES; - - switch (cdns_phy->num_lanes) { - case 1: - case 2: - case 4: - /* valid number of lanes */ - break; - default: - dev_err(dev, "unsupported number of lanes: %d\n", - cdns_phy->num_lanes); - return -EINVAL; - } - - err = device_property_read_u32(dev, "max_bit_rate", - &(cdns_phy->max_bit_rate)); - if (err) - cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE; - - switch (cdns_phy->max_bit_rate) { - case 2160: - case 2430: - case 2700: - case 3240: - case 4320: - case 5400: - case 8100: - /* valid bit rate */ - break; - default: - dev_err(dev, "unsupported max bit rate: %dMbps\n", - cdns_phy->max_bit_rate); - return -EINVAL; - } - - phy_set_drvdata(phy, cdns_phy); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n", - cdns_phy->num_lanes, - cdns_phy->max_bit_rate / 1000, - cdns_phy->max_bit_rate % 1000); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id cdns_dp_phy_of_match[] = { - { - .compatible = "cdns,dp-phy" - }, - {} -}; -MODULE_DEVICE_TABLE(of, cdns_dp_phy_of_match); - -static struct platform_driver cdns_dp_phy_driver = { - .probe = cdns_dp_phy_probe, - .driver = { - .name = "cdns-dp-phy", - .of_match_table = cdns_dp_phy_of_match, - } -}; -module_platform_driver(cdns_dp_phy_driver); - -MODULE_AUTHOR("Cadence Design Systems, Inc."); -MODULE_DESCRIPTION("Cadence MHDP PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c new file mode 100644 index 000000000000..e569f5f67578 --- /dev/null +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Salvo PHY is a 28nm PHY, it is a legacy PHY, and only + * for USB3 and USB2. + * + * Copyright (c) 2019-2020 NXP + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +/* PHY register definition */ +#define PHY_PMA_CMN_CTRL1 0xC800 +#define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0 +#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084 +#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR 0x0085 +#define TB_ADDR_CMN_PLL0_INTDIV 0x0094 +#define TB_ADDR_CMN_PLL0_FRACDIV 0x0095 +#define TB_ADDR_CMN_PLL0_HIGH_THR 0x0096 +#define TB_ADDR_CMN_PLL0_SS_CTRL1 0x0098 +#define TB_ADDR_CMN_PLL0_SS_CTRL2 0x0099 +#define TB_ADDR_CMN_PLL0_DSM_DIAG 0x0097 +#define TB_ADDR_CMN_DIAG_PLL0_OVRD 0x01c2 +#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD 0x01c0 +#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD 0x01c1 +#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE 0x01C5 +#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE 0x01C6 +#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG 0x01C7 +#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE 0x01c4 +#define TB_ADDR_CMN_PSM_CLK_CTRL 0x0061 +#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR 0x40ea +#define TB_ADDR_XCVR_PSM_RCTRL 0x4001 +#define TB_ADDR_TX_PSC_A0 0x4100 +#define TB_ADDR_TX_PSC_A1 0x4101 +#define TB_ADDR_TX_PSC_A2 0x4102 +#define TB_ADDR_TX_PSC_A3 0x4103 +#define TB_ADDR_TX_DIAG_ECTRL_OVRD 0x41f5 +#define TB_ADDR_TX_PSC_CAL 0x4106 +#define TB_ADDR_TX_PSC_RDY 0x4107 +#define TB_ADDR_RX_PSC_A0 0x8000 +#define TB_ADDR_RX_PSC_A1 0x8001 +#define TB_ADDR_RX_PSC_A2 0x8002 +#define TB_ADDR_RX_PSC_A3 0x8003 +#define TB_ADDR_RX_PSC_CAL 0x8006 +#define TB_ADDR_RX_PSC_RDY 0x8007 +#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 0x4058 +#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 +#define TB_ADDR_RX_SLC_CU_ITER_TMR 0x80e3 +#define TB_ADDR_RX_SIGDET_HL_FILT_TMR 0x8090 +#define TB_ADDR_RX_SAMP_DAC_CTRL 0x8058 +#define TB_ADDR_RX_DIAG_SIGDET_TUNE 0x81dc +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 0x81df +#define TB_ADDR_RX_DIAG_BS_TM 0x81f5 +#define TB_ADDR_RX_DIAG_DFE_CTRL1 0x81d3 +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 0x81c7 +#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 0x81c2 +#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 0x81c1 +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 0x81c9 +#define TB_ADDR_RX_DIAG_RXFE_TM3 0x81f8 +#define TB_ADDR_RX_DIAG_RXFE_TM4 0x81f9 +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE 0x81dd +#define TB_ADDR_RX_DIAG_DFE_CTRL3 0x81d5 +#define TB_ADDR_RX_DIAG_SC2C_DELAY 0x81e1 +#define TB_ADDR_RX_REE_VGA_GAIN_NODFE 0x81bf +#define TB_ADDR_XCVR_PSM_CAL_TMR 0x4002 +#define TB_ADDR_XCVR_PSM_A0BYP_TMR 0x4004 +#define TB_ADDR_XCVR_PSM_A0IN_TMR 0x4003 +#define TB_ADDR_XCVR_PSM_A1IN_TMR 0x4005 +#define TB_ADDR_XCVR_PSM_A2IN_TMR 0x4006 +#define TB_ADDR_XCVR_PSM_A3IN_TMR 0x4007 +#define TB_ADDR_XCVR_PSM_A4IN_TMR 0x4008 +#define TB_ADDR_XCVR_PSM_A5IN_TMR 0x4009 +#define TB_ADDR_XCVR_PSM_A0OUT_TMR 0x400a +#define TB_ADDR_XCVR_PSM_A1OUT_TMR 0x400b +#define TB_ADDR_XCVR_PSM_A2OUT_TMR 0x400c +#define TB_ADDR_XCVR_PSM_A3OUT_TMR 0x400d +#define TB_ADDR_XCVR_PSM_A4OUT_TMR 0x400e +#define TB_ADDR_XCVR_PSM_A5OUT_TMR 0x400f +#define TB_ADDR_TX_RCVDET_EN_TMR 0x4122 +#define TB_ADDR_TX_RCVDET_ST_TMR 0x4123 +#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2 +#define TB_ADDR_TX_RCVDETSC_CTRL 0x4124 + +/* TB_ADDR_TX_RCVDETSC_CTRL */ +#define RXDET_IN_P3_32KHZ BIT(0) + +struct cdns_reg_pairs { + u16 val; + u32 off; +}; + +struct cdns_salvo_data { + u8 reg_offset_shift; + const struct cdns_reg_pairs *init_sequence_val; + u8 init_sequence_length; +}; + +struct cdns_salvo_phy { + struct phy *phy; + struct clk *clk; + void __iomem *base; + struct cdns_salvo_data *data; +}; + +static const struct of_device_id cdns_salvo_phy_of_match[]; +static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg) +{ + return (u16)readl(salvo_phy->base + + reg * (1 << salvo_phy->data->reg_offset_shift)); +} + +static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, + u32 reg, u16 val) +{ + writel(val, salvo_phy->base + + reg * (1 << salvo_phy->data->reg_offset_shift)); +} + +/* + * Below bringup sequence pair are from Cadence PHY's User Guide + * and NXP platform tuning results. + */ +static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = { + {0x0830, PHY_PMA_CMN_CTRL1}, + {0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL}, + {0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR}, + {0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR}, + {0x00d0, TB_ADDR_CMN_PLL0_INTDIV}, + {0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV}, + {0x0034, TB_ADDR_CMN_PLL0_HIGH_THR}, + {0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1}, + {0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2}, + {0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD}, + {0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE}, + {0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE}, + {0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG}, + {0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE}, + {0x000a, TB_ADDR_CMN_PSM_CLK_CTRL}, + {0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR}, + {0xbefc, TB_ADDR_XCVR_PSM_RCTRL}, + + {0x7799, TB_ADDR_TX_PSC_A0}, + {0x7798, TB_ADDR_TX_PSC_A1}, + {0x509b, TB_ADDR_TX_PSC_A2}, + {0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD}, + {0x509b, TB_ADDR_TX_PSC_A3}, + {0x2090, TB_ADDR_TX_PSC_CAL}, + {0x2090, TB_ADDR_TX_PSC_RDY}, + + {0xA6FD, TB_ADDR_RX_PSC_A0}, + {0xA6FD, TB_ADDR_RX_PSC_A1}, + {0xA410, TB_ADDR_RX_PSC_A2}, + {0x2410, TB_ADDR_RX_PSC_A3}, + + {0x23FF, TB_ADDR_RX_PSC_CAL}, + {0x2010, TB_ADDR_RX_PSC_RDY}, + + {0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000}, + {0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY}, + {0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR}, + {0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR}, + {0x0000, TB_ADDR_RX_SAMP_DAC_CTRL}, + {0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE}, + {0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2}, + {0x0480, TB_ADDR_RX_DIAG_BS_TM}, + {0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1}, + {0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4}, + {0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0}, + {0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0}, + {0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6}, + {0x8000, TB_ADDR_RX_DIAG_RXFE_TM3}, + {0x0003, TB_ADDR_RX_DIAG_RXFE_TM4}, + {0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE}, + {0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3}, + {0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY}, + {0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE}, + + {0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR}, + {0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR}, + + {0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR}, + /* Change rx detect parameter */ + {0x0960, TB_ADDR_TX_RCVDET_EN_TMR}, + {0x01e0, TB_ADDR_TX_RCVDET_ST_TMR}, + {0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR}, +}; + +static int cdns_salvo_phy_init(struct phy *phy) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + struct cdns_salvo_data *data = salvo_phy->data; + int ret, i; + u16 value; + + ret = clk_prepare_enable(salvo_phy->clk); + if (ret) + return ret; + + for (i = 0; i < data->init_sequence_length; i++) { + const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i; + + cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val); + } + + /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */ + value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL); + value |= RXDET_IN_P3_32KHZ; + cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL, + RXDET_IN_P3_32KHZ); + + udelay(10); + + clk_disable_unprepare(salvo_phy->clk); + + return ret; +} + +static int cdns_salvo_phy_power_on(struct phy *phy) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + + return clk_prepare_enable(salvo_phy->clk); +} + +static int cdns_salvo_phy_power_off(struct phy *phy) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(salvo_phy->clk); + + return 0; +} + +static const struct phy_ops cdns_salvo_phy_ops = { + .init = cdns_salvo_phy_init, + .power_on = cdns_salvo_phy_power_on, + .power_off = cdns_salvo_phy_power_off, + .owner = THIS_MODULE, +}; + +static int cdns_salvo_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct cdns_salvo_phy *salvo_phy; + struct cdns_salvo_data *data; + + data = (struct cdns_salvo_data *)of_device_get_match_data(dev); + salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL); + if (!salvo_phy) + return -ENOMEM; + + salvo_phy->data = data; + salvo_phy->clk = devm_clk_get_optional(dev, "salvo_phy_clk"); + if (IS_ERR(salvo_phy->clk)) + return PTR_ERR(salvo_phy->clk); + + salvo_phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(salvo_phy->base)) + return PTR_ERR(salvo_phy->base); + + salvo_phy->phy = devm_phy_create(dev, NULL, &cdns_salvo_phy_ops); + if (IS_ERR(salvo_phy->phy)) + return PTR_ERR(salvo_phy->phy); + + phy_set_drvdata(salvo_phy->phy, salvo_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct cdns_salvo_data cdns_nxp_salvo_data = { + 2, + cdns_nxp_sequence_pair, + ARRAY_SIZE(cdns_nxp_sequence_pair), +}; + +static const struct of_device_id cdns_salvo_phy_of_match[] = { + { + .compatible = "nxp,salvo-phy", + .data = &cdns_nxp_salvo_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match); + +static struct platform_driver cdns_salvo_phy_driver = { + .probe = cdns_salvo_phy_probe, + .driver = { + .name = "cdns-salvo-phy", + .of_match_table = cdns_salvo_phy_of_match, + } +}; +module_platform_driver(cdns_salvo_phy_driver); + +MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence SALVO PHY Driver"); diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index a5c08e5bd2bf..6e86a6517f37 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -7,6 +7,7 @@ * */ #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> @@ -20,17 +21,34 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <dt-bindings/phy/phy.h> +#include <dt-bindings/phy/phy-cadence.h> + +#define NUM_SSC_MODE 3 +#define NUM_PHY_TYPE 4 /* PHY register offsets */ #define SIERRA_COMMON_CDB_OFFSET 0x0 #define SIERRA_MACRO_ID_REG 0x0 +#define SIERRA_CMN_PLLLC_GEN_PREG 0x42 #define SIERRA_CMN_PLLLC_MODE_PREG 0x48 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A #define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B +#define SIERRA_CMN_PLLLC_CLK1_PREG 0x4D #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50 +#define SIERRA_CMN_PLLLC_DSMCORR_PREG 0x51 +#define SIERRA_CMN_PLLLC_SS_PREG 0x52 +#define SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG 0x53 +#define SIERRA_CMN_PLLLC_SSTWOPT_PREG 0x54 #define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62 +#define SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG 0x63 +#define SIERRA_CMN_REFRCV_PREG 0x98 +#define SIERRA_CMN_REFRCV1_PREG 0xB8 +#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2 +#define SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG 0xCA +#define SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG 0xD0 +#define SIERRA_CMN_PLLLC1_SS_TIME_STEPSIZE_MODE_PREG 0xE2 #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ ((0x4000 << (block_offset)) + \ @@ -43,7 +61,11 @@ #define SIERRA_DET_STANDEC_E_PREG 0x004 #define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG 0x008 #define SIERRA_PSM_A0IN_TMR_PREG 0x009 +#define SIERRA_PSM_A3IN_TMR_PREG 0x00C #define SIERRA_PSM_DIAG_PREG 0x015 +#define SIERRA_PSC_LN_A3_PREG 0x023 +#define SIERRA_PSC_LN_A4_PREG 0x024 +#define SIERRA_PSC_LN_IDLE_PREG 0x026 #define SIERRA_PSC_TX_A0_PREG 0x028 #define SIERRA_PSC_TX_A1_PREG 0x029 #define SIERRA_PSC_TX_A2_PREG 0x02A @@ -53,18 +75,23 @@ #define SIERRA_PSC_RX_A2_PREG 0x032 #define SIERRA_PSC_RX_A3_PREG 0x033 #define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A +#define SIERRA_PLLCTRL_GEN_A_PREG 0x03B #define SIERRA_PLLCTRL_GEN_D_PREG 0x03E #define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F #define SIERRA_PLLCTRL_STATUS_PREG 0x044 #define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B #define SIERRA_DFE_BIASTRIM_PREG 0x04C #define SIERRA_DRVCTRL_ATTEN_PREG 0x06A +#define SIERRA_DRVCTRL_BOOST_PREG 0x06F +#define SIERRA_TX_RCVDET_OVRD_PREG 0x072 #define SIERRA_CLKPATHCTRL_TMR_PREG 0x081 #define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085 #define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG 0x086 #define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087 #define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088 +#define SIERRA_CREQ_DCBIASATTEN_OVR_PREG 0x08C #define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E +#define SIERRA_RX_CTLE_CAL_PREG 0x08F #define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091 #define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092 #define SIERRA_CREQ_EQ_CTRL_PREG 0x093 @@ -114,15 +141,28 @@ #define SIERRA_DEQ_ALUT12 0x114 #define SIERRA_DEQ_ALUT13 0x115 #define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128 +#define SIERRA_DEQ_DFETAP0 0x129 +#define SIERRA_DEQ_DFETAP1 0x12B +#define SIERRA_DEQ_DFETAP2 0x12D +#define SIERRA_DEQ_DFETAP3 0x12F +#define SIERRA_DEQ_DFETAP4 0x131 #define SIERRA_DFE_EN_1010_IGNORE_PREG 0x134 +#define SIERRA_DEQ_PRECUR_PREG 0x138 +#define SIERRA_DEQ_POSTCUR_PREG 0x140 +#define SIERRA_DEQ_POSTCUR_DECR_PREG 0x142 #define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 #define SIERRA_DEQ_TAU_CTRL2_PREG 0x151 +#define SIERRA_DEQ_TAU_CTRL3_PREG 0x152 +#define SIERRA_DEQ_OPENEYE_CTRL_PREG 0x158 #define SIERRA_DEQ_PICTRL_PREG 0x161 #define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170 #define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171 #define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174 #define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C +#define SIERRA_CPI_RESBIAS_BIN_PREG 0x17E +#define SIERRA_CPI_TRIM_PREG 0x17F #define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183 +#define SIERRA_EPI_CTRL_PREG 0x187 #define SIERRA_LFPSDET_SUPPORT_PREG 0x188 #define SIERRA_LFPSFILT_NS_PREG 0x18A #define SIERRA_LFPSFILT_RD_PREG 0x18B @@ -136,27 +176,144 @@ #define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F #define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 -#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \ - (0xc000 << (block_offset)) +/* PHY PCS common registers */ +#define SIERRA_PHY_PCS_COMMON_OFFSET(block_offset) \ + (0xc000 << (block_offset)) +#define SIERRA_PHY_PIPE_CMN_CTRL1 0x0 #define SIERRA_PHY_PLL_CFG 0xe +/* PHY PCS lane registers */ +#define SIERRA_PHY_PCS_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0xD000 << (block_offset)) + \ + (((ln) << 8) << (reg_offset))) + +#define SIERRA_PHY_ISO_LINK_CTRL 0xB + +/* PHY PMA common registers */ +#define SIERRA_PHY_PMA_COMMON_OFFSET(block_offset) \ + (0xE000 << (block_offset)) +#define SIERRA_PHY_PMA_CMN_CTRL 0x000 + +/* PHY PMA lane registers */ +#define SIERRA_PHY_PMA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0xF000 << (block_offset)) + \ + (((ln) << 8) << (reg_offset))) + +#define SIERRA_PHY_PMA_XCVR_CTRL 0x000 + #define SIERRA_MACRO_ID 0x00007364 #define SIERRA_MAX_LANES 16 #define PLL_LOCK_TIME 100000 +#define CDNS_SIERRA_OUTPUT_CLOCKS 3 +#define CDNS_SIERRA_INPUT_CLOCKS 5 +enum cdns_sierra_clock_input { + PHY_CLK, + CMN_REFCLK_DIG_DIV, + CMN_REFCLK1_DIG_DIV, + PLL0_REFCLK, + PLL1_REFCLK, +}; + +#define SIERRA_NUM_CMN_PLLC 2 +#define SIERRA_NUM_CMN_PLLC_PARENTS 2 + static const struct reg_field macro_id_type = REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); static const struct reg_field phy_pll_cfg_1 = REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1); +static const struct reg_field pma_cmn_ready = + REG_FIELD(SIERRA_PHY_PMA_CMN_CTRL, 0, 0); static const struct reg_field pllctrl_lock = REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0); +static const struct reg_field phy_iso_link_ctrl_1 = + REG_FIELD(SIERRA_PHY_ISO_LINK_CTRL, 1, 1); +static const struct reg_field cmn_plllc_clk1outdiv_preg = + REG_FIELD(SIERRA_CMN_PLLLC_CLK1_PREG, 0, 6); +static const struct reg_field cmn_plllc_clk1_en_preg = + REG_FIELD(SIERRA_CMN_PLLLC_CLK1_PREG, 12, 12); + +static const char * const clk_names[] = { + [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc", + [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1", + [CDNS_SIERRA_DERIVED_REFCLK] = "refclk_der", +}; + +enum cdns_sierra_cmn_plllc { + CMN_PLLLC, + CMN_PLLLC1, +}; + +struct cdns_sierra_pll_mux_reg_fields { + struct reg_field pfdclk_sel_preg; + struct reg_field plllc1en_field; + struct reg_field termen_field; +}; + +static const struct cdns_sierra_pll_mux_reg_fields cmn_plllc_pfdclk1_sel_preg[] = { + [CMN_PLLLC] = { + .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1), + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8, 8), + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0, 0), + }, + [CMN_PLLLC1] = { + .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1), + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8, 8), + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0), + }, +}; + +struct cdns_sierra_pll_mux { + struct clk_hw hw; + struct regmap_field *pfdclk_sel_preg; + struct regmap_field *plllc1en_field; + struct regmap_field *termen_field; + struct clk_init_data clk_data; +}; + +#define to_cdns_sierra_pll_mux(_hw) \ + container_of(_hw, struct cdns_sierra_pll_mux, hw) + +static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = { + [CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK }, + [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK }, +}; + +static u32 cdns_sierra_pll_mux_table[][SIERRA_NUM_CMN_PLLC_PARENTS] = { + [CMN_PLLLC] = { 0, 1 }, + [CMN_PLLLC1] = { 1, 0 }, +}; + +struct cdns_sierra_derived_refclk { + struct clk_hw hw; + struct regmap_field *cmn_plllc_clk1outdiv_preg; + struct regmap_field *cmn_plllc_clk1_en_preg; + struct clk_init_data clk_data; +}; + +#define to_cdns_sierra_derived_refclk(_hw) \ + container_of(_hw, struct cdns_sierra_derived_refclk, hw) + +enum cdns_sierra_phy_type { + TYPE_NONE, + TYPE_PCIE, + TYPE_USB, + TYPE_QSGMII +}; + +enum cdns_sierra_ssc_mode { + NO_SSC, + EXTERNAL_SSC, + INTERNAL_SSC +}; struct cdns_sierra_inst { struct phy *phy; - u32 phy_type; + enum cdns_sierra_phy_type phy_type; u32 num_lanes; u32 mlane; struct reset_control *lnk_rst; + enum cdns_sierra_ssc_mode ssc_mode; }; struct cdns_reg_pairs { @@ -164,18 +321,23 @@ struct cdns_reg_pairs { u32 off; }; +struct cdns_sierra_vals { + const struct cdns_reg_pairs *reg_pairs; + u32 num_regs; +}; + struct cdns_sierra_data { - u32 id_value; - u8 block_offset_shift; - u8 reg_offset_shift; - u32 pcie_cmn_regs; - u32 pcie_ln_regs; - u32 usb_cmn_regs; - u32 usb_ln_regs; - struct cdns_reg_pairs *pcie_cmn_vals; - struct cdns_reg_pairs *pcie_ln_vals; - struct cdns_reg_pairs *usb_cmn_vals; - struct cdns_reg_pairs *usb_ln_vals; + u32 id_value; + u8 block_offset_shift; + u8 reg_offset_shift; + struct cdns_sierra_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_sierra_vals *phy_pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_sierra_vals *pma_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_sierra_vals *pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; }; struct cdns_regmap_cdb_context { @@ -186,23 +348,31 @@ struct cdns_regmap_cdb_context { struct cdns_sierra_phy { struct device *dev; - struct regmap *regmap; - struct cdns_sierra_data *init_data; + const struct cdns_sierra_data *init_data; struct cdns_sierra_inst phys[SIERRA_MAX_LANES]; struct reset_control *phy_rst; struct reset_control *apb_rst; struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES]; - struct regmap *regmap_phy_config_ctrl; + struct regmap *regmap_phy_pcs_common_cdb; + struct regmap *regmap_phy_pcs_lane_cdb[SIERRA_MAX_LANES]; + struct regmap *regmap_phy_pma_common_cdb; + struct regmap *regmap_phy_pma_lane_cdb[SIERRA_MAX_LANES]; struct regmap *regmap_common_cdb; struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; + struct regmap_field *pma_cmn_ready; struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; - struct clk *clk; - struct clk *cmn_refclk_dig_div; - struct clk *cmn_refclk1_dig_div; + struct regmap_field *phy_iso_link_ctrl_1[SIERRA_MAX_LANES]; + struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC]; + struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC]; + struct regmap_field *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC]; + struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS]; int nsubnodes; u32 num_lanes; bool autoconf; + int already_configured; + struct clk_onecell_data clk_data; + struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS]; }; static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val) @@ -233,7 +403,7 @@ static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val) .reg_read = cdns_regmap_read, \ } -static struct regmap_config cdns_sierra_lane_cdb_config[] = { +static const struct regmap_config cdns_sierra_lane_cdb_config[] = { SIERRA_LANE_CDB_REGMAP_CONF("0"), SIERRA_LANE_CDB_REGMAP_CONF("1"), SIERRA_LANE_CDB_REGMAP_CONF("2"), @@ -252,7 +422,7 @@ static struct regmap_config cdns_sierra_lane_cdb_config[] = { SIERRA_LANE_CDB_REGMAP_CONF("15"), }; -static struct regmap_config cdns_sierra_common_cdb_config = { +static const struct regmap_config cdns_sierra_common_cdb_config = { .name = "sierra_common_cdb", .reg_stride = 1, .fast_io = true, @@ -260,51 +430,141 @@ static struct regmap_config cdns_sierra_common_cdb_config = { .reg_read = cdns_regmap_read, }; -static struct regmap_config cdns_sierra_phy_config_ctrl_config = { - .name = "sierra_phy_config_ctrl", +static const struct regmap_config cdns_sierra_phy_pcs_cmn_cdb_config = { + .name = "sierra_phy_pcs_cmn_cdb", .reg_stride = 1, .fast_io = true, .reg_write = cdns_regmap_write, .reg_read = cdns_regmap_read, }; +#define SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "sierra_phy_pcs_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static const struct regmap_config cdns_sierra_phy_pcs_lane_cdb_config[] = { + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("0"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("1"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("2"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("3"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("4"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("5"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("6"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("7"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("8"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("9"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("10"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("11"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("12"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("13"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("14"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("15"), +}; + +static const struct regmap_config cdns_sierra_phy_pma_cmn_cdb_config = { + .name = "sierra_phy_pma_cmn_cdb", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + +#define SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "sierra_phy_pma_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static const struct regmap_config cdns_sierra_phy_pma_lane_cdb_config[] = { + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("0"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("1"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("2"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("3"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("4"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("5"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("6"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("7"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("8"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("9"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("10"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("11"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("12"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("13"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("14"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("15"), +}; + static int cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); + const struct cdns_sierra_data *init_data = phy->init_data; + struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; + enum cdns_sierra_phy_type phy_type = ins->phy_type; + enum cdns_sierra_ssc_mode ssc = ins->ssc_mode; + struct cdns_sierra_vals *phy_pma_ln_vals; + const struct cdns_reg_pairs *reg_pairs; + struct cdns_sierra_vals *pcs_cmn_vals; struct regmap *regmap; + u32 num_regs; int i, j; - struct cdns_reg_pairs *cmn_vals, *ln_vals; - u32 num_cmn_regs, num_ln_regs; /* Initialise the PHY registers, unless auto configured */ - if (phy->autoconf) + if (phy->autoconf || phy->already_configured || phy->nsubnodes > 1) return 0; - clk_set_rate(phy->cmn_refclk_dig_div, 25000000); - clk_set_rate(phy->cmn_refclk1_dig_div, 25000000); - if (ins->phy_type == PHY_TYPE_PCIE) { - num_cmn_regs = phy->init_data->pcie_cmn_regs; - num_ln_regs = phy->init_data->pcie_ln_regs; - cmn_vals = phy->init_data->pcie_cmn_vals; - ln_vals = phy->init_data->pcie_ln_vals; - } else if (ins->phy_type == PHY_TYPE_USB3) { - num_cmn_regs = phy->init_data->usb_cmn_regs; - num_ln_regs = phy->init_data->usb_ln_regs; - cmn_vals = phy->init_data->usb_cmn_vals; - ln_vals = phy->init_data->usb_ln_vals; - } else { - return -EINVAL; + clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000); + clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000); + + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = phy->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } + + /* PHY PMA lane registers configurations */ + phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_type][TYPE_NONE][ssc]; + if (phy_pma_ln_vals) { + reg_pairs = phy_pma_ln_vals->reg_pairs; + num_regs = phy_pma_ln_vals->num_regs; + for (i = 0; i < ins->num_lanes; i++) { + regmap = phy->regmap_phy_pma_lane_cdb[i + ins->mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); + } } - regmap = phy->regmap_common_cdb; - for (j = 0; j < num_cmn_regs ; j++) - regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val); + /* PMA common registers configurations */ + pma_cmn_vals = init_data->pma_cmn_vals[phy_type][TYPE_NONE][ssc]; + if (pma_cmn_vals) { + reg_pairs = pma_cmn_vals->reg_pairs; + num_regs = pma_cmn_vals->num_regs; + regmap = phy->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } - for (i = 0; i < ins->num_lanes; i++) { - for (j = 0; j < num_ln_regs ; j++) { + /* PMA lane registers configurations */ + pma_ln_vals = init_data->pma_ln_vals[phy_type][TYPE_NONE][ssc]; + if (pma_ln_vals) { + reg_pairs = pma_ln_vals->reg_pairs; + num_regs = pma_ln_vals->num_regs; + for (i = 0; i < ins->num_lanes; i++) { regmap = phy->regmap_lane_cdb[i + ins->mlane]; - regmap_write(regmap, ln_vals[j].off, ln_vals[j].val); + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); } } @@ -319,6 +579,15 @@ static int cdns_sierra_phy_on(struct phy *gphy) u32 val; int ret; + if (sp->nsubnodes == 1) { + /* Take the PHY out of reset */ + ret = reset_control_deassert(sp->phy_rst); + if (ret) { + dev_err(dev, "Failed to take the PHY out of reset\n"); + return ret; + } + } + /* Take the PHY lane group out of reset */ ret = reset_control_deassert(ins->lnk_rst); if (ret) { @@ -326,6 +595,26 @@ static int cdns_sierra_phy_on(struct phy *gphy) return ret; } + if (ins->phy_type == TYPE_PCIE || ins->phy_type == TYPE_USB) { + ret = regmap_field_read_poll_timeout(sp->phy_iso_link_ctrl_1[ins->mlane], + val, !val, 1000, PLL_LOCK_TIME); + if (ret) { + dev_err(dev, "Timeout waiting for PHY status ready\n"); + return ret; + } + } + + /* + * Wait for cmn_ready assertion + * PHY_PMA_CMN_CTRL[0] == 1 + */ + ret = regmap_field_read_poll_timeout(sp->pma_cmn_ready, val, val, + 1000, PLL_LOCK_TIME); + if (ret) { + dev_err(dev, "Timeout waiting for CMN ready\n"); + return ret; + } + ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane], val, val, 1000, PLL_LOCK_TIME); if (ret < 0) @@ -358,23 +647,308 @@ static const struct phy_ops ops = { .owner = THIS_MODULE, }; +static int cdns_sierra_noop_phy_on(struct phy *gphy) +{ + usleep_range(5000, 10000); + + return 0; +} + +static const struct phy_ops noop_ops = { + .power_on = cdns_sierra_noop_phy_on, + .owner = THIS_MODULE, +}; + +static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw) +{ + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw); + struct regmap_field *plllc1en_field = mux->plllc1en_field; + struct regmap_field *termen_field = mux->termen_field; + struct regmap_field *field = mux->pfdclk_sel_preg; + unsigned int val; + int index; + + regmap_field_read(field, &val); + + if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) { + index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC1], 0, val); + if (index == 1) { + regmap_field_write(plllc1en_field, 1); + regmap_field_write(termen_field, 1); + } + } else { + index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC], 0, val); + } + + return index; +} + +static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw); + struct regmap_field *plllc1en_field = mux->plllc1en_field; + struct regmap_field *termen_field = mux->termen_field; + struct regmap_field *field = mux->pfdclk_sel_preg; + int val, ret; + + ret = regmap_field_write(plllc1en_field, 0); + ret |= regmap_field_write(termen_field, 0); + if (index == 1) { + ret |= regmap_field_write(plllc1en_field, 1); + ret |= regmap_field_write(termen_field, 1); + } + + if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) + val = cdns_sierra_pll_mux_table[CMN_PLLLC1][index]; + else + val = cdns_sierra_pll_mux_table[CMN_PLLLC][index]; + + ret |= regmap_field_write(field, val); + + return ret; +} + +static const struct clk_ops cdns_sierra_pll_mux_ops = { + .set_parent = cdns_sierra_pll_mux_set_parent, + .get_parent = cdns_sierra_pll_mux_get_parent, +}; + +static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp, + struct regmap_field *pfdclk1_sel_field, + struct regmap_field *plllc1en_field, + struct regmap_field *termen_field, + int clk_index) +{ + struct cdns_sierra_pll_mux *mux; + struct device *dev = sp->dev; + struct clk_init_data *init; + const char **parent_names; + unsigned int num_parents; + char clk_name[100]; + struct clk *clk; + int i; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + num_parents = SIERRA_NUM_CMN_PLLC_PARENTS; + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + clk = sp->input_clks[pll_mux_parent_index[clk_index][i]]; + if (IS_ERR_OR_NULL(clk)) { + dev_err(dev, "No parent clock for PLL mux clocks\n"); + return IS_ERR(clk) ? PTR_ERR(clk) : -ENOENT; + } + parent_names[i] = __clk_get_name(clk); + } + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[clk_index]); + + init = &mux->clk_data; + + init->ops = &cdns_sierra_pll_mux_ops; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + init->name = clk_name; + + mux->pfdclk_sel_preg = pfdclk1_sel_field; + mux->plllc1en_field = plllc1en_field; + mux->termen_field = termen_field; + mux->hw.init = init; + + clk = devm_clk_register(dev, &mux->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + sp->output_clks[clk_index] = clk; + + return 0; +} + +static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp) +{ + struct regmap_field *pfdclk1_sel_field; + struct regmap_field *plllc1en_field; + struct regmap_field *termen_field; + struct device *dev = sp->dev; + int ret = 0, i, clk_index; + + clk_index = CDNS_SIERRA_PLL_CMNLC; + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) { + pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i]; + plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i]; + termen_field = sp->cmn_refrcv_refclk_termen_preg[i]; + + ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field, plllc1en_field, + termen_field, clk_index); + if (ret) { + dev_err(dev, "Fail to register cmn plllc mux\n"); + return ret; + } + } + + return 0; +} + +static int cdns_sierra_derived_refclk_enable(struct clk_hw *hw) +{ + struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw); + + regmap_field_write(derived_refclk->cmn_plllc_clk1_en_preg, 0x1); + + /* Programming to get 100Mhz clock output in ref_der_clk_out 5GHz VCO/50 = 100MHz */ + regmap_field_write(derived_refclk->cmn_plllc_clk1outdiv_preg, 0x2E); + + return 0; +} + +static void cdns_sierra_derived_refclk_disable(struct clk_hw *hw) +{ + struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw); + + regmap_field_write(derived_refclk->cmn_plllc_clk1_en_preg, 0); +} + +static int cdns_sierra_derived_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw); + int val; + + regmap_field_read(derived_refclk->cmn_plllc_clk1_en_preg, &val); + + return !!val; +} + +static const struct clk_ops cdns_sierra_derived_refclk_ops = { + .enable = cdns_sierra_derived_refclk_enable, + .disable = cdns_sierra_derived_refclk_disable, + .is_enabled = cdns_sierra_derived_refclk_is_enabled, +}; + +static int cdns_sierra_derived_refclk_register(struct cdns_sierra_phy *sp) +{ + struct cdns_sierra_derived_refclk *derived_refclk; + struct device *dev = sp->dev; + struct regmap_field *field; + struct clk_init_data *init; + struct regmap *regmap; + char clk_name[100]; + struct clk *clk; + + derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); + if (!derived_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_SIERRA_DERIVED_REFCLK]); + + init = &derived_refclk->clk_data; + + init->ops = &cdns_sierra_derived_refclk_ops; + init->flags = 0; + init->name = clk_name; + + regmap = sp->regmap_common_cdb; + + field = devm_regmap_field_alloc(dev, regmap, cmn_plllc_clk1outdiv_preg); + if (IS_ERR(field)) { + dev_err(dev, "cmn_plllc_clk1outdiv_preg reg field init failed\n"); + return PTR_ERR(field); + } + derived_refclk->cmn_plllc_clk1outdiv_preg = field; + + field = devm_regmap_field_alloc(dev, regmap, cmn_plllc_clk1_en_preg); + if (IS_ERR(field)) { + dev_err(dev, "cmn_plllc_clk1_en_preg reg field init failed\n"); + return PTR_ERR(field); + } + derived_refclk->cmn_plllc_clk1_en_preg = field; + + derived_refclk->hw.init = init; + + clk = devm_clk_register(dev, &derived_refclk->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + sp->output_clks[CDNS_SIERRA_DERIVED_REFCLK] = clk; + + return 0; +} + +static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp) +{ + struct device *dev = sp->dev; + struct device_node *node = dev->of_node; + + of_clk_del_provider(node); +} + +static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp) +{ + struct device *dev = sp->dev; + struct device_node *node = dev->of_node; + int ret; + + ret = cdns_sierra_phy_register_pll_mux(sp); + if (ret) { + dev_err(dev, "Failed to pll mux clocks\n"); + return ret; + } + + ret = cdns_sierra_derived_refclk_register(sp); + if (ret) { + dev_err(dev, "Failed to register derived refclk\n"); + return ret; + } + + sp->clk_data.clks = sp->output_clks; + sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS; + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp->clk_data); + if (ret) + dev_err(dev, "Failed to add clock provider: %s\n", node->name); + + return ret; +} + static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, struct device_node *child) { + u32 phy_type; + if (of_property_read_u32(child, "reg", &inst->mlane)) return -EINVAL; if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes)) return -EINVAL; - if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type)) + if (of_property_read_u32(child, "cdns,phy-type", &phy_type)) return -EINVAL; + switch (phy_type) { + case PHY_TYPE_PCIE: + inst->phy_type = TYPE_PCIE; + break; + case PHY_TYPE_USB3: + inst->phy_type = TYPE_USB; + break; + case PHY_TYPE_QSGMII: + inst->phy_type = TYPE_QSGMII; + break; + default: + return -EINVAL; + } + + inst->ssc_mode = EXTERNAL_SSC; + of_property_read_u32(child, "cdns,ssc-mode", &inst->ssc_mode); + return 0; } -static const struct of_device_id cdns_sierra_id_table[]; - static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, u32 block_offset, u8 reg_offset_shift, const struct regmap_config *config) @@ -396,6 +970,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) { struct device *dev = sp->dev; struct regmap_field *field; + struct reg_field reg_field; struct regmap *regmap; int i; @@ -407,7 +982,33 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) } sp->macro_id_type = field; - regmap = sp->regmap_phy_config_ctrl; + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) { + reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg; + field = devm_regmap_field_alloc(dev, regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i); + return PTR_ERR(field); + } + sp->cmn_plllc_pfdclk1_sel_preg[i] = field; + + reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field; + field = devm_regmap_field_alloc(dev, regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n", i); + return PTR_ERR(field); + } + sp->cmn_refrcv_refclk_plllc1en_preg[i] = field; + + reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field; + field = devm_regmap_field_alloc(dev, regmap, reg_field); + if (IS_ERR(field)) { + dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n", i); + return PTR_ERR(field); + } + sp->cmn_refrcv_refclk_termen_preg[i] = field; + } + + regmap = sp->regmap_phy_pcs_common_cdb; field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1); if (IS_ERR(field)) { dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n"); @@ -415,6 +1016,14 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) } sp->phy_pll_cfg_1 = field; + regmap = sp->regmap_phy_pma_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, pma_cmn_ready); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PMA_CMN_CTRL reg field init failed\n"); + return PTR_ERR(field); + } + sp->pma_cmn_ready = field; + for (i = 0; i < SIERRA_MAX_LANES; i++) { regmap = sp->regmap_lane_cdb[i]; field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock); @@ -422,7 +1031,17 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) dev_err(dev, "P%d_ENABLE reg field init failed\n", i); return PTR_ERR(field); } - sp->pllctrl_lock[i] = field; + sp->pllctrl_lock[i] = field; + } + + for (i = 0; i < SIERRA_MAX_LANES; i++) { + regmap = sp->regmap_phy_pcs_lane_cdb[i]; + field = devm_regmap_field_alloc(dev, regmap, phy_iso_link_ctrl_1); + if (IS_ERR(field)) { + dev_err(dev, "PHY_ISO_LINK_CTRL reg field init for lane %d failed\n", i); + return PTR_ERR(field); + } + sp->phy_iso_link_ctrl_1[i] = field; } return 0; @@ -459,14 +1078,275 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, } sp->regmap_common_cdb = regmap; - block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift); + block_offset = SIERRA_PHY_PCS_COMMON_OFFSET(block_offset_shift); regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, - &cdns_sierra_phy_config_ctrl_config); + &cdns_sierra_phy_pcs_cmn_cdb_config); if (IS_ERR(regmap)) { - dev_err(dev, "Failed to init PHY config and control regmap\n"); + dev_err(dev, "Failed to init PHY PCS common CDB regmap\n"); return PTR_ERR(regmap); } - sp->regmap_phy_config_ctrl = regmap; + sp->regmap_phy_pcs_common_cdb = regmap; + + for (i = 0; i < SIERRA_MAX_LANES; i++) { + block_offset = SIERRA_PHY_PCS_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, + reg_offset_shift, + &cdns_sierra_phy_pcs_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PCS lane CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_pcs_lane_cdb[i] = regmap; + } + + block_offset = SIERRA_PHY_PMA_COMMON_OFFSET(block_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, + &cdns_sierra_phy_pma_cmn_cdb_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PMA common CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_pma_common_cdb = regmap; + + for (i = 0; i < SIERRA_MAX_LANES; i++) { + block_offset = SIERRA_PHY_PMA_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, + reg_offset_shift, + &cdns_sierra_phy_pma_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PMA lane CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_pma_lane_cdb[i] = regmap; + } + + return 0; +} + +static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp, + struct device *dev) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[CMN_REFCLK_DIG_DIV] = clk; + + clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk; + + clk = devm_clk_get_optional(dev, "pll0_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "pll0_refclk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[PLL0_REFCLK] = clk; + + clk = devm_clk_get_optional(dev, "pll1_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "pll1_refclk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->input_clks[PLL1_REFCLK] = clk; + + return 0; +} + +static int cdns_sierra_phy_clk(struct cdns_sierra_phy *sp) +{ + struct device *dev = sp->dev; + struct clk *clk; + int ret; + + clk = devm_clk_get_optional(dev, "phy_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get clock phy_clk\n"); + return PTR_ERR(clk); + } + sp->input_clks[PHY_CLK] = clk; + + ret = clk_prepare_enable(sp->input_clks[PHY_CLK]); + if (ret) + return ret; + + return 0; +} + +static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp) +{ + int ret; + + ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); + if (ret) + return ret; + + ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]); + if (ret) + goto err_pll_cmnlc1; + + return 0; + +err_pll_cmnlc1: + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); + + return ret; +} + +static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp) +{ + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]); + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]); + if (!sp->already_configured) + clk_disable_unprepare(sp->input_clks[PHY_CLK]); +} + +static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp, + struct device *dev) +{ + struct reset_control *rst; + + rst = devm_reset_control_get_exclusive(dev, "sierra_reset"); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(rst); + } + sp->phy_rst = rst; + + rst = devm_reset_control_get_optional_exclusive(dev, "sierra_apb"); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get apb reset\n"); + return PTR_ERR(rst); + } + sp->apb_rst = rst; + + return 0; +} + +static int cdns_sierra_phy_configure_multilink(struct cdns_sierra_phy *sp) +{ + const struct cdns_sierra_data *init_data = sp->init_data; + struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; + enum cdns_sierra_phy_type phy_t1, phy_t2; + struct cdns_sierra_vals *phy_pma_ln_vals; + const struct cdns_reg_pairs *reg_pairs; + struct cdns_sierra_vals *pcs_cmn_vals; + int i, j, node, mlane, num_lanes, ret; + enum cdns_sierra_ssc_mode ssc; + struct regmap *regmap; + u32 num_regs; + + /* Maximum 2 links (subnodes) are supported */ + if (sp->nsubnodes != 2) + return -EINVAL; + + clk_set_rate(sp->input_clks[CMN_REFCLK_DIG_DIV], 25000000); + clk_set_rate(sp->input_clks[CMN_REFCLK1_DIG_DIV], 25000000); + + /* PHY configured to use both PLL LC and LC1 */ + regmap_field_write(sp->phy_pll_cfg_1, 0x1); + + phy_t1 = sp->phys[0].phy_type; + phy_t2 = sp->phys[1].phy_type; + + /* + * PHY configuration for multi-link operation is done in two steps. + * e.g. Consider a case for a 4 lane PHY with PCIe using 2 lanes and QSGMII other 2 lanes. + * Sierra PHY has 2 PLLs, viz. PLLLC and PLLLC1. So in this case, PLLLC is used for PCIe + * and PLLLC1 is used for QSGMII. PHY is configured in two steps as described below. + * + * [1] For first step, phy_t1 = TYPE_PCIE and phy_t2 = TYPE_QSGMII + * So the register values are selected as [TYPE_PCIE][TYPE_QSGMII][ssc]. + * This will configure PHY registers associated for PCIe (i.e. first protocol) + * involving PLLLC registers and registers for first 2 lanes of PHY. + * [2] In second step, the variables phy_t1 and phy_t2 are swapped. So now, + * phy_t1 = TYPE_QSGMII and phy_t2 = TYPE_PCIE. And the register values are selected as + * [TYPE_QSGMII][TYPE_PCIE][ssc]. + * This will configure PHY registers associated for QSGMII (i.e. second protocol) + * involving PLLLC1 registers and registers for other 2 lanes of PHY. + * + * This completes the PHY configuration for multilink operation. This approach enables + * dividing the large number of PHY register configurations into protocol specific + * smaller groups. + */ + for (node = 0; node < sp->nsubnodes; node++) { + if (node == 1) { + /* + * If first link with phy_t1 is configured, then configure the PHY for + * second link with phy_t2. Get the array values as [phy_t2][phy_t1][ssc]. + */ + swap(phy_t1, phy_t2); + } + + mlane = sp->phys[node].mlane; + ssc = sp->phys[node].ssc_mode; + num_lanes = sp->phys[node].num_lanes; + + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = sp->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } + + /* PHY PMA lane registers configurations */ + phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_t1][phy_t2][ssc]; + if (phy_pma_ln_vals) { + reg_pairs = phy_pma_ln_vals->reg_pairs; + num_regs = phy_pma_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = sp->regmap_phy_pma_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); + } + } + + /* PMA common registers configurations */ + pma_cmn_vals = init_data->pma_cmn_vals[phy_t1][phy_t2][ssc]; + if (pma_cmn_vals) { + reg_pairs = pma_cmn_vals->reg_pairs; + num_regs = pma_cmn_vals->num_regs; + regmap = sp->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } + + /* PMA lane registers configurations */ + pma_ln_vals = init_data->pma_ln_vals[phy_t1][phy_t2][ssc]; + if (pma_ln_vals) { + reg_pairs = pma_ln_vals->reg_pairs; + num_regs = pma_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = sp->regmap_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); + } + } + + if (phy_t1 == TYPE_QSGMII) + reset_control_deassert(sp->phys[node].lnk_rst); + } + + /* Take the PHY out of reset */ + ret = reset_control_deassert(sp->phy_rst); + if (ret) + return ret; return 0; } @@ -476,25 +1356,20 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) struct cdns_sierra_phy *sp; struct phy_provider *phy_provider; struct device *dev = &pdev->dev; - const struct of_device_id *match; - struct cdns_sierra_data *data; + const struct cdns_sierra_data *data; unsigned int id_value; - struct resource *res; - int i, ret, node = 0; + int ret, node = 0; void __iomem *base; - struct clk *clk; struct device_node *dn = dev->of_node, *child; if (of_get_child_count(dn) == 0) return -ENODEV; /* Get init data for this PHY */ - match = of_match_device(cdns_sierra_id_table, dev); - if (!match) + data = of_device_get_match_data(dev); + if (!data) return -EINVAL; - data = (struct cdns_sierra_data *)match->data; - sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); if (!sp) return -ENOMEM; @@ -502,8 +1377,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) sp->dev = dev; sp->init_data = data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { dev_err(dev, "missing \"reg\"\n"); return PTR_ERR(base); @@ -520,52 +1394,38 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sp); - sp->clk = devm_clk_get_optional(dev, "phy_clk"); - if (IS_ERR(sp->clk)) { - dev_err(dev, "failed to get clock phy_clk\n"); - return PTR_ERR(sp->clk); - } - - sp->phy_rst = devm_reset_control_get(dev, "sierra_reset"); - if (IS_ERR(sp->phy_rst)) { - dev_err(dev, "failed to get reset\n"); - return PTR_ERR(sp->phy_rst); - } - - sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb"); - if (IS_ERR(sp->apb_rst)) { - dev_err(dev, "failed to get apb reset\n"); - return PTR_ERR(sp->apb_rst); - } - - clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); - if (IS_ERR(clk)) { - dev_err(dev, "cmn_refclk_dig_div clock not found\n"); - ret = PTR_ERR(clk); + ret = cdns_sierra_phy_get_clocks(sp, dev); + if (ret) return ret; - } - sp->cmn_refclk_dig_div = clk; - clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); - if (IS_ERR(clk)) { - dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); - ret = PTR_ERR(clk); + ret = cdns_sierra_clk_register(sp); + if (ret) return ret; - } - sp->cmn_refclk1_dig_div = clk; - ret = clk_prepare_enable(sp->clk); + ret = cdns_sierra_phy_enable_clocks(sp); if (ret) - return ret; + goto unregister_clk; + + regmap_field_read(sp->pma_cmn_ready, &sp->already_configured); + + if (!sp->already_configured) { + ret = cdns_sierra_phy_clk(sp); + if (ret) + goto clk_disable; - /* Enable APB */ - reset_control_deassert(sp->apb_rst); + ret = cdns_sierra_phy_get_resets(sp, dev); + if (ret) + goto clk_disable; + + /* Enable APB */ + reset_control_deassert(sp->apb_rst); + } /* Check that PHY is present */ regmap_field_read(sp->macro_id_type, &id_value); if (sp->init_data->id_value != id_value) { ret = -EINVAL; - goto clk_disable; + goto ctrl_assert; } sp->autoconf = of_property_read_bool(dn, "cdns,autoconf"); @@ -573,6 +1433,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) for_each_available_child_of_node(dn, child) { struct phy *gphy; + if (!(of_node_name_eq(child, "phy") || + of_node_name_eq(child, "link"))) + continue; + sp->phys[node].lnk_rst = of_reset_control_array_get_exclusive(child); @@ -580,7 +1444,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) dev_err(dev, "failed to get reset %s\n", child->full_name); ret = PTR_ERR(sp->phys[node].lnk_rst); - goto put_child2; + of_node_put(child); + goto put_control; } if (!sp->autoconf) { @@ -588,17 +1453,23 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "missing property in node %s\n", child->name); - goto put_child; + of_node_put(child); + reset_control_put(sp->phys[node].lnk_rst); + goto put_control; } } sp->num_lanes += sp->phys[node].num_lanes; - gphy = devm_phy_create(dev, child, &ops); - + if (!sp->already_configured) + gphy = devm_phy_create(dev, child, &ops); + else + gphy = devm_phy_create(dev, child, &noop_ops); if (IS_ERR(gphy)) { ret = PTR_ERR(gphy); - goto put_child; + of_node_put(child); + reset_control_put(sp->phys[node].lnk_rst); + goto put_control; } sp->phys[node].phy = gphy; phy_set_drvdata(gphy, &sp->phys[node]); @@ -608,28 +1479,37 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) sp->nsubnodes = node; if (sp->num_lanes > SIERRA_MAX_LANES) { + ret = -EINVAL; dev_err(dev, "Invalid lane configuration\n"); - goto put_child2; + goto put_control; } /* If more than one subnode, configure the PHY as multilink */ - if (!sp->autoconf && sp->nsubnodes > 1) - regmap_field_write(sp->phy_pll_cfg_1, 0x1); + if (!sp->already_configured && !sp->autoconf && sp->nsubnodes > 1) { + ret = cdns_sierra_phy_configure_multilink(sp); + if (ret) + goto put_control; + } pm_runtime_enable(dev); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - reset_control_deassert(sp->phy_rst); - return PTR_ERR_OR_ZERO(phy_provider); - -put_child: - node++; -put_child2: - for (i = 0; i < node; i++) - reset_control_put(sp->phys[i].lnk_rst); - of_node_put(child); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + goto put_control; + } + + return 0; + +put_control: + while (--node >= 0) + reset_control_put(sp->phys[node].lnk_rst); +ctrl_assert: + if (!sp->already_configured) + reset_control_assert(sp->apb_rst); clk_disable: - clk_disable_unprepare(sp->clk); - reset_control_assert(sp->apb_rst); + cdns_sierra_phy_disable_clocks(sp); +unregister_clk: + cdns_sierra_clk_unregister(sp); return ret; } @@ -642,6 +1522,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev) reset_control_assert(phy->apb_rst); pm_runtime_disable(&pdev->dev); + cdns_sierra_phy_disable_clocks(phy); /* * The device level resets will be put automatically. * Need to put the subnode resets here though. @@ -650,11 +1531,643 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev) reset_control_assert(phy->phys[i].lnk_rst); reset_control_put(phy->phys[i].lnk_rst); } + + cdns_sierra_clk_unregister(phy); + return 0; } +/* QSGMII PHY PMA lane configuration */ +static struct cdns_reg_pairs qsgmii_phy_pma_ln_regs[] = { + {0x9010, SIERRA_PHY_PMA_XCVR_CTRL} +}; + +static struct cdns_sierra_vals qsgmii_phy_pma_ln_vals = { + .reg_pairs = qsgmii_phy_pma_ln_regs, + .num_regs = ARRAY_SIZE(qsgmii_phy_pma_ln_regs), +}; + +/* QSGMII refclk 100MHz, 20b, opt1, No BW cal, no ssc, PLL LC1 */ +static const struct cdns_reg_pairs qsgmii_100_no_ssc_plllc1_cmn_regs[] = { + {0x2085, SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC1_SS_TIME_STEPSIZE_MODE_PREG} +}; + +static const struct cdns_reg_pairs qsgmii_100_no_ssc_plllc1_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x0252, SIERRA_DET_STANDEC_E_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x0FFE, SIERRA_PSC_RX_A0_PREG}, + {0x0011, SIERRA_PLLCTRL_SUBRATE_PREG}, + {0x0001, SIERRA_PLLCTRL_GEN_A_PREG}, + {0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG}, + {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, + {0x0089, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x3C3C, SIERRA_CREQ_CCLKDET_MODE01_PREG}, + {0x3222, SIERRA_CREQ_FSMCLK_SEL_PREG}, + {0x0000, SIERRA_CREQ_EQ_CTRL_PREG}, + {0x8422, SIERRA_CTLELUT_CTRL_PREG}, + {0x4111, SIERRA_DFE_ECMP_RATESEL_PREG}, + {0x4111, SIERRA_DFE_SMP_RATESEL_PREG}, + {0x0002, SIERRA_DEQ_PHALIGN_CTRL}, + {0x9595, SIERRA_DEQ_VGATUNE_CTRL_PREG}, + {0x0186, SIERRA_DEQ_GLUT0}, + {0x0186, SIERRA_DEQ_GLUT1}, + {0x0186, SIERRA_DEQ_GLUT2}, + {0x0186, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x0861, SIERRA_DEQ_ALUT0}, + {0x07E0, SIERRA_DEQ_ALUT1}, + {0x079E, SIERRA_DEQ_ALUT2}, + {0x071D, SIERRA_DEQ_ALUT3}, + {0x03F5, SIERRA_DEQ_DFETAP_CTRL_PREG}, + {0x0C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG}, + {0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C04, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0033, SIERRA_DEQ_PICTRL_PREG}, + {0x0660, SIERRA_CPICAL_TMRVAL_MODE0_PREG}, + {0x00D5, SIERRA_CPI_OUTBUF_RATESEL_PREG}, + {0x0B6D, SIERRA_CPI_RESBIAS_BIN_PREG}, + {0x0102, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x0002, SIERRA_RXBUFFER_RCDFECTRL_PREG} +}; + +static struct cdns_sierra_vals qsgmii_100_no_ssc_plllc1_cmn_vals = { + .reg_pairs = qsgmii_100_no_ssc_plllc1_cmn_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_plllc1_cmn_regs), +}; + +static struct cdns_sierra_vals qsgmii_100_no_ssc_plllc1_ln_vals = { + .reg_pairs = qsgmii_100_no_ssc_plllc1_ln_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_plllc1_ln_regs), +}; + +/* PCIE PHY PCS common configuration */ +static struct cdns_reg_pairs pcie_phy_pcs_cmn_regs[] = { + {0x0430, SIERRA_PHY_PIPE_CMN_CTRL1} +}; + +static struct cdns_sierra_vals pcie_phy_pcs_cmn_vals = { + .reg_pairs = pcie_phy_pcs_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_phy_pcs_cmn_regs), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_no_ssc, pcie_links_using_plllc, pipe_bw_3 */ +static const struct cdns_reg_pairs pcie_100_no_ssc_plllc_cmn_regs[] = { + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG} +}; + +/* + * refclk100MHz_32b_PCIe_ln_no_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ml_pcie_100_no_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_no_ssc_plllc_cmn_vals = { + .reg_pairs = pcie_100_no_ssc_plllc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_no_ssc_plllc_cmn_regs), +}; + +static struct cdns_sierra_vals ml_pcie_100_no_ssc_ln_vals = { + .reg_pairs = ml_pcie_100_no_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ml_pcie_100_no_ssc_ln_regs), +}; + +/* + * TI J721E: + * refclk100MHz_32b_PCIe_ln_no_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ti_ml_pcie_100_no_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}, + {0x0002, SIERRA_TX_RCVDET_OVRD_PREG} +}; + +static struct cdns_sierra_vals ti_ml_pcie_100_no_ssc_ln_vals = { + .reg_pairs = ti_ml_pcie_100_no_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ti_ml_pcie_100_no_ssc_ln_regs), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_int_ssc, pcie_links_using_plllc, pipe_bw_3 */ +static const struct cdns_reg_pairs pcie_100_int_ssc_plllc_cmn_regs[] = { + {0x000E, SIERRA_CMN_PLLLC_MODE_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x0581, SIERRA_CMN_PLLLC_DSMCORR_PREG}, + {0x7F80, SIERRA_CMN_PLLLC_SS_PREG}, + {0x0041, SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG}, + {0x0464, SIERRA_CMN_PLLLC_SSTWOPT_PREG}, + {0x0D0D, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}, + {0x0060, SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG} +}; + +/* + * refclk100MHz_32b_PCIe_ln_int_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ml_pcie_100_int_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_int_ssc_plllc_cmn_vals = { + .reg_pairs = pcie_100_int_ssc_plllc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_int_ssc_plllc_cmn_regs), +}; + +static struct cdns_sierra_vals ml_pcie_100_int_ssc_ln_vals = { + .reg_pairs = ml_pcie_100_int_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ml_pcie_100_int_ssc_ln_regs), +}; + +/* + * TI J721E: + * refclk100MHz_32b_PCIe_ln_int_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ti_ml_pcie_100_int_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}, + {0x0002, SIERRA_TX_RCVDET_OVRD_PREG} +}; + +static struct cdns_sierra_vals ti_ml_pcie_100_int_ssc_ln_vals = { + .reg_pairs = ti_ml_pcie_100_int_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ti_ml_pcie_100_int_ssc_ln_regs), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc, pcie_links_using_plllc, pipe_bw_3 */ +static const struct cdns_reg_pairs pcie_100_ext_ssc_plllc_cmn_regs[] = { + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG} +}; + +/* + * refclk100MHz_32b_PCIe_ln_ext_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ml_pcie_100_ext_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_ext_ssc_plllc_cmn_vals = { + .reg_pairs = pcie_100_ext_ssc_plllc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_ext_ssc_plllc_cmn_regs), +}; + +static struct cdns_sierra_vals ml_pcie_100_ext_ssc_ln_vals = { + .reg_pairs = ml_pcie_100_ext_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ml_pcie_100_ext_ssc_ln_regs), +}; + +/* + * TI J721E: + * refclk100MHz_32b_PCIe_ln_ext_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ti_ml_pcie_100_ext_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG}, + {0x0002, SIERRA_TX_RCVDET_OVRD_PREG} +}; + +static struct cdns_sierra_vals ti_ml_pcie_100_ext_ssc_ln_vals = { + .reg_pairs = ti_ml_pcie_100_ext_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ti_ml_pcie_100_ext_ssc_ln_regs), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_no_ssc */ +static const struct cdns_reg_pairs cdns_pcie_cmn_regs_no_ssc[] = { + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG} +}; + +/* refclk100MHz_32b_PCIe_ln_no_ssc */ +static const struct cdns_reg_pairs cdns_pcie_ln_regs_no_ssc[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_no_ssc_cmn_vals = { + .reg_pairs = cdns_pcie_cmn_regs_no_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_no_ssc), +}; + +static struct cdns_sierra_vals pcie_100_no_ssc_ln_vals = { + .reg_pairs = cdns_pcie_ln_regs_no_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_no_ssc), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_int_ssc */ +static const struct cdns_reg_pairs cdns_pcie_cmn_regs_int_ssc[] = { + {0x000E, SIERRA_CMN_PLLLC_MODE_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x0581, SIERRA_CMN_PLLLC_DSMCORR_PREG}, + {0x7F80, SIERRA_CMN_PLLLC_SS_PREG}, + {0x0041, SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG}, + {0x0464, SIERRA_CMN_PLLLC_SSTWOPT_PREG}, + {0x0D0D, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}, + {0x0060, SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG} +}; + +/* refclk100MHz_32b_PCIe_ln_int_ssc */ +static const struct cdns_reg_pairs cdns_pcie_ln_regs_int_ssc[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_int_ssc_cmn_vals = { + .reg_pairs = cdns_pcie_cmn_regs_int_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_int_ssc), +}; + +static struct cdns_sierra_vals pcie_100_int_ssc_ln_vals = { + .reg_pairs = cdns_pcie_ln_regs_int_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_int_ssc), +}; + /* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */ -static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { +static const struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, @@ -663,18 +2176,67 @@ static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { }; /* refclk100MHz_32b_PCIe_ln_ext_ssc */ -static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = { +static const struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, - {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG} + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_ext_ssc_cmn_vals = { + .reg_pairs = cdns_pcie_cmn_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), +}; + +static struct cdns_sierra_vals pcie_100_ext_ssc_ln_vals = { + .reg_pairs = cdns_pcie_ln_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), }; /* refclk100MHz_20b_USB_cmn_pll_ext_ssc */ -static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = { +static const struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = { {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, @@ -682,13 +2244,13 @@ static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = { }; /* refclk100MHz_20b_USB_ln_ext_ssc */ -static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { +static const struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0xFE0A, SIERRA_DET_STANDEC_A_PREG}, {0x000F, SIERRA_DET_STANDEC_B_PREG}, - {0x00A5, SIERRA_DET_STANDEC_C_PREG}, + {0x55A5, SIERRA_DET_STANDEC_C_PREG}, {0x69ad, SIERRA_DET_STANDEC_D_PREG}, {0x0241, SIERRA_DET_STANDEC_E_PREG}, - {0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG}, + {0x0110, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG}, {0x0014, SIERRA_PSM_A0IN_TMR_PREG}, {0xCF00, SIERRA_PSM_DIAG_PREG}, {0x001F, SIERRA_PSC_TX_A0_PREG}, @@ -696,7 +2258,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x0003, SIERRA_PSC_TX_A2_PREG}, {0x0003, SIERRA_PSC_TX_A3_PREG}, {0x0FFF, SIERRA_PSC_RX_A0_PREG}, - {0x0619, SIERRA_PSC_RX_A1_PREG}, + {0x0003, SIERRA_PSC_RX_A1_PREG}, {0x0003, SIERRA_PSC_RX_A2_PREG}, {0x0001, SIERRA_PSC_RX_A3_PREG}, {0x0001, SIERRA_PLLCTRL_SUBRATE_PREG}, @@ -705,19 +2267,19 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG}, {0x2512, SIERRA_DFE_BIASTRIM_PREG}, {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, - {0x873E, SIERRA_CLKPATHCTRL_TMR_PREG}, - {0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, - {0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x823E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG}, - {0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x023C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, {0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG}, {0x0000, SIERRA_CREQ_EQ_CTRL_PREG}, - {0x8000, SIERRA_CREQ_SPARE_PREG}, + {0x0000, SIERRA_CREQ_SPARE_PREG}, {0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, - {0x8453, SIERRA_CTLELUT_CTRL_PREG}, - {0x4110, SIERRA_DFE_ECMP_RATESEL_PREG}, - {0x4110, SIERRA_DFE_SMP_RATESEL_PREG}, - {0x0002, SIERRA_DEQ_PHALIGN_CTRL}, + {0x8452, SIERRA_CTLELUT_CTRL_PREG}, + {0x4121, SIERRA_DFE_ECMP_RATESEL_PREG}, + {0x4121, SIERRA_DFE_SMP_RATESEL_PREG}, + {0x0003, SIERRA_DEQ_PHALIGN_CTRL}, {0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG}, {0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG}, {0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, @@ -725,7 +2287,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG}, {0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG}, {0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG}, - {0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG}, + {0x9999, SIERRA_DEQ_VGATUNE_CTRL_PREG}, {0x0014, SIERRA_DEQ_GLUT0}, {0x0014, SIERRA_DEQ_GLUT1}, {0x0014, SIERRA_DEQ_GLUT2}, @@ -772,6 +2334,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x000F, SIERRA_LFPSFILT_NS_PREG}, {0x0009, SIERRA_LFPSFILT_RD_PREG}, {0x0001, SIERRA_LFPSFILT_MP_PREG}, + {0x6013, SIERRA_SIGDET_SUPPORT_PREG}, {0x8013, SIERRA_SDFILT_H2L_A_PREG}, {0x8009, SIERRA_SDFILT_L2H_PREG}, {0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG}, @@ -779,32 +2342,167 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x4243, SIERRA_RXBUFFER_DFECTRL_PREG} }; +static struct cdns_sierra_vals usb_100_ext_ssc_cmn_vals = { + .reg_pairs = cdns_usb_cmn_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), +}; + +static struct cdns_sierra_vals usb_100_ext_ssc_ln_vals = { + .reg_pairs = cdns_usb_ln_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), +}; + static const struct cdns_sierra_data cdns_map_sierra = { - SIERRA_MACRO_ID, - 0x2, - 0x2, - ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), - cdns_pcie_cmn_regs_ext_ssc, - cdns_pcie_ln_regs_ext_ssc, - cdns_usb_cmn_regs_ext_ssc, - cdns_usb_ln_regs_ext_ssc, + .id_value = SIERRA_MACRO_ID, + .block_offset_shift = 0x2, + .reg_offset_shift = 0x2, + .pcs_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + }, + }, + .pma_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_plllc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_plllc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_plllc_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + }, + }, + }, + .pma_ln_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_ln_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &ml_pcie_100_no_ssc_ln_vals, + [EXTERNAL_SSC] = &ml_pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &ml_pcie_100_int_ssc_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + }, + }, + }, }; static const struct cdns_sierra_data cdns_ti_map_sierra = { - SIERRA_MACRO_ID, - 0x0, - 0x1, - ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), - cdns_pcie_cmn_regs_ext_ssc, - cdns_pcie_ln_regs_ext_ssc, - cdns_usb_cmn_regs_ext_ssc, - cdns_usb_ln_regs_ext_ssc, + .id_value = SIERRA_MACRO_ID, + .block_offset_shift = 0x0, + .reg_offset_shift = 0x1, + .pcs_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + }, + }, + .phy_pma_ln_vals = { + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_phy_pma_ln_vals, + [EXTERNAL_SSC] = &qsgmii_phy_pma_ln_vals, + [INTERNAL_SSC] = &qsgmii_phy_pma_ln_vals, + }, + }, + }, + .pma_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_plllc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_plllc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_plllc_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + }, + }, + }, + .pma_ln_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_ln_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &ti_ml_pcie_100_no_ssc_ln_vals, + [EXTERNAL_SSC] = &ti_ml_pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &ti_ml_pcie_100_int_ssc_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + }, + }, + }, }; static const struct of_device_id cdns_sierra_id_table[] = { diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c new file mode 100644 index 000000000000..f099053c583c --- /dev/null +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -0,0 +1,4721 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cadence Torrent SD0801 PHY driver. + * + * Copyright 2018 Cadence Design Systems, Inc. + * + */ + +#include <dt-bindings/phy/phy.h> +#include <dt-bindings/phy/phy-cadence.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#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/reset.h> +#include <linux/regmap.h> + +#define REF_CLK_19_2MHZ 19200000 +#define REF_CLK_25MHZ 25000000 +#define REF_CLK_100MHZ 100000000 + +#define MAX_NUM_LANES 4 +#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */ + +#define NUM_SSC_MODE 3 +#define NUM_REF_CLK 3 +#define NUM_PHY_TYPE 6 + +#define POLL_TIMEOUT_US 5000 +#define PLL_LOCK_TIMEOUT 100000 + +#define TORRENT_COMMON_CDB_OFFSET 0x0 + +#define TORRENT_TX_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0x4000 << (block_offset)) + \ + (((ln) << 9) << (reg_offset))) + +#define TORRENT_RX_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0x8000 << (block_offset)) + \ + (((ln) << 9) << (reg_offset))) + +#define TORRENT_PHY_PCS_COMMON_OFFSET(block_offset) \ + (0xC000 << (block_offset)) + +#define TORRENT_PHY_PCS_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0xD000 << (block_offset)) + \ + (((ln) << 8) << (reg_offset))) + +#define TORRENT_PHY_PMA_COMMON_OFFSET(block_offset) \ + (0xE000 << (block_offset)) + +#define TORRENT_DPTX_PHY_OFFSET 0x0 + +/* + * register offsets from DPTX PHY register block base (i.e MHDP + * register base + 0x30a00) + */ +#define PHY_AUX_CTRL 0x04 +#define PHY_RESET 0x20 +#define PMA_TX_ELEC_IDLE_MASK 0xF0U +#define PMA_TX_ELEC_IDLE_SHIFT 4 +#define PHY_L00_RESET_N_MASK 0x01U +#define PHY_PMA_XCVR_PLLCLK_EN 0x24 +#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28 +#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c +#define PHY_POWER_STATE_LN_0 0x0000 +#define PHY_POWER_STATE_LN_1 0x0008 +#define PHY_POWER_STATE_LN_2 0x0010 +#define PHY_POWER_STATE_LN_3 0x0018 +#define PMA_XCVR_POWER_STATE_REQ_LN_MASK 0x3FU +#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30 +#define PHY_PMA_CMN_READY 0x34 + +/* + * register offsets from SD0801 PHY register block base (i.e MHDP + * register base + 0x500000) + */ +#define CMN_SSM_BANDGAP_TMR 0x0021U +#define CMN_SSM_BIAS_TMR 0x0022U +#define CMN_PLLSM0_PLLPRE_TMR 0x002AU +#define CMN_PLLSM0_PLLLOCK_TMR 0x002CU +#define CMN_PLLSM1_PLLPRE_TMR 0x0032U +#define CMN_PLLSM1_PLLLOCK_TMR 0x0034U +#define CMN_CDIAG_CDB_PWRI_OVRD 0x0041U +#define CMN_CDIAG_XCVRC_PWRI_OVRD 0x0047U +#define CMN_CDIAG_REFCLK_OVRD 0x004CU +#define CMN_CDIAG_REFCLK_DRV0_CTRL 0x0050U +#define CMN_BGCAL_INIT_TMR 0x0064U +#define CMN_BGCAL_ITER_TMR 0x0065U +#define CMN_IBCAL_INIT_TMR 0x0074U +#define CMN_PLL0_VCOCAL_TCTRL 0x0082U +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084U +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085U +#define CMN_PLL0_VCOCAL_REFTIM_START 0x0086U +#define CMN_PLL0_VCOCAL_PLLCNT_START 0x0088U +#define CMN_PLL0_INTDIV_M0 0x0090U +#define CMN_PLL0_FRACDIVL_M0 0x0091U +#define CMN_PLL0_FRACDIVH_M0 0x0092U +#define CMN_PLL0_HIGH_THR_M0 0x0093U +#define CMN_PLL0_DSM_DIAG_M0 0x0094U +#define CMN_PLL0_DSM_FBH_OVRD_M0 0x0095U +#define CMN_PLL0_SS_CTRL1_M0 0x0098U +#define CMN_PLL0_SS_CTRL2_M0 0x0099U +#define CMN_PLL0_SS_CTRL3_M0 0x009AU +#define CMN_PLL0_SS_CTRL4_M0 0x009BU +#define CMN_PLL0_LOCK_REFCNT_START 0x009CU +#define CMN_PLL0_LOCK_PLLCNT_START 0x009EU +#define CMN_PLL0_LOCK_PLLCNT_THR 0x009FU +#define CMN_PLL0_INTDIV_M1 0x00A0U +#define CMN_PLL0_FRACDIVH_M1 0x00A2U +#define CMN_PLL0_HIGH_THR_M1 0x00A3U +#define CMN_PLL0_DSM_DIAG_M1 0x00A4U +#define CMN_PLL0_SS_CTRL1_M1 0x00A8U +#define CMN_PLL0_SS_CTRL2_M1 0x00A9U +#define CMN_PLL0_SS_CTRL3_M1 0x00AAU +#define CMN_PLL0_SS_CTRL4_M1 0x00ABU +#define CMN_PLL1_VCOCAL_TCTRL 0x00C2U +#define CMN_PLL1_VCOCAL_INIT_TMR 0x00C4U +#define CMN_PLL1_VCOCAL_ITER_TMR 0x00C5U +#define CMN_PLL1_VCOCAL_REFTIM_START 0x00C6U +#define CMN_PLL1_VCOCAL_PLLCNT_START 0x00C8U +#define CMN_PLL1_INTDIV_M0 0x00D0U +#define CMN_PLL1_FRACDIVL_M0 0x00D1U +#define CMN_PLL1_FRACDIVH_M0 0x00D2U +#define CMN_PLL1_HIGH_THR_M0 0x00D3U +#define CMN_PLL1_DSM_DIAG_M0 0x00D4U +#define CMN_PLL1_DSM_FBH_OVRD_M0 0x00D5U +#define CMN_PLL1_DSM_FBL_OVRD_M0 0x00D6U +#define CMN_PLL1_SS_CTRL1_M0 0x00D8U +#define CMN_PLL1_SS_CTRL2_M0 0x00D9U +#define CMN_PLL1_SS_CTRL3_M0 0x00DAU +#define CMN_PLL1_SS_CTRL4_M0 0x00DBU +#define CMN_PLL1_LOCK_REFCNT_START 0x00DCU +#define CMN_PLL1_LOCK_PLLCNT_START 0x00DEU +#define CMN_PLL1_LOCK_PLLCNT_THR 0x00DFU +#define CMN_TXPUCAL_TUNE 0x0103U +#define CMN_TXPUCAL_INIT_TMR 0x0104U +#define CMN_TXPUCAL_ITER_TMR 0x0105U +#define CMN_TXPDCAL_TUNE 0x010BU +#define CMN_TXPDCAL_INIT_TMR 0x010CU +#define CMN_TXPDCAL_ITER_TMR 0x010DU +#define CMN_RXCAL_INIT_TMR 0x0114U +#define CMN_RXCAL_ITER_TMR 0x0115U +#define CMN_SD_CAL_INIT_TMR 0x0124U +#define CMN_SD_CAL_ITER_TMR 0x0125U +#define CMN_SD_CAL_REFTIM_START 0x0126U +#define CMN_SD_CAL_PLLCNT_START 0x0128U +#define CMN_PDIAG_PLL0_CTRL_M0 0x01A0U +#define CMN_PDIAG_PLL0_CLK_SEL_M0 0x01A1U +#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x01A4U +#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x01A5U +#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x01A6U +#define CMN_PDIAG_PLL0_CTRL_M1 0x01B0U +#define CMN_PDIAG_PLL0_CLK_SEL_M1 0x01B1U +#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x01B4U +#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x01B5U +#define CMN_PDIAG_PLL0_FILT_PADJ_M1 0x01B6U +#define CMN_PDIAG_PLL1_CTRL_M0 0x01C0U +#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x01C1U +#define CMN_PDIAG_PLL1_CP_PADJ_M0 0x01C4U +#define CMN_PDIAG_PLL1_CP_IADJ_M0 0x01C5U +#define CMN_PDIAG_PLL1_FILT_PADJ_M0 0x01C6U +#define CMN_DIAG_BIAS_OVRD1 0x01E1U + +/* PMA TX Lane registers */ +#define TX_TXCC_CTRL 0x0040U +#define TX_TXCC_CPOST_MULT_00 0x004CU +#define TX_TXCC_CPOST_MULT_01 0x004DU +#define TX_TXCC_MGNFS_MULT_000 0x0050U +#define TX_TXCC_MGNFS_MULT_100 0x0054U +#define DRV_DIAG_TX_DRV 0x00C6U +#define XCVR_DIAG_PLLDRC_CTRL 0x00E5U +#define XCVR_DIAG_HSCLK_SEL 0x00E6U +#define XCVR_DIAG_HSCLK_DIV 0x00E7U +#define XCVR_DIAG_RXCLK_CTRL 0x00E9U +#define XCVR_DIAG_BIDI_CTRL 0x00EAU +#define XCVR_DIAG_PSC_OVRD 0x00EBU +#define TX_PSC_A0 0x0100U +#define TX_PSC_A1 0x0101U +#define TX_PSC_A2 0x0102U +#define TX_PSC_A3 0x0103U +#define TX_RCVDET_ST_TMR 0x0123U +#define TX_DIAG_ACYA 0x01E7U +#define TX_DIAG_ACYA_HBDC_MASK 0x0001U + +/* PMA RX Lane registers */ +#define RX_PSC_A0 0x0000U +#define RX_PSC_A1 0x0001U +#define RX_PSC_A2 0x0002U +#define RX_PSC_A3 0x0003U +#define RX_PSC_CAL 0x0006U +#define RX_CDRLF_CNFG 0x0080U +#define RX_CDRLF_CNFG3 0x0082U +#define RX_SIGDET_HL_FILT_TMR 0x0090U +#define RX_REE_GCSM1_CTRL 0x0108U +#define RX_REE_GCSM1_EQENM_PH1 0x0109U +#define RX_REE_GCSM1_EQENM_PH2 0x010AU +#define RX_REE_GCSM2_CTRL 0x0110U +#define RX_REE_PERGCSM_CTRL 0x0118U +#define RX_REE_ATTEN_THR 0x0149U +#define RX_REE_TAP1_CLIP 0x0171U +#define RX_REE_TAP2TON_CLIP 0x0172U +#define RX_REE_SMGM_CTRL1 0x0177U +#define RX_REE_SMGM_CTRL2 0x0178U +#define RX_DIAG_DFE_CTRL 0x01E0U +#define RX_DIAG_DFE_AMP_TUNE_2 0x01E2U +#define RX_DIAG_DFE_AMP_TUNE_3 0x01E3U +#define RX_DIAG_NQST_CTRL 0x01E5U +#define RX_DIAG_SIGDET_TUNE 0x01E8U +#define RX_DIAG_PI_RATE 0x01F4U +#define RX_DIAG_PI_CAP 0x01F5U +#define RX_DIAG_ACYA 0x01FFU + +/* PHY PCS common registers */ +#define PHY_PIPE_CMN_CTRL1 0x0000U +#define PHY_PLL_CFG 0x000EU +#define PHY_PIPE_USB3_GEN2_PRE_CFG0 0x0020U +#define PHY_PIPE_USB3_GEN2_POST_CFG0 0x0022U +#define PHY_PIPE_USB3_GEN2_POST_CFG1 0x0023U + +/* PHY PCS lane registers */ +#define PHY_PCS_ISO_LINK_CTRL 0x000BU + +/* PHY PMA common registers */ +#define PHY_PMA_CMN_CTRL1 0x0000U +#define PHY_PMA_CMN_CTRL2 0x0001U +#define PHY_PMA_PLL_RAW_CTRL 0x0003U + +#define CDNS_TORRENT_OUTPUT_CLOCKS 3 + +static const char * const clk_names[] = { + [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", + [CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der", + [CDNS_TORRENT_RECEIVED_REFCLK] = "refclk-rec", +}; + +static const struct reg_field phy_pll_cfg = + REG_FIELD(PHY_PLL_CFG, 0, 1); + +static const struct reg_field phy_pma_cmn_ctrl_1 = + REG_FIELD(PHY_PMA_CMN_CTRL1, 0, 0); + +static const struct reg_field phy_pma_cmn_ctrl_2 = + REG_FIELD(PHY_PMA_CMN_CTRL2, 0, 7); + +static const struct reg_field phy_pma_pll_raw_ctrl = + REG_FIELD(PHY_PMA_PLL_RAW_CTRL, 0, 1); + +static const struct reg_field phy_reset_ctrl = + REG_FIELD(PHY_RESET, 8, 8); + +static const struct reg_field phy_pcs_iso_link_ctrl_1 = + REG_FIELD(PHY_PCS_ISO_LINK_CTRL, 1, 1); + +static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL1, 0, 0); + +static const struct reg_field cmn_cdiag_refclk_ovrd_4 = + REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4); + +#define REFCLK_OUT_NUM_CMN_CONFIG 4 + +enum cdns_torrent_refclk_out_cmn { + CMN_CDIAG_REFCLK_DRV0_CTRL_1, + CMN_CDIAG_REFCLK_DRV0_CTRL_4, + CMN_CDIAG_REFCLK_DRV0_CTRL_5, + CMN_CDIAG_REFCLK_DRV0_CTRL_6, +}; + +static const struct reg_field refclk_out_cmn_cfg[] = { + [CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1), + [CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4), + [CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5), + [CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6), +}; + +static const int refclk_driver_parent_index[] = { + CDNS_TORRENT_DERIVED_REFCLK, + CDNS_TORRENT_RECEIVED_REFCLK +}; + +static u32 cdns_torrent_refclk_driver_mux_table[] = { 1, 0 }; + +enum cdns_torrent_phy_type { + TYPE_NONE, + TYPE_DP, + TYPE_PCIE, + TYPE_SGMII, + TYPE_QSGMII, + TYPE_USB, +}; + +enum cdns_torrent_ref_clk { + CLK_19_2_MHZ, + CLK_25_MHZ, + CLK_100_MHZ +}; + +enum cdns_torrent_ssc_mode { + NO_SSC, + EXTERNAL_SSC, + INTERNAL_SSC +}; + +struct cdns_torrent_inst { + struct phy *phy; + u32 mlane; + enum cdns_torrent_phy_type phy_type; + u32 num_lanes; + struct reset_control *lnk_rst; + enum cdns_torrent_ssc_mode ssc_mode; +}; + +struct cdns_torrent_phy { + void __iomem *base; /* DPTX registers base */ + void __iomem *sd_base; /* SD0801 registers base */ + u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */ + struct reset_control *phy_rst; + struct reset_control *apb_rst; + struct device *dev; + struct clk *clk; + enum cdns_torrent_ref_clk ref_clk_rate; + struct cdns_torrent_inst phys[MAX_NUM_LANES]; + int nsubnodes; + const struct cdns_torrent_data *init_data; + struct regmap *regmap_common_cdb; + struct regmap *regmap_phy_pcs_common_cdb; + struct regmap *regmap_phy_pma_common_cdb; + struct regmap *regmap_tx_lane_cdb[MAX_NUM_LANES]; + struct regmap *regmap_rx_lane_cdb[MAX_NUM_LANES]; + struct regmap *regmap_phy_pcs_lane_cdb[MAX_NUM_LANES]; + struct regmap *regmap_dptx_phy_reg; + struct regmap_field *phy_pll_cfg; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; + struct regmap_field *phy_pma_cmn_ctrl_1; + struct regmap_field *phy_pma_cmn_ctrl_2; + struct regmap_field *phy_pma_pll_raw_ctrl; + struct regmap_field *phy_reset_ctrl; + struct regmap_field *phy_pcs_iso_link_ctrl_1[MAX_NUM_LANES]; + struct clk_hw_onecell_data *clk_hw_data; +}; + +enum phy_powerstate { + POWERSTATE_A0 = 0, + /* Powerstate A1 is unused */ + POWERSTATE_A2 = 2, + POWERSTATE_A3 = 3, +}; + +struct cdns_torrent_refclk_driver { + struct clk_hw hw; + struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG]; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_refclk_driver(_hw) \ + container_of(_hw, struct cdns_torrent_refclk_driver, hw) + +struct cdns_torrent_derived_refclk { + struct clk_hw hw; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_derived_refclk(_hw) \ + container_of(_hw, struct cdns_torrent_derived_refclk, hw) + +struct cdns_torrent_received_refclk { + struct clk_hw hw; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_received_refclk(_hw) \ + container_of(_hw, struct cdns_torrent_received_refclk, hw) + +struct cdns_reg_pairs { + u32 val; + u32 off; +}; + +struct cdns_torrent_vals { + struct cdns_reg_pairs *reg_pairs; + u32 num_regs; +}; + +struct cdns_torrent_data { + u8 block_offset_shift; + u8 reg_offset_shift; + struct cdns_torrent_vals *link_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_torrent_vals *xcvr_diag_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_torrent_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_torrent_vals *cmn_vals[NUM_REF_CLK][NUM_PHY_TYPE] + [NUM_PHY_TYPE][NUM_SSC_MODE]; + struct cdns_torrent_vals *tx_ln_vals[NUM_REF_CLK][NUM_PHY_TYPE] + [NUM_PHY_TYPE][NUM_SSC_MODE]; + struct cdns_torrent_vals *rx_ln_vals[NUM_REF_CLK][NUM_PHY_TYPE] + [NUM_PHY_TYPE][NUM_SSC_MODE]; +}; + +struct cdns_regmap_cdb_context { + struct device *dev; + void __iomem *base; + u8 reg_offset_shift; +}; + +static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val) +{ + struct cdns_regmap_cdb_context *ctx = context; + u32 offset = reg << ctx->reg_offset_shift; + + writew(val, ctx->base + offset); + + return 0; +} + +static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val) +{ + struct cdns_regmap_cdb_context *ctx = context; + u32 offset = reg << ctx->reg_offset_shift; + + *val = readw(ctx->base + offset); + return 0; +} + +static int cdns_regmap_dptx_write(void *context, unsigned int reg, + unsigned int val) +{ + struct cdns_regmap_cdb_context *ctx = context; + u32 offset = reg; + + writel(val, ctx->base + offset); + + return 0; +} + +static int cdns_regmap_dptx_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct cdns_regmap_cdb_context *ctx = context; + u32 offset = reg; + + *val = readl(ctx->base + offset); + return 0; +} + +#define TORRENT_TX_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "torrent_tx_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +#define TORRENT_RX_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "torrent_rx_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static const struct regmap_config cdns_torrent_tx_lane_cdb_config[] = { + TORRENT_TX_LANE_CDB_REGMAP_CONF("0"), + TORRENT_TX_LANE_CDB_REGMAP_CONF("1"), + TORRENT_TX_LANE_CDB_REGMAP_CONF("2"), + TORRENT_TX_LANE_CDB_REGMAP_CONF("3"), +}; + +static const struct regmap_config cdns_torrent_rx_lane_cdb_config[] = { + TORRENT_RX_LANE_CDB_REGMAP_CONF("0"), + TORRENT_RX_LANE_CDB_REGMAP_CONF("1"), + TORRENT_RX_LANE_CDB_REGMAP_CONF("2"), + TORRENT_RX_LANE_CDB_REGMAP_CONF("3"), +}; + +static const struct regmap_config cdns_torrent_common_cdb_config = { + .name = "torrent_common_cdb", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + +#define TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "torrent_phy_pcs_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static const struct regmap_config cdns_torrent_phy_pcs_lane_cdb_config[] = { + TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("0"), + TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("1"), + TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("2"), + TORRENT_PHY_PCS_LANE_CDB_REGMAP_CONF("3"), +}; + +static const struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = { + .name = "torrent_phy_pcs_cmn_cdb", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + +static const struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = { + .name = "torrent_phy_pma_cmn_cdb", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + +static const struct regmap_config cdns_torrent_dptx_phy_config = { + .name = "torrent_dptx_phy", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_dptx_write, + .reg_read = cdns_regmap_dptx_read, +}; + +/* PHY mmr access functions */ + +static void cdns_torrent_phy_write(struct regmap *regmap, u32 offset, u32 val) +{ + regmap_write(regmap, offset, val); +} + +static u32 cdns_torrent_phy_read(struct regmap *regmap, u32 offset) +{ + unsigned int val; + + regmap_read(regmap, offset, &val); + return val; +} + +/* DPTX mmr access functions */ + +static void cdns_torrent_dp_write(struct regmap *regmap, u32 offset, u32 val) +{ + regmap_write(regmap, offset, val); +} + +static u32 cdns_torrent_dp_read(struct regmap *regmap, u32 offset) +{ + u32 val; + + regmap_read(regmap, offset, &val); + return val; +} + +/* + * Structure used to store values of PHY registers for voltage-related + * coefficients, for particular voltage swing and pre-emphasis level. Values + * are shared across all physical lanes. + */ +struct coefficients { + /* Value of DRV_DIAG_TX_DRV register to use */ + u16 diag_tx_drv; + /* Value of TX_TXCC_MGNFS_MULT_000 register to use */ + u16 mgnfs_mult; + /* Value of TX_TXCC_CPOST_MULT_00 register to use */ + u16 cpost_mult; +}; + +/* + * Array consists of values of voltage-related registers for sd0801 PHY. A value + * of 0xFFFF is a placeholder for invalid combination, and will never be used. + */ +static const struct coefficients vltg_coeff[4][4] = { + /* voltage swing 0, pre-emphasis 0->3 */ + { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x002A, + .cpost_mult = 0x0000}, + {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F, + .cpost_mult = 0x0014}, + {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0012, + .cpost_mult = 0x0020}, + {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000, + .cpost_mult = 0x002A} + }, + + /* voltage swing 1, pre-emphasis 0->3 */ + { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F, + .cpost_mult = 0x0000}, + {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013, + .cpost_mult = 0x0012}, + {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000, + .cpost_mult = 0x001F}, + {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF, + .cpost_mult = 0xFFFF} + }, + + /* voltage swing 2, pre-emphasis 0->3 */ + { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013, + .cpost_mult = 0x0000}, + {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000, + .cpost_mult = 0x0013}, + {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF, + .cpost_mult = 0xFFFF}, + {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF, + .cpost_mult = 0xFFFF} + }, + + /* voltage swing 3, pre-emphasis 0->3 */ + { {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000, + .cpost_mult = 0x0000}, + {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF, + .cpost_mult = 0xFFFF}, + {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF, + .cpost_mult = 0xFFFF}, + {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF, + .cpost_mult = 0xFFFF} + } +}; + +static const char *cdns_torrent_get_phy_type(enum cdns_torrent_phy_type phy_type) +{ + switch (phy_type) { + case TYPE_DP: + return "DisplayPort"; + case TYPE_PCIE: + return "PCIe"; + case TYPE_SGMII: + return "SGMII"; + case TYPE_QSGMII: + return "QSGMII"; + case TYPE_USB: + return "USB"; + default: + return "None"; + } +} + +/* + * Set registers responsible for enabling and configuring SSC, with second and + * third register values provided by parameters. + */ +static +void cdns_torrent_dp_enable_ssc_19_2mhz(struct cdns_torrent_phy *cdns_phy, + u32 ctrl2_val, u32 ctrl3_val) +{ + struct regmap *regmap = cdns_phy->regmap_common_cdb; + + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0001); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl2_val); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl3_val); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0003); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0001); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl2_val); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl3_val); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0003); +} + +static +void cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy, + u32 rate, bool ssc) +{ + struct regmap *regmap = cdns_phy->regmap_common_cdb; + + /* Assumes 19.2 MHz refclock */ + switch (rate) { + /* Setting VCO for 10.8GHz */ + case 2700: + case 5400: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0119); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x4000); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00BC); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0012); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0119); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x4000); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00BC); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0012); + if (ssc) + cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x033A, 0x006A); + break; + /* Setting VCO for 9.72GHz */ + case 1620: + case 2430: + case 3240: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01FA); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x4000); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0152); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01FA); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x4000); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0152); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + if (ssc) + cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x05DD, 0x0069); + break; + /* Setting VCO for 8.64GHz */ + case 2160: + case 4320: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01C2); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x012C); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01C2); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x012C); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + if (ssc) + cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x0536, 0x0069); + break; + /* Setting VCO for 8.1GHz */ + case 8100: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01A5); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0xE000); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x011A); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01A5); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0xE000); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x011A); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + if (ssc) + cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x04D7, 0x006A); + break; + } + + if (ssc) { + cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_PLLCNT_START, 0x025E); + cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_THR, 0x0005); + cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_PLLCNT_START, 0x025E); + cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_THR, 0x0005); + } else { + cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_PLLCNT_START, 0x0260); + cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_PLLCNT_START, 0x0260); + /* Set reset register values to disable SSC */ + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL2_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL3_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_THR, 0x0003); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL2_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL3_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_THR, 0x0003); + } + + cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_REFCNT_START, 0x0099); + cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_START, 0x0099); + cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_REFCNT_START, 0x0099); + cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_START, 0x0099); +} + +/* + * Set registers responsible for enabling and configuring SSC, with second + * register value provided by a parameter. + */ +static void cdns_torrent_dp_enable_ssc_25mhz(struct cdns_torrent_phy *cdns_phy, + u32 ctrl2_val) +{ + struct regmap *regmap = cdns_phy->regmap_common_cdb; + + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0001); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl2_val); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x007F); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0003); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0001); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl2_val); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x007F); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0003); +} + +static +void cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(struct cdns_torrent_phy *cdns_phy, + u32 rate, bool ssc) +{ + struct regmap *regmap = cdns_phy->regmap_common_cdb; + + /* Assumes 25 MHz refclock */ + switch (rate) { + /* Setting VCO for 10.8GHz */ + case 2700: + case 5400: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01B0); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0120); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01B0); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0120); + if (ssc) + cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x0423); + break; + /* Setting VCO for 9.72GHz */ + case 1620: + case 2430: + case 3240: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0184); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0xCCCD); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0104); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0184); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0xCCCD); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0104); + if (ssc) + cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x03B9); + break; + /* Setting VCO for 8.64GHz */ + case 2160: + case 4320: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0159); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x999A); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00E7); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0159); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x999A); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00E7); + if (ssc) + cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x034F); + break; + /* Setting VCO for 8.1GHz */ + case 8100: + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0144); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00D8); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0144); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00D8); + if (ssc) + cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x031A); + break; + } + + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + + if (ssc) { + cdns_torrent_phy_write(regmap, + CMN_PLL0_VCOCAL_PLLCNT_START, 0x0315); + cdns_torrent_phy_write(regmap, + CMN_PLL0_LOCK_PLLCNT_THR, 0x0005); + cdns_torrent_phy_write(regmap, + CMN_PLL1_VCOCAL_PLLCNT_START, 0x0315); + cdns_torrent_phy_write(regmap, + CMN_PLL1_LOCK_PLLCNT_THR, 0x0005); + } else { + cdns_torrent_phy_write(regmap, + CMN_PLL0_VCOCAL_PLLCNT_START, 0x0317); + cdns_torrent_phy_write(regmap, + CMN_PLL1_VCOCAL_PLLCNT_START, 0x0317); + /* Set reset register values to disable SSC */ + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL2_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL3_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0000); + cdns_torrent_phy_write(regmap, + CMN_PLL0_LOCK_PLLCNT_THR, 0x0003); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL2_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL3_M0, 0x0000); + cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0000); + cdns_torrent_phy_write(regmap, + CMN_PLL1_LOCK_PLLCNT_THR, 0x0003); + } + + cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_REFCNT_START, 0x00C7); + cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_START, 0x00C7); + cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_REFCNT_START, 0x00C7); + cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_START, 0x00C7); +} + +static +void cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(struct cdns_torrent_phy *cdns_phy, + u32 rate, bool ssc) +{ + struct regmap *regmap = cdns_phy->regmap_common_cdb; + + /* Assumes 100 MHz refclock */ + switch (rate) { + /* Setting VCO for 10.8GHz */ + case 2700: + case 5400: + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0028); + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_FBH_OVRD_M0, 0x0022); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBH_OVRD_M0, 0x0022); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBL_OVRD_M0, 0x000C); + break; + /* Setting VCO for 9.72GHz */ + case 1620: + case 2430: + case 3240: + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0061); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0061); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x3333); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x3333); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0042); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0042); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + break; + /* Setting VCO for 8.64GHz */ + case 2160: + case 4320: + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0056); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0056); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x6666); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x6666); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x003A); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x003A); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + break; + /* Setting VCO for 8.1GHz */ + case 8100: + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0051); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0051); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0036); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0036); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + break; + } +} + +/* + * Enable or disable PLL for selected lanes. + */ +static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy, + struct phy_configure_opts_dp *dp, + bool enable) +{ + u32 rd_val; + u32 ret; + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + + /* + * Used to determine, which bits to check for or enable in + * PHY_PMA_XCVR_PLLCLK_EN register. + */ + u32 pll_bits; + /* Used to enable or disable lanes. */ + u32 pll_val; + + /* Select values of registers and mask, depending on enabled lane + * count. + */ + switch (dp->lanes) { + /* lane 0 */ + case (1): + pll_bits = 0x00000001; + break; + /* lanes 0-1 */ + case (2): + pll_bits = 0x00000003; + break; + /* lanes 0-3, all */ + default: + pll_bits = 0x0000000F; + break; + } + + if (enable) + pll_val = pll_bits; + else + pll_val = 0x00000000; + + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_val); + + /* Wait for acknowledgment from PHY. */ + ret = regmap_read_poll_timeout(regmap, + PHY_PMA_XCVR_PLLCLK_EN_ACK, + rd_val, + (rd_val & pll_bits) == pll_val, + 0, POLL_TIMEOUT_US); + ndelay(100); + return ret; +} + +static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy, + u32 num_lanes, + enum phy_powerstate powerstate) +{ + /* Register value for power state for a single byte. */ + u32 value_part; + u32 value; + u32 mask; + u32 read_val; + u32 ret; + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + + switch (powerstate) { + case (POWERSTATE_A0): + value_part = 0x01U; + break; + case (POWERSTATE_A2): + value_part = 0x04U; + break; + default: + /* Powerstate A3 */ + value_part = 0x08U; + break; + } + + /* Select values of registers and mask, depending on enabled + * lane count. + */ + switch (num_lanes) { + /* lane 0 */ + case (1): + value = value_part; + mask = 0x0000003FU; + break; + /* lanes 0-1 */ + case (2): + value = (value_part + | (value_part << 8)); + mask = 0x00003F3FU; + break; + /* lanes 0-3, all */ + default: + value = (value_part + | (value_part << 8) + | (value_part << 16) + | (value_part << 24)); + mask = 0x3F3F3F3FU; + break; + } + + /* Set power state A<n>. */ + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, value); + /* Wait, until PHY acknowledges power state completion. */ + ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_POWER_STATE_ACK, + read_val, (read_val & mask) == value, 0, + POLL_TIMEOUT_US); + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, 0x00000000); + ndelay(100); + + return ret; +} + +static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes) +{ + unsigned int read_val; + int ret; + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + + /* + * waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the + * master lane + */ + ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_PLLCLK_EN_ACK, + read_val, read_val & 1, + 0, POLL_TIMEOUT_US); + if (ret == -ETIMEDOUT) { + dev_err(cdns_phy->dev, + "timeout waiting for link PLL clock enable ack\n"); + return ret; + } + + ndelay(100); + + ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes, + POWERSTATE_A2); + if (ret) + return ret; + + ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes, + POWERSTATE_A0); + + return ret; +} + +static int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy) +{ + unsigned int reg; + int ret; + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + + ret = regmap_read_poll_timeout(regmap, PHY_PMA_CMN_READY, reg, + reg & 1, 0, POLL_TIMEOUT_US); + if (ret == -ETIMEDOUT) { + dev_err(cdns_phy->dev, + "timeout waiting for PMA common ready\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy, + u32 rate, u32 num_lanes) +{ + unsigned int clk_sel_val = 0; + unsigned int hsclk_div_val = 0; + unsigned int i; + + switch (rate) { + case 1620: + clk_sel_val = 0x0f01; + hsclk_div_val = 2; + break; + case 2160: + case 2430: + case 2700: + clk_sel_val = 0x0701; + hsclk_div_val = 1; + break; + case 3240: + clk_sel_val = 0x0b00; + hsclk_div_val = 2; + break; + case 4320: + case 5400: + clk_sel_val = 0x0301; + hsclk_div_val = 0; + break; + case 8100: + clk_sel_val = 0x0200; + hsclk_div_val = 0; + break; + } + + cdns_torrent_phy_write(cdns_phy->regmap_common_cdb, + CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val); + cdns_torrent_phy_write(cdns_phy->regmap_common_cdb, + CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val); + + /* PMA lane configuration to deal with multi-link operation */ + for (i = 0; i < num_lanes; i++) + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[i], + XCVR_DIAG_HSCLK_DIV, hsclk_div_val); +} + +/* + * Perform register operations related to setting link rate, once powerstate is + * set and PLL disable request was processed. + */ +static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy, + struct phy_configure_opts_dp *dp) +{ + u32 read_val, ret; + + /* Disable the cmn_pll0_en before re-programming the new data rate. */ + regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x0); + + /* + * Wait for PLL ready de-assertion. + * For PLL0 - PHY_PMA_CMN_CTRL2[2] == 1 + */ + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, + read_val, + ((read_val >> 2) & 0x01) != 0, + 0, POLL_TIMEOUT_US); + if (ret) + return ret; + ndelay(200); + + /* DP Rate Change - VCO Output settings. */ + if (cdns_phy->ref_clk_rate == CLK_19_2_MHZ) + /* PMA common configuration 19.2MHz */ + cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy, dp->link_rate, dp->ssc); + else if (cdns_phy->ref_clk_rate == CLK_25_MHZ) + /* PMA common configuration 25MHz */ + cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy, dp->link_rate, dp->ssc); + else if (cdns_phy->ref_clk_rate == CLK_100_MHZ) + /* PMA common configuration 100MHz */ + cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(cdns_phy, dp->link_rate, dp->ssc); + + cdns_torrent_dp_pma_cmn_rate(cdns_phy, dp->link_rate, dp->lanes); + + /* Enable the cmn_pll0_en. */ + regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x3); + + /* + * Wait for PLL ready assertion. + * For PLL0 - PHY_PMA_CMN_CTRL2[0] == 1 + */ + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, + read_val, + (read_val & 0x01) != 0, + 0, POLL_TIMEOUT_US); + return ret; +} + +/* + * Verify, that parameters to configure PHY with are correct. + */ +static int cdns_torrent_dp_verify_config(struct cdns_torrent_inst *inst, + struct phy_configure_opts_dp *dp) +{ + u8 i; + + /* If changing link rate was required, verify it's supported. */ + if (dp->set_rate) { + switch (dp->link_rate) { + case 1620: + case 2160: + case 2430: + case 2700: + case 3240: + case 4320: + case 5400: + case 8100: + /* valid bit rate */ + break; + default: + return -EINVAL; + } + } + + /* Verify lane count. */ + switch (dp->lanes) { + case 1: + case 2: + case 4: + /* valid lane count. */ + break; + default: + return -EINVAL; + } + + /* Check against actual number of PHY's lanes. */ + if (dp->lanes > inst->num_lanes) + return -EINVAL; + + /* + * If changing voltages is required, check swing and pre-emphasis + * levels, per-lane. + */ + if (dp->set_voltages) { + /* Lane count verified previously. */ + for (i = 0; i < dp->lanes; i++) { + if (dp->voltage[i] > 3 || dp->pre[i] > 3) + return -EINVAL; + + /* Sum of voltage swing and pre-emphasis levels cannot + * exceed 3. + */ + if (dp->voltage[i] + dp->pre[i] > 3) + return -EINVAL; + } + } + + return 0; +} + +/* Set power state A0 and PLL clock enable to 0 on enabled lanes. */ +static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy, + u32 num_lanes) +{ + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + u32 pwr_state = cdns_torrent_dp_read(regmap, + PHY_PMA_XCVR_POWER_STATE_REQ); + u32 pll_clk_en = cdns_torrent_dp_read(regmap, + PHY_PMA_XCVR_PLLCLK_EN); + + /* Lane 0 is always enabled. */ + pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << + PHY_POWER_STATE_LN_0); + pll_clk_en &= ~0x01U; + + if (num_lanes > 1) { + /* lane 1 */ + pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << + PHY_POWER_STATE_LN_1); + pll_clk_en &= ~(0x01U << 1); + } + + if (num_lanes > 2) { + /* lanes 2 and 3 */ + pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << + PHY_POWER_STATE_LN_2); + pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << + PHY_POWER_STATE_LN_3); + pll_clk_en &= ~(0x01U << 2); + pll_clk_en &= ~(0x01U << 3); + } + + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, pwr_state); + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_clk_en); +} + +/* Configure lane count as required. */ +static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy, + struct phy_configure_opts_dp *dp) +{ + u32 value; + u32 ret; + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + u8 lane_mask = (1 << dp->lanes) - 1; + + value = cdns_torrent_dp_read(regmap, PHY_RESET); + /* clear pma_tx_elec_idle_ln_* bits. */ + value &= ~PMA_TX_ELEC_IDLE_MASK; + /* Assert pma_tx_elec_idle_ln_* for disabled lanes. */ + value |= ((~lane_mask) << PMA_TX_ELEC_IDLE_SHIFT) & + PMA_TX_ELEC_IDLE_MASK; + cdns_torrent_dp_write(regmap, PHY_RESET, value); + + /* reset the link by asserting phy_l00_reset_n low */ + cdns_torrent_dp_write(regmap, PHY_RESET, + value & (~PHY_L00_RESET_N_MASK)); + + /* + * Assert lane reset on unused lanes and lane 0 so they remain in reset + * and powered down when re-enabling the link + */ + value = (value & 0x0000FFF0) | (0x0000000E & lane_mask); + cdns_torrent_dp_write(regmap, PHY_RESET, value); + + cdns_torrent_dp_set_a0_pll(cdns_phy, dp->lanes); + + /* release phy_l0*_reset_n based on used laneCount */ + value = (value & 0x0000FFF0) | (0x0000000F & lane_mask); + cdns_torrent_dp_write(regmap, PHY_RESET, value); + + /* Wait, until PHY gets ready after releasing PHY reset signal. */ + ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy); + if (ret) + return ret; + + ndelay(100); + + /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */ + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001); + + ret = cdns_torrent_dp_run(cdns_phy, dp->lanes); + + return ret; +} + +/* Configure link rate as required. */ +static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy, + struct phy_configure_opts_dp *dp) +{ + u32 ret; + + ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes, + POWERSTATE_A3); + if (ret) + return ret; + ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false); + if (ret) + return ret; + ndelay(200); + + ret = cdns_torrent_dp_configure_rate(cdns_phy, dp); + if (ret) + return ret; + ndelay(200); + + ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true); + if (ret) + return ret; + ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes, + POWERSTATE_A2); + if (ret) + return ret; + ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes, + POWERSTATE_A0); + if (ret) + return ret; + ndelay(900); + + return ret; +} + +/* Configure voltage swing and pre-emphasis for all enabled lanes. */ +static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy, + struct phy_configure_opts_dp *dp) +{ + u8 lane; + u16 val; + + for (lane = 0; lane < dp->lanes; lane++) { + val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane], + TX_DIAG_ACYA); + /* + * Write 1 to register bit TX_DIAG_ACYA[0] to freeze the + * current state of the analog TX driver. + */ + val |= TX_DIAG_ACYA_HBDC_MASK; + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + TX_DIAG_ACYA, val); + + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + TX_TXCC_CTRL, 0x08A4); + val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].diag_tx_drv; + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + DRV_DIAG_TX_DRV, val); + val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].mgnfs_mult; + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + TX_TXCC_MGNFS_MULT_000, + val); + val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].cpost_mult; + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + TX_TXCC_CPOST_MULT_00, + val); + + val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane], + TX_DIAG_ACYA); + /* + * Write 0 to register bit TX_DIAG_ACYA[0] to allow the state of + * analog TX driver to reflect the new programmed one. + */ + val &= ~TX_DIAG_ACYA_HBDC_MASK; + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + TX_DIAG_ACYA, val); + } +}; + +static int cdns_torrent_dp_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct cdns_torrent_inst *inst = phy_get_drvdata(phy); + struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); + int ret; + + ret = cdns_torrent_dp_verify_config(inst, &opts->dp); + if (ret) { + dev_err(&phy->dev, "invalid params for phy configure\n"); + return ret; + } + + if (opts->dp.set_lanes) { + ret = cdns_torrent_dp_set_lanes(cdns_phy, &opts->dp); + if (ret) { + dev_err(&phy->dev, "cdns_torrent_dp_set_lanes failed\n"); + return ret; + } + } + + if (opts->dp.set_rate) { + ret = cdns_torrent_dp_set_rate(cdns_phy, &opts->dp); + if (ret) { + dev_err(&phy->dev, "cdns_torrent_dp_set_rate failed\n"); + return ret; + } + } + + if (opts->dp.set_voltages) + cdns_torrent_dp_set_voltages(cdns_phy, &opts->dp); + + return ret; +} + +static int cdns_torrent_phy_on(struct phy *phy) +{ + struct cdns_torrent_inst *inst = phy_get_drvdata(phy); + struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); + u32 read_val; + int ret; + + if (cdns_phy->nsubnodes == 1) { + /* Take the PHY lane group out of reset */ + reset_control_deassert(inst->lnk_rst); + + /* Take the PHY out of reset */ + ret = reset_control_deassert(cdns_phy->phy_rst); + if (ret) + return ret; + } + + /* + * Wait for cmn_ready assertion + * PHY_PMA_CMN_CTRL1[0] == 1 + */ + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_1, + read_val, read_val, 1000, + PLL_LOCK_TIMEOUT); + if (ret) { + dev_err(cdns_phy->dev, "Timeout waiting for CMN ready\n"); + return ret; + } + + if (inst->phy_type == TYPE_PCIE || inst->phy_type == TYPE_USB) { + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pcs_iso_link_ctrl_1[inst->mlane], + read_val, !read_val, 1000, + PLL_LOCK_TIMEOUT); + if (ret == -ETIMEDOUT) { + dev_err(cdns_phy->dev, "Timeout waiting for PHY status ready\n"); + return ret; + } + } + + return 0; +} + +static int cdns_torrent_phy_off(struct phy *phy) +{ + struct cdns_torrent_inst *inst = phy_get_drvdata(phy); + struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); + int ret; + + if (cdns_phy->nsubnodes != 1) + return 0; + + ret = reset_control_assert(cdns_phy->phy_rst); + if (ret) + return ret; + + return reset_control_assert(inst->lnk_rst); +} + +static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst) +{ + struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + unsigned char lane_bits; + + cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */ + + /* + * Set lines power state to A0 + * Set lines pll clk enable to 0 + */ + cdns_torrent_dp_set_a0_pll(cdns_phy, inst->num_lanes); + + /* + * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on + * used lanes + */ + lane_bits = (1 << inst->num_lanes) - 1; + cdns_torrent_dp_write(regmap, PHY_RESET, + ((0xF & ~lane_bits) << 4) | (0xF & lane_bits)); + + /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */ + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001); + + /* + * PHY PMA registers configuration functions + * Initialize PHY with max supported link rate, without SSC. + */ + if (cdns_phy->ref_clk_rate == CLK_19_2_MHZ) + cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy, + cdns_phy->max_bit_rate, + false); + else if (cdns_phy->ref_clk_rate == CLK_25_MHZ) + cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy, + cdns_phy->max_bit_rate, + false); + else if (cdns_phy->ref_clk_rate == CLK_100_MHZ) + cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(cdns_phy, + cdns_phy->max_bit_rate, + false); + + cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate, + inst->num_lanes); + + /* take out of reset */ + regmap_field_write(cdns_phy->phy_reset_ctrl, 0x1); +} + +static int cdns_torrent_dp_start(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, + struct phy *phy) +{ + int ret; + + cdns_torrent_phy_on(phy); + + ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy); + if (ret) + return ret; + + ret = cdns_torrent_dp_run(cdns_phy, inst->num_lanes); + + return ret; +} + +static int cdns_torrent_dp_init(struct phy *phy) +{ + struct cdns_torrent_inst *inst = phy_get_drvdata(phy); + struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); + + switch (cdns_phy->ref_clk_rate) { + case CLK_19_2_MHZ: + case CLK_25_MHZ: + case CLK_100_MHZ: + /* Valid Ref Clock Rate */ + break; + default: + dev_err(cdns_phy->dev, "Unsupported Ref Clock Rate\n"); + return -EINVAL; + } + + cdns_torrent_dp_common_init(cdns_phy, inst); + + return cdns_torrent_dp_start(cdns_phy, inst, phy); +} + +static int cdns_torrent_derived_refclk_enable(struct clk_hw *hw) +{ + struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); + + regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 1); + regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 1); + + return 0; +} + +static void cdns_torrent_derived_refclk_disable(struct clk_hw *hw) +{ + struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); + + regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 0); + regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 0); +} + +static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); + int val; + + regmap_field_read(derived_refclk->cmn_cdiag_refclk_ovrd_4, &val); + + return !!val; +} + +static const struct clk_ops cdns_torrent_derived_refclk_ops = { + .enable = cdns_torrent_derived_refclk_enable, + .disable = cdns_torrent_derived_refclk_disable, + .is_enabled = cdns_torrent_derived_refclk_is_enabled, +}; + +static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_derived_refclk *derived_refclk; + struct device *dev = cdns_phy->dev; + struct clk_init_data *init; + const char *parent_name; + char clk_name[100]; + struct clk_hw *hw; + struct clk *clk; + int ret; + + derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); + if (!derived_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_DERIVED_REFCLK]); + + clk = devm_clk_get_optional(dev, "phy_en_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "No parent clock for derived_refclk\n"); + return PTR_ERR(clk); + } + + init = &derived_refclk->clk_data; + + if (clk) { + parent_name = __clk_get_name(clk); + init->parent_names = &parent_name; + init->num_parents = 1; + } + init->ops = &cdns_torrent_derived_refclk_ops; + init->flags = 0; + init->name = clk_name; + + derived_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0; + derived_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4; + + derived_refclk->hw.init = init; + + hw = &derived_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK] = hw; + + return 0; +} + +static int cdns_torrent_received_refclk_enable(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + + regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 1); + + return 0; +} + +static void cdns_torrent_received_refclk_disable(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + + regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 0); +} + +static int cdns_torrent_received_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + int val, cmn_val; + + regmap_field_read(received_refclk->phy_pipe_cmn_ctrl1_0, &val); + regmap_field_read(received_refclk->cmn_cdiag_refclk_ovrd_4, &cmn_val); + + return val && !cmn_val; +} + +static const struct clk_ops cdns_torrent_received_refclk_ops = { + .enable = cdns_torrent_received_refclk_enable, + .disable = cdns_torrent_received_refclk_disable, + .is_enabled = cdns_torrent_received_refclk_is_enabled, +}; + +static int cdns_torrent_received_refclk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_received_refclk *received_refclk; + struct device *dev = cdns_phy->dev; + struct clk_init_data *init; + const char *parent_name; + char clk_name[100]; + struct clk_hw *hw; + struct clk *clk; + int ret; + + received_refclk = devm_kzalloc(dev, sizeof(*received_refclk), GFP_KERNEL); + if (!received_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_RECEIVED_REFCLK]); + + clk = devm_clk_get_optional(dev, "phy_en_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "No parent clock for received_refclk\n"); + return PTR_ERR(clk); + } + + init = &received_refclk->clk_data; + + if (clk) { + parent_name = __clk_get_name(clk); + init->parent_names = &parent_name; + init->num_parents = 1; + } + init->ops = &cdns_torrent_received_refclk_ops; + init->flags = 0; + init->name = clk_name; + + received_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0; + received_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4; + + received_refclk->hw.init = init; + + hw = &received_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_RECEIVED_REFCLK] = hw; + + return 0; +} + +static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); + + return 0; +} + +static void cdns_torrent_refclk_driver_disable(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 1); +} + +static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + int val; + + regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], &val); + + return !val; +} + +static u8 cdns_torrent_refclk_driver_get_parent(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + unsigned int val; + + regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], &val); + return clk_mux_val_to_index(hw, cdns_torrent_refclk_driver_mux_table, 0, val); +} + +static int cdns_torrent_refclk_driver_set_parent(struct clk_hw *hw, u8 index) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + unsigned int val; + + val = cdns_torrent_refclk_driver_mux_table[index]; + return regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], val); +} + +static const struct clk_ops cdns_torrent_refclk_driver_ops = { + .enable = cdns_torrent_refclk_driver_enable, + .disable = cdns_torrent_refclk_driver_disable, + .is_enabled = cdns_torrent_refclk_driver_is_enabled, + .set_parent = cdns_torrent_refclk_driver_set_parent, + .get_parent = cdns_torrent_refclk_driver_get_parent, +}; + +static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_refclk_driver *refclk_driver; + struct device *dev = cdns_phy->dev; + struct regmap_field *field; + struct clk_init_data *init; + const char **parent_names; + unsigned int num_parents; + struct regmap *regmap; + char clk_name[100]; + struct clk_hw *hw; + int i, ret; + + refclk_driver = devm_kzalloc(dev, sizeof(*refclk_driver), GFP_KERNEL); + if (!refclk_driver) + return -ENOMEM; + + num_parents = ARRAY_SIZE(refclk_driver_parent_index); + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + hw = cdns_phy->clk_hw_data->hws[refclk_driver_parent_index[i]]; + if (IS_ERR_OR_NULL(hw)) { + dev_err(dev, "No parent clock for refclk driver clock\n"); + return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; + } + parent_names[i] = clk_hw_get_name(hw); + } + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_REFCLK_DRIVER]); + + init = &refclk_driver->clk_data; + + init->ops = &cdns_torrent_refclk_driver_ops; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + init->name = clk_name; + + regmap = cdns_phy->regmap_common_cdb; + + for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) { + field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]); + if (IS_ERR(field)) { + dev_err(dev, "Refclk driver CMN reg field init failed\n"); + return PTR_ERR(field); + } + refclk_driver->cmn_fields[i] = field; + } + + /* Enable Derived reference clock as default */ + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); + + refclk_driver->hw.init = init; + + hw = &refclk_driver->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_REFCLK_DRIVER] = hw; + + return 0; +} + +static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, + u32 block_offset, + u8 reg_offset_shift, + const struct regmap_config *config) +{ + struct cdns_regmap_cdb_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + ctx->base = base + block_offset; + ctx->reg_offset_shift = reg_offset_shift; + + return devm_regmap_init(dev, NULL, ctx, config); +} + +static int cdns_torrent_dp_regfield_init(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + struct regmap_field *field; + struct regmap *regmap; + + regmap = cdns_phy->regmap_dptx_phy_reg; + field = devm_regmap_field_alloc(dev, regmap, phy_reset_ctrl); + if (IS_ERR(field)) { + dev_err(dev, "PHY_RESET reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_reset_ctrl = field; + + return 0; +} + +static int cdns_torrent_regfield_init(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + struct regmap_field *field; + struct regmap *regmap; + int i; + + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PLL_CFG reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pll_cfg = field; + + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0); + if (IS_ERR(field)) { + dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pipe_cmn_ctrl1_0 = field; + + regmap = cdns_phy->regmap_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, cmn_cdiag_refclk_ovrd_4); + if (IS_ERR(field)) { + dev_err(dev, "cmn_cdiag_refclk_ovrd_4 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->cmn_cdiag_refclk_ovrd_4 = field; + + regmap = cdns_phy->regmap_phy_pma_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_1); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PMA_CMN_CTRL1 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pma_cmn_ctrl_1 = field; + + regmap = cdns_phy->regmap_phy_pma_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_2); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PMA_CMN_CTRL2 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pma_cmn_ctrl_2 = field; + + regmap = cdns_phy->regmap_phy_pma_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pma_pll_raw_ctrl); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PMA_PLL_RAW_CTRL reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pma_pll_raw_ctrl = field; + + for (i = 0; i < MAX_NUM_LANES; i++) { + regmap = cdns_phy->regmap_phy_pcs_lane_cdb[i]; + field = devm_regmap_field_alloc(dev, regmap, phy_pcs_iso_link_ctrl_1); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PCS_ISO_LINK_CTRL reg field init for ln %d failed\n", i); + return PTR_ERR(field); + } + cdns_phy->phy_pcs_iso_link_ctrl_1[i] = field; + } + + return 0; +} + +static int cdns_torrent_dp_regmap_init(struct cdns_torrent_phy *cdns_phy) +{ + void __iomem *base = cdns_phy->base; + struct device *dev = cdns_phy->dev; + struct regmap *regmap; + u8 reg_offset_shift; + u32 block_offset; + + reg_offset_shift = cdns_phy->init_data->reg_offset_shift; + + block_offset = TORRENT_DPTX_PHY_OFFSET; + regmap = cdns_regmap_init(dev, base, block_offset, + reg_offset_shift, + &cdns_torrent_dptx_phy_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init DPTX PHY regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_dptx_phy_reg = regmap; + + return 0; +} + +static int cdns_torrent_regmap_init(struct cdns_torrent_phy *cdns_phy) +{ + void __iomem *sd_base = cdns_phy->sd_base; + u8 block_offset_shift, reg_offset_shift; + struct device *dev = cdns_phy->dev; + struct regmap *regmap; + u32 block_offset; + int i; + + block_offset_shift = cdns_phy->init_data->block_offset_shift; + reg_offset_shift = cdns_phy->init_data->reg_offset_shift; + + for (i = 0; i < MAX_NUM_LANES; i++) { + block_offset = TORRENT_TX_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, sd_base, block_offset, + reg_offset_shift, + &cdns_torrent_tx_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init tx lane CDB regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_tx_lane_cdb[i] = regmap; + + block_offset = TORRENT_RX_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, sd_base, block_offset, + reg_offset_shift, + &cdns_torrent_rx_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init rx lane CDB regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_rx_lane_cdb[i] = regmap; + + block_offset = TORRENT_PHY_PCS_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, sd_base, block_offset, + reg_offset_shift, + &cdns_torrent_phy_pcs_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PCS lane CDB regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_phy_pcs_lane_cdb[i] = regmap; + } + + block_offset = TORRENT_COMMON_CDB_OFFSET; + regmap = cdns_regmap_init(dev, sd_base, block_offset, + reg_offset_shift, + &cdns_torrent_common_cdb_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init common CDB regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_common_cdb = regmap; + + block_offset = TORRENT_PHY_PCS_COMMON_OFFSET(block_offset_shift); + regmap = cdns_regmap_init(dev, sd_base, block_offset, + reg_offset_shift, + &cdns_torrent_phy_pcs_cmn_cdb_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PCS common CDB regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_phy_pcs_common_cdb = regmap; + + block_offset = TORRENT_PHY_PMA_COMMON_OFFSET(block_offset_shift); + regmap = cdns_regmap_init(dev, sd_base, block_offset, + reg_offset_shift, + &cdns_torrent_phy_pma_cmn_cdb_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PMA common CDB regmap\n"); + return PTR_ERR(regmap); + } + cdns_phy->regmap_phy_pma_common_cdb = regmap; + + return 0; +} + +static int cdns_torrent_phy_init(struct phy *phy) +{ + struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); + const struct cdns_torrent_data *init_data = cdns_phy->init_data; + struct cdns_torrent_vals *cmn_vals, *tx_ln_vals, *rx_ln_vals; + enum cdns_torrent_ref_clk ref_clk = cdns_phy->ref_clk_rate; + struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals; + struct cdns_torrent_inst *inst = phy_get_drvdata(phy); + enum cdns_torrent_phy_type phy_type = inst->phy_type; + enum cdns_torrent_ssc_mode ssc = inst->ssc_mode; + struct cdns_torrent_vals *pcs_cmn_vals; + struct cdns_reg_pairs *reg_pairs; + struct regmap *regmap; + u32 num_regs; + int i, j; + + if (cdns_phy->nsubnodes > 1) + return 0; + + /** + * Spread spectrum generation is not required or supported + * for SGMII/QSGMII + */ + if (phy_type == TYPE_SGMII || phy_type == TYPE_QSGMII) + ssc = NO_SSC; + + /* PHY configuration specific registers for single link */ + link_cmn_vals = init_data->link_cmn_vals[phy_type][TYPE_NONE][ssc]; + if (link_cmn_vals) { + reg_pairs = link_cmn_vals->reg_pairs; + num_regs = link_cmn_vals->num_regs; + regmap = cdns_phy->regmap_common_cdb; + + /** + * First array value in link_cmn_vals must be of + * PHY_PLL_CFG register + */ + regmap_field_write(cdns_phy->phy_pll_cfg, reg_pairs[0].val); + + for (i = 1; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + + xcvr_diag_vals = init_data->xcvr_diag_vals[phy_type][TYPE_NONE][ssc]; + if (xcvr_diag_vals) { + reg_pairs = xcvr_diag_vals->reg_pairs; + num_regs = xcvr_diag_vals->num_regs; + for (i = 0; i < inst->num_lanes; i++) { + regmap = cdns_phy->regmap_tx_lane_cdb[i + inst->mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } + } + + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + + /* PMA common registers configurations */ + cmn_vals = init_data->cmn_vals[ref_clk][phy_type][TYPE_NONE][ssc]; + if (cmn_vals) { + reg_pairs = cmn_vals->reg_pairs; + num_regs = cmn_vals->num_regs; + regmap = cdns_phy->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + + /* PMA TX lane registers configurations */ + tx_ln_vals = init_data->tx_ln_vals[ref_clk][phy_type][TYPE_NONE][ssc]; + if (tx_ln_vals) { + reg_pairs = tx_ln_vals->reg_pairs; + num_regs = tx_ln_vals->num_regs; + for (i = 0; i < inst->num_lanes; i++) { + regmap = cdns_phy->regmap_tx_lane_cdb[i + inst->mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } + } + + /* PMA RX lane registers configurations */ + rx_ln_vals = init_data->rx_ln_vals[ref_clk][phy_type][TYPE_NONE][ssc]; + if (rx_ln_vals) { + reg_pairs = rx_ln_vals->reg_pairs; + num_regs = rx_ln_vals->num_regs; + for (i = 0; i < inst->num_lanes; i++) { + regmap = cdns_phy->regmap_rx_lane_cdb[i + inst->mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } + } + + if (phy_type == TYPE_DP) + return cdns_torrent_dp_init(phy); + + return 0; +} + +static const struct phy_ops cdns_torrent_phy_ops = { + .init = cdns_torrent_phy_init, + .configure = cdns_torrent_dp_configure, + .power_on = cdns_torrent_phy_on, + .power_off = cdns_torrent_phy_off, + .owner = THIS_MODULE, +}; + +static int cdns_torrent_noop_phy_on(struct phy *phy) +{ + /* Give 5ms to 10ms delay for the PIPE clock to be stable */ + usleep_range(5000, 10000); + + return 0; +} + +static const struct phy_ops noop_ops = { + .power_on = cdns_torrent_noop_phy_on, + .owner = THIS_MODULE, +}; + +static +int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) +{ + const struct cdns_torrent_data *init_data = cdns_phy->init_data; + struct cdns_torrent_vals *cmn_vals, *tx_ln_vals, *rx_ln_vals; + enum cdns_torrent_ref_clk ref_clk = cdns_phy->ref_clk_rate; + struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals; + enum cdns_torrent_phy_type phy_t1, phy_t2; + struct cdns_torrent_vals *pcs_cmn_vals; + int i, j, node, mlane, num_lanes, ret; + struct cdns_reg_pairs *reg_pairs; + enum cdns_torrent_ssc_mode ssc; + struct regmap *regmap; + u32 num_regs; + + /* Maximum 2 links (subnodes) are supported */ + if (cdns_phy->nsubnodes != 2) + return -EINVAL; + + phy_t1 = cdns_phy->phys[0].phy_type; + phy_t2 = cdns_phy->phys[1].phy_type; + + /** + * First configure the PHY for first link with phy_t1. Get the array + * values as [phy_t1][phy_t2][ssc]. + */ + for (node = 0; node < cdns_phy->nsubnodes; node++) { + if (node == 1) { + /** + * If first link with phy_t1 is configured, then + * configure the PHY for second link with phy_t2. + * Get the array values as [phy_t2][phy_t1][ssc]. + */ + swap(phy_t1, phy_t2); + } + + mlane = cdns_phy->phys[node].mlane; + ssc = cdns_phy->phys[node].ssc_mode; + num_lanes = cdns_phy->phys[node].num_lanes; + + /** + * PHY configuration specific registers: + * link_cmn_vals depend on combination of PHY types being + * configured and are common for both PHY types, so array + * values should be same for [phy_t1][phy_t2][ssc] and + * [phy_t2][phy_t1][ssc]. + * xcvr_diag_vals also depend on combination of PHY types + * being configured, but these can be different for particular + * PHY type and are per lane. + */ + link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc]; + if (link_cmn_vals) { + reg_pairs = link_cmn_vals->reg_pairs; + num_regs = link_cmn_vals->num_regs; + regmap = cdns_phy->regmap_common_cdb; + + /** + * First array value in link_cmn_vals must be of + * PHY_PLL_CFG register + */ + regmap_field_write(cdns_phy->phy_pll_cfg, + reg_pairs[0].val); + + for (i = 1; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + + xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc]; + if (xcvr_diag_vals) { + reg_pairs = xcvr_diag_vals->reg_pairs; + num_regs = xcvr_diag_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } + } + + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + + /* PMA common registers configurations */ + cmn_vals = init_data->cmn_vals[ref_clk][phy_t1][phy_t2][ssc]; + if (cmn_vals) { + reg_pairs = cmn_vals->reg_pairs; + num_regs = cmn_vals->num_regs; + regmap = cdns_phy->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + + /* PMA TX lane registers configurations */ + tx_ln_vals = init_data->tx_ln_vals[ref_clk][phy_t1][phy_t2][ssc]; + if (tx_ln_vals) { + reg_pairs = tx_ln_vals->reg_pairs; + num_regs = tx_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } + } + + /* PMA RX lane registers configurations */ + rx_ln_vals = init_data->rx_ln_vals[ref_clk][phy_t1][phy_t2][ssc]; + if (rx_ln_vals) { + reg_pairs = rx_ln_vals->reg_pairs; + num_regs = rx_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, + reg_pairs[j].val); + } + } + + reset_control_deassert(cdns_phy->phys[node].lnk_rst); + } + + /* Take the PHY out of reset */ + ret = reset_control_deassert(cdns_phy->phy_rst); + if (ret) + return ret; + + return 0; +} + +static void cdns_torrent_clk_cleanup(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + + of_clk_del_provider(dev->of_node); +} + +static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + struct device_node *node = dev->of_node; + struct clk_hw_onecell_data *data; + int ret; + + data = devm_kzalloc(dev, struct_size(data, hws, CDNS_TORRENT_OUTPUT_CLOCKS), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num = CDNS_TORRENT_OUTPUT_CLOCKS; + cdns_phy->clk_hw_data = data; + + ret = cdns_torrent_derived_refclk_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register derived refclk\n"); + return ret; + } + + ret = cdns_torrent_received_refclk_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register received refclk\n"); + return ret; + } + + ret = cdns_torrent_refclk_driver_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register refclk driver\n"); + return ret; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data); + if (ret) { + dev_err(dev, "Failed to add clock provider: %s\n", node->name); + return ret; + } + + return 0; +} + +static int cdns_torrent_reset(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + + cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0); + if (IS_ERR(cdns_phy->phy_rst)) { + dev_err(dev, "%s: failed to get reset\n", + dev->of_node->full_name); + return PTR_ERR(cdns_phy->phy_rst); + } + + cdns_phy->apb_rst = devm_reset_control_get_optional_exclusive(dev, "torrent_apb"); + if (IS_ERR(cdns_phy->apb_rst)) { + dev_err(dev, "%s: failed to get apb reset\n", + dev->of_node->full_name); + return PTR_ERR(cdns_phy->apb_rst); + } + + return 0; +} + +static int cdns_torrent_clk(struct cdns_torrent_phy *cdns_phy) +{ + struct device *dev = cdns_phy->dev; + unsigned long ref_clk_rate; + int ret; + + cdns_phy->clk = devm_clk_get(dev, "refclk"); + if (IS_ERR(cdns_phy->clk)) { + dev_err(dev, "phy ref clock not found\n"); + return PTR_ERR(cdns_phy->clk); + } + + ret = clk_prepare_enable(cdns_phy->clk); + if (ret) { + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); + return ret; + } + + ref_clk_rate = clk_get_rate(cdns_phy->clk); + if (!ref_clk_rate) { + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); + clk_disable_unprepare(cdns_phy->clk); + return -EINVAL; + } + + switch (ref_clk_rate) { + case REF_CLK_19_2MHZ: + cdns_phy->ref_clk_rate = CLK_19_2_MHZ; + break; + case REF_CLK_25MHZ: + cdns_phy->ref_clk_rate = CLK_25_MHZ; + break; + case REF_CLK_100MHZ: + cdns_phy->ref_clk_rate = CLK_100_MHZ; + break; + default: + dev_err(cdns_phy->dev, "Invalid Ref Clock Rate\n"); + clk_disable_unprepare(cdns_phy->clk); + return -EINVAL; + } + + return 0; +} + +static int cdns_torrent_phy_probe(struct platform_device *pdev) +{ + struct cdns_torrent_phy *cdns_phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + const struct cdns_torrent_data *data; + struct device_node *child; + int ret, subnodes, node = 0, i; + u32 total_num_lanes = 0; + int already_configured; + u8 init_dp_regmap = 0; + u32 phy_type; + + /* Get init data for this PHY */ + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL); + if (!cdns_phy) + return -ENOMEM; + + dev_set_drvdata(dev, cdns_phy); + cdns_phy->dev = dev; + cdns_phy->init_data = data; + + cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(cdns_phy->sd_base)) + return PTR_ERR(cdns_phy->sd_base); + + subnodes = of_get_available_child_count(dev->of_node); + if (subnodes == 0) { + dev_err(dev, "No available link subnodes found\n"); + return -EINVAL; + } + + ret = cdns_torrent_regmap_init(cdns_phy); + if (ret) + return ret; + + ret = cdns_torrent_regfield_init(cdns_phy); + if (ret) + return ret; + + ret = cdns_torrent_clk_register(cdns_phy); + if (ret) + return ret; + + regmap_field_read(cdns_phy->phy_pma_cmn_ctrl_1, &already_configured); + + if (!already_configured) { + ret = cdns_torrent_reset(cdns_phy); + if (ret) + goto clk_cleanup; + + ret = cdns_torrent_clk(cdns_phy); + if (ret) + goto clk_cleanup; + + /* Enable APB */ + reset_control_deassert(cdns_phy->apb_rst); + } + + for_each_available_child_of_node(dev->of_node, child) { + struct phy *gphy; + + /* PHY subnode name must be 'phy'. */ + if (!(of_node_name_eq(child, "phy"))) + continue; + + cdns_phy->phys[node].lnk_rst = + of_reset_control_array_get_exclusive(child); + if (IS_ERR(cdns_phy->phys[node].lnk_rst)) { + dev_err(dev, "%s: failed to get reset\n", + child->full_name); + ret = PTR_ERR(cdns_phy->phys[node].lnk_rst); + goto put_lnk_rst; + } + + if (of_property_read_u32(child, "reg", + &cdns_phy->phys[node].mlane)) { + dev_err(dev, "%s: No \"reg\"-property.\n", + child->full_name); + ret = -EINVAL; + goto put_child; + } + + if (of_property_read_u32(child, "cdns,phy-type", &phy_type)) { + dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n", + child->full_name); + ret = -EINVAL; + goto put_child; + } + + switch (phy_type) { + case PHY_TYPE_PCIE: + cdns_phy->phys[node].phy_type = TYPE_PCIE; + break; + case PHY_TYPE_DP: + cdns_phy->phys[node].phy_type = TYPE_DP; + break; + case PHY_TYPE_SGMII: + cdns_phy->phys[node].phy_type = TYPE_SGMII; + break; + case PHY_TYPE_QSGMII: + cdns_phy->phys[node].phy_type = TYPE_QSGMII; + break; + case PHY_TYPE_USB3: + cdns_phy->phys[node].phy_type = TYPE_USB; + break; + default: + dev_err(dev, "Unsupported protocol\n"); + ret = -EINVAL; + goto put_child; + } + + if (of_property_read_u32(child, "cdns,num-lanes", + &cdns_phy->phys[node].num_lanes)) { + dev_err(dev, "%s: No \"cdns,num-lanes\"-property.\n", + child->full_name); + ret = -EINVAL; + goto put_child; + } + + total_num_lanes += cdns_phy->phys[node].num_lanes; + + /* Get SSC mode */ + cdns_phy->phys[node].ssc_mode = NO_SSC; + of_property_read_u32(child, "cdns,ssc-mode", + &cdns_phy->phys[node].ssc_mode); + + if (!already_configured) + gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops); + else + gphy = devm_phy_create(dev, child, &noop_ops); + if (IS_ERR(gphy)) { + ret = PTR_ERR(gphy); + goto put_child; + } + + if (cdns_phy->phys[node].phy_type == TYPE_DP) { + switch (cdns_phy->phys[node].num_lanes) { + case 1: + case 2: + case 4: + /* valid number of lanes */ + break; + default: + dev_err(dev, "unsupported number of lanes: %d\n", + cdns_phy->phys[node].num_lanes); + ret = -EINVAL; + goto put_child; + } + + cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE; + of_property_read_u32(child, "cdns,max-bit-rate", + &cdns_phy->max_bit_rate); + + switch (cdns_phy->max_bit_rate) { + case 1620: + case 2160: + case 2430: + case 2700: + case 3240: + case 4320: + case 5400: + case 8100: + /* valid bit rate */ + break; + default: + dev_err(dev, "unsupported max bit rate: %dMbps\n", + cdns_phy->max_bit_rate); + ret = -EINVAL; + goto put_child; + } + + /* DPTX registers */ + cdns_phy->base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(cdns_phy->base)) { + ret = PTR_ERR(cdns_phy->base); + goto put_child; + } + + if (!init_dp_regmap) { + ret = cdns_torrent_dp_regmap_init(cdns_phy); + if (ret) + goto put_child; + + ret = cdns_torrent_dp_regfield_init(cdns_phy); + if (ret) + goto put_child; + + init_dp_regmap++; + } + + dev_dbg(dev, "DP max bit rate %d.%03d Gbps\n", + cdns_phy->max_bit_rate / 1000, + cdns_phy->max_bit_rate % 1000); + + gphy->attrs.bus_width = cdns_phy->phys[node].num_lanes; + gphy->attrs.max_link_rate = cdns_phy->max_bit_rate; + gphy->attrs.mode = PHY_MODE_DP; + } + + cdns_phy->phys[node].phy = gphy; + phy_set_drvdata(gphy, &cdns_phy->phys[node]); + + node++; + } + cdns_phy->nsubnodes = node; + + if (total_num_lanes > MAX_NUM_LANES) { + dev_err(dev, "Invalid lane configuration\n"); + ret = -EINVAL; + goto put_lnk_rst; + } + + if (cdns_phy->nsubnodes > 1 && !already_configured) { + ret = cdns_torrent_phy_configure_multilink(cdns_phy); + if (ret) + goto put_lnk_rst; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + goto put_lnk_rst; + } + + if (cdns_phy->nsubnodes > 1) + dev_dbg(dev, "Multi-link: %s (%d lanes) & %s (%d lanes)", + cdns_torrent_get_phy_type(cdns_phy->phys[0].phy_type), + cdns_phy->phys[0].num_lanes, + cdns_torrent_get_phy_type(cdns_phy->phys[1].phy_type), + cdns_phy->phys[1].num_lanes); + else + dev_dbg(dev, "Single link: %s (%d lanes)", + cdns_torrent_get_phy_type(cdns_phy->phys[0].phy_type), + cdns_phy->phys[0].num_lanes); + + return 0; + +put_child: + node++; +put_lnk_rst: + for (i = 0; i < node; i++) + reset_control_put(cdns_phy->phys[i].lnk_rst); + of_node_put(child); + reset_control_assert(cdns_phy->apb_rst); + clk_disable_unprepare(cdns_phy->clk); +clk_cleanup: + cdns_torrent_clk_cleanup(cdns_phy); + return ret; +} + +static int cdns_torrent_phy_remove(struct platform_device *pdev) +{ + struct cdns_torrent_phy *cdns_phy = platform_get_drvdata(pdev); + int i; + + reset_control_assert(cdns_phy->phy_rst); + reset_control_assert(cdns_phy->apb_rst); + for (i = 0; i < cdns_phy->nsubnodes; i++) { + reset_control_assert(cdns_phy->phys[i].lnk_rst); + reset_control_put(cdns_phy->phys[i].lnk_rst); + } + + clk_disable_unprepare(cdns_phy->clk); + cdns_torrent_clk_cleanup(cdns_phy); + + return 0; +} + +/* Single DisplayPort(DP) link configuration */ +static struct cdns_reg_pairs sl_dp_link_cmn_regs[] = { + {0x0000, PHY_PLL_CFG}, +}; + +static struct cdns_reg_pairs sl_dp_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals sl_dp_link_cmn_vals = { + .reg_pairs = sl_dp_link_cmn_regs, + .num_regs = ARRAY_SIZE(sl_dp_link_cmn_regs), +}; + +static struct cdns_torrent_vals sl_dp_xcvr_diag_ln_vals = { + .reg_pairs = sl_dp_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_xcvr_diag_ln_regs), +}; + +/* Single DP, 19.2 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_dp_19_2_no_ssc_cmn_regs[] = { + {0x0014, CMN_SSM_BIAS_TMR}, + {0x0027, CMN_PLLSM0_PLLPRE_TMR}, + {0x00A1, CMN_PLLSM0_PLLLOCK_TMR}, + {0x0027, CMN_PLLSM1_PLLPRE_TMR}, + {0x00A1, CMN_PLLSM1_PLLLOCK_TMR}, + {0x0060, CMN_BGCAL_INIT_TMR}, + {0x0060, CMN_BGCAL_ITER_TMR}, + {0x0014, CMN_IBCAL_INIT_TMR}, + {0x0018, CMN_TXPUCAL_INIT_TMR}, + {0x0005, CMN_TXPUCAL_ITER_TMR}, + {0x0018, CMN_TXPDCAL_INIT_TMR}, + {0x0005, CMN_TXPDCAL_ITER_TMR}, + {0x0240, CMN_RXCAL_INIT_TMR}, + {0x0005, CMN_RXCAL_ITER_TMR}, + {0x0002, CMN_SD_CAL_INIT_TMR}, + {0x0002, CMN_SD_CAL_ITER_TMR}, + {0x000B, CMN_SD_CAL_REFTIM_START}, + {0x0137, CMN_SD_CAL_PLLCNT_START}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x00C0, CMN_PLL0_VCOCAL_INIT_TMR}, + {0x0004, CMN_PLL0_VCOCAL_ITER_TMR}, + {0x00C0, CMN_PLL1_VCOCAL_INIT_TMR}, + {0x0004, CMN_PLL1_VCOCAL_ITER_TMR}, + {0x0260, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0260, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +static struct cdns_reg_pairs sl_dp_19_2_no_ssc_tx_ln_regs[] = { + {0x0780, TX_RCVDET_ST_TMR}, + {0x00FB, TX_PSC_A0}, + {0x04AA, TX_PSC_A2}, + {0x04AA, TX_PSC_A3}, + {0x000F, XCVR_DIAG_BIDI_CTRL} +}; + +static struct cdns_reg_pairs sl_dp_19_2_no_ssc_rx_ln_regs[] = { + {0x0000, RX_PSC_A0}, + {0x0000, RX_PSC_A2}, + {0x0000, RX_PSC_A3}, + {0x0000, RX_PSC_CAL}, + {0x0000, RX_REE_GCSM1_CTRL}, + {0x0000, RX_REE_GCSM2_CTRL}, + {0x0000, RX_REE_PERGCSM_CTRL} +}; + +static struct cdns_torrent_vals sl_dp_19_2_no_ssc_cmn_vals = { + .reg_pairs = sl_dp_19_2_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_dp_19_2_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals sl_dp_19_2_no_ssc_tx_ln_vals = { + .reg_pairs = sl_dp_19_2_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_19_2_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals sl_dp_19_2_no_ssc_rx_ln_vals = { + .reg_pairs = sl_dp_19_2_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_19_2_no_ssc_rx_ln_regs), +}; + +/* Single DP, 25 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_dp_25_no_ssc_cmn_regs[] = { + {0x0019, CMN_SSM_BIAS_TMR}, + {0x0032, CMN_PLLSM0_PLLPRE_TMR}, + {0x00D1, CMN_PLLSM0_PLLLOCK_TMR}, + {0x0032, CMN_PLLSM1_PLLPRE_TMR}, + {0x00D1, CMN_PLLSM1_PLLLOCK_TMR}, + {0x007D, CMN_BGCAL_INIT_TMR}, + {0x007D, CMN_BGCAL_ITER_TMR}, + {0x0019, CMN_IBCAL_INIT_TMR}, + {0x001E, CMN_TXPUCAL_INIT_TMR}, + {0x0006, CMN_TXPUCAL_ITER_TMR}, + {0x001E, CMN_TXPDCAL_INIT_TMR}, + {0x0006, CMN_TXPDCAL_ITER_TMR}, + {0x02EE, CMN_RXCAL_INIT_TMR}, + {0x0006, CMN_RXCAL_ITER_TMR}, + {0x0002, CMN_SD_CAL_INIT_TMR}, + {0x0002, CMN_SD_CAL_ITER_TMR}, + {0x000E, CMN_SD_CAL_REFTIM_START}, + {0x012B, CMN_SD_CAL_PLLCNT_START}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x00FA, CMN_PLL0_VCOCAL_INIT_TMR}, + {0x0004, CMN_PLL0_VCOCAL_ITER_TMR}, + {0x00FA, CMN_PLL1_VCOCAL_INIT_TMR}, + {0x0004, CMN_PLL1_VCOCAL_ITER_TMR}, + {0x0317, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0317, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +static struct cdns_reg_pairs sl_dp_25_no_ssc_tx_ln_regs[] = { + {0x09C4, TX_RCVDET_ST_TMR}, + {0x00FB, TX_PSC_A0}, + {0x04AA, TX_PSC_A2}, + {0x04AA, TX_PSC_A3}, + {0x000F, XCVR_DIAG_BIDI_CTRL} +}; + +static struct cdns_reg_pairs sl_dp_25_no_ssc_rx_ln_regs[] = { + {0x0000, RX_PSC_A0}, + {0x0000, RX_PSC_A2}, + {0x0000, RX_PSC_A3}, + {0x0000, RX_PSC_CAL}, + {0x0000, RX_REE_GCSM1_CTRL}, + {0x0000, RX_REE_GCSM2_CTRL}, + {0x0000, RX_REE_PERGCSM_CTRL} +}; + +static struct cdns_torrent_vals sl_dp_25_no_ssc_cmn_vals = { + .reg_pairs = sl_dp_25_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_dp_25_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals sl_dp_25_no_ssc_tx_ln_vals = { + .reg_pairs = sl_dp_25_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_25_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals sl_dp_25_no_ssc_rx_ln_vals = { + .reg_pairs = sl_dp_25_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_25_no_ssc_rx_ln_regs), +}; + +/* Single DP, 100 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_dp_100_no_ssc_cmn_regs[] = { + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +static struct cdns_reg_pairs sl_dp_100_no_ssc_tx_ln_regs[] = { + {0x00FB, TX_PSC_A0}, + {0x04AA, TX_PSC_A2}, + {0x04AA, TX_PSC_A3}, + {0x000F, XCVR_DIAG_BIDI_CTRL} +}; + +static struct cdns_reg_pairs sl_dp_100_no_ssc_rx_ln_regs[] = { + {0x0000, RX_PSC_A0}, + {0x0000, RX_PSC_A2}, + {0x0000, RX_PSC_A3}, + {0x0000, RX_PSC_CAL}, + {0x0000, RX_REE_GCSM1_CTRL}, + {0x0000, RX_REE_GCSM2_CTRL}, + {0x0000, RX_REE_PERGCSM_CTRL} +}; + +static struct cdns_torrent_vals sl_dp_100_no_ssc_cmn_vals = { + .reg_pairs = sl_dp_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_dp_100_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals sl_dp_100_no_ssc_tx_ln_vals = { + .reg_pairs = sl_dp_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals sl_dp_100_no_ssc_rx_ln_vals = { + .reg_pairs = sl_dp_100_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(sl_dp_100_no_ssc_rx_ln_regs), +}; + +/* USB and SGMII/QSGMII link configuration */ +static struct cdns_reg_pairs usb_sgmii_link_cmn_regs[] = { + {0x0002, PHY_PLL_CFG}, + {0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0}, + {0x0601, CMN_PDIAG_PLL1_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs usb_sgmii_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0041, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_reg_pairs sgmii_usb_xcvr_diag_ln_regs[] = { + {0x0011, XCVR_DIAG_HSCLK_SEL}, + {0x0003, XCVR_DIAG_HSCLK_DIV}, + {0x009B, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals usb_sgmii_link_cmn_vals = { + .reg_pairs = usb_sgmii_link_cmn_regs, + .num_regs = ARRAY_SIZE(usb_sgmii_link_cmn_regs), +}; + +static struct cdns_torrent_vals usb_sgmii_xcvr_diag_ln_vals = { + .reg_pairs = usb_sgmii_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(usb_sgmii_xcvr_diag_ln_regs), +}; + +static struct cdns_torrent_vals sgmii_usb_xcvr_diag_ln_vals = { + .reg_pairs = sgmii_usb_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(sgmii_usb_xcvr_diag_ln_regs), +}; + +/* PCIe and USB Unique SSC link configuration */ +static struct cdns_reg_pairs pcie_usb_link_cmn_regs[] = { + {0x0003, PHY_PLL_CFG}, + {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0}, + {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1}, + {0x8600, CMN_PDIAG_PLL1_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs pcie_usb_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0012, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_reg_pairs usb_pcie_xcvr_diag_ln_regs[] = { + {0x0011, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x00C9, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals pcie_usb_link_cmn_vals = { + .reg_pairs = pcie_usb_link_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_usb_link_cmn_regs), +}; + +static struct cdns_torrent_vals pcie_usb_xcvr_diag_ln_vals = { + .reg_pairs = pcie_usb_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(pcie_usb_xcvr_diag_ln_regs), +}; + +static struct cdns_torrent_vals usb_pcie_xcvr_diag_ln_vals = { + .reg_pairs = usb_pcie_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(usb_pcie_xcvr_diag_ln_regs), +}; + +/* USB 100 MHz Ref clk, internal SSC */ +static struct cdns_reg_pairs usb_100_int_ssc_cmn_regs[] = { + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M1}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0064, CMN_PLL0_INTDIV_M0}, + {0x0050, CMN_PLL0_INTDIV_M1}, + {0x0064, CMN_PLL1_INTDIV_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M1}, + {0x0002, CMN_PLL1_FRACDIVH_M0}, + {0x0044, CMN_PLL0_HIGH_THR_M0}, + {0x0036, CMN_PLL0_HIGH_THR_M1}, + {0x0044, CMN_PLL1_HIGH_THR_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M1}, + {0x0002, CMN_PDIAG_PLL1_CTRL_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M1}, + {0x0001, CMN_PLL1_SS_CTRL1_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M1}, + {0x011B, CMN_PLL1_SS_CTRL2_M0}, + {0x006E, CMN_PLL0_SS_CTRL3_M0}, + {0x0058, CMN_PLL0_SS_CTRL3_M1}, + {0x006E, CMN_PLL1_SS_CTRL3_M0}, + {0x000E, CMN_PLL0_SS_CTRL4_M0}, + {0x0012, CMN_PLL0_SS_CTRL4_M1}, + {0x000E, CMN_PLL1_SS_CTRL4_M0}, + {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, + {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, + {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, + {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, + {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_torrent_vals usb_100_int_ssc_cmn_vals = { + .reg_pairs = usb_100_int_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(usb_100_int_ssc_cmn_regs), +}; + +/* Single USB link configuration */ +static struct cdns_reg_pairs sl_usb_link_cmn_regs[] = { + {0x0000, PHY_PLL_CFG}, + {0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs sl_usb_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0041, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals sl_usb_link_cmn_vals = { + .reg_pairs = sl_usb_link_cmn_regs, + .num_regs = ARRAY_SIZE(sl_usb_link_cmn_regs), +}; + +static struct cdns_torrent_vals sl_usb_xcvr_diag_ln_vals = { + .reg_pairs = sl_usb_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(sl_usb_xcvr_diag_ln_regs), +}; + +/* USB PHY PCS common configuration */ +static struct cdns_reg_pairs usb_phy_pcs_cmn_regs[] = { + {0x0A0A, PHY_PIPE_USB3_GEN2_PRE_CFG0}, + {0x1000, PHY_PIPE_USB3_GEN2_POST_CFG0}, + {0x0010, PHY_PIPE_USB3_GEN2_POST_CFG1} +}; + +static struct cdns_torrent_vals usb_phy_pcs_cmn_vals = { + .reg_pairs = usb_phy_pcs_cmn_regs, + .num_regs = ARRAY_SIZE(usb_phy_pcs_cmn_regs), +}; + +/* USB 100 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_usb_100_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL}, + {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, + {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD} +}; + +static struct cdns_torrent_vals sl_usb_100_no_ssc_cmn_vals = { + .reg_pairs = sl_usb_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_usb_100_no_ssc_cmn_regs), +}; + +static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = { + {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, + {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_reg_pairs usb_100_no_ssc_tx_ln_regs[] = { + {0x02FF, TX_PSC_A0}, + {0x06AF, TX_PSC_A1}, + {0x06AE, TX_PSC_A2}, + {0x06AE, TX_PSC_A3}, + {0x2A82, TX_TXCC_CTRL}, + {0x0014, TX_TXCC_CPOST_MULT_01}, + {0x0003, XCVR_DIAG_PSC_OVRD} +}; + +static struct cdns_reg_pairs usb_100_no_ssc_rx_ln_regs[] = { + {0x0D1D, RX_PSC_A0}, + {0x0D1D, RX_PSC_A1}, + {0x0D00, RX_PSC_A2}, + {0x0500, RX_PSC_A3}, + {0x0013, RX_SIGDET_HL_FILT_TMR}, + {0x0000, RX_REE_GCSM1_CTRL}, + {0x0C02, RX_REE_ATTEN_THR}, + {0x0330, RX_REE_SMGM_CTRL1}, + {0x0300, RX_REE_SMGM_CTRL2}, + {0x0019, RX_REE_TAP1_CLIP}, + {0x0019, RX_REE_TAP2TON_CLIP}, + {0x1004, RX_DIAG_SIGDET_TUNE}, + {0x00F9, RX_DIAG_NQST_CTRL}, + {0x0C01, RX_DIAG_DFE_AMP_TUNE_2}, + {0x0002, RX_DIAG_DFE_AMP_TUNE_3}, + {0x0000, RX_DIAG_PI_CAP}, + {0x0031, RX_DIAG_PI_RATE}, + {0x0001, RX_DIAG_ACYA}, + {0x018C, RX_CDRLF_CNFG}, + {0x0003, RX_CDRLF_CNFG3} +}; + +static struct cdns_torrent_vals usb_100_no_ssc_cmn_vals = { + .reg_pairs = usb_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(usb_100_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals usb_100_no_ssc_tx_ln_vals = { + .reg_pairs = usb_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(usb_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals usb_100_no_ssc_rx_ln_vals = { + .reg_pairs = usb_100_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(usb_100_no_ssc_rx_ln_regs), +}; + +/* Single link USB, 100 MHz Ref clk, internal SSC */ +static struct cdns_reg_pairs sl_usb_100_int_ssc_cmn_regs[] = { + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0064, CMN_PLL0_INTDIV_M0}, + {0x0064, CMN_PLL1_INTDIV_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M0}, + {0x0002, CMN_PLL1_FRACDIVH_M0}, + {0x0044, CMN_PLL0_HIGH_THR_M0}, + {0x0044, CMN_PLL1_HIGH_THR_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M0}, + {0x0002, CMN_PDIAG_PLL1_CTRL_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M0}, + {0x0001, CMN_PLL1_SS_CTRL1_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M0}, + {0x011B, CMN_PLL1_SS_CTRL2_M0}, + {0x006E, CMN_PLL0_SS_CTRL3_M0}, + {0x006E, CMN_PLL1_SS_CTRL3_M0}, + {0x000E, CMN_PLL0_SS_CTRL4_M0}, + {0x000E, CMN_PLL1_SS_CTRL4_M0}, + {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL}, + {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, + {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, + {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, + {0x8200, CMN_CDIAG_CDB_PWRI_OVRD}, + {0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD} +}; + +static struct cdns_torrent_vals sl_usb_100_int_ssc_cmn_vals = { + .reg_pairs = sl_usb_100_int_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_usb_100_int_ssc_cmn_regs), +}; + +/* PCIe and SGMII/QSGMII Unique SSC link configuration */ +static struct cdns_reg_pairs pcie_sgmii_link_cmn_regs[] = { + {0x0003, PHY_PLL_CFG}, + {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0}, + {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1}, + {0x0601, CMN_PDIAG_PLL1_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs pcie_sgmii_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0012, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_reg_pairs sgmii_pcie_xcvr_diag_ln_regs[] = { + {0x0011, XCVR_DIAG_HSCLK_SEL}, + {0x0003, XCVR_DIAG_HSCLK_DIV}, + {0x009B, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals pcie_sgmii_link_cmn_vals = { + .reg_pairs = pcie_sgmii_link_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_sgmii_link_cmn_regs), +}; + +static struct cdns_torrent_vals pcie_sgmii_xcvr_diag_ln_vals = { + .reg_pairs = pcie_sgmii_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(pcie_sgmii_xcvr_diag_ln_regs), +}; + +static struct cdns_torrent_vals sgmii_pcie_xcvr_diag_ln_vals = { + .reg_pairs = sgmii_pcie_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(sgmii_pcie_xcvr_diag_ln_regs), +}; + +/* SGMII 100 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_sgmii_100_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +static struct cdns_torrent_vals sl_sgmii_100_no_ssc_cmn_vals = { + .reg_pairs = sl_sgmii_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_sgmii_100_no_ssc_cmn_regs), +}; + +static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = { + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_reg_pairs sgmii_100_no_ssc_tx_ln_regs[] = { + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x00B3, DRV_DIAG_TX_DRV} +}; + +static struct cdns_reg_pairs ti_sgmii_100_no_ssc_tx_ln_regs[] = { + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x00B3, DRV_DIAG_TX_DRV}, + {0x4000, XCVR_DIAG_RXCLK_CTRL}, +}; + +static struct cdns_reg_pairs sgmii_100_no_ssc_rx_ln_regs[] = { + {0x091D, RX_PSC_A0}, + {0x0900, RX_PSC_A2}, + {0x0100, RX_PSC_A3}, + {0x03C7, RX_REE_GCSM1_EQENM_PH1}, + {0x01C7, RX_REE_GCSM1_EQENM_PH2}, + {0x0000, RX_DIAG_DFE_CTRL}, + {0x0019, RX_REE_TAP1_CLIP}, + {0x0019, RX_REE_TAP2TON_CLIP}, + {0x0098, RX_DIAG_NQST_CTRL}, + {0x0C01, RX_DIAG_DFE_AMP_TUNE_2}, + {0x0000, RX_DIAG_DFE_AMP_TUNE_3}, + {0x0000, RX_DIAG_PI_CAP}, + {0x0010, RX_DIAG_PI_RATE}, + {0x0001, RX_DIAG_ACYA}, + {0x018C, RX_CDRLF_CNFG}, +}; + +static struct cdns_torrent_vals sgmii_100_no_ssc_cmn_vals = { + .reg_pairs = sgmii_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals sgmii_100_no_ssc_tx_ln_vals = { + .reg_pairs = sgmii_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals ti_sgmii_100_no_ssc_tx_ln_vals = { + .reg_pairs = ti_sgmii_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(ti_sgmii_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals sgmii_100_no_ssc_rx_ln_vals = { + .reg_pairs = sgmii_100_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(sgmii_100_no_ssc_rx_ln_regs), +}; + +/* SGMII 100 MHz Ref clk, internal SSC */ +static struct cdns_reg_pairs sgmii_100_int_ssc_cmn_regs[] = { + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M1}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0064, CMN_PLL0_INTDIV_M0}, + {0x0050, CMN_PLL0_INTDIV_M1}, + {0x0064, CMN_PLL1_INTDIV_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M1}, + {0x0002, CMN_PLL1_FRACDIVH_M0}, + {0x0044, CMN_PLL0_HIGH_THR_M0}, + {0x0036, CMN_PLL0_HIGH_THR_M1}, + {0x0044, CMN_PLL1_HIGH_THR_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M1}, + {0x0002, CMN_PDIAG_PLL1_CTRL_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M1}, + {0x0001, CMN_PLL1_SS_CTRL1_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M1}, + {0x011B, CMN_PLL1_SS_CTRL2_M0}, + {0x006E, CMN_PLL0_SS_CTRL3_M0}, + {0x0058, CMN_PLL0_SS_CTRL3_M1}, + {0x006E, CMN_PLL1_SS_CTRL3_M0}, + {0x000E, CMN_PLL0_SS_CTRL4_M0}, + {0x0012, CMN_PLL0_SS_CTRL4_M1}, + {0x000E, CMN_PLL1_SS_CTRL4_M0}, + {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, + {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, + {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_torrent_vals sgmii_100_int_ssc_cmn_vals = { + .reg_pairs = sgmii_100_int_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sgmii_100_int_ssc_cmn_regs), +}; + +/* QSGMII 100 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_qsgmii_100_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +static struct cdns_torrent_vals sl_qsgmii_100_no_ssc_cmn_vals = { + .reg_pairs = sl_qsgmii_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_qsgmii_100_no_ssc_cmn_regs), +}; + +static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = { + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_reg_pairs qsgmii_100_no_ssc_tx_ln_regs[] = { + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x0011, TX_TXCC_MGNFS_MULT_100}, + {0x0003, DRV_DIAG_TX_DRV} +}; + +static struct cdns_reg_pairs ti_qsgmii_100_no_ssc_tx_ln_regs[] = { + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x0011, TX_TXCC_MGNFS_MULT_100}, + {0x0003, DRV_DIAG_TX_DRV}, + {0x4000, XCVR_DIAG_RXCLK_CTRL}, +}; + +static struct cdns_reg_pairs qsgmii_100_no_ssc_rx_ln_regs[] = { + {0x091D, RX_PSC_A0}, + {0x0900, RX_PSC_A2}, + {0x0100, RX_PSC_A3}, + {0x03C7, RX_REE_GCSM1_EQENM_PH1}, + {0x01C7, RX_REE_GCSM1_EQENM_PH2}, + {0x0000, RX_DIAG_DFE_CTRL}, + {0x0019, RX_REE_TAP1_CLIP}, + {0x0019, RX_REE_TAP2TON_CLIP}, + {0x0098, RX_DIAG_NQST_CTRL}, + {0x0C01, RX_DIAG_DFE_AMP_TUNE_2}, + {0x0000, RX_DIAG_DFE_AMP_TUNE_3}, + {0x0000, RX_DIAG_PI_CAP}, + {0x0010, RX_DIAG_PI_RATE}, + {0x0001, RX_DIAG_ACYA}, + {0x018C, RX_CDRLF_CNFG}, +}; + +static struct cdns_torrent_vals qsgmii_100_no_ssc_cmn_vals = { + .reg_pairs = qsgmii_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals qsgmii_100_no_ssc_tx_ln_vals = { + .reg_pairs = qsgmii_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals ti_qsgmii_100_no_ssc_tx_ln_vals = { + .reg_pairs = ti_qsgmii_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(ti_qsgmii_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals qsgmii_100_no_ssc_rx_ln_vals = { + .reg_pairs = qsgmii_100_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_rx_ln_regs), +}; + +/* QSGMII 100 MHz Ref clk, internal SSC */ +static struct cdns_reg_pairs qsgmii_100_int_ssc_cmn_regs[] = { + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M1}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0064, CMN_PLL0_INTDIV_M0}, + {0x0050, CMN_PLL0_INTDIV_M1}, + {0x0064, CMN_PLL1_INTDIV_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M1}, + {0x0002, CMN_PLL1_FRACDIVH_M0}, + {0x0044, CMN_PLL0_HIGH_THR_M0}, + {0x0036, CMN_PLL0_HIGH_THR_M1}, + {0x0044, CMN_PLL1_HIGH_THR_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M1}, + {0x0002, CMN_PDIAG_PLL1_CTRL_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M1}, + {0x0001, CMN_PLL1_SS_CTRL1_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M1}, + {0x011B, CMN_PLL1_SS_CTRL2_M0}, + {0x006E, CMN_PLL0_SS_CTRL3_M0}, + {0x0058, CMN_PLL0_SS_CTRL3_M1}, + {0x006E, CMN_PLL1_SS_CTRL3_M0}, + {0x000E, CMN_PLL0_SS_CTRL4_M0}, + {0x0012, CMN_PLL0_SS_CTRL4_M1}, + {0x000E, CMN_PLL1_SS_CTRL4_M0}, + {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, + {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, + {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR}, + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_torrent_vals qsgmii_100_int_ssc_cmn_vals = { + .reg_pairs = qsgmii_100_int_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_int_ssc_cmn_regs), +}; + +/* Single SGMII/QSGMII link configuration */ +static struct cdns_reg_pairs sl_sgmii_link_cmn_regs[] = { + {0x0000, PHY_PLL_CFG}, + {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs sl_sgmii_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0003, XCVR_DIAG_HSCLK_DIV}, + {0x0013, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals sl_sgmii_link_cmn_vals = { + .reg_pairs = sl_sgmii_link_cmn_regs, + .num_regs = ARRAY_SIZE(sl_sgmii_link_cmn_regs), +}; + +static struct cdns_torrent_vals sl_sgmii_xcvr_diag_ln_vals = { + .reg_pairs = sl_sgmii_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(sl_sgmii_xcvr_diag_ln_regs), +}; + +/* Multi link PCIe, 100 MHz Ref clk, internal SSC */ +static struct cdns_reg_pairs pcie_100_int_ssc_cmn_regs[] = { + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M1}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0064, CMN_PLL0_INTDIV_M0}, + {0x0050, CMN_PLL0_INTDIV_M1}, + {0x0064, CMN_PLL1_INTDIV_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M1}, + {0x0002, CMN_PLL1_FRACDIVH_M0}, + {0x0044, CMN_PLL0_HIGH_THR_M0}, + {0x0036, CMN_PLL0_HIGH_THR_M1}, + {0x0044, CMN_PLL1_HIGH_THR_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M1}, + {0x0002, CMN_PDIAG_PLL1_CTRL_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M1}, + {0x0001, CMN_PLL1_SS_CTRL1_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M1}, + {0x011B, CMN_PLL1_SS_CTRL2_M0}, + {0x006E, CMN_PLL0_SS_CTRL3_M0}, + {0x0058, CMN_PLL0_SS_CTRL3_M1}, + {0x006E, CMN_PLL1_SS_CTRL3_M0}, + {0x000E, CMN_PLL0_SS_CTRL4_M0}, + {0x0012, CMN_PLL0_SS_CTRL4_M1}, + {0x000E, CMN_PLL1_SS_CTRL4_M0}, + {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, + {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, + {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR} +}; + +static struct cdns_torrent_vals pcie_100_int_ssc_cmn_vals = { + .reg_pairs = pcie_100_int_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_int_ssc_cmn_regs), +}; + +/* Single link PCIe, 100 MHz Ref clk, internal SSC */ +static struct cdns_reg_pairs sl_pcie_100_int_ssc_cmn_regs[] = { + {0x0004, CMN_PLL0_DSM_DIAG_M0}, + {0x0004, CMN_PLL0_DSM_DIAG_M1}, + {0x0004, CMN_PLL1_DSM_DIAG_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0}, + {0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1}, + {0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0}, + {0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1}, + {0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0}, + {0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1}, + {0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0}, + {0x0064, CMN_PLL0_INTDIV_M0}, + {0x0050, CMN_PLL0_INTDIV_M1}, + {0x0050, CMN_PLL1_INTDIV_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M0}, + {0x0002, CMN_PLL0_FRACDIVH_M1}, + {0x0002, CMN_PLL1_FRACDIVH_M0}, + {0x0044, CMN_PLL0_HIGH_THR_M0}, + {0x0036, CMN_PLL0_HIGH_THR_M1}, + {0x0036, CMN_PLL1_HIGH_THR_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M0}, + {0x0002, CMN_PDIAG_PLL0_CTRL_M1}, + {0x0002, CMN_PDIAG_PLL1_CTRL_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M0}, + {0x0001, CMN_PLL0_SS_CTRL1_M1}, + {0x0001, CMN_PLL1_SS_CTRL1_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M0}, + {0x011B, CMN_PLL0_SS_CTRL2_M1}, + {0x011B, CMN_PLL1_SS_CTRL2_M0}, + {0x006E, CMN_PLL0_SS_CTRL3_M0}, + {0x0058, CMN_PLL0_SS_CTRL3_M1}, + {0x0058, CMN_PLL1_SS_CTRL3_M0}, + {0x000E, CMN_PLL0_SS_CTRL4_M0}, + {0x0012, CMN_PLL0_SS_CTRL4_M1}, + {0x0012, CMN_PLL1_SS_CTRL4_M0}, + {0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x00C7, CMN_PLL0_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL1_LOCK_REFCNT_START}, + {0x00C7, CMN_PLL0_LOCK_PLLCNT_START}, + {0x00C7, CMN_PLL1_LOCK_PLLCNT_START}, + {0x0005, CMN_PLL0_LOCK_PLLCNT_THR}, + {0x0005, CMN_PLL1_LOCK_PLLCNT_THR} +}; + +static struct cdns_torrent_vals sl_pcie_100_int_ssc_cmn_vals = { + .reg_pairs = sl_pcie_100_int_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_pcie_100_int_ssc_cmn_regs), +}; + +/* PCIe, 100 MHz Ref clk, no SSC & external SSC */ +static struct cdns_reg_pairs pcie_100_ext_no_ssc_cmn_regs[] = { + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x001E, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x000C, CMN_PLL1_DSM_FBL_OVRD_M0} +}; + +static struct cdns_reg_pairs pcie_100_ext_no_ssc_rx_ln_regs[] = { + {0x0019, RX_REE_TAP1_CLIP}, + {0x0019, RX_REE_TAP2TON_CLIP}, + {0x0001, RX_DIAG_ACYA} +}; + +static struct cdns_torrent_vals pcie_100_no_ssc_cmn_vals = { + .reg_pairs = pcie_100_ext_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals pcie_100_no_ssc_rx_ln_vals = { + .reg_pairs = pcie_100_ext_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_rx_ln_regs), +}; + +static const struct cdns_torrent_data cdns_map_torrent = { + .block_offset_shift = 0x2, + .reg_offset_shift = 0x2, + .link_cmn_vals = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_link_cmn_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_usb_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, + [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_link_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_link_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_usb_link_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_link_cmn_vals, + [INTERNAL_SSC] = &sl_usb_link_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_usb_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, + [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + }, + }, + .xcvr_diag_vals = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_xcvr_diag_ln_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_pcie_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + }, + }, + }, + .pcs_cmn_vals = { + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + }, + }, + .cmn_vals = { + [CLK_19_2_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_19_2_no_ssc_cmn_vals, + }, + }, + }, + [CLK_25_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_25_no_ssc_cmn_vals, + }, + }, + }, + [CLK_100_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + }, + }, + }, + .tx_ln_vals = { + [CLK_19_2_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_19_2_no_ssc_tx_ln_vals, + }, + }, + }, + [CLK_25_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_25_no_ssc_tx_ln_vals, + }, + }, + }, + [CLK_100_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_SGMII] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_QSGMII] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_USB] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + }, + }, + }, + .rx_ln_vals = { + [CLK_19_2_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_19_2_no_ssc_rx_ln_vals, + }, + }, + }, + [CLK_25_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_25_no_ssc_rx_ln_vals, + }, + }, + }, + [CLK_100_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + }, + }, + }, +}; + +static const struct cdns_torrent_data ti_j721e_map_torrent = { + .block_offset_shift = 0x0, + .reg_offset_shift = 0x1, + .link_cmn_vals = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_link_cmn_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_usb_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, + [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_link_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_link_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_usb_link_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_link_cmn_vals, + [INTERNAL_SSC] = &sl_usb_link_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_usb_link_cmn_vals, + [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, + [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_sgmii_link_cmn_vals, + [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, + }, + }, + }, + .xcvr_diag_vals = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_xcvr_diag_ln_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_usb_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_pcie_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, + }, + }, + }, + .pcs_cmn_vals = { + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, + }, + }, + }, + .cmn_vals = { + [CLK_19_2_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_19_2_no_ssc_cmn_vals, + }, + }, + }, + [CLK_25_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_25_no_ssc_cmn_vals, + }, + }, + }, + [CLK_100_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + }, + }, + }, + .tx_ln_vals = { + [CLK_19_2_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_19_2_no_ssc_tx_ln_vals, + }, + }, + }, + [CLK_25_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_25_no_ssc_tx_ln_vals, + }, + }, + }, + [CLK_100_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_SGMII] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_QSGMII] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + [TYPE_USB] = { + [NO_SSC] = NULL, + [EXTERNAL_SSC] = NULL, + [INTERNAL_SSC] = NULL, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, + }, + }, + }, + .rx_ln_vals = { + [CLK_19_2_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_19_2_no_ssc_rx_ln_vals, + }, + }, + }, + [CLK_25_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_25_no_ssc_rx_ln_vals, + }, + }, + }, + [CLK_100_MHZ] = { + [TYPE_DP] = { + [TYPE_NONE] = { + [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_QSGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + [TYPE_PCIE] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + [TYPE_SGMII] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, + }, + }, + }, +}; + +static const struct of_device_id cdns_torrent_phy_of_match[] = { + { + .compatible = "cdns,torrent-phy", + .data = &cdns_map_torrent, + }, + { + .compatible = "ti,j721e-serdes-10g", + .data = &ti_j721e_map_torrent, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cdns_torrent_phy_of_match); + +static struct platform_driver cdns_torrent_phy_driver = { + .probe = cdns_torrent_phy_probe, + .remove = cdns_torrent_phy_remove, + .driver = { + .name = "cdns-torrent-phy", + .of_match_table = cdns_torrent_phy_of_match, + } +}; +module_platform_driver(cdns_torrent_phy_driver); + +MODULE_AUTHOR("Cadence Design Systems, Inc."); +MODULE_DESCRIPTION("Cadence Torrent PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 320630ffe3cd..853958fb2c06 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -1,10 +1,22 @@ # SPDX-License-Identifier: GPL-2.0-only + +if (ARCH_MXC && ARM64) || COMPILE_TEST + config PHY_FSL_IMX8MQ_USB tristate "Freescale i.MX8M USB3 PHY" depends on OF && HAS_IOMEM select GENERIC_PHY default ARCH_MXC && ARM64 +config PHY_MIXEL_LVDS_PHY + tristate "Mixel LVDS PHY support" + depends on OF + select GENERIC_PHY + select REGMAP_MMIO + help + Enable this to add support for the Mixel LVDS PHY as found + on NXP's i.MX8qm SoC. + config PHY_MIXEL_MIPI_DPHY tristate "Mixel MIPI DSI PHY support" depends on OF && HAS_IOMEM @@ -14,3 +26,24 @@ config PHY_MIXEL_MIPI_DPHY help Enable this to add support for the Mixel DSI PHY as found on NXP's i.MX8 family of SOCs. + +config PHY_FSL_IMX8M_PCIE + tristate "Freescale i.MX8M PCIE PHY" + depends on OF && HAS_IOMEM + select GENERIC_PHY + help + Enable this to add support for the PCIE PHY as found on + i.MX8M family of SOCs. + +endif + +config PHY_FSL_LYNX_28G + tristate "Freescale Layerscape Lynx 28G SerDes PHY support" + depends on OF + depends on ARCH_LAYERSCAPE || COMPILE_TEST + select GENERIC_PHY + help + Enable this to add support for the Lynx SerDes 28G PHY as + found on NXP's Layerscape platforms such as LX2160A. + Used to change the protocol running on SerDes lanes at runtime. + Only useful for a restricted set of Ethernet protocols. diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index 1d02e3869b45..cedb328bc4d2 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o +obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o +obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o +obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o diff --git a/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c b/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c index 9f2c1da14f5a..e625b32889bf 100644 --- a/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c +++ b/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c @@ -4,17 +4,33 @@ * Copyright 2019 Purism SPC */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> +#include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/svc/misc.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <dt-bindings/firmware/imx/rsrc.h> + +/* Control and Status Registers(CSR) */ +#define PHY_CTRL 0x00 +#define CCM_MASK GENMASK(7, 5) +#define CCM(n) FIELD_PREP(CCM_MASK, (n)) +#define CCM_1_2V 0x5 +#define CA_MASK GENMASK(4, 2) +#define CA_3_51MA 0x4 +#define CA(n) FIELD_PREP(CA_MASK, (n)) +#define RFB BIT(1) +#define LVDS_EN BIT(0) /* DPHY registers */ #define DPHY_PD_DPHY 0x00 @@ -55,8 +71,15 @@ #define PWR_ON 0 #define PWR_OFF 1 +#define MIN_VCO_FREQ 640000000 +#define MAX_VCO_FREQ 1500000000 + +#define MIN_LVDS_REFCLK_FREQ 24000000 +#define MAX_LVDS_REFCLK_FREQ 150000000 + enum mixel_dphy_devtype { MIXEL_IMX8MQ, + MIXEL_IMX8QXP, }; struct mixel_dphy_devdata { @@ -65,6 +88,7 @@ struct mixel_dphy_devdata { u8 reg_rxlprp; u8 reg_rxcdrp; u8 reg_rxhs_settle; + bool is_combo; /* MIPI DPHY and LVDS PHY combo */ }; static const struct mixel_dphy_devdata mixel_dphy_devdata[] = { @@ -74,6 +98,10 @@ static const struct mixel_dphy_devdata mixel_dphy_devdata[] = { .reg_rxlprp = 0x40, .reg_rxcdrp = 0x44, .reg_rxhs_settle = 0x48, + .is_combo = false, + }, + [MIXEL_IMX8QXP] = { + .is_combo = true, }, }; @@ -95,8 +123,12 @@ struct mixel_dphy_cfg { struct mixel_dphy_priv { struct mixel_dphy_cfg cfg; struct regmap *regmap; + struct regmap *lvds_regmap; struct clk *phy_ref_clk; const struct mixel_dphy_devdata *devdata; + struct imx_sc_ipc *ipc_handle; + bool is_slave; + int id; }; static const struct regmap_config mixel_dphy_regmap_config = { @@ -317,7 +349,8 @@ static int mixel_dphy_set_pll_params(struct phy *phy) return 0; } -static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +static int +mixel_dphy_configure_mipi_dphy(struct phy *phy, union phy_configure_opts *opts) { struct mixel_dphy_priv *priv = phy_get_drvdata(phy); struct mixel_dphy_cfg cfg = { 0 }; @@ -345,15 +378,126 @@ static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts) return 0; } +static int +mixel_dphy_configure_lvds_phy(struct phy *phy, union phy_configure_opts *opts) +{ + struct mixel_dphy_priv *priv = phy_get_drvdata(phy); + struct phy_configure_opts_lvds *lvds_opts = &opts->lvds; + unsigned long data_rate; + unsigned long fvco; + u32 rsc; + u32 co; + int ret; + + priv->is_slave = lvds_opts->is_slave; + + /* LVDS interface pins */ + regmap_write(priv->lvds_regmap, PHY_CTRL, + CCM(CCM_1_2V) | CA(CA_3_51MA) | RFB); + + /* enable MODE8 only for slave LVDS PHY */ + rsc = priv->id ? IMX_SC_R_MIPI_1 : IMX_SC_R_MIPI_0; + ret = imx_sc_misc_set_control(priv->ipc_handle, rsc, IMX_SC_C_DUAL_MODE, + lvds_opts->is_slave); + if (ret) { + dev_err(&phy->dev, "Failed to configure MODE8: %d\n", ret); + return ret; + } + + /* + * Choose an appropriate divider ratio to meet the requirement of + * PLL VCO frequency range. + * + * ----- 640MHz ~ 1500MHz ------------ --------------- + * | VCO | ----------------> | CO divider | -> | LVDS data rate| + * ----- FVCO ------------ --------------- + * 1/2/4/8 div 7 * differential_clk_rate + */ + data_rate = 7 * lvds_opts->differential_clk_rate; + for (co = 1; co <= 8; co *= 2) { + fvco = data_rate * co; + + if (fvco >= MIN_VCO_FREQ) + break; + } + + if (fvco < MIN_VCO_FREQ || fvco > MAX_VCO_FREQ) { + dev_err(&phy->dev, "VCO frequency %lu is out of range\n", fvco); + return -ERANGE; + } + + /* + * CO is configurable, while CN and CM are not, + * as fixed ratios 1 and 7 are applied respectively. + */ + phy_write(phy, __ffs(co), DPHY_CO); + + /* set reference clock rate */ + clk_set_rate(priv->phy_ref_clk, lvds_opts->differential_clk_rate); + + return ret; +} + +static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + if (!opts) { + dev_err(&phy->dev, "No configuration options\n"); + return -EINVAL; + } + + if (phy->attrs.mode == PHY_MODE_MIPI_DPHY) + return mixel_dphy_configure_mipi_dphy(phy, opts); + else if (phy->attrs.mode == PHY_MODE_LVDS) + return mixel_dphy_configure_lvds_phy(phy, opts); + + dev_err(&phy->dev, + "Failed to configure PHY with invalid PHY mode: %d\n", phy->attrs.mode); + + return -EINVAL; +} + +static int +mixel_dphy_validate_lvds_phy(struct phy *phy, union phy_configure_opts *opts) +{ + struct phy_configure_opts_lvds *lvds_cfg = &opts->lvds; + + if (lvds_cfg->bits_per_lane_and_dclk_cycle != 7) { + dev_err(&phy->dev, "Invalid bits per LVDS data lane: %u\n", + lvds_cfg->bits_per_lane_and_dclk_cycle); + return -EINVAL; + } + + if (lvds_cfg->lanes != 4) { + dev_err(&phy->dev, "Invalid LVDS data lanes: %u\n", lvds_cfg->lanes); + return -EINVAL; + } + + if (lvds_cfg->differential_clk_rate < MIN_LVDS_REFCLK_FREQ || + lvds_cfg->differential_clk_rate > MAX_LVDS_REFCLK_FREQ) { + dev_err(&phy->dev, + "Invalid LVDS differential clock rate: %lu\n", + lvds_cfg->differential_clk_rate); + return -EINVAL; + } + + return 0; +} + static int mixel_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts) { - struct mixel_dphy_cfg cfg = { 0 }; + if (mode == PHY_MODE_MIPI_DPHY) { + struct mixel_dphy_cfg mipi_dphy_cfg = { 0 }; - if (mode != PHY_MODE_MIPI_DPHY) - return -EINVAL; + return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, + &mipi_dphy_cfg); + } else if (mode == PHY_MODE_LVDS) { + return mixel_dphy_validate_lvds_phy(phy, opts); + } - return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); + dev_err(&phy->dev, + "Failed to validate PHY with invalid PHY mode: %d\n", mode); + return -EINVAL; } static int mixel_dphy_init(struct phy *phy) @@ -373,27 +517,75 @@ static int mixel_dphy_exit(struct phy *phy) return 0; } -static int mixel_dphy_power_on(struct phy *phy) +static int mixel_dphy_power_on_mipi_dphy(struct phy *phy) { struct mixel_dphy_priv *priv = phy_get_drvdata(phy); u32 locked; int ret; - ret = clk_prepare_enable(priv->phy_ref_clk); - if (ret < 0) - return ret; - phy_write(phy, PWR_ON, DPHY_PD_PLL); ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked, locked, PLL_LOCK_SLEEP, PLL_LOCK_TIMEOUT); if (ret < 0) { dev_err(&phy->dev, "Could not get DPHY lock (%d)!\n", ret); - goto clock_disable; + return ret; } phy_write(phy, PWR_ON, DPHY_PD_DPHY); return 0; +} + +static int mixel_dphy_power_on_lvds_phy(struct phy *phy) +{ + struct mixel_dphy_priv *priv = phy_get_drvdata(phy); + u32 locked; + int ret; + + regmap_update_bits(priv->lvds_regmap, PHY_CTRL, LVDS_EN, LVDS_EN); + + phy_write(phy, PWR_ON, DPHY_PD_DPHY); + phy_write(phy, PWR_ON, DPHY_PD_PLL); + + /* do not wait for slave LVDS PHY being locked */ + if (priv->is_slave) + return 0; + + ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked, + locked, PLL_LOCK_SLEEP, + PLL_LOCK_TIMEOUT); + if (ret < 0) { + dev_err(&phy->dev, "Could not get LVDS PHY lock (%d)!\n", ret); + return ret; + } + + return 0; +} + +static int mixel_dphy_power_on(struct phy *phy) +{ + struct mixel_dphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(priv->phy_ref_clk); + if (ret < 0) + return ret; + + if (phy->attrs.mode == PHY_MODE_MIPI_DPHY) { + ret = mixel_dphy_power_on_mipi_dphy(phy); + } else if (phy->attrs.mode == PHY_MODE_LVDS) { + ret = mixel_dphy_power_on_lvds_phy(phy); + } else { + dev_err(&phy->dev, + "Failed to power on PHY with invalid PHY mode: %d\n", + phy->attrs.mode); + ret = -EINVAL; + } + + if (ret) + goto clock_disable; + + return 0; clock_disable: clk_disable_unprepare(priv->phy_ref_clk); return ret; @@ -406,16 +598,51 @@ static int mixel_dphy_power_off(struct phy *phy) phy_write(phy, PWR_OFF, DPHY_PD_PLL); phy_write(phy, PWR_OFF, DPHY_PD_DPHY); + if (phy->attrs.mode == PHY_MODE_LVDS) + regmap_update_bits(priv->lvds_regmap, PHY_CTRL, LVDS_EN, 0); + clk_disable_unprepare(priv->phy_ref_clk); return 0; } +static int mixel_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct mixel_dphy_priv *priv = phy_get_drvdata(phy); + int ret; + + if (priv->devdata->is_combo && mode != PHY_MODE_LVDS) { + dev_err(&phy->dev, "Failed to set PHY mode for combo PHY\n"); + return -EINVAL; + } + + if (!priv->devdata->is_combo && mode != PHY_MODE_MIPI_DPHY) { + dev_err(&phy->dev, "Failed to set PHY mode to MIPI DPHY\n"); + return -EINVAL; + } + + if (priv->devdata->is_combo) { + u32 rsc = priv->id ? IMX_SC_R_MIPI_1 : IMX_SC_R_MIPI_0; + + ret = imx_sc_misc_set_control(priv->ipc_handle, + rsc, IMX_SC_C_MODE, + mode == PHY_MODE_LVDS); + if (ret) { + dev_err(&phy->dev, + "Failed to set PHY mode via SCU ipc: %d\n", ret); + return ret; + } + } + + return 0; +} + static const struct phy_ops mixel_dphy_phy_ops = { .init = mixel_dphy_init, .exit = mixel_dphy_exit, .power_on = mixel_dphy_power_on, .power_off = mixel_dphy_power_off, + .set_mode = mixel_dphy_set_mode, .configure = mixel_dphy_configure, .validate = mixel_dphy_validate, .owner = THIS_MODULE, @@ -424,6 +651,8 @@ static const struct phy_ops mixel_dphy_phy_ops = { static const struct of_device_id mixel_dphy_of_match[] = { { .compatible = "fsl,imx8mq-mipi-dphy", .data = &mixel_dphy_devdata[MIXEL_IMX8MQ] }, + { .compatible = "fsl,imx8qxp-mipi-dphy", + .data = &mixel_dphy_devdata[MIXEL_IMX8QXP] }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, mixel_dphy_of_match); @@ -434,9 +663,9 @@ static int mixel_dphy_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct phy_provider *phy_provider; struct mixel_dphy_priv *priv; - struct resource *res; struct phy *phy; void __iomem *base; + int ret; if (!np) return -ENODEV; @@ -449,8 +678,7 @@ static int mixel_dphy_probe(struct platform_device *pdev) if (!priv->devdata) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -469,6 +697,30 @@ static int mixel_dphy_probe(struct platform_device *pdev) dev_dbg(dev, "phy_ref clock rate: %lu\n", clk_get_rate(priv->phy_ref_clk)); + if (priv->devdata->is_combo) { + priv->lvds_regmap = + syscon_regmap_lookup_by_phandle(np, "fsl,syscon"); + if (IS_ERR(priv->lvds_regmap)) { + ret = PTR_ERR(priv->lvds_regmap); + dev_err_probe(dev, ret, "Failed to get LVDS regmap\n"); + return ret; + } + + priv->id = of_alias_get_id(np, "mipi_dphy"); + if (priv->id < 0) { + dev_err(dev, "Failed to get phy node alias id: %d\n", + priv->id); + return priv->id; + } + + ret = imx_scu_get_handle(&priv->ipc_handle); + if (ret) { + dev_err_probe(dev, ret, + "Failed to get SCU ipc handle\n"); + return ret; + } + } + dev_set_drvdata(dev, priv); phy = devm_phy_create(dev, np, &mixel_dphy_phy_ops); diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c new file mode 100644 index 000000000000..c93286483b42 --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx7-iomuxc-gpr.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <dt-bindings/phy/phy-imx8-pcie.h> + +#define IMX8MM_PCIE_PHY_CMN_REG061 0x184 +#define ANA_PLL_CLK_OUT_TO_EXT_IO_EN BIT(0) +#define IMX8MM_PCIE_PHY_CMN_REG062 0x188 +#define ANA_PLL_CLK_OUT_TO_EXT_IO_SEL BIT(3) +#define IMX8MM_PCIE_PHY_CMN_REG063 0x18C +#define AUX_PLL_REFCLK_SEL_SYS_PLL GENMASK(7, 6) +#define IMX8MM_PCIE_PHY_CMN_REG064 0x190 +#define ANA_AUX_RX_TX_SEL_TX BIT(7) +#define ANA_AUX_RX_TERM_GND_EN BIT(3) +#define ANA_AUX_TX_TERM BIT(2) +#define IMX8MM_PCIE_PHY_CMN_REG065 0x194 +#define ANA_AUX_RX_TERM (BIT(7) | BIT(4)) +#define ANA_AUX_TX_LVL GENMASK(3, 0) +#define IMX8MM_PCIE_PHY_CMN_REG75 0x1D4 +#define PCIE_PHY_CMN_REG75_PLL_DONE 0x3 +#define PCIE_PHY_TRSV_REG5 0x414 +#define PCIE_PHY_TRSV_REG5_GEN1_DEEMP 0x2D +#define PCIE_PHY_TRSV_REG6 0x418 +#define PCIE_PHY_TRSV_REG6_GEN2_DEEMP 0xF + +#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24) +#define IMX8MM_GPR_PCIE_REF_CLK_PLL FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3) +#define IMX8MM_GPR_PCIE_REF_CLK_EXT FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x2) +#define IMX8MM_GPR_PCIE_AUX_EN BIT(19) +#define IMX8MM_GPR_PCIE_CMN_RST BIT(18) +#define IMX8MM_GPR_PCIE_POWER_OFF BIT(17) +#define IMX8MM_GPR_PCIE_SSC_EN BIT(16) +#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9) + +struct imx8_pcie_phy { + void __iomem *base; + struct clk *clk; + struct phy *phy; + struct regmap *iomuxc_gpr; + struct reset_control *reset; + u32 refclk_pad_mode; + u32 tx_deemph_gen1; + u32 tx_deemph_gen2; + bool clkreq_unused; +}; + +static int imx8_pcie_phy_power_on(struct phy *phy) +{ + int ret; + u32 val, pad_mode; + struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); + + reset_control_assert(imx8_phy->reset); + + pad_mode = imx8_phy->refclk_pad_mode; + /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */ + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE, + imx8_phy->clkreq_unused ? + 0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE); + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_AUX_EN, + IMX8MM_GPR_PCIE_AUX_EN); + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_POWER_OFF, 0); + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_SSC_EN, 0); + + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_REF_CLK_SEL, + pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ? + IMX8MM_GPR_PCIE_REF_CLK_EXT : + IMX8MM_GPR_PCIE_REF_CLK_PLL); + usleep_range(100, 200); + + /* Do the PHY common block reset */ + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_CMN_RST, + IMX8MM_GPR_PCIE_CMN_RST); + usleep_range(200, 500); + + if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT || + pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) { + /* Configure the pad as input */ + val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); + writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); + } else { + /* Configure the PHY to output the refclock via pad */ + writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); + } + + if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT || + pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) { + /* Source clock from SoC internal PLL */ + writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062); + writel(AUX_PLL_REFCLK_SEL_SYS_PLL, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063); + val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM; + writel(val | ANA_AUX_RX_TERM_GND_EN, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064); + writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065); + } + + /* Tune PHY de-emphasis setting to pass PCIe compliance. */ + if (imx8_phy->tx_deemph_gen1) + writel(imx8_phy->tx_deemph_gen1, + imx8_phy->base + PCIE_PHY_TRSV_REG5); + if (imx8_phy->tx_deemph_gen2) + writel(imx8_phy->tx_deemph_gen2, + imx8_phy->base + PCIE_PHY_TRSV_REG6); + + reset_control_deassert(imx8_phy->reset); + + /* Polling to check the phy is ready or not. */ + ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG75, + val, val == PCIE_PHY_CMN_REG75_PLL_DONE, + 10, 20000); + return ret; +} + +static int imx8_pcie_phy_init(struct phy *phy) +{ + struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); + + return clk_prepare_enable(imx8_phy->clk); +} + +static int imx8_pcie_phy_exit(struct phy *phy) +{ + struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(imx8_phy->clk); + + return 0; +} + +static const struct phy_ops imx8_pcie_phy_ops = { + .init = imx8_pcie_phy_init, + .exit = imx8_pcie_phy_exit, + .power_on = imx8_pcie_phy_power_on, + .owner = THIS_MODULE, +}; + +static int imx8_pcie_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct imx8_pcie_phy *imx8_phy; + struct resource *res; + + imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL); + if (!imx8_phy) + return -ENOMEM; + + /* get PHY refclk pad mode */ + of_property_read_u32(np, "fsl,refclk-pad-mode", + &imx8_phy->refclk_pad_mode); + + if (of_property_read_u32(np, "fsl,tx-deemph-gen1", + &imx8_phy->tx_deemph_gen1)) + imx8_phy->tx_deemph_gen1 = 0; + + if (of_property_read_u32(np, "fsl,tx-deemph-gen2", + &imx8_phy->tx_deemph_gen2)) + imx8_phy->tx_deemph_gen2 = 0; + + if (of_property_read_bool(np, "fsl,clkreq-unsupported")) + imx8_phy->clkreq_unused = true; + else + imx8_phy->clkreq_unused = false; + + imx8_phy->clk = devm_clk_get(dev, "ref"); + if (IS_ERR(imx8_phy->clk)) { + dev_err(dev, "failed to get imx pcie phy clock\n"); + return PTR_ERR(imx8_phy->clk); + } + + /* Grab GPR config register range */ + imx8_phy->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imx8_phy->iomuxc_gpr)) { + dev_err(dev, "unable to find iomuxc registers\n"); + return PTR_ERR(imx8_phy->iomuxc_gpr); + } + + imx8_phy->reset = devm_reset_control_get_exclusive(dev, "pciephy"); + if (IS_ERR(imx8_phy->reset)) { + dev_err(dev, "Failed to get PCIEPHY reset control\n"); + return PTR_ERR(imx8_phy->reset); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx8_phy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(imx8_phy->base)) + return PTR_ERR(imx8_phy->base); + + imx8_phy->phy = devm_phy_create(dev, NULL, &imx8_pcie_phy_ops); + if (IS_ERR(imx8_phy->phy)) + return PTR_ERR(imx8_phy->phy); + + phy_set_drvdata(imx8_phy->phy, imx8_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id imx8_pcie_phy_of_match[] = { + {.compatible = "fsl,imx8mm-pcie-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match); + +static struct platform_driver imx8_pcie_phy_driver = { + .probe = imx8_pcie_phy_probe, + .driver = { + .name = "imx8-pcie-phy", + .of_match_table = imx8_pcie_phy_of_match, + } +}; +module_platform_driver(imx8_pcie_phy_driver); + +MODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 0c4833da7be0..a29b4a6f7c24 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -1,15 +1,20 @@ // SPDX-License-Identifier: GPL-2.0+ /* Copyright (c) 2017 NXP. */ +#include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #define PHY_CTRL0 0x0 #define PHY_CTRL0_REF_SSP_EN BIT(2) +#define PHY_CTRL0_FSEL_MASK GENMASK(10, 5) +#define PHY_CTRL0_FSEL_24M 0x2a #define PHY_CTRL1 0x4 #define PHY_CTRL1_RESET BIT(0) @@ -20,6 +25,11 @@ #define PHY_CTRL2 0x8 #define PHY_CTRL2_TXENABLEN0 BIT(8) +#define PHY_CTRL2_OTG_DISABLE BIT(9) + +#define PHY_CTRL6 0x18 +#define PHY_CTRL6_ALT_CLK_EN BIT(1) +#define PHY_CTRL6_ALT_CLK_SEL BIT(0) struct imx8mq_usb_phy { struct phy *phy; @@ -54,6 +64,44 @@ static int imx8mq_usb_phy_init(struct phy *phy) return 0; } +static int imx8mp_usb_phy_init(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + u32 value; + + /* USB3.0 PHY signal fsel for 24M ref */ + value = readl(imx_phy->base + PHY_CTRL0); + value &= ~PHY_CTRL0_FSEL_MASK; + value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M); + writel(value, imx_phy->base + PHY_CTRL0); + + /* Disable alt_clk_en and use internal MPLL clocks */ + value = readl(imx_phy->base + PHY_CTRL6); + value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN); + writel(value, imx_phy->base + PHY_CTRL6); + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0); + value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; + writel(value, imx_phy->base + PHY_CTRL1); + + value = readl(imx_phy->base + PHY_CTRL0); + value |= PHY_CTRL0_REF_SSP_EN; + writel(value, imx_phy->base + PHY_CTRL0); + + value = readl(imx_phy->base + PHY_CTRL2); + value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE; + writel(value, imx_phy->base + PHY_CTRL2); + + udelay(10); + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); + writel(value, imx_phy->base + PHY_CTRL1); + + return 0; +} + static int imx8mq_phy_power_on(struct phy *phy) { struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); @@ -76,19 +124,35 @@ static int imx8mq_phy_power_off(struct phy *phy) return 0; } -static struct phy_ops imx8mq_usb_phy_ops = { +static const struct phy_ops imx8mq_usb_phy_ops = { .init = imx8mq_usb_phy_init, .power_on = imx8mq_phy_power_on, .power_off = imx8mq_phy_power_off, .owner = THIS_MODULE, }; +static const struct phy_ops imx8mp_usb_phy_ops = { + .init = imx8mp_usb_phy_init, + .power_on = imx8mq_phy_power_on, + .power_off = imx8mq_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id imx8mq_usb_phy_of_match[] = { + {.compatible = "fsl,imx8mq-usb-phy", + .data = &imx8mq_usb_phy_ops,}, + {.compatible = "fsl,imx8mp-usb-phy", + .data = &imx8mp_usb_phy_ops,}, + { } +}; +MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); + static int imx8mq_usb_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct imx8mq_usb_phy *imx_phy; - struct resource *res; + const struct phy_ops *phy_ops; imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); if (!imx_phy) @@ -100,12 +164,15 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(imx_phy->clk); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx_phy->base = devm_ioremap_resource(dev, res); + imx_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(imx_phy->base)) return PTR_ERR(imx_phy->base); - imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops); + phy_ops = of_device_get_match_data(dev); + if (!phy_ops) + return -EINVAL; + + imx_phy->phy = devm_phy_create(dev, NULL, phy_ops); if (IS_ERR(imx_phy->phy)) return PTR_ERR(imx_phy->phy); @@ -120,12 +187,6 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } -static const struct of_device_id imx8mq_usb_phy_of_match[] = { - {.compatible = "fsl,imx8mq-usb-phy",}, - { }, -}; -MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); - static struct platform_driver imx8mq_usb_phy_driver = { .probe = imx8mq_usb_phy_probe, .driver = { diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c new file mode 100644 index 000000000000..e514b64bfdab --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017-2020,2022 NXP + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#define REG_SET 0x4 +#define REG_CLR 0x8 + +#define PHY_CTRL 0x0 +#define M_MASK GENMASK(18, 17) +#define M(n) FIELD_PREP(M_MASK, (n)) +#define CCM_MASK GENMASK(16, 14) +#define CCM(n) FIELD_PREP(CCM_MASK, (n)) +#define CA_MASK GENMASK(13, 11) +#define CA(n) FIELD_PREP(CA_MASK, (n)) +#define TST_MASK GENMASK(10, 5) +#define TST(n) FIELD_PREP(TST_MASK, (n)) +#define CH_EN(id) BIT(3 + (id)) +#define NB BIT(2) +#define RFB BIT(1) +#define PD BIT(0) + +/* Power On Reset(POR) value */ +#define CTRL_RESET_VAL (M(0x0) | CCM(0x4) | CA(0x4) | TST(0x25)) + +/* PHY initialization value and mask */ +#define CTRL_INIT_MASK (M_MASK | CCM_MASK | CA_MASK | TST_MASK | NB | RFB) +#define CTRL_INIT_VAL (M(0x0) | CCM(0x5) | CA(0x4) | TST(0x25) | RFB) + +#define PHY_STATUS 0x10 +#define LOCK BIT(0) + +#define PHY_NUM 2 + +#define MIN_CLKIN_FREQ (25 * MEGA) +#define MAX_CLKIN_FREQ (165 * MEGA) + +#define PLL_LOCK_SLEEP 10 +#define PLL_LOCK_TIMEOUT 1000 + +struct mixel_lvds_phy { + struct phy *phy; + struct phy_configure_opts_lvds cfg; + unsigned int id; +}; + +struct mixel_lvds_phy_priv { + struct regmap *regmap; + struct mutex lock; /* protect remap access and cfg of our own */ + struct clk *phy_ref_clk; + struct mixel_lvds_phy *phys[PHY_NUM]; +}; + +static int mixel_lvds_phy_init(struct phy *phy) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + + mutex_lock(&priv->lock); + regmap_update_bits(priv->regmap, + PHY_CTRL, CTRL_INIT_MASK, CTRL_INIT_VAL); + mutex_unlock(&priv->lock); + + return 0; +} + +static int mixel_lvds_phy_power_on(struct phy *phy) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy); + struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1]; + struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg; + u32 val = 0; + u32 locked; + int ret; + + /* The master PHY would power on the slave PHY. */ + if (cfg->is_slave) + return 0; + + ret = clk_prepare_enable(priv->phy_ref_clk); + if (ret < 0) { + dev_err(&phy->dev, + "failed to enable PHY reference clock: %d\n", ret); + return ret; + } + + mutex_lock(&priv->lock); + if (cfg->bits_per_lane_and_dclk_cycle == 7) { + if (cfg->differential_clk_rate < 44000000) + val |= M(0x2); + else if (cfg->differential_clk_rate < 90000000) + val |= M(0x1); + else + val |= M(0x0); + } else { + val = NB; + + if (cfg->differential_clk_rate < 32000000) + val |= M(0x2); + else if (cfg->differential_clk_rate < 63000000) + val |= M(0x1); + else + val |= M(0x0); + } + regmap_update_bits(priv->regmap, PHY_CTRL, M_MASK | NB, val); + + /* + * Enable two channels synchronously, + * if the companion PHY is a slave PHY. + */ + if (companion->cfg.is_slave) + val = CH_EN(0) | CH_EN(1); + else + val = CH_EN(lvds_phy->id); + regmap_write(priv->regmap, PHY_CTRL + REG_SET, val); + + ret = regmap_read_poll_timeout(priv->regmap, PHY_STATUS, locked, + locked, PLL_LOCK_SLEEP, + PLL_LOCK_TIMEOUT); + if (ret < 0) { + dev_err(&phy->dev, "failed to get PHY lock: %d\n", ret); + clk_disable_unprepare(priv->phy_ref_clk); + } + mutex_unlock(&priv->lock); + + return ret; +} + +static int mixel_lvds_phy_power_off(struct phy *phy) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy); + struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1]; + struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg; + + /* The master PHY would power off the slave PHY. */ + if (cfg->is_slave) + return 0; + + mutex_lock(&priv->lock); + if (companion->cfg.is_slave) + regmap_write(priv->regmap, PHY_CTRL + REG_CLR, + CH_EN(0) | CH_EN(1)); + else + regmap_write(priv->regmap, PHY_CTRL + REG_CLR, + CH_EN(lvds_phy->id)); + mutex_unlock(&priv->lock); + + clk_disable_unprepare(priv->phy_ref_clk); + + return 0; +} + +static int mixel_lvds_phy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + struct phy_configure_opts_lvds *cfg = &opts->lvds; + int ret; + + ret = clk_set_rate(priv->phy_ref_clk, cfg->differential_clk_rate); + if (ret) + dev_err(&phy->dev, "failed to set PHY reference clock rate(%lu): %d\n", + cfg->differential_clk_rate, ret); + + return ret; +} + +/* Assume the master PHY's configuration set is cached first. */ +static int mixel_lvds_phy_check_slave(struct phy *slave_phy) +{ + struct device *dev = &slave_phy->dev; + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev->parent); + struct mixel_lvds_phy *slv = phy_get_drvdata(slave_phy); + struct mixel_lvds_phy *mst = priv->phys[slv->id ^ 1]; + struct phy_configure_opts_lvds *mst_cfg = &mst->cfg; + struct phy_configure_opts_lvds *slv_cfg = &slv->cfg; + + if (mst_cfg->bits_per_lane_and_dclk_cycle != + slv_cfg->bits_per_lane_and_dclk_cycle) { + dev_err(dev, "number bits mismatch(mst: %u vs slv: %u)\n", + mst_cfg->bits_per_lane_and_dclk_cycle, + slv_cfg->bits_per_lane_and_dclk_cycle); + return -EINVAL; + } + + if (mst_cfg->differential_clk_rate != + slv_cfg->differential_clk_rate) { + dev_err(dev, "dclk rate mismatch(mst: %lu vs slv: %lu)\n", + mst_cfg->differential_clk_rate, + slv_cfg->differential_clk_rate); + return -EINVAL; + } + + if (mst_cfg->lanes != slv_cfg->lanes) { + dev_err(dev, "lanes mismatch(mst: %u vs slv: %u)\n", + mst_cfg->lanes, slv_cfg->lanes); + return -EINVAL; + } + + if (mst_cfg->is_slave == slv_cfg->is_slave) { + dev_err(dev, "master PHY is not found\n"); + return -EINVAL; + } + + return 0; +} + +static int mixel_lvds_phy_validate(struct phy *phy, enum phy_mode mode, + int submode, union phy_configure_opts *opts) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); + struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy); + struct phy_configure_opts_lvds *cfg = &opts->lvds; + int ret = 0; + + if (mode != PHY_MODE_LVDS) { + dev_err(&phy->dev, "invalid PHY mode(%d)\n", mode); + return -EINVAL; + } + + if (cfg->bits_per_lane_and_dclk_cycle != 7 && + cfg->bits_per_lane_and_dclk_cycle != 10) { + dev_err(&phy->dev, "invalid bits per data lane(%u)\n", + cfg->bits_per_lane_and_dclk_cycle); + return -EINVAL; + } + + if (cfg->lanes != 4 && cfg->lanes != 3) { + dev_err(&phy->dev, "invalid data lanes(%u)\n", cfg->lanes); + return -EINVAL; + } + + if (cfg->differential_clk_rate < MIN_CLKIN_FREQ || + cfg->differential_clk_rate > MAX_CLKIN_FREQ) { + dev_err(&phy->dev, "invalid differential clock rate(%lu)\n", + cfg->differential_clk_rate); + return -EINVAL; + } + + mutex_lock(&priv->lock); + /* cache configuration set of our own for check */ + memcpy(&lvds_phy->cfg, cfg, sizeof(*cfg)); + + if (cfg->is_slave) { + ret = mixel_lvds_phy_check_slave(phy); + if (ret) + dev_err(&phy->dev, "failed to check slave PHY: %d\n", ret); + } + mutex_unlock(&priv->lock); + + return ret; +} + +static const struct phy_ops mixel_lvds_phy_ops = { + .init = mixel_lvds_phy_init, + .power_on = mixel_lvds_phy_power_on, + .power_off = mixel_lvds_phy_power_off, + .configure = mixel_lvds_phy_configure, + .validate = mixel_lvds_phy_validate, + .owner = THIS_MODULE, +}; + +static int mixel_lvds_phy_reset(struct device *dev) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "failed to get PM runtime: %d\n", ret); + return ret; + } + + regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL); + + ret = pm_runtime_put(dev); + if (ret < 0) + dev_err(dev, "failed to put PM runtime: %d\n", ret); + + return ret; +} + +static struct phy *mixel_lvds_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); + unsigned int phy_id; + + if (args->args_count != 1) { + dev_err(dev, + "invalid argument number(%d) for 'phys' property\n", + args->args_count); + return ERR_PTR(-EINVAL); + } + + phy_id = args->args[0]; + + if (phy_id >= PHY_NUM) { + dev_err(dev, "invalid PHY index(%d)\n", phy_id); + return ERR_PTR(-ENODEV); + } + + return priv->phys[phy_id]->phy; +} + +static int mixel_lvds_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct mixel_lvds_phy_priv *priv; + struct mixel_lvds_phy *lvds_phy; + struct phy *phy; + int i; + int ret; + + if (!dev->of_node) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = syscon_node_to_regmap(dev->of_node->parent); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "failed to get regmap\n"); + + priv->phy_ref_clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->phy_ref_clk)) + return dev_err_probe(dev, PTR_ERR(priv->phy_ref_clk), + "failed to get PHY reference clock\n"); + + mutex_init(&priv->lock); + + dev_set_drvdata(dev, priv); + + pm_runtime_enable(dev); + + ret = mixel_lvds_phy_reset(dev); + if (ret) { + dev_err(dev, "failed to do POR reset: %d\n", ret); + return ret; + } + + for (i = 0; i < PHY_NUM; i++) { + lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL); + if (!lvds_phy) { + ret = -ENOMEM; + goto err; + } + + phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops); + if (IS_ERR(phy)) { + ret = PTR_ERR(phy); + dev_err(dev, "failed to create PHY for channel%d: %d\n", + i, ret); + goto err; + } + + lvds_phy->phy = phy; + lvds_phy->id = i; + priv->phys[i] = lvds_phy; + + phy_set_drvdata(phy, lvds_phy); + } + + phy_provider = devm_of_phy_provider_register(dev, mixel_lvds_phy_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + dev_err(dev, "failed to register PHY provider: %d\n", ret); + goto err; + } + + return 0; +err: + pm_runtime_disable(dev); + + return ret; +} + +static int mixel_lvds_phy_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); + + /* power down */ + mutex_lock(&priv->lock); + regmap_write(priv->regmap, PHY_CTRL + REG_SET, PD); + mutex_unlock(&priv->lock); + + return 0; +} + +static int __maybe_unused mixel_lvds_phy_runtime_resume(struct device *dev) +{ + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); + + /* power up + control initialization */ + mutex_lock(&priv->lock); + regmap_update_bits(priv->regmap, PHY_CTRL, + CTRL_INIT_MASK | PD, CTRL_INIT_VAL); + mutex_unlock(&priv->lock); + + return 0; +} + +static const struct dev_pm_ops mixel_lvds_phy_pm_ops = { + SET_RUNTIME_PM_OPS(mixel_lvds_phy_runtime_suspend, + mixel_lvds_phy_runtime_resume, NULL) +}; + +static const struct of_device_id mixel_lvds_phy_of_match[] = { + { .compatible = "fsl,imx8qm-lvds-phy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); + +static struct platform_driver mixel_lvds_phy_driver = { + .probe = mixel_lvds_phy_probe, + .remove = mixel_lvds_phy_remove, + .driver = { + .pm = &mixel_lvds_phy_pm_ops, + .name = "mixel-lvds-phy", + .of_match_table = mixel_lvds_phy_of_match, + } +}; +module_platform_driver(mixel_lvds_phy_driver); + +MODULE_DESCRIPTION("Mixel LVDS PHY driver"); +MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c new file mode 100644 index 000000000000..569f12af2aaf --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2021-2022 NXP. */ + +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> + +#define LYNX_28G_NUM_LANE 8 +#define LYNX_28G_NUM_PLL 2 + +/* General registers per SerDes block */ +#define LYNX_28G_PCC8 0x10a0 +#define LYNX_28G_PCC8_SGMII 0x1 +#define LYNX_28G_PCC8_SGMII_DIS 0x0 + +#define LYNX_28G_PCCC 0x10b0 +#define LYNX_28G_PCCC_10GBASER 0x9 +#define LYNX_28G_PCCC_USXGMII 0x1 +#define LYNX_28G_PCCC_SXGMII_DIS 0x0 + +#define LYNX_28G_LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1)) + +/* Per PLL registers */ +#define LYNX_28G_PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0) +#define LYNX_28G_PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24) +#define LYNX_28G_PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23) + +#define LYNX_28G_PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4) +#define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0) (((cr0) & GENMASK(20, 16))) +#define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ 0x0 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ 0x10000 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ 0x20000 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ 0x30000 +#define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ 0x40000 + +#define LYNX_28G_PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8) +#define LYNX_28G_PLLnCR1_FRATE_SEL(cr1) (((cr1) & GENMASK(28, 24))) +#define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO 0x0 +#define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO 0x10000000 +#define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO 0x6000000 + +/* Per SerDes lane registers */ +/* Lane a General Control Register */ +#define LYNX_28G_LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0) +#define LYNX_28G_LNaGCR0_PROTO_SEL_MSK GENMASK(7, 3) +#define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII 0x8 +#define LYNX_28G_LNaGCR0_PROTO_SEL_XFI 0x50 +#define LYNX_28G_LNaGCR0_IF_WIDTH_MSK GENMASK(2, 0) +#define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT 0x0 +#define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT 0x2 + +/* Lane a Tx Reset Control Register */ +#define LYNX_28G_LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) +#define LYNX_28G_LNaTRSTCTL_HLT_REQ BIT(27) +#define LYNX_28G_LNaTRSTCTL_RST_DONE BIT(30) +#define LYNX_28G_LNaTRSTCTL_RST_REQ BIT(31) + +/* Lane a Tx General Control Register */ +#define LYNX_28G_LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24) +#define LYNX_28G_LNaTGCR0_USE_PLLF 0x0 +#define LYNX_28G_LNaTGCR0_USE_PLLS BIT(28) +#define LYNX_28G_LNaTGCR0_USE_PLL_MSK BIT(28) +#define LYNX_28G_LNaTGCR0_N_RATE_FULL 0x0 +#define LYNX_28G_LNaTGCR0_N_RATE_HALF 0x1000000 +#define LYNX_28G_LNaTGCR0_N_RATE_QUARTER 0x2000000 +#define LYNX_28G_LNaTGCR0_N_RATE_MSK GENMASK(26, 24) + +#define LYNX_28G_LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30) + +/* Lane a Rx Reset Control Register */ +#define LYNX_28G_LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40) +#define LYNX_28G_LNaRRSTCTL_HLT_REQ BIT(27) +#define LYNX_28G_LNaRRSTCTL_RST_DONE BIT(30) +#define LYNX_28G_LNaRRSTCTL_RST_REQ BIT(31) +#define LYNX_28G_LNaRRSTCTL_CDR_LOCK BIT(12) + +/* Lane a Rx General Control Register */ +#define LYNX_28G_LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44) +#define LYNX_28G_LNaRGCR0_USE_PLLF 0x0 +#define LYNX_28G_LNaRGCR0_USE_PLLS BIT(28) +#define LYNX_28G_LNaRGCR0_USE_PLL_MSK BIT(28) +#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24) +#define LYNX_28G_LNaRGCR0_N_RATE_FULL 0x0 +#define LYNX_28G_LNaRGCR0_N_RATE_HALF 0x1000000 +#define LYNX_28G_LNaRGCR0_N_RATE_QUARTER 0x2000000 +#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24) + +#define LYNX_28G_LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48) + +#define LYNX_28G_LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50) +#define LYNX_28G_LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54) +#define LYNX_28G_LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58) + +#define LYNX_28G_LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74) + +#define LYNX_28G_LNaPSS(lane) (0x1000 + (lane) * 0x4) +#define LYNX_28G_LNaPSS_TYPE(pss) (((pss) & GENMASK(30, 24)) >> 24) +#define LYNX_28G_LNaPSS_TYPE_SGMII 0x4 +#define LYNX_28G_LNaPSS_TYPE_XFI 0x28 + +#define LYNX_28G_SGMIIaCR1(lane) (0x1804 + (lane) * 0x10) +#define LYNX_28G_SGMIIaCR1_SGPCS_EN BIT(11) +#define LYNX_28G_SGMIIaCR1_SGPCS_DIS 0x0 +#define LYNX_28G_SGMIIaCR1_SGPCS_MSK BIT(11) + +struct lynx_28g_priv; + +struct lynx_28g_pll { + struct lynx_28g_priv *priv; + u32 rstctl, cr0, cr1; + int id; + DECLARE_PHY_INTERFACE_MASK(supported); +}; + +struct lynx_28g_lane { + struct lynx_28g_priv *priv; + struct phy *phy; + bool powered_up; + bool init; + unsigned int id; + phy_interface_t interface; +}; + +struct lynx_28g_priv { + void __iomem *base; + struct device *dev; + struct lynx_28g_pll pll[LYNX_28G_NUM_PLL]; + struct lynx_28g_lane lane[LYNX_28G_NUM_LANE]; + + struct delayed_work cdr_check; +}; + +static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off, + u32 val, u32 mask) +{ + void __iomem *reg = priv->base + off; + u32 orig, tmp; + + orig = ioread32(reg); + tmp = orig & ~mask; + tmp |= val; + iowrite32(tmp, reg); +} + +#define lynx_28g_lane_rmw(lane, reg, val, mask) \ + lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \ + LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask) +#define lynx_28g_lane_read(lane, reg) \ + ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id)) +#define lynx_28g_pll_read(pll, reg) \ + ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id)) + +static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf) +{ + int i; + + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { + if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl)) + continue; + + if (test_bit(intf, priv->pll[i].supported)) + return true; + } + + return false; +} + +static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, + phy_interface_t intf) +{ + struct lynx_28g_pll *pll; + int i; + + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { + pll = &priv->pll[i]; + + if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl)) + continue; + + if (test_bit(intf, pll->supported)) + return pll; + } + + return NULL; +} + +static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, + struct lynx_28g_pll *pll, + phy_interface_t intf) +{ + switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) { + case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO: + case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO: + switch (intf) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK); + break; + default: + break; + } + break; + case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO: + switch (intf) { + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK); + break; + default: + break; + } + break; + default: + break; + } +} + +static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane, + struct lynx_28g_pll *pll) +{ + if (pll->id == 0) { + lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK); + } else { + lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK); + lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK); + } +} + +static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane) +{ + u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); + struct lynx_28g_priv *priv = lane->priv; + + /* Cleanup the protocol configuration registers of the current protocol */ + switch (lane->interface) { + case PHY_INTERFACE_MODE_10GBASER: + lynx_28g_rmw(priv, LYNX_28G_PCCC, + LYNX_28G_PCCC_SXGMII_DIS << lane_offset, + GENMASK(3, 0) << lane_offset); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + lynx_28g_rmw(priv, LYNX_28G_PCC8, + LYNX_28G_PCC8_SGMII_DIS << lane_offset, + GENMASK(3, 0) << lane_offset); + break; + default: + break; + } +} + +static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane) +{ + u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); + struct lynx_28g_priv *priv = lane->priv; + struct lynx_28g_pll *pll; + + lynx_28g_cleanup_lane(lane); + + /* Setup the lane to run in SGMII */ + lynx_28g_rmw(priv, LYNX_28G_PCC8, + LYNX_28G_PCC8_SGMII << lane_offset, + GENMASK(3, 0) << lane_offset); + + /* Setup the protocol select and SerDes parallel interface width */ + lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK); + lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK); + + /* Switch to the PLL that works with this interface type */ + pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII); + lynx_28g_lane_set_pll(lane, pll); + + /* Choose the portion of clock net to be used on this lane */ + lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII); + + /* Enable the SGMII PCS */ + lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK); + + /* Configure the appropriate equalization parameters for the protocol */ + iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id)); + iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id)); + iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id)); + iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id)); + iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id)); + iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id)); +} + +static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane) +{ + u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); + struct lynx_28g_priv *priv = lane->priv; + struct lynx_28g_pll *pll; + + lynx_28g_cleanup_lane(lane); + + /* Enable the SXGMII lane */ + lynx_28g_rmw(priv, LYNX_28G_PCCC, + LYNX_28G_PCCC_10GBASER << lane_offset, + GENMASK(3, 0) << lane_offset); + + /* Setup the protocol select and SerDes parallel interface width */ + lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK); + lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK); + + /* Switch to the PLL that works with this interface type */ + pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER); + lynx_28g_lane_set_pll(lane, pll); + + /* Choose the portion of clock net to be used on this lane */ + lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER); + + /* Disable the SGMII PCS */ + lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK); + + /* Configure the appropriate equalization parameters for the protocol */ + iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id)); + iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id)); + iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id)); + iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id)); + iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id)); + iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id)); +} + +static int lynx_28g_power_off(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + u32 trstctl, rrstctl; + + if (!lane->powered_up) + return 0; + + /* Issue a halt request */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ); + + /* Wait until the halting process is complete */ + do { + trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) || + (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ)); + + lane->powered_up = false; + + return 0; +} + +static int lynx_28g_power_on(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + u32 trstctl, rrstctl; + + if (lane->powered_up) + return 0; + + /* Issue a reset request on the lane */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ); + + /* Wait until the reset sequence is completed */ + do { + trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) || + !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE)); + + lane->powered_up = true; + + return 0; +} + +static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + struct lynx_28g_priv *priv = lane->priv; + int powered_up = lane->powered_up; + int err = 0; + + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + if (lane->interface == PHY_INTERFACE_MODE_NA) + return -EOPNOTSUPP; + + if (!lynx_28g_supports_interface(priv, submode)) + return -EOPNOTSUPP; + + /* If the lane is powered up, put the lane into the halt state while + * the reconfiguration is being done. + */ + if (powered_up) + lynx_28g_power_off(phy); + + switch (submode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + lynx_28g_lane_set_sgmii(lane); + break; + case PHY_INTERFACE_MODE_10GBASER: + lynx_28g_lane_set_10gbaser(lane); + break; + default: + err = -EOPNOTSUPP; + goto out; + } + + lane->interface = submode; + +out: + /* Power up the lane if necessary */ + if (powered_up) + lynx_28g_power_on(phy); + + return err; +} + +static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts __always_unused) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + struct lynx_28g_priv *priv = lane->priv; + + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + if (!lynx_28g_supports_interface(priv, submode)) + return -EOPNOTSUPP; + + return 0; +} + +static int lynx_28g_init(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + + /* Mark the fact that the lane was init */ + lane->init = true; + + /* SerDes lanes are powered on at boot time. Any lane that is managed + * by this driver will get powered down at init time aka at dpaa2-eth + * probe time. + */ + lane->powered_up = true; + lynx_28g_power_off(phy); + + return 0; +} + +static const struct phy_ops lynx_28g_ops = { + .init = lynx_28g_init, + .power_on = lynx_28g_power_on, + .power_off = lynx_28g_power_off, + .set_mode = lynx_28g_set_mode, + .validate = lynx_28g_validate, + .owner = THIS_MODULE, +}; + +static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) +{ + struct lynx_28g_pll *pll; + int i; + + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { + pll = &priv->pll[i]; + pll->priv = priv; + pll->id = i; + + pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL); + pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0); + pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1); + + if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl)) + continue; + + switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) { + case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO: + case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO: + /* 5GHz clock net */ + __set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported); + __set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported); + break; + case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO: + /* 10.3125GHz clock net */ + __set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported); + break; + default: + /* 6GHz, 12.890625GHz, 8GHz */ + break; + } + } +} + +#define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work) + +static void lynx_28g_cdr_lock_check(struct work_struct *work) +{ + struct lynx_28g_priv *priv = work_to_lynx(work); + struct lynx_28g_lane *lane; + u32 rrstctl; + int i; + + for (i = 0; i < LYNX_28G_NUM_LANE; i++) { + lane = &priv->lane[i]; + + if (!lane->init) + continue; + + if (!lane->powered_up) + continue; + + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) { + lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ); + do { + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE)); + } + } + queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, + msecs_to_jiffies(1000)); +} + +static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) +{ + u32 pss, protocol; + + pss = lynx_28g_lane_read(lane, LNaPSS); + protocol = LYNX_28G_LNaPSS_TYPE(pss); + switch (protocol) { + case LYNX_28G_LNaPSS_TYPE_SGMII: + lane->interface = PHY_INTERFACE_MODE_SGMII; + break; + case LYNX_28G_LNaPSS_TYPE_XFI: + lane->interface = PHY_INTERFACE_MODE_10GBASER; + break; + default: + lane->interface = PHY_INTERFACE_MODE_NA; + } +} + +static struct phy *lynx_28g_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct lynx_28g_priv *priv = dev_get_drvdata(dev); + int idx = args->args[0]; + + if (WARN_ON(idx >= LYNX_28G_NUM_LANE)) + return ERR_PTR(-EINVAL); + + return priv->lane[idx].phy; +} + +static int lynx_28g_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct lynx_28g_priv *priv; + int i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = &pdev->dev; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + lynx_28g_pll_read_configuration(priv); + + for (i = 0; i < LYNX_28G_NUM_LANE; i++) { + struct lynx_28g_lane *lane = &priv->lane[i]; + struct phy *phy; + + memset(lane, 0, sizeof(*lane)); + + phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + lane->priv = priv; + lane->phy = phy; + lane->id = i; + phy_set_drvdata(phy, lane); + lynx_28g_lane_read_configuration(lane); + } + + dev_set_drvdata(dev, priv); + + INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); + + queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, + msecs_to_jiffies(1000)); + + dev_set_drvdata(&pdev->dev, priv); + provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id lynx_28g_of_match_table[] = { + { .compatible = "fsl,lynx-28g" }, + { }, +}; +MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table); + +static struct platform_driver lynx_28g_driver = { + .probe = lynx_28g_probe, + .driver = { + .name = "lynx-28g", + .of_match_table = lynx_28g_of_match_table, + }, +}; +module_platform_driver(lynx_28g_driver); + +MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>"); +MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig index 1c73053bcc98..d3b92c288554 100644 --- a/drivers/phy/hisilicon/Kconfig +++ b/drivers/phy/hisilicon/Kconfig @@ -23,6 +23,26 @@ config PHY_HI3660_USB To compile this driver as a module, choose M here. +config PHY_HI3670_USB + tristate "hi3670 USB PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the HISILICON HI3670 USB PHY. + + To compile this driver as a module, choose M here. + +config PHY_HI3670_PCIE + tristate "hi3670 PCIe PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the HiSilicon hi3670 PCIe PHY. + + To compile this driver as a module, choose M here. + config PHY_HISTB_COMBPHY tristate "HiSilicon STB SoCs COMBPHY support" depends on (ARCH_HISI && ARM64) || COMPILE_TEST diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile index 92e874ae9c74..4029d3813b1e 100644 --- a/drivers/phy/hisilicon/Makefile +++ b/drivers/phy/hisilicon/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o +obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o +obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o diff --git a/drivers/phy/hisilicon/phy-hi3660-usb3.c b/drivers/phy/hisilicon/phy-hi3660-usb3.c index cc0af2c044d0..84adce9b4277 100644 --- a/drivers/phy/hisilicon/phy-hi3660-usb3.c +++ b/drivers/phy/hisilicon/phy-hi3660-usb3.c @@ -161,7 +161,7 @@ out: return ret; } -static struct phy_ops hi3660_phy_ops = { +static const struct phy_ops hi3660_phy_ops = { .init = hi3660_phy_init, .exit = hi3660_phy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c new file mode 100644 index 000000000000..0ac9634b398d --- /dev/null +++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe phy driver for Kirin 970 + * + * Copyright (C) 2017 HiSilicon Electronics Co., Ltd. + * https://www.huawei.com + * Copyright (C) 2021 Huawei Technologies Co., Ltd. + * https://www.huawei.com + * + * Authors: + * Mauro Carvalho Chehab <mchehab+huawei@kernel.org> + * Manivannan Sadhasivam <mani@kernel.org> + * + * Based on: + * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/ + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define AXI_CLK_FREQ 207500000 +#define REF_CLK_FREQ 100000000 + +/* PCIe CTRL registers */ +#define SOC_PCIECTRL_CTRL7_ADDR 0x01c +#define SOC_PCIECTRL_CTRL12_ADDR 0x030 +#define SOC_PCIECTRL_CTRL20_ADDR 0x050 +#define SOC_PCIECTRL_CTRL21_ADDR 0x054 + +#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0) +#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550 +#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070 +#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10) +#define PCIE_OUTPUT_PULL_DOWN BIT(1) + +/* PCIe PHY registers */ +#define SOC_PCIEPHY_CTRL0_ADDR 0x000 +#define SOC_PCIEPHY_CTRL1_ADDR 0x004 +#define SOC_PCIEPHY_CTRL38_ADDR 0x0098 +#define SOC_PCIEPHY_STATE0_ADDR 0x400 + +#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004 +#define SUP_DIG_LVL_OVRD_IN 0x003c +#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008 +#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c + +#define PCIEPHY_RESET_BIT BIT(17) +#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19) +#define PCIE_TXDETECT_RX_FAIL BIT(2) +#define PCIE_CLK_SOURCE BIT(8) +#define PCIE_IS_CLOCK_STABLE BIT(19) +#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22) +#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2) + +#define EYEPARAM_NOCFG 0xffffffff +#define EYE_PARM0_MASK GENMASK(8, 6) +#define EYE_PARM1_MASK GENMASK(11, 8) +#define EYE_PARM2_MASK GENMASK(5, 0) +#define EYE_PARM3_MASK GENMASK(12, 7) +#define EYE_PARM4_MASK GENMASK(14, 9) +#define EYE_PARM0_EN BIT(9) +#define EYE_PARM1_EN BIT(12) +#define EYE_PARM2_EN BIT(6) +#define EYE_PARM3_EN BIT(13) +#define EYE_PARM4_EN BIT(15) + +/* hi3670 pciephy register */ +#define APB_PHY_START_ADDR 0x40000 +#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04 +#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40 +#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44 +#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50 +#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54 +#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00 + +#define CRGPERIPH_PEREN12 0x470 +#define CRGPERIPH_PERDIS12 0x474 +#define CRGPERIPH_PCIECTRL0 0x800 + +#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16) +#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0) +#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8) +#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12) +#define PCIE_FNPLL_PLL_MODE_MASK BIT(25) + +#define PCIE_FNPLL_DLL_EN BIT(27) +#define PCIE_FNPLL_FBDIV 0xd0 +#define PCIE_FNPLL_FRACDIV 0x555555 +#define PCIE_FNPLL_POSTDIV1 0x5 +#define PCIE_FNPLL_POSTDIV2 0x4 +#define PCIE_FNPLL_PLL_MODE 0x0 + +#define PCIE_PHY_MMC1PLL 0x20 +#define PCIE_PHY_CHOOSE_FNPLL BIT(27) +#define PCIE_PHY_MMC1PLL_DISABLE BIT(0) +#define PCIE_PHY_PCIEPL_BP BIT(16) + +/* define ie,oe cfg */ +#define IO_OE_HARD_GT_MODE BIT(1) +#define IO_IE_EN_HARD_BYPASS BIT(27) +#define IO_OE_EN_HARD_BYPASS BIT(11) +#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10) +#define IO_OE_GT_MODE BIT(8) +#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20) +#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13) + +#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15)) +#define IO_PHYREF_SOFT_GT_MODE BIT(14) +#define IO_REF_SOFT_GT_MODE BIT(13) +#define IO_REF_HARD_GT_MODE BIT(0) + +/* noc power domain */ +#define NOC_POWER_IDLEREQ_1 0x38c +#define NOC_POWER_IDLE_1 0x394 +#define NOC_PW_MASK 0x10000 +#define NOC_PW_SET_BIT 0x1 + +#define NUM_EYEPARAM 5 + +/* info located in sysctrl */ +#define SCTRL_PCIE_CMOS_OFFSET 0x60 +#define SCTRL_PCIE_CMOS_BIT 0x10 +#define SCTRL_PCIE_ISO_OFFSET 0x44 +#define SCTRL_PCIE_ISO_BIT 0x30 +#define SCTRL_PCIE_HPCLK_OFFSET 0x190 +#define SCTRL_PCIE_HPCLK_BIT 0x184000 +#define SCTRL_PCIE_OE_OFFSET 0x14a +#define PCIE_DEBOUNCE_PARAM 0xf0f400 +#define PCIE_OE_BYPASS GENMASK(29, 28) + +/* peri_crg ctrl */ +#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88 +#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000 + +#define FNPLL_HAS_LOCKED BIT(4) + +/* Time for delay */ +#define TIME_CMOS_MIN 100 +#define TIME_CMOS_MAX 105 +#define PIPE_CLK_STABLE_TIME 100 +#define PLL_CTRL_WAIT_TIME 200 +#define NOC_POWER_TIME 100 + +struct hi3670_pcie_phy { + struct device *dev; + void __iomem *base; + struct regmap *apb; + struct regmap *crgctrl; + struct regmap *sysctrl; + struct regmap *pmctrl; + struct clk *apb_sys_clk; + struct clk *apb_phy_clk; + struct clk *phy_ref_clk; + struct clk *aclk; + struct clk *aux_clk; + u32 eye_param[NUM_EYEPARAM]; +}; + +/* Registers in PCIePHY */ +static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val, + u32 reg) +{ + writel(val, phy->base + APB_PHY_START_ADDR + reg); +} + +static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg) +{ + return readl(phy->base + APB_PHY_START_ADDR + reg); +} + +static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy, + u32 val, u32 mask, u32 reg) +{ + u32 regval; + + regval = hi3670_apb_phy_readl(phy, reg); + regval &= ~mask; + regval |= val; + hi3670_apb_phy_writel(phy, regval, reg); +} + +static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy, + u32 val, u32 reg) +{ + writel(val, phy->base + reg); +} + +static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy, + u32 reg) +{ + return readl(phy->base + reg); +} + +static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable) +{ + u32 val; + + regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val); + val |= PCIE_DEBOUNCE_PARAM; + if (enable) + val &= ~PCIE_OE_BYPASS; + else + val |= PCIE_OE_BYPASS; + regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val); +} + +static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy) +{ + struct device *dev = phy->dev; + struct device_node *np; + int ret, i; + + np = dev->of_node; + + ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param", + phy->eye_param, NUM_EYEPARAM); + if (!ret) + return; + + /* There's no optional eye_param property. Set array to default */ + for (i = 0; i < NUM_EYEPARAM; i++) + phy->eye_param[i] = EYEPARAM_NOCFG; +} + +static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy) +{ + u32 val; + + val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1); + + if (phy->eye_param[1] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM1_MASK; + val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]); + val |= EYE_PARM1_EN; + } + kirin_apb_natural_phy_writel(phy, val, + RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1); + + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2); + val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK); + if (phy->eye_param[2] != EYEPARAM_NOCFG) { + val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]); + val |= EYE_PARM2_EN; + } + + if (phy->eye_param[3] != EYEPARAM_NOCFG) { + val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]); + val |= EYE_PARM3_EN; + } + + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2); + + val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN); + if (phy->eye_param[0] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM0_MASK; + val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]); + val |= EYE_PARM0_EN; + } + kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN); + + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1); + if (phy->eye_param[4] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM4_MASK; + val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]); + val |= EYE_PARM4_EN; + } + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1); +} + +static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy) +{ + u32 val; + + /* change 2p mem_ctrl */ + regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR, + SOC_PCIECTRL_CTRL20_2P_MEM_CTRL); + + regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val); + val |= PCIE_PULL_UP_SYS_AUX_PWR_DET; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val); + + /* output, pull down */ + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val); + val &= ~PCIE_OUTPUT_PULL_BITS; + val |= PCIE_OUTPUT_PULL_DOWN; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val); + + /* Handle phy_reset and lane0_reset to HW */ + hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT, + PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT, + SOC_PCIEPHY_CTRL1_ADDR); + + /* fix chip bug: TxDetectRx fail */ + hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL, + SOC_PCIEPHY_CTRL38_ADDR); +} + +static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy) +{ + hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL, + SOC_PCIEPHY_MMC1PLL_CTRL1); + + hi3670_apb_phy_updatel(phy, + FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV), + PCIE_FNPLL_FBDIV_MASK, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + hi3670_apb_phy_updatel(phy, + FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV), + PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17); + + hi3670_apb_phy_updatel(phy, + PCIE_FNPLL_DLL_EN | + FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) | + FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) | + FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE), + PCIE_FNPLL_POSTDIV1_MASK | + PCIE_FNPLL_POSTDIV2_MASK | + PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN, + SOC_PCIEPHY_MMC1PLL_CTRL20); + + hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL, + SOC_PCIEPHY_MMC1PLL_CTRL21); +} + +static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable) +{ + struct device *dev = phy->dev; + u32 val; + int time = PLL_CTRL_WAIT_TIME; + + if (enable) { + /* pd = 0 */ + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + /* choose FNPLL */ + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0); + while (!(val & FNPLL_HAS_LOCKED)) { + if (!time) { + dev_err(dev, "wait for pll_lock timeout\n"); + return -EINVAL; + } + time--; + udelay(1); + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0); + } + + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP, + SOC_PCIEPHY_MMC1PLL_CTRL20); + + } else { + hi3670_apb_phy_updatel(phy, + PCIE_PHY_MMC1PLL_DISABLE, + PCIE_PHY_MMC1PLL_DISABLE, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP, + PCIE_PHY_PCIEPL_BP, + SOC_PCIEPHY_MMC1PLL_CTRL20); + } + + return 0; +} + +static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open) +{ + if (open) + /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */ + regmap_write(phy->crgctrl, CRGPERIPH_PEREN12, + IO_HP_DEBOUNCE_GT); + else + /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_HP_DEBOUNCE_GT); +} + +static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open) +{ + unsigned int val; + + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + + if (open) + val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */ + else + val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */ + + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE); +} + +static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag) +{ + unsigned int val; + + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + + /* set ie cfg */ + val |= IO_IE_EN_HARD_BYPASS; + + /* set oe cfg */ + val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS; + + /* set phy_debounce in&out time */ + val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT); + + /* select oe_gt_mode */ + val |= IO_OE_GT_MODE; + + if (en_flag) + val &= ~IO_OE_EN_HARD_BYPASS; + else + val |= IO_OE_EN_HARD_BYPASS; + + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); +} + +static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open) +{ + unsigned int val; + + if (open) { + regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR, + SOC_PCIECTRL_CTRL21_DEFAULT); + + hi3670_pcie_oe_ctrl(phy, true); + + /* en hard gt mode */ + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + val &= ~IO_REF_HARD_GT_MODE; + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_REF_SOFT_GT_MODE); + + } else { + /* disable hard gt mode */ + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + val |= IO_REF_HARD_GT_MODE; + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_REF_SOFT_GT_MODE); + + hi3670_pcie_oe_ctrl(phy, false); + } +} + +static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on) +{ + struct device *dev = phy->dev; + int ret = 0; + + if (!clk_on) + goto close_clocks; + + /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */ + hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE, + SOC_PCIEPHY_CTRL1_ADDR); + + hi3670_pcie_pll_init(phy); + + ret = hi3670_pcie_pll_ctrl(phy, true); + if (ret) { + dev_err(dev, "Failed to enable pll\n"); + return -EINVAL; + } + hi3670_pcie_hp_debounce_gt(phy, true); + hi3670_pcie_phyref_gt(phy, true); + hi3670_pcie_ioref_gt(phy, true); + + ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ); + if (ret) { + dev_err(dev, "Failed to set rate\n"); + goto close_clocks; + } + + return 0; + +close_clocks: + hi3670_pcie_ioref_gt(phy, false); + hi3670_pcie_phyref_gt(phy, false); + hi3670_pcie_hp_debounce_gt(phy, false); + + hi3670_pcie_pll_ctrl(phy, false); + + return ret; +} + +static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy) +{ + struct device *dev = phy->dev; + u32 val; + u32 time = PIPE_CLK_STABLE_TIME; + u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE; + + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR); + while (val & pipe_clk_stable) { + mdelay(1); + if (!time) { + dev_err(dev, "PIPE clk is not stable\n"); + return false; + } + time--; + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR); + } + + return true; +} + +static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable) +{ + struct device *dev = phy->dev; + u32 time = NOC_POWER_TIME; + unsigned int val = NOC_PW_MASK; + int rst; + + if (enable) + val = NOC_PW_MASK | NOC_PW_SET_BIT; + else + val = NOC_PW_MASK; + rst = enable ? 1 : 0; + + regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val); + + time = NOC_POWER_TIME; + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val); + while ((val & NOC_PW_SET_BIT) != rst) { + udelay(10); + if (!time) { + dev_err(dev, "Failed to reverse noc power-status\n"); + return -EINVAL; + } + time--; + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val); + } + + return 0; +} + +static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy) +{ + struct device_node *pcie_port; + struct device *dev = phy->dev; + struct device *pcie_dev; + + pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie"); + if (!pcie_port) { + dev_err(dev, "no pcie node found in %s\n", + dev->parent->of_node->full_name); + return -ENODEV; + } + + pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port); + if (!pcie_dev) { + dev_err(dev, "Didn't find pcie device\n"); + return -ENODEV; + } + + /* + * We might just use NULL instead of the APB name, as the + * pcie-kirin currently registers directly just one regmap (although + * the DWC driver register other regmaps). + * + * Yet, it sounds safer to warrant that it will be accessing the + * right regmap. So, let's use the named version. + */ + phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb"); + if (!phy->apb) { + dev_err(dev, "Failed to get APB regmap\n"); + return -ENODEV; + } + + return 0; +} + +static int kirin_pcie_clk_ctrl(struct hi3670_pcie_phy *phy, bool enable) +{ + int ret = 0; + + if (!enable) + goto close_clk; + + ret = clk_set_rate(phy->phy_ref_clk, REF_CLK_FREQ); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->phy_ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->apb_sys_clk); + if (ret) + goto apb_sys_fail; + + ret = clk_prepare_enable(phy->apb_phy_clk); + if (ret) + goto apb_phy_fail; + + ret = clk_prepare_enable(phy->aclk); + if (ret) + goto aclk_fail; + + ret = clk_prepare_enable(phy->aux_clk); + if (ret) + goto aux_clk_fail; + + return 0; + +close_clk: + clk_disable_unprepare(phy->aux_clk); +aux_clk_fail: + clk_disable_unprepare(phy->aclk); +aclk_fail: + clk_disable_unprepare(phy->apb_phy_clk); +apb_phy_fail: + clk_disable_unprepare(phy->apb_sys_clk); +apb_sys_fail: + clk_disable_unprepare(phy->phy_ref_clk); + + return ret; +} + +static int hi3670_pcie_phy_init(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + int ret; + + /* + * The code under hi3670_pcie_get_resources_from_pcie() need to + * access the reset-gpios and the APB registers, both from the + * pcie-kirin driver. + * + * The APB is obtained via the pcie driver's regmap + * Such kind of resource can only be obtained during the PCIe + * power_on sequence, as the code inside pcie-kirin needs to + * be already probed, as it needs to register the APB regmap. + */ + + ret = hi3670_pcie_get_resources_from_pcie(phy); + if (ret) + return ret; + + return 0; +} + +static int hi3670_pcie_phy_power_on(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + int val, ret; + + /* Power supply for Host */ + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT); + usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX); + + hi3670_pcie_phy_oe_enable(phy, true); + + ret = kirin_pcie_clk_ctrl(phy, true); + if (ret) + return ret; + + /* ISO disable, PCIeCtrl, PHY assert and clk gate clear */ + regmap_write(phy->sysctrl, SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT); + regmap_write(phy->crgctrl, CRGCTRL_PCIE_ASSERT_OFFSET, + CRGCTRL_PCIE_ASSERT_BIT); + regmap_write(phy->sysctrl, SCTRL_PCIE_HPCLK_OFFSET, + SCTRL_PCIE_HPCLK_BIT); + + hi3670_pcie_natural_cfg(phy); + + ret = hi3670_pcie_allclk_ctrl(phy, true); + if (ret) + goto disable_clks; + + /* pull down phy_test_powerdown signal */ + hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN, + SOC_PCIEPHY_CTRL0_ADDR); + + /* deassert controller perst_n */ + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val); + val |= PCIE_DEASSERT_CONTROLLER_PERST; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val); + udelay(10); + + ret = is_pipe_clk_stable(phy); + if (!ret) + goto disable_clks; + + hi3670_pcie_set_eyeparam(phy); + + ret = hi3670_pcie_noc_power(phy, false); + if (ret) + goto disable_clks; + + return 0; + +disable_clks: + kirin_pcie_clk_ctrl(phy, false); + return ret; +} + +static int hi3670_pcie_phy_power_off(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + + hi3670_pcie_phy_oe_enable(phy, false); + + hi3670_pcie_allclk_ctrl(phy, false); + + /* Drop power supply for Host */ + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0); + + /* + * FIXME: The enabled clocks should be disabled here by calling + * kirin_pcie_clk_ctrl(phy, false); + * However, some clocks used at Kirin 970 should be marked as + * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off + * cause an Asynchronous SError interrupt, which produces panic(). + * While clk-hi3670 is not fixed, we cannot risk disabling clocks here. + */ + + return 0; +} + +static const struct phy_ops hi3670_phy_ops = { + .init = hi3670_pcie_phy_init, + .power_on = hi3670_pcie_phy_power_on, + .power_off = hi3670_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* syscon */ + phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl"); + if (IS_ERR(phy->crgctrl)) + return PTR_ERR(phy->crgctrl); + + phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl"); + if (IS_ERR(phy->sysctrl)) + return PTR_ERR(phy->sysctrl); + + phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl"); + if (IS_ERR(phy->pmctrl)) + return PTR_ERR(phy->pmctrl); + + /* clocks */ + phy->phy_ref_clk = devm_clk_get(dev, "phy_ref"); + if (IS_ERR(phy->phy_ref_clk)) + return PTR_ERR(phy->phy_ref_clk); + + phy->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(phy->aux_clk)) + return PTR_ERR(phy->aux_clk); + + phy->apb_phy_clk = devm_clk_get(dev, "apb_phy"); + if (IS_ERR(phy->apb_phy_clk)) + return PTR_ERR(phy->apb_phy_clk); + + phy->apb_sys_clk = devm_clk_get(dev, "apb_sys"); + if (IS_ERR(phy->apb_sys_clk)) + return PTR_ERR(phy->apb_sys_clk); + + phy->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(phy->aclk)) + return PTR_ERR(phy->aclk); + + /* registers */ + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) + return PTR_ERR(phy->base); + + hi3670_pcie_get_eyeparam(phy); + + return 0; +} + +static int hi3670_pcie_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct hi3670_pcie_phy *phy; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = dev; + + ret = hi3670_pcie_phy_get_resources(phy, pdev); + if (ret) + return ret; + + generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hi3670_pcie_phy_match[] = { + { + .compatible = "hisilicon,hi970-pcie-phy", + }, + {}, +}; + +static struct platform_driver hi3670_pcie_phy_driver = { + .probe = hi3670_pcie_phy_probe, + .driver = { + .of_match_table = hi3670_pcie_phy_match, + .name = "hi3670_pcie_phy", + .suppress_bind_attrs = true, + } +}; +builtin_platform_driver(hi3670_pcie_phy_driver); + +MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match); +MODULE_DESCRIPTION("PCIe phy driver for Kirin 970"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>"); +MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/hisilicon/phy-hi3670-usb3.c b/drivers/phy/hisilicon/phy-hi3670-usb3.c new file mode 100644 index 000000000000..b9ffe08abaab --- /dev/null +++ b/drivers/phy/hisilicon/phy-hi3670-usb3.c @@ -0,0 +1,661 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phy provider for USB 3.1 controller on HiSilicon Kirin970 platform + * + * Copyright (C) 2017-2020 Hilisicon Electronics Co., Ltd. + * http://www.huawei.com + * + * Authors: Yu Chen <chenyu56@huawei.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define SCTRL_SCDEEPSLEEPED (0x0) +#define USB_CLK_SELECTED BIT(20) + +#define PERI_CRG_PEREN0 (0x00) +#define PERI_CRG_PERDIS0 (0x04) +#define PERI_CRG_PEREN4 (0x40) +#define PERI_CRG_PERDIS4 (0x44) +#define PERI_CRG_PERRSTEN4 (0x90) +#define PERI_CRG_PERRSTDIS4 (0x94) +#define PERI_CRG_ISODIS (0x148) +#define PERI_CRG_PEREN6 (0x410) +#define PERI_CRG_PERDIS6 (0x414) + +#define USB_REFCLK_ISO_EN BIT(25) + +#define GT_CLK_USB2PHY_REF BIT(19) + +#define PCTRL_PERI_CTRL3 (0x10) +#define PCTRL_PERI_CTRL3_MSK_START (16) +#define USB_TCXO_EN BIT(1) + +#define PCTRL_PERI_CTRL24 (0x64) +#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25) + +#define USB3OTG_CTRL0 (0x00) +#define USB3OTG_CTRL3 (0x0c) +#define USB3OTG_CTRL4 (0x10) +#define USB3OTG_CTRL5 (0x14) +#define USB3OTG_CTRL7 (0x1c) +#define USB_MISC_CFG50 (0x50) +#define USB_MISC_CFG54 (0x54) +#define USB_MISC_CFG58 (0x58) +#define USB_MISC_CFG5C (0x5c) +#define USB_MISC_CFGA0 (0xa0) +#define TCA_CLK_RST (0x200) +#define TCA_INTR_EN (0x204) +#define TCA_INTR_STS (0x208) +#define TCA_GCFG (0x210) +#define TCA_TCPC (0x214) +#define TCA_SYSMODE_CFG (0x218) +#define TCA_VBUS_CTRL (0x240) + +#define CTRL0_USB3_VBUSVLD BIT(7) +#define CTRL0_USB3_VBUSVLD_SEL BIT(6) + +#define CTRL3_USB2_VBUSVLDEXT0 BIT(6) +#define CTRL3_USB2_VBUSVLDEXTSEL0 BIT(5) + +#define CTRL5_USB2_SIDDQ BIT(0) + +#define CTRL7_USB2_REFCLKSEL_MASK GENMASK(4, 3) +#define CTRL7_USB2_REFCLKSEL_ABB (BIT(4) | BIT(3)) +#define CTRL7_USB2_REFCLKSEL_PAD BIT(4) + +#define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23) + +#define CFG54_USB31PHY_CR_ADDR_MASK GENMASK(31, 16) + +#define CFG54_USB3PHY_REF_USE_PAD BIT(12) +#define CFG54_PHY0_PMA_PWR_STABLE BIT(11) +#define CFG54_PHY0_PCS_PWR_STABLE BIT(9) +#define CFG54_USB31PHY_CR_ACK BIT(7) +#define CFG54_USB31PHY_CR_WR_EN BIT(5) +#define CFG54_USB31PHY_CR_SEL BIT(4) +#define CFG54_USB31PHY_CR_RD_EN BIT(3) +#define CFG54_USB31PHY_CR_CLK BIT(2) +#define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1) + +#define CFG58_USB31PHY_CR_DATA_MASK GENMASK(31, 16) + +#define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1) + +#define CFGA0_VAUX_RESET BIT(9) +#define CFGA0_USB31C_RESET BIT(8) +#define CFGA0_USB2PHY_REFCLK_SELECT BIT(4) +#define CFGA0_USB3PHY_RESET BIT(1) +#define CFGA0_USB2PHY_POR BIT(0) + +#define INTR_EN_XA_TIMEOUT_EVT_EN BIT(1) +#define INTR_EN_XA_ACK_EVT_EN BIT(0) + +#define CLK_RST_TCA_REF_CLK_EN BIT(1) +#define CLK_RST_SUSPEND_CLK_EN BIT(0) + +#define GCFG_ROLE_HSTDEV BIT(4) +#define GCFG_OP_MODE GENMASK(1, 0) +#define GCFG_OP_MODE_CTRL_SYNC_MODE BIT(0) + +#define TCPC_VALID BIT(4) +#define TCPC_LOW_POWER_EN BIT(3) +#define TCPC_MUX_CONTROL_MASK GENMASK(1, 0) +#define TCPC_MUX_CONTROL_USB31 BIT(0) + +#define SYSMODE_CFG_TYPEC_DISABLE BIT(3) + +#define VBUS_CTRL_POWERPRESENT_OVERRD GENMASK(3, 2) +#define VBUS_CTRL_VBUSVALID_OVERRD GENMASK(1, 0) + +#define KIRIN970_USB_DEFAULT_PHY_PARAM (0xfdfee4) +#define KIRIN970_USB_DEFAULT_PHY_VBOOST (0x5) + +#define TX_VBOOST_LVL_REG (0xf) +#define TX_VBOOST_LVL_START (6) +#define TX_VBOOST_LVL_ENABLE BIT(9) + +struct hi3670_priv { + struct device *dev; + struct regmap *peri_crg; + struct regmap *pctrl; + struct regmap *sctrl; + struct regmap *usb31misc; + + u32 eye_diagram_param; + u32 tx_vboost_lvl; + + u32 peri_crg_offset; + u32 pctrl_offset; + u32 usb31misc_offset; +}; + +static int hi3670_phy_cr_clk(struct regmap *usb31misc) +{ + int ret; + + /* Clock up */ + ret = regmap_update_bits(usb31misc, USB_MISC_CFG54, + CFG54_USB31PHY_CR_CLK, CFG54_USB31PHY_CR_CLK); + if (ret) + return ret; + + /* Clock down */ + return regmap_update_bits(usb31misc, USB_MISC_CFG54, + CFG54_USB31PHY_CR_CLK, 0); +} + +static int hi3670_phy_cr_set_sel(struct regmap *usb31misc) +{ + return regmap_update_bits(usb31misc, USB_MISC_CFG54, + CFG54_USB31PHY_CR_SEL, CFG54_USB31PHY_CR_SEL); +} + +static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction) +{ + int ret, reg; + + if (direction) + reg = CFG54_USB31PHY_CR_WR_EN; + else + reg = CFG54_USB31PHY_CR_RD_EN; + + ret = regmap_update_bits(usb31misc, USB_MISC_CFG54, reg, reg); + + if (ret) + return ret; + + ret = hi3670_phy_cr_clk(usb31misc); + if (ret) + return ret; + + return regmap_update_bits(usb31misc, USB_MISC_CFG54, + CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0); +} + +static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc) +{ + u32 reg; + int retry = 10; + int ret; + + while (retry-- > 0) { + ret = regmap_read(usb31misc, USB_MISC_CFG54, ®); + if (ret) + return ret; + if ((reg & CFG54_USB31PHY_CR_ACK) == CFG54_USB31PHY_CR_ACK) + return 0; + + ret = hi3670_phy_cr_clk(usb31misc); + if (ret) + return ret; + + usleep_range(10, 20); + } + + return -ETIMEDOUT; +} + +static int hi3670_phy_cr_set_addr(struct regmap *usb31misc, u32 addr) +{ + u32 reg; + int ret; + + ret = regmap_read(usb31misc, USB_MISC_CFG54, ®); + if (ret) + return ret; + + reg = FIELD_PREP(CFG54_USB31PHY_CR_ADDR_MASK, addr); + + return regmap_update_bits(usb31misc, USB_MISC_CFG54, + CFG54_USB31PHY_CR_ADDR_MASK, reg); +} + +static int hi3670_phy_cr_read(struct regmap *usb31misc, u32 addr, u32 *val) +{ + int reg, i, ret; + + for (i = 0; i < 100; i++) { + ret = hi3670_phy_cr_clk(usb31misc); + if (ret) + return ret; + } + + ret = hi3670_phy_cr_set_sel(usb31misc); + if (ret) + return ret; + + ret = hi3670_phy_cr_set_addr(usb31misc, addr); + if (ret) + return ret; + + ret = hi3670_phy_cr_start(usb31misc, 0); + if (ret) + return ret; + + ret = hi3670_phy_cr_wait_ack(usb31misc); + if (ret) + return ret; + + ret = regmap_read(usb31misc, USB_MISC_CFG58, ®); + if (ret) + return ret; + + *val = FIELD_GET(CFG58_USB31PHY_CR_DATA_MASK, reg); + + return 0; +} + +static int hi3670_phy_cr_write(struct regmap *usb31misc, u32 addr, u32 val) +{ + int i; + int ret; + + for (i = 0; i < 100; i++) { + ret = hi3670_phy_cr_clk(usb31misc); + if (ret) + return ret; + } + + ret = hi3670_phy_cr_set_sel(usb31misc); + if (ret) + return ret; + + ret = hi3670_phy_cr_set_addr(usb31misc, addr); + if (ret) + return ret; + + ret = regmap_write(usb31misc, USB_MISC_CFG58, + FIELD_PREP(CFG58_USB31PHY_CR_DATA_MASK, val)); + if (ret) + return ret; + + ret = hi3670_phy_cr_start(usb31misc, 1); + if (ret) + return ret; + + return hi3670_phy_cr_wait_ack(usb31misc); +} + +static int hi3670_phy_set_params(struct hi3670_priv *priv) +{ + u32 reg; + int ret; + int retry = 3; + + ret = regmap_write(priv->usb31misc, USB3OTG_CTRL4, + priv->eye_diagram_param); + if (ret) { + dev_err(priv->dev, "set USB3OTG_CTRL4 failed\n"); + return ret; + } + + while (retry-- > 0) { + ret = hi3670_phy_cr_read(priv->usb31misc, + TX_VBOOST_LVL_REG, ®); + if (!ret) + break; + + if (ret != -ETIMEDOUT) { + dev_err(priv->dev, "read TX_VBOOST_LVL_REG failed\n"); + return ret; + } + } + if (ret) + return ret; + + reg |= (TX_VBOOST_LVL_ENABLE | (priv->tx_vboost_lvl << TX_VBOOST_LVL_START)); + ret = hi3670_phy_cr_write(priv->usb31misc, TX_VBOOST_LVL_REG, reg); + if (ret) + dev_err(priv->dev, "write TX_VBOOST_LVL_REG failed\n"); + + return ret; +} + +static bool hi3670_is_abbclk_selected(struct hi3670_priv *priv) +{ + u32 reg; + + if (!priv->sctrl) { + dev_err(priv->dev, "priv->sctrl is null!\n"); + return false; + } + + if (regmap_read(priv->sctrl, SCTRL_SCDEEPSLEEPED, ®)) { + dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n"); + return false; + } + + if ((reg & USB_CLK_SELECTED) == 0) + return false; + + return true; +} + +static int hi3670_config_phy_clock(struct hi3670_priv *priv) +{ + u32 val, mask; + int ret; + + if (!hi3670_is_abbclk_selected(priv)) { + /* usb refclk iso disable */ + ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS, + USB_REFCLK_ISO_EN); + if (ret) + goto out; + + /* enable usb_tcxo_en */ + ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, + USB_TCXO_EN | + (USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START)); + + /* select usbphy clk from abb */ + mask = SC_CLK_USB3PHY_3MUX1_SEL; + ret = regmap_update_bits(priv->pctrl, + PCTRL_PERI_CTRL24, mask, 0); + if (ret) + goto out; + + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, + CFGA0_USB2PHY_REFCLK_SELECT, 0); + if (ret) + goto out; + + ret = regmap_read(priv->usb31misc, USB3OTG_CTRL7, &val); + if (ret) + goto out; + val &= ~CTRL7_USB2_REFCLKSEL_MASK; + val |= CTRL7_USB2_REFCLKSEL_ABB; + ret = regmap_write(priv->usb31misc, USB3OTG_CTRL7, val); + if (ret) + goto out; + + return 0; + } + + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG54, + CFG54_USB3PHY_REF_USE_PAD, + CFG54_USB3PHY_REF_USE_PAD); + if (ret) + goto out; + + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, + CFGA0_USB2PHY_REFCLK_SELECT, + CFGA0_USB2PHY_REFCLK_SELECT); + if (ret) + goto out; + + ret = regmap_read(priv->usb31misc, USB3OTG_CTRL7, &val); + if (ret) + goto out; + val &= ~CTRL7_USB2_REFCLKSEL_MASK; + val |= CTRL7_USB2_REFCLKSEL_PAD; + ret = regmap_write(priv->usb31misc, USB3OTG_CTRL7, val); + if (ret) + goto out; + + ret = regmap_write(priv->peri_crg, + PERI_CRG_PEREN6, GT_CLK_USB2PHY_REF); + if (ret) + goto out; + + return 0; +out: + dev_err(priv->dev, "failed to config phy clock ret: %d\n", ret); + return ret; +} + +static int hi3670_config_tca(struct hi3670_priv *priv) +{ + u32 val, mask; + int ret; + + ret = regmap_write(priv->usb31misc, TCA_INTR_STS, 0xffff); + if (ret) + goto out; + + ret = regmap_write(priv->usb31misc, TCA_INTR_EN, + INTR_EN_XA_TIMEOUT_EVT_EN | INTR_EN_XA_ACK_EVT_EN); + if (ret) + goto out; + + mask = CLK_RST_TCA_REF_CLK_EN | CLK_RST_SUSPEND_CLK_EN; + ret = regmap_update_bits(priv->usb31misc, TCA_CLK_RST, mask, 0); + if (ret) + goto out; + + ret = regmap_update_bits(priv->usb31misc, TCA_GCFG, + GCFG_ROLE_HSTDEV | GCFG_OP_MODE, + GCFG_ROLE_HSTDEV | GCFG_OP_MODE_CTRL_SYNC_MODE); + if (ret) + goto out; + + ret = regmap_update_bits(priv->usb31misc, TCA_SYSMODE_CFG, + SYSMODE_CFG_TYPEC_DISABLE, 0); + if (ret) + goto out; + + ret = regmap_read(priv->usb31misc, TCA_TCPC, &val); + if (ret) + goto out; + val &= ~(TCPC_VALID | TCPC_LOW_POWER_EN | TCPC_MUX_CONTROL_MASK); + val |= (TCPC_VALID | TCPC_MUX_CONTROL_USB31); + ret = regmap_write(priv->usb31misc, TCA_TCPC, val); + if (ret) + goto out; + + ret = regmap_write(priv->usb31misc, TCA_VBUS_CTRL, + VBUS_CTRL_POWERPRESENT_OVERRD | VBUS_CTRL_VBUSVALID_OVERRD); + if (ret) + goto out; + + return 0; +out: + dev_err(priv->dev, "failed to config phy clock ret: %d\n", ret); + return ret; +} + +static int hi3670_phy_init(struct phy *phy) +{ + struct hi3670_priv *priv = phy_get_drvdata(phy); + u32 val; + int ret; + + /* assert controller */ + val = CFGA0_VAUX_RESET | CFGA0_USB31C_RESET | + CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR; + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, val, 0); + if (ret) + goto out; + + ret = hi3670_config_phy_clock(priv); + if (ret) + goto out; + + /* Exit from IDDQ mode */ + ret = regmap_update_bits(priv->usb31misc, USB3OTG_CTRL5, + CTRL5_USB2_SIDDQ, 0); + if (ret) + goto out; + + /* Release USB31 PHY out of TestPowerDown mode */ + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG50, + CFG50_USB3_PHY_TEST_POWERDOWN, 0); + if (ret) + goto out; + + /* Deassert phy */ + val = CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR; + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, val, val); + if (ret) + goto out; + + usleep_range(100, 120); + + /* Tell the PHY power is stable */ + val = CFG54_USB3_PHY0_ANA_PWR_EN | CFG54_PHY0_PCS_PWR_STABLE | + CFG54_PHY0_PMA_PWR_STABLE; + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG54, + val, val); + if (ret) + goto out; + + ret = hi3670_config_tca(priv); + if (ret) + goto out; + + /* Enable SSC */ + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFG5C, + CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN, + CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN); + if (ret) + goto out; + + /* Deassert controller */ + val = CFGA0_VAUX_RESET | CFGA0_USB31C_RESET; + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, val, val); + if (ret) + goto out; + + usleep_range(100, 120); + + /* Set fake vbus valid signal */ + val = CTRL0_USB3_VBUSVLD | CTRL0_USB3_VBUSVLD_SEL; + ret = regmap_update_bits(priv->usb31misc, USB3OTG_CTRL0, val, val); + if (ret) + goto out; + + val = CTRL3_USB2_VBUSVLDEXT0 | CTRL3_USB2_VBUSVLDEXTSEL0; + ret = regmap_update_bits(priv->usb31misc, USB3OTG_CTRL3, val, val); + if (ret) + goto out; + + usleep_range(100, 120); + + ret = hi3670_phy_set_params(priv); + if (ret) + goto out; + + return 0; +out: + dev_err(priv->dev, "failed to init phy ret: %d\n", ret); + return ret; +} + +static int hi3670_phy_exit(struct phy *phy) +{ + struct hi3670_priv *priv = phy_get_drvdata(phy); + u32 mask; + int ret; + + /* Assert phy */ + mask = CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR; + ret = regmap_update_bits(priv->usb31misc, USB_MISC_CFGA0, mask, 0); + if (ret) + goto out; + + if (!hi3670_is_abbclk_selected(priv)) { + /* disable usb_tcxo_en */ + ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, + USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START); + } else { + ret = regmap_write(priv->peri_crg, PERI_CRG_PERDIS6, + GT_CLK_USB2PHY_REF); + if (ret) + goto out; + } + + return 0; +out: + dev_err(priv->dev, "failed to exit phy ret: %d\n", ret); + return ret; +} + +static const struct phy_ops hi3670_phy_ops = { + .init = hi3670_phy_init, + .exit = hi3670_phy_exit, + .owner = THIS_MODULE, +}; + +static int hi3670_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct phy *phy; + struct hi3670_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->peri_crg = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,pericrg-syscon"); + if (IS_ERR(priv->peri_crg)) { + dev_err(dev, "no hisilicon,pericrg-syscon\n"); + return PTR_ERR(priv->peri_crg); + } + + priv->pctrl = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,pctrl-syscon"); + if (IS_ERR(priv->pctrl)) { + dev_err(dev, "no hisilicon,pctrl-syscon\n"); + return PTR_ERR(priv->pctrl); + } + + priv->sctrl = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,sctrl-syscon"); + if (IS_ERR(priv->sctrl)) { + dev_err(dev, "no hisilicon,sctrl-syscon\n"); + return PTR_ERR(priv->sctrl); + } + + /* node of hi3670 phy is a sub-node of usb3_otg_bc */ + priv->usb31misc = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->usb31misc)) { + dev_err(dev, "no hisilicon,usb3-otg-bc-syscon\n"); + return PTR_ERR(priv->usb31misc); + } + + if (of_property_read_u32(dev->of_node, "hisilicon,eye-diagram-param", + &priv->eye_diagram_param)) + priv->eye_diagram_param = KIRIN970_USB_DEFAULT_PHY_PARAM; + + if (of_property_read_u32(dev->of_node, "hisilicon,tx-vboost-lvl", + &priv->tx_vboost_lvl)) + priv->tx_vboost_lvl = KIRIN970_USB_DEFAULT_PHY_VBOOST; + + phy = devm_phy_create(dev, NULL, &hi3670_phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hi3670_phy_of_match[] = { + { .compatible = "hisilicon,hi3670-usb-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, hi3670_phy_of_match); + +static struct platform_driver hi3670_phy_driver = { + .probe = hi3670_phy_probe, + .driver = { + .name = "hi3670-usb-phy", + .of_match_table = hi3670_phy_of_match, + } +}; +module_platform_driver(hi3670_phy_driver); + +MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Hilisicon Kirin970 USB31 PHY Driver"); diff --git a/drivers/phy/hisilicon/phy-hi6220-usb.c b/drivers/phy/hisilicon/phy-hi6220-usb.c index be05292df8b8..e92ba78da4c8 100644 --- a/drivers/phy/hisilicon/phy-hi6220-usb.c +++ b/drivers/phy/hisilicon/phy-hi6220-usb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Linaro Ltd. - * Copyright (c) 2015 Hisilicon Limited. + * Copyright (c) 2015 HiSilicon Limited. */ #include <linux/mfd/syscon.h> diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c index 34a6a9a1ceb2..b133ae06757a 100644 --- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c +++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c @@ -140,14 +140,19 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) struct phy *phy; rst = of_reset_control_get_exclusive(child, NULL); - if (IS_ERR(rst)) + if (IS_ERR(rst)) { + of_node_put(child); return PTR_ERR(rst); + } + priv->ports[i].utmi_rst = rst; priv->ports[i].priv = priv; phy = devm_phy_create(dev, child, &hisi_inno_phy_ops); - if (IS_ERR(phy)) + if (IS_ERR(phy)) { + of_node_put(child); return PTR_ERR(phy); + } phy_set_bus_width(phy, 8); phy_set_drvdata(phy, &priv->ports[i]); @@ -155,6 +160,7 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) if (i > INNO_PHY_PORT_NUM) { dev_warn(dev, "Support %d ports in maximum\n", i); + of_node_put(child); break; } } diff --git a/drivers/phy/hisilicon/phy-hix5hd2-sata.c b/drivers/phy/hisilicon/phy-hix5hd2-sata.c index c67b78cd2602..b0f99a9ac857 100644 --- a/drivers/phy/hisilicon/phy-hix5hd2-sata.c +++ b/drivers/phy/hisilicon/phy-hix5hd2-sata.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014 Linaro Ltd. - * Copyright (c) 2014 Hisilicon Limited. + * Copyright (c) 2014 HiSilicon Limited. */ #include <linux/delay.h> diff --git a/drivers/phy/ingenic/Kconfig b/drivers/phy/ingenic/Kconfig new file mode 100644 index 000000000000..f23cc109324b --- /dev/null +++ b/drivers/phy/ingenic/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Phy drivers for Ingenic platforms +# +config PHY_INGENIC_USB + tristate "Ingenic SoCs USB PHY Driver" + depends on MIPS || COMPILE_TEST + depends on USB_SUPPORT + depends on HAS_IOMEM + select GENERIC_PHY + help + This driver provides USB PHY support for the USB controller found + on the JZ-series and X-series SoCs from Ingenic. diff --git a/drivers/phy/ingenic/Makefile b/drivers/phy/ingenic/Makefile new file mode 100644 index 000000000000..1cb158d7233f --- /dev/null +++ b/drivers/phy/ingenic/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_INGENIC_USB) += phy-ingenic-usb.o diff --git a/drivers/phy/ingenic/phy-ingenic-usb.c b/drivers/phy/ingenic/phy-ingenic-usb.c new file mode 100644 index 000000000000..28c28d816484 --- /dev/null +++ b/drivers/phy/ingenic/phy-ingenic-usb.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Ingenic SoCs USB PHY driver + * Copyright (c) Paul Cercueil <paul@crapouillou.net> + * Copyright (c) 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com> + * Copyright (c) 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +/* OTGPHY register offsets */ +#define REG_USBPCR_OFFSET 0x00 +#define REG_USBRDT_OFFSET 0x04 +#define REG_USBVBFIL_OFFSET 0x08 +#define REG_USBPCR1_OFFSET 0x0c + +/* bits within the USBPCR register */ +#define USBPCR_USB_MODE BIT(31) +#define USBPCR_AVLD_REG BIT(30) +#define USBPCR_COMMONONN BIT(25) +#define USBPCR_VBUSVLDEXT BIT(24) +#define USBPCR_VBUSVLDEXTSEL BIT(23) +#define USBPCR_POR BIT(22) +#define USBPCR_SIDDQ BIT(21) +#define USBPCR_OTG_DISABLE BIT(20) +#define USBPCR_TXPREEMPHTUNE BIT(6) + +#define USBPCR_IDPULLUP_MASK GENMASK(29, 28) +#define USBPCR_IDPULLUP_ALWAYS 0x2 +#define USBPCR_IDPULLUP_SUSPEND 0x1 +#define USBPCR_IDPULLUP_OTG 0x0 + +#define USBPCR_COMPDISTUNE_MASK GENMASK(19, 17) +#define USBPCR_COMPDISTUNE_DFT 0x4 + +#define USBPCR_OTGTUNE_MASK GENMASK(16, 14) +#define USBPCR_OTGTUNE_DFT 0x4 + +#define USBPCR_SQRXTUNE_MASK GENMASK(13, 11) +#define USBPCR_SQRXTUNE_DCR_20PCT 0x7 +#define USBPCR_SQRXTUNE_DFT 0x3 + +#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, 7) +#define USBPCR_TXFSLSTUNE_DCR_50PPT 0xf +#define USBPCR_TXFSLSTUNE_DCR_25PPT 0x7 +#define USBPCR_TXFSLSTUNE_DFT 0x3 +#define USBPCR_TXFSLSTUNE_INC_25PPT 0x1 +#define USBPCR_TXFSLSTUNE_INC_50PPT 0x0 + +#define USBPCR_TXHSXVTUNE_MASK GENMASK(5, 4) +#define USBPCR_TXHSXVTUNE_DFT 0x3 +#define USBPCR_TXHSXVTUNE_DCR_15MV 0x1 + +#define USBPCR_TXRISETUNE_MASK GENMASK(5, 4) +#define USBPCR_TXRISETUNE_DFT 0x3 + +#define USBPCR_TXVREFTUNE_MASK GENMASK(3, 0) +#define USBPCR_TXVREFTUNE_INC_75PPT 0xb +#define USBPCR_TXVREFTUNE_INC_25PPT 0x7 +#define USBPCR_TXVREFTUNE_DFT 0x5 + +/* bits within the USBRDTR register */ +#define USBRDT_UTMI_RST BIT(27) +#define USBRDT_HB_MASK BIT(26) +#define USBRDT_VBFIL_LD_EN BIT(25) +#define USBRDT_IDDIG_EN BIT(24) +#define USBRDT_IDDIG_REG BIT(23) +#define USBRDT_VBFIL_EN BIT(2) + +/* bits within the USBPCR1 register */ +#define USBPCR1_BVLD_REG BIT(31) +#define USBPCR1_DPPD BIT(29) +#define USBPCR1_DMPD BIT(28) +#define USBPCR1_USB_SEL BIT(28) +#define USBPCR1_PORT_RST BIT(21) +#define USBPCR1_WORD_IF_16BIT BIT(19) + +struct ingenic_soc_info { + void (*usb_phy_init)(struct phy *phy); +}; + +struct ingenic_usb_phy { + const struct ingenic_soc_info *soc_info; + + struct phy *phy; + void __iomem *base; + struct clk *clk; + struct regulator *vcc_supply; +}; + +static int ingenic_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + int err; + u32 reg; + + err = clk_prepare_enable(priv->clk); + if (err) { + dev_err(&phy->dev, "Unable to start clock: %d\n", err); + return err; + } + + priv->soc_info->usb_phy_init(phy); + + /* Wait for PHY to reset */ + usleep_range(30, 300); + reg = readl(priv->base + REG_USBPCR_OFFSET); + writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET); + usleep_range(300, 1000); + + return 0; +} + +static int ingenic_usb_phy_exit(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->clk); + regulator_disable(priv->vcc_supply); + + return 0; +} + +static int ingenic_usb_phy_power_on(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + int err; + + err = regulator_enable(priv->vcc_supply); + if (err) { + dev_err(&phy->dev, "Unable to enable VCC: %d\n", err); + return err; + } + + return 0; +} + +static int ingenic_usb_phy_power_off(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + + regulator_disable(priv->vcc_supply); + + return 0; +} + +static int ingenic_usb_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + switch (mode) { + case PHY_MODE_USB_HOST: + reg = readl(priv->base + REG_USBPCR_OFFSET); + u32p_replace_bits(®, 1, USBPCR_USB_MODE); + u32p_replace_bits(®, 0, USBPCR_VBUSVLDEXT); + u32p_replace_bits(®, 0, USBPCR_VBUSVLDEXTSEL); + u32p_replace_bits(®, 0, USBPCR_OTG_DISABLE); + writel(reg, priv->base + REG_USBPCR_OFFSET); + + break; + case PHY_MODE_USB_DEVICE: + reg = readl(priv->base + REG_USBPCR_OFFSET); + u32p_replace_bits(®, 0, USBPCR_USB_MODE); + u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXT); + u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXTSEL); + u32p_replace_bits(®, 1, USBPCR_OTG_DISABLE); + writel(reg, priv->base + REG_USBPCR_OFFSET); + + break; + case PHY_MODE_USB_OTG: + reg = readl(priv->base + REG_USBPCR_OFFSET); + u32p_replace_bits(®, 1, USBPCR_USB_MODE); + u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXT); + u32p_replace_bits(®, 1, USBPCR_VBUSVLDEXTSEL); + u32p_replace_bits(®, 0, USBPCR_OTG_DISABLE); + writel(reg, priv->base + REG_USBPCR_OFFSET); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct phy_ops ingenic_usb_phy_ops = { + .init = ingenic_usb_phy_init, + .exit = ingenic_usb_phy_exit, + .power_on = ingenic_usb_phy_power_on, + .power_off = ingenic_usb_phy_power_off, + .set_mode = ingenic_usb_phy_set_mode, + .owner = THIS_MODULE, +}; + +static void jz4770_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_POR | + FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_ALWAYS) | + FIELD_PREP(USBPCR_COMPDISTUNE_MASK, USBPCR_COMPDISTUNE_DFT) | + FIELD_PREP(USBPCR_OTGTUNE_MASK, USBPCR_OTGTUNE_DFT) | + FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DFT) | + FIELD_PREP(USBPCR_TXFSLSTUNE_MASK, USBPCR_TXFSLSTUNE_DFT) | + FIELD_PREP(USBPCR_TXRISETUNE_MASK, USBPCR_TXRISETUNE_DFT) | + FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_DFT); + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void jz4775_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | + USBPCR1_WORD_IF_16BIT; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_COMMONONN | USBPCR_POR | + FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_75PPT); + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void jz4780_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | + USBPCR1_WORD_IF_16BIT; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR; + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void x1000_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR | + FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DCR_20PCT) | + FIELD_PREP(USBPCR_TXHSXVTUNE_MASK, USBPCR_TXHSXVTUNE_DCR_15MV) | + FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_25PPT); + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void x1830_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + /* rdt */ + writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET); + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT | + USBPCR1_DMPD | USBPCR1_DPPD; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR | + FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG); + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void x2000_usb_phy_init(struct phy *phy) +{ + struct ingenic_usb_phy *priv = phy_get_drvdata(phy); + u32 reg; + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_DPPD | USBPCR1_DMPD; + writel(reg & ~USBPCR1_PORT_RST, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_POR | FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG); + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static const struct ingenic_soc_info jz4770_soc_info = { + .usb_phy_init = jz4770_usb_phy_init, +}; + +static const struct ingenic_soc_info jz4775_soc_info = { + .usb_phy_init = jz4775_usb_phy_init, +}; + +static const struct ingenic_soc_info jz4780_soc_info = { + .usb_phy_init = jz4780_usb_phy_init, +}; + +static const struct ingenic_soc_info x1000_soc_info = { + .usb_phy_init = x1000_usb_phy_init, +}; + +static const struct ingenic_soc_info x1830_soc_info = { + .usb_phy_init = x1830_usb_phy_init, +}; + +static const struct ingenic_soc_info x2000_soc_info = { + .usb_phy_init = x2000_usb_phy_init, +}; + +static int ingenic_usb_phy_probe(struct platform_device *pdev) +{ + struct ingenic_usb_phy *priv; + struct phy_provider *provider; + struct device *dev = &pdev->dev; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->soc_info = device_get_match_data(dev); + if (!priv->soc_info) { + dev_err(dev, "Error: No device match found\n"); + return -ENODEV; + } + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) { + dev_err(dev, "Failed to map registers\n"); + return PTR_ERR(priv->base); + } + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + err = PTR_ERR(priv->clk); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get clock\n"); + return err; + } + + priv->vcc_supply = devm_regulator_get(dev, "vcc"); + if (IS_ERR(priv->vcc_supply)) { + err = PTR_ERR(priv->vcc_supply); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get regulator\n"); + return err; + } + + priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops); + if (IS_ERR(priv->phy)) + return PTR_ERR(priv->phy); + + phy_set_drvdata(priv->phy, priv); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id ingenic_usb_phy_of_matches[] = { + { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info }, + { .compatible = "ingenic,jz4775-phy", .data = &jz4775_soc_info }, + { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info }, + { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info }, + { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info }, + { .compatible = "ingenic,x2000-phy", .data = &x2000_soc_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches); + +static struct platform_driver ingenic_usb_phy_driver = { + .probe = ingenic_usb_phy_probe, + .driver = { + .name = "ingenic-usb-phy", + .of_match_table = ingenic_usb_phy_of_matches, + }, +}; +module_platform_driver(ingenic_usb_phy_driver); + +MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>"); +MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>"); +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig index 4ea6a8897cd7..18a3cc5b98c0 100644 --- a/drivers/phy/intel/Kconfig +++ b/drivers/phy/intel/Kconfig @@ -1,9 +1,58 @@ # SPDX-License-Identifier: GPL-2.0 # -# Phy drivers for Intel Lightning Mountain(LGM) platform +# Phy drivers for Intel platforms # -config PHY_INTEL_EMMC - tristate "Intel EMMC PHY driver" +config PHY_INTEL_KEEMBAY_EMMC + tristate "Intel Keem Bay EMMC PHY driver" + depends on ARCH_KEEMBAY || COMPILE_TEST + depends on HAS_IOMEM + select GENERIC_PHY + select REGMAP_MMIO + help + Choose this option if you have an Intel Keem Bay SoC. + + To compile this driver as a module, choose M here: the module + will be called phy-keembay-emmc.ko. + +config PHY_INTEL_KEEMBAY_USB + tristate "Intel Keem Bay USB PHY driver" + depends on ARCH_KEEMBAY || COMPILE_TEST + depends on HAS_IOMEM + select GENERIC_PHY + select REGMAP_MMIO + help + Choose this option if you have an Intel Keem Bay SoC. + + To compile this driver as a module, choose M here: the module + will be called phy-keembay-usb.ko. + +config PHY_INTEL_LGM_COMBO + bool "Intel Lightning Mountain ComboPHY driver" + depends on X86 || COMPILE_TEST + depends on OF && HAS_IOMEM + select MFD_SYSCON + select GENERIC_PHY + select REGMAP + help + Enable this to support Intel ComboPhy. + + This driver configures ComboPhy subsystem on Intel gateway + chipsets which provides PHYs for various controllers, EMAC, + SATA and PCIe. + +config PHY_INTEL_LGM_EMMC + tristate "Intel Lightning Mountain EMMC PHY driver" + depends on X86 || COMPILE_TEST select GENERIC_PHY help Enable this to support the Intel EMMC PHY + +config PHY_INTEL_THUNDERBAY_EMMC + tristate "Intel Thunder Bay eMMC PHY driver" + depends on OF && (ARCH_THUNDERBAY || COMPILE_TEST) + select GENERIC_PHY + help + This option enables support for Intel Thunder Bay SoC eMMC PHY. + + To compile this driver as a module, choose M here: the module + will be called phy-intel-thunderbay-emmc.ko. diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile index 6b876a75599d..b7321d56b0bb 100644 --- a/drivers/phy/intel/Makefile +++ b/drivers/phy/intel/Makefile @@ -1,2 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o +obj-$(CONFIG_PHY_INTEL_KEEMBAY_EMMC) += phy-intel-keembay-emmc.o +obj-$(CONFIG_PHY_INTEL_KEEMBAY_USB) += phy-intel-keembay-usb.o +obj-$(CONFIG_PHY_INTEL_LGM_COMBO) += phy-intel-lgm-combo.o +obj-$(CONFIG_PHY_INTEL_LGM_EMMC) += phy-intel-lgm-emmc.o +obj-$(CONFIG_PHY_INTEL_THUNDERBAY_EMMC) += phy-intel-thunderbay-emmc.o diff --git a/drivers/phy/intel/phy-intel-keembay-emmc.c b/drivers/phy/intel/phy-intel-keembay-emmc.c new file mode 100644 index 000000000000..0eb11ac7c2e2 --- /dev/null +++ b/drivers/phy/intel/phy-intel-keembay-emmc.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Keem Bay eMMC PHY driver + * Copyright (C) 2020 Intel Corporation + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* eMMC/SD/SDIO core/phy configuration registers */ +#define PHY_CFG_0 0x24 +#define SEL_DLY_TXCLK_MASK BIT(29) +#define OTAP_DLY_ENA_MASK BIT(27) +#define OTAP_DLY_SEL_MASK GENMASK(26, 23) +#define DLL_EN_MASK BIT(10) +#define PWR_DOWN_MASK BIT(0) + +#define PHY_CFG_2 0x2c +#define SEL_FREQ_MASK GENMASK(12, 10) + +#define PHY_STAT 0x40 +#define CAL_DONE_MASK BIT(6) +#define IS_CALDONE(x) ((x) & CAL_DONE_MASK) +#define DLL_RDY_MASK BIT(5) +#define IS_DLLRDY(x) ((x) & DLL_RDY_MASK) + +/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */ +#define FREQSEL_200M_170M 0x0 +#define FREQSEL_170M_140M 0x1 +#define FREQSEL_140M_110M 0x2 +#define FREQSEL_110M_80M 0x3 +#define FREQSEL_80M_50M 0x4 + +struct keembay_emmc_phy { + struct regmap *syscfg; + struct clk *emmcclk; +}; + +static const struct regmap_config keembay_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int keembay_emmc_phy_power(struct phy *phy, bool on_off) +{ + struct keembay_emmc_phy *priv = phy_get_drvdata(phy); + unsigned int caldone; + unsigned int dllrdy; + unsigned int freqsel; + unsigned int mhz; + int ret; + + /* + * Keep phyctrl_pdb and phyctrl_endll low to allow + * initialization of CALIO state M/C DFFs + */ + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK, + FIELD_PREP(PWR_DOWN_MASK, 0)); + if (ret) { + dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK, + FIELD_PREP(DLL_EN_MASK, 0)); + if (ret) { + dev_err(&phy->dev, "turn off the dll failed: %d\n", ret); + return ret; + } + + /* Already finish power off above */ + if (!on_off) + return 0; + + mhz = DIV_ROUND_CLOSEST(clk_get_rate(priv->emmcclk), 1000000); + if (mhz <= 200 && mhz >= 170) + freqsel = FREQSEL_200M_170M; + else if (mhz <= 170 && mhz >= 140) + freqsel = FREQSEL_170M_140M; + else if (mhz <= 140 && mhz >= 110) + freqsel = FREQSEL_140M_110M; + else if (mhz <= 110 && mhz >= 80) + freqsel = FREQSEL_110M_80M; + else if (mhz <= 80 && mhz >= 50) + freqsel = FREQSEL_80M_50M; + else + freqsel = 0x0; + + /* Check for EMMC clock rate*/ + if (mhz > 175) + dev_warn(&phy->dev, "Unsupported rate: %d MHz\n", mhz); + + /* + * According to the user manual, calpad calibration + * cycle takes more than 2us without the minimal recommended + * value, so we may need a little margin here + */ + udelay(5); + + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK, + FIELD_PREP(PWR_DOWN_MASK, 1)); + if (ret) { + dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret); + return ret; + } + + /* + * According to the user manual, it asks driver to wait 5us for + * calpad busy trimming. However it is documented that this value is + * PVT(A.K.A. process, voltage and temperature) relevant, so some + * failure cases are found which indicates we should be more tolerant + * to calpad busy trimming. + */ + ret = regmap_read_poll_timeout(priv->syscfg, PHY_STAT, + caldone, IS_CALDONE(caldone), + 0, 50); + if (ret) { + dev_err(&phy->dev, "caldone failed, ret=%d\n", ret); + return ret; + } + + /* Set the frequency of the DLL operation */ + ret = regmap_update_bits(priv->syscfg, PHY_CFG_2, SEL_FREQ_MASK, + FIELD_PREP(SEL_FREQ_MASK, freqsel)); + if (ret) { + dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret); + return ret; + } + + /* Turn on the DLL */ + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK, + FIELD_PREP(DLL_EN_MASK, 1)); + if (ret) { + dev_err(&phy->dev, "turn on the dll failed: %d\n", ret); + return ret; + } + + /* + * We turned on the DLL even though the rate was 0 because we the + * clock might be turned on later. ...but we can't wait for the DLL + * to lock when the rate is 0 because it will never lock with no + * input clock. + * + * Technically we should be checking the lock later when the clock + * is turned on, but for now we won't. + */ + if (mhz == 0) + return 0; + + /* + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100kHz) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. + */ + ret = regmap_read_poll_timeout(priv->syscfg, PHY_STAT, + dllrdy, IS_DLLRDY(dllrdy), + 0, 50 * USEC_PER_MSEC); + if (ret) + dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret); + + return ret; +} + +static int keembay_emmc_phy_init(struct phy *phy) +{ + struct keembay_emmc_phy *priv = phy_get_drvdata(phy); + + /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to init the PHY + * + * The clock is optional, so upon any error just return it like + * any other error to user. + */ + priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk"); + + return PTR_ERR_OR_ZERO(priv->emmcclk); +} + +static int keembay_emmc_phy_exit(struct phy *phy) +{ + struct keembay_emmc_phy *priv = phy_get_drvdata(phy); + + clk_put(priv->emmcclk); + + return 0; +}; + +static int keembay_emmc_phy_power_on(struct phy *phy) +{ + struct keembay_emmc_phy *priv = phy_get_drvdata(phy); + int ret; + + /* Delay chain based txclk: enable */ + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + FIELD_PREP(SEL_DLY_TXCLK_MASK, 1)); + if (ret) { + dev_err(&phy->dev, "ERROR: delay chain txclk set: %d\n", ret); + return ret; + } + + /* Output tap delay: enable */ + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_ENA_MASK, + FIELD_PREP(OTAP_DLY_ENA_MASK, 1)); + if (ret) { + dev_err(&phy->dev, "ERROR: output tap delay set: %d\n", ret); + return ret; + } + + /* Output tap delay */ + ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_SEL_MASK, + FIELD_PREP(OTAP_DLY_SEL_MASK, 2)); + if (ret) { + dev_err(&phy->dev, "ERROR: output tap delay select: %d\n", ret); + return ret; + } + + /* Power up eMMC phy analog blocks */ + return keembay_emmc_phy_power(phy, true); +} + +static int keembay_emmc_phy_power_off(struct phy *phy) +{ + /* Power down eMMC phy analog blocks */ + return keembay_emmc_phy_power(phy, false); +} + +static const struct phy_ops ops = { + .init = keembay_emmc_phy_init, + .exit = keembay_emmc_phy_exit, + .power_on = keembay_emmc_phy_power_on, + .power_off = keembay_emmc_phy_power_off, + .owner = THIS_MODULE, +}; + +static int keembay_emmc_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct keembay_emmc_phy *priv; + struct phy *generic_phy; + struct phy_provider *phy_provider; + void __iomem *base; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->syscfg = devm_regmap_init_mmio(dev, base, &keembay_regmap_config); + if (IS_ERR(priv->syscfg)) + return PTR_ERR(priv->syscfg); + + generic_phy = devm_phy_create(dev, np, &ops); + if (IS_ERR(generic_phy)) + return dev_err_probe(dev, PTR_ERR(generic_phy), + "failed to create PHY\n"); + + phy_set_drvdata(generic_phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id keembay_emmc_phy_dt_ids[] = { + { .compatible = "intel,keembay-emmc-phy" }, + {} +}; +MODULE_DEVICE_TABLE(of, keembay_emmc_phy_dt_ids); + +static struct platform_driver keembay_emmc_phy_driver = { + .probe = keembay_emmc_phy_probe, + .driver = { + .name = "keembay-emmc-phy", + .of_match_table = keembay_emmc_phy_dt_ids, + }, +}; +module_platform_driver(keembay_emmc_phy_driver); + +MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>"); +MODULE_DESCRIPTION("Intel Keem Bay eMMC PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/intel/phy-intel-keembay-usb.c b/drivers/phy/intel/phy-intel-keembay-usb.c new file mode 100644 index 000000000000..c8b05f7b2445 --- /dev/null +++ b/drivers/phy/intel/phy-intel-keembay-usb.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Keem Bay USB PHY driver + * Copyright (C) 2020 Intel Corporation + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* USS (USB Subsystem) clock control registers */ +#define USS_CPR_CLK_EN 0x00 +#define USS_CPR_CLK_SET 0x04 +#define USS_CPR_CLK_CLR 0x08 +#define USS_CPR_RST_EN 0x10 +#define USS_CPR_RST_SET 0x14 +#define USS_CPR_RST_CLR 0x18 + +/* USS clock/reset bit fields */ +#define USS_CPR_PHY_TST BIT(6) +#define USS_CPR_LOW_JIT BIT(5) +#define USS_CPR_CORE BIT(4) +#define USS_CPR_SUSPEND BIT(3) +#define USS_CPR_ALT_REF BIT(2) +#define USS_CPR_REF BIT(1) +#define USS_CPR_SYS BIT(0) +#define USS_CPR_MASK GENMASK(6, 0) + +/* USS APB slave registers */ +#define USS_USB_CTRL_CFG0 0x10 +#define VCC_RESET_N_MASK BIT(31) + +#define USS_USB_PHY_CFG0 0x30 +#define POR_MASK BIT(15) +#define PHY_RESET_MASK BIT(14) +#define PHY_REF_USE_PAD_MASK BIT(5) + +#define USS_USB_PHY_CFG6 0x64 +#define PHY0_SRAM_EXT_LD_DONE_MASK BIT(23) + +#define USS_USB_PARALLEL_IF_CTRL 0xa0 +#define USB_PHY_CR_PARA_SEL_MASK BIT(2) + +#define USS_USB_TSET_SIGNALS_AND_GLOB 0xac +#define USB_PHY_CR_PARA_CLK_EN_MASK BIT(7) + +#define USS_USB_STATUS_REG 0xb8 +#define PHY0_SRAM_INIT_DONE_MASK BIT(3) + +#define USS_USB_TIEOFFS_CONSTANTS_REG1 0xc0 +#define IDDQ_ENABLE_MASK BIT(10) + +struct keembay_usb_phy { + struct device *dev; + struct regmap *regmap_cpr; + struct regmap *regmap_slv; +}; + +static const struct regmap_config keembay_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = USS_USB_TIEOFFS_CONSTANTS_REG1, +}; + +static int keembay_usb_clocks_on(struct keembay_usb_phy *priv) +{ + int ret; + + ret = regmap_update_bits(priv->regmap_cpr, USS_CPR_CLK_SET, + USS_CPR_MASK, USS_CPR_MASK); + if (ret) { + dev_err(priv->dev, "error clock set: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(priv->regmap_cpr, USS_CPR_RST_SET, + USS_CPR_MASK, USS_CPR_MASK); + if (ret) { + dev_err(priv->dev, "error reset set: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(priv->regmap_slv, + USS_USB_TIEOFFS_CONSTANTS_REG1, + IDDQ_ENABLE_MASK, + FIELD_PREP(IDDQ_ENABLE_MASK, 0)); + if (ret) { + dev_err(priv->dev, "error iddq disable: %d\n", ret); + return ret; + } + + /* Wait 30us to ensure all analog blocks are powered up. */ + usleep_range(30, 60); + + ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG0, + PHY_REF_USE_PAD_MASK, + FIELD_PREP(PHY_REF_USE_PAD_MASK, 1)); + if (ret) + dev_err(priv->dev, "error ref clock select: %d\n", ret); + + return ret; +} + +static int keembay_usb_core_off(struct keembay_usb_phy *priv) +{ + int ret; + + ret = regmap_update_bits(priv->regmap_slv, USS_USB_CTRL_CFG0, + VCC_RESET_N_MASK, + FIELD_PREP(VCC_RESET_N_MASK, 0)); + if (ret) + dev_err(priv->dev, "error core reset: %d\n", ret); + + return ret; +} + +static int keembay_usb_core_on(struct keembay_usb_phy *priv) +{ + int ret; + + ret = regmap_update_bits(priv->regmap_slv, USS_USB_CTRL_CFG0, + VCC_RESET_N_MASK, + FIELD_PREP(VCC_RESET_N_MASK, 1)); + if (ret) + dev_err(priv->dev, "error core on: %d\n", ret); + + return ret; +} + +static int keembay_usb_phys_on(struct keembay_usb_phy *priv) +{ + int ret; + + ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG0, + POR_MASK | PHY_RESET_MASK, + FIELD_PREP(POR_MASK | PHY_RESET_MASK, 0)); + if (ret) + dev_err(priv->dev, "error phys on: %d\n", ret); + + return ret; +} + +static int keembay_usb_phy_init(struct phy *phy) +{ + struct keembay_usb_phy *priv = phy_get_drvdata(phy); + u32 val; + int ret; + + ret = keembay_usb_core_off(priv); + if (ret) + return ret; + + /* + * According to Keem Bay datasheet, wait minimum 20us after clock + * enable before bringing PHYs out of reset. + */ + usleep_range(20, 40); + + ret = keembay_usb_phys_on(priv); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap_slv, + USS_USB_TSET_SIGNALS_AND_GLOB, + USB_PHY_CR_PARA_CLK_EN_MASK, + FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 0)); + if (ret) { + dev_err(priv->dev, "error cr clock disable: %d\n", ret); + return ret; + } + + /* + * According to Keem Bay datasheet, wait 2us after disabling the + * clock into the USB 3.x parallel interface. + */ + udelay(2); + + ret = regmap_update_bits(priv->regmap_slv, + USS_USB_PARALLEL_IF_CTRL, + USB_PHY_CR_PARA_SEL_MASK, + FIELD_PREP(USB_PHY_CR_PARA_SEL_MASK, 1)); + if (ret) { + dev_err(priv->dev, "error cr select: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(priv->regmap_slv, + USS_USB_TSET_SIGNALS_AND_GLOB, + USB_PHY_CR_PARA_CLK_EN_MASK, + FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 1)); + if (ret) { + dev_err(priv->dev, "error cr clock enable: %d\n", ret); + return ret; + } + + ret = regmap_read_poll_timeout(priv->regmap_slv, USS_USB_STATUS_REG, + val, val & PHY0_SRAM_INIT_DONE_MASK, + USEC_PER_MSEC, 10 * USEC_PER_MSEC); + if (ret) { + dev_err(priv->dev, "SRAM init not done: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG6, + PHY0_SRAM_EXT_LD_DONE_MASK, + FIELD_PREP(PHY0_SRAM_EXT_LD_DONE_MASK, 1)); + if (ret) { + dev_err(priv->dev, "error SRAM init done set: %d\n", ret); + return ret; + } + + /* + * According to Keem Bay datasheet, wait 20us after setting the + * SRAM load done bit, before releasing the controller reset. + */ + usleep_range(20, 40); + + return keembay_usb_core_on(priv); +} + +static const struct phy_ops ops = { + .init = keembay_usb_phy_init, + .owner = THIS_MODULE, +}; + +static int keembay_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct keembay_usb_phy *priv; + struct phy *generic_phy; + struct phy_provider *phy_provider; + void __iomem *base; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource_byname(pdev, "cpr-apb-base"); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap_cpr = devm_regmap_init_mmio(dev, base, + &keembay_regmap_config); + if (IS_ERR(priv->regmap_cpr)) + return PTR_ERR(priv->regmap_cpr); + + base = devm_platform_ioremap_resource_byname(pdev, "slv-apb-base"); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap_slv = devm_regmap_init_mmio(dev, base, + &keembay_regmap_config); + if (IS_ERR(priv->regmap_slv)) + return PTR_ERR(priv->regmap_slv); + + generic_phy = devm_phy_create(dev, dev->of_node, &ops); + if (IS_ERR(generic_phy)) + return dev_err_probe(dev, PTR_ERR(generic_phy), + "failed to create PHY\n"); + + phy_set_drvdata(generic_phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return dev_err_probe(dev, PTR_ERR(phy_provider), + "failed to register phy provider\n"); + + /* Setup USB subsystem clocks */ + ret = keembay_usb_clocks_on(priv); + if (ret) + return ret; + + /* and turn on the DWC3 core, prior to DWC3 driver init. */ + return keembay_usb_core_on(priv); +} + +static const struct of_device_id keembay_usb_phy_dt_ids[] = { + { .compatible = "intel,keembay-usb-phy" }, + {} +}; +MODULE_DEVICE_TABLE(of, keembay_usb_phy_dt_ids); + +static struct platform_driver keembay_usb_phy_driver = { + .probe = keembay_usb_phy_probe, + .driver = { + .name = "keembay-usb-phy", + .of_match_table = keembay_usb_phy_dt_ids, + }, +}; +module_platform_driver(keembay_usb_phy_driver); + +MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>"); +MODULE_DESCRIPTION("Intel Keem Bay USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/intel/phy-intel-lgm-combo.c b/drivers/phy/intel/phy-intel-lgm-combo.c new file mode 100644 index 000000000000..6010e246d52e --- /dev/null +++ b/drivers/phy/intel/phy-intel-lgm-combo.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Combo-PHY driver + * + * Copyright (C) 2019-2020 Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <dt-bindings/phy/phy.h> + +#define PCIE_PHY_GEN_CTRL 0x00 +#define PCIE_PHY_CLK_PAD BIT(17) + +#define PAD_DIS_CFG 0x174 + +#define PCS_XF_ATE_OVRD_IN_2 0x3008 +#define ADAPT_REQ_MSK GENMASK(5, 4) + +#define PCS_XF_RX_ADAPT_ACK 0x3010 +#define RX_ADAPT_ACK_BIT BIT(0) + +#define CR_ADDR(addr, lane) (((addr) + (lane) * 0x100) << 2) +#define REG_COMBO_MODE(x) ((x) * 0x200) +#define REG_CLK_DISABLE(x) ((x) * 0x200 + 0x124) + +#define COMBO_PHY_ID(x) ((x)->parent->id) +#define PHY_ID(x) ((x)->id) + +#define CLK_100MHZ 100000000 +#define CLK_156_25MHZ 156250000 + +static const unsigned long intel_iphy_clk_rates[] = { + CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ, +}; + +enum { + PHY_0, + PHY_1, + PHY_MAX_NUM +}; + +/* + * Clock Register bit fields to enable clocks + * for ComboPhy according to the mode. + */ +enum intel_phy_mode { + PHY_PCIE_MODE = 0, + PHY_XPCS_MODE, + PHY_SATA_MODE, +}; + +/* ComboPhy mode Register values */ +enum intel_combo_mode { + PCIE0_PCIE1_MODE = 0, + PCIE_DL_MODE, + RXAUI_MODE, + XPCS0_XPCS1_MODE, + SATA0_SATA1_MODE, +}; + +enum aggregated_mode { + PHY_SL_MODE, + PHY_DL_MODE, +}; + +struct intel_combo_phy; + +struct intel_cbphy_iphy { + struct phy *phy; + struct intel_combo_phy *parent; + struct reset_control *app_rst; + u32 id; +}; + +struct intel_combo_phy { + struct device *dev; + struct clk *core_clk; + unsigned long clk_rate; + void __iomem *app_base; + void __iomem *cr_base; + struct regmap *syscfg; + struct regmap *hsiocfg; + u32 id; + u32 bid; + struct reset_control *phy_rst; + struct reset_control *core_rst; + struct intel_cbphy_iphy iphy[PHY_MAX_NUM]; + enum intel_phy_mode phy_mode; + enum aggregated_mode aggr_mode; + u32 init_cnt; + struct mutex lock; +}; + +static int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set) +{ + struct intel_combo_phy *cbphy = iphy->parent; + u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id); + u32 val; + + /* Register: 0 is enable, 1 is disable */ + val = set ? 0 : mask; + + return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid), + mask, val); +} + +static int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set) +{ + struct intel_combo_phy *cbphy = iphy->parent; + u32 mask = BIT(cbphy->id * 2 + iphy->id); + u32 val; + + /* Register: 0 is enable, 1 is disable */ + val = set ? 0 : mask; + + return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val); +} + +static inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg, + u32 mask, u32 val) +{ + u32 reg_val; + + reg_val = readl(base + reg); + reg_val &= ~mask; + reg_val |= val; + writel(reg_val, base + reg); +} + +static int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy, + int (*phy_cfg)(struct intel_cbphy_iphy *)) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = phy_cfg(iphy); + if (ret) + return ret; + + if (cbphy->aggr_mode != PHY_DL_MODE) + return 0; + + return phy_cfg(&cbphy->iphy[PHY_1]); +} + +static int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = intel_cbphy_pcie_refclk_cfg(iphy, true); + if (ret) { + dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n"); + return ret; + } + + if (cbphy->init_cnt) + return 0; + + combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL, + PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 0)); + + /* Delay for stable clock PLL */ + usleep_range(50, 100); + + return 0; +} + +static int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = intel_cbphy_pcie_refclk_cfg(iphy, false); + if (ret) { + dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n"); + return ret; + } + + if (cbphy->init_cnt) + return 0; + + combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL, + PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 1)); + + return 0; +} + +static int intel_cbphy_set_mode(struct intel_combo_phy *cbphy) +{ + enum intel_combo_mode cb_mode; + enum aggregated_mode aggr = cbphy->aggr_mode; + struct device *dev = cbphy->dev; + enum intel_phy_mode mode; + int ret; + + mode = cbphy->phy_mode; + + switch (mode) { + case PHY_PCIE_MODE: + cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE; + break; + + case PHY_XPCS_MODE: + cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE; + break; + + case PHY_SATA_MODE: + if (aggr == PHY_DL_MODE) { + dev_err(dev, "Mode:%u not support dual lane!\n", mode); + return -EINVAL; + } + + cb_mode = SATA0_SATA1_MODE; + break; + default: + return -EINVAL; + } + + ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode); + if (ret) + dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret); + + return ret; +} + +static void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy) +{ + reset_control_assert(cbphy->core_rst); + reset_control_assert(cbphy->phy_rst); +} + +static void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy) +{ + reset_control_deassert(cbphy->core_rst); + reset_control_deassert(cbphy->phy_rst); + /* Delay to ensure reset process is done */ + usleep_range(10, 20); +} + +static int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + if (!cbphy->init_cnt) { + ret = clk_prepare_enable(cbphy->core_clk); + if (ret) { + dev_err(cbphy->dev, "Clock enable failed!\n"); + return ret; + } + + ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate); + if (ret) { + dev_err(cbphy->dev, "Clock freq set to %lu failed!\n", + cbphy->clk_rate); + goto clk_err; + } + + intel_cbphy_rst_assert(cbphy); + intel_cbphy_rst_deassert(cbphy); + ret = intel_cbphy_set_mode(cbphy); + if (ret) + goto clk_err; + } + + ret = intel_cbphy_iphy_enable(iphy, true); + if (ret) { + dev_err(cbphy->dev, "Failed enabling PHY core\n"); + goto clk_err; + } + + ret = reset_control_deassert(iphy->app_rst); + if (ret) { + dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n", + COMBO_PHY_ID(iphy), PHY_ID(iphy)); + goto clk_err; + } + + /* Delay to ensure reset process is done */ + udelay(1); + + return 0; + +clk_err: + clk_disable_unprepare(cbphy->core_clk); + + return ret; +} + +static int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = reset_control_assert(iphy->app_rst); + if (ret) { + dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n", + COMBO_PHY_ID(iphy), PHY_ID(iphy)); + return ret; + } + + ret = intel_cbphy_iphy_enable(iphy, false); + if (ret) { + dev_err(cbphy->dev, "Failed disabling PHY core\n"); + return ret; + } + + if (cbphy->init_cnt) + return 0; + + clk_disable_unprepare(cbphy->core_clk); + intel_cbphy_rst_assert(cbphy); + + return 0; +} + +static int intel_cbphy_init(struct phy *phy) +{ + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + mutex_lock(&cbphy->lock); + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on); + if (ret) + goto err; + + if (cbphy->phy_mode == PHY_PCIE_MODE) { + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk); + if (ret) + goto err; + } + + cbphy->init_cnt++; + +err: + mutex_unlock(&cbphy->lock); + + return ret; +} + +static int intel_cbphy_exit(struct phy *phy) +{ + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + mutex_lock(&cbphy->lock); + cbphy->init_cnt--; + if (cbphy->phy_mode == PHY_PCIE_MODE) { + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk); + if (ret) + goto err; + } + + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off); + +err: + mutex_unlock(&cbphy->lock); + + return ret; +} + +static int intel_cbphy_calibrate(struct phy *phy) +{ + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); + struct intel_combo_phy *cbphy = iphy->parent; + void __iomem *cr_base = cbphy->cr_base; + int val, ret, id; + + if (cbphy->phy_mode != PHY_XPCS_MODE) + return 0; + + id = PHY_ID(iphy); + + /* trigger auto RX adaptation */ + combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id), + ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 3)); + /* Wait RX adaptation to finish */ + ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id), + val, val & RX_ADAPT_ACK_BIT, 10, 5000); + if (ret) + dev_err(cbphy->dev, "RX Adaptation failed!\n"); + else + dev_dbg(cbphy->dev, "RX Adaptation success!\n"); + + /* Stop RX adaptation */ + combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id), + ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 0)); + + return ret; +} + +static int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy) +{ + struct device *dev = cbphy->dev; + struct platform_device *pdev = to_platform_device(dev); + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_reference_args ref; + int ret; + u32 val; + + cbphy->core_clk = devm_clk_get(dev, NULL); + if (IS_ERR(cbphy->core_clk)) + return dev_err_probe(dev, PTR_ERR(cbphy->core_clk), + "Get clk failed!\n"); + + cbphy->core_rst = devm_reset_control_get_optional(dev, "core"); + if (IS_ERR(cbphy->core_rst)) + return dev_err_probe(dev, PTR_ERR(cbphy->core_rst), + "Get core reset control err!\n"); + + cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy"); + if (IS_ERR(cbphy->phy_rst)) + return dev_err_probe(dev, PTR_ERR(cbphy->phy_rst), + "Get PHY reset control err!\n"); + + cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0"); + if (IS_ERR(cbphy->iphy[0].app_rst)) + return dev_err_probe(dev, PTR_ERR(cbphy->iphy[0].app_rst), + "Get phy0 reset control err!\n"); + + cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1"); + if (IS_ERR(cbphy->iphy[1].app_rst)) + return dev_err_probe(dev, PTR_ERR(cbphy->iphy[1].app_rst), + "Get phy1 reset control err!\n"); + + cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); + if (IS_ERR(cbphy->app_base)) + return PTR_ERR(cbphy->app_base); + + cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core"); + if (IS_ERR(cbphy->cr_base)) + return PTR_ERR(cbphy->cr_base); + + /* + * syscfg and hsiocfg variables stores the handle of the registers set + * in which ComboPhy subsystem specific registers are subset. Using + * Register map framework to access the registers set. + */ + ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL, + 1, 0, &ref); + if (ret < 0) + return ret; + + cbphy->id = ref.args[0]; + cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode)); + fwnode_handle_put(ref.fwnode); + + ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1, + 0, &ref); + if (ret < 0) + return ret; + + cbphy->bid = ref.args[0]; + cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode)); + fwnode_handle_put(ref.fwnode); + + ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1); + if (ret) + return ret; + + switch (val) { + case PHY_TYPE_PCIE: + cbphy->phy_mode = PHY_PCIE_MODE; + break; + + case PHY_TYPE_SATA: + cbphy->phy_mode = PHY_SATA_MODE; + break; + + case PHY_TYPE_XPCS: + cbphy->phy_mode = PHY_XPCS_MODE; + break; + + default: + dev_err(dev, "Invalid PHY mode: %u\n", val); + return -EINVAL; + } + + cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode]; + + if (fwnode_property_present(fwnode, "intel,aggregation")) + cbphy->aggr_mode = PHY_DL_MODE; + else + cbphy->aggr_mode = PHY_SL_MODE; + + return 0; +} + +static const struct phy_ops intel_cbphy_ops = { + .init = intel_cbphy_init, + .exit = intel_cbphy_exit, + .calibrate = intel_cbphy_calibrate, + .owner = THIS_MODULE, +}; + +static struct phy *intel_cbphy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct intel_combo_phy *cbphy = dev_get_drvdata(dev); + u32 iphy_id; + + if (args->args_count < 1) { + dev_err(dev, "Invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + iphy_id = args->args[0]; + if (iphy_id >= PHY_MAX_NUM) { + dev_err(dev, "Invalid phy instance %d\n", iphy_id); + return ERR_PTR(-EINVAL); + } + + if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) { + dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id); + return ERR_PTR(-EINVAL); + } + + return cbphy->iphy[iphy_id].phy; +} + +static int intel_cbphy_create(struct intel_combo_phy *cbphy) +{ + struct phy_provider *phy_provider; + struct device *dev = cbphy->dev; + struct intel_cbphy_iphy *iphy; + int i; + + for (i = 0; i < PHY_MAX_NUM; i++) { + iphy = &cbphy->iphy[i]; + iphy->parent = cbphy; + iphy->id = i; + + /* In dual lane mode skip phy creation for the second phy */ + if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1) + continue; + + iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops); + if (IS_ERR(iphy->phy)) { + dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n", + COMBO_PHY_ID(iphy), PHY_ID(iphy)); + + return PTR_ERR(iphy->phy); + } + + phy_set_drvdata(iphy->phy, iphy); + } + + dev_set_drvdata(dev, cbphy); + phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate); + if (IS_ERR(phy_provider)) + dev_err(dev, "Register PHY provider failed!\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int intel_cbphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_combo_phy *cbphy; + int ret; + + cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL); + if (!cbphy) + return -ENOMEM; + + cbphy->dev = dev; + cbphy->init_cnt = 0; + mutex_init(&cbphy->lock); + ret = intel_cbphy_fwnode_parse(cbphy); + if (ret) + return ret; + + platform_set_drvdata(pdev, cbphy); + + return intel_cbphy_create(cbphy); +} + +static int intel_cbphy_remove(struct platform_device *pdev) +{ + struct intel_combo_phy *cbphy = platform_get_drvdata(pdev); + + intel_cbphy_rst_assert(cbphy); + clk_disable_unprepare(cbphy->core_clk); + return 0; +} + +static const struct of_device_id of_intel_cbphy_match[] = { + { .compatible = "intel,combo-phy" }, + { .compatible = "intel,combophy-lgm" }, + {} +}; + +static struct platform_driver intel_cbphy_driver = { + .probe = intel_cbphy_probe, + .remove = intel_cbphy_remove, + .driver = { + .name = "intel-combo-phy", + .of_match_table = of_intel_cbphy_match, + } +}; + +module_platform_driver(intel_cbphy_driver); + +MODULE_DESCRIPTION("Intel Combo-phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/intel/phy-intel-emmc.c b/drivers/phy/intel/phy-intel-lgm-emmc.c index 703aeb122541..703aeb122541 100644 --- a/drivers/phy/intel/phy-intel-emmc.c +++ b/drivers/phy/intel/phy-intel-lgm-emmc.c diff --git a/drivers/phy/intel/phy-intel-thunderbay-emmc.c b/drivers/phy/intel/phy-intel-thunderbay-emmc.c new file mode 100644 index 000000000000..593f6970b81e --- /dev/null +++ b/drivers/phy/intel/phy-intel-thunderbay-emmc.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel ThunderBay eMMC PHY driver + * + * Copyright (C) 2021 Intel Corporation + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +/* eMMC/SD/SDIO core/phy configuration registers */ +#define CTRL_CFG_0 0x00 +#define CTRL_CFG_1 0x04 +#define CTRL_PRESET_0 0x08 +#define CTRL_PRESET_1 0x0c +#define CTRL_PRESET_2 0x10 +#define CTRL_PRESET_3 0x14 +#define CTRL_PRESET_4 0x18 +#define CTRL_CFG_2 0x1c +#define CTRL_CFG_3 0x20 +#define PHY_CFG_0 0x24 +#define PHY_CFG_1 0x28 +#define PHY_CFG_2 0x2c +#define PHYBIST_CTRL 0x30 +#define SDHC_STAT3 0x34 +#define PHY_STAT 0x38 +#define PHYBIST_STAT_0 0x3c +#define PHYBIST_STAT_1 0x40 +#define EMMC_AXI 0x44 + +/* CTRL_PRESET_3 */ +#define CTRL_PRESET3_MASK GENMASK(31, 0) +#define CTRL_PRESET3_SHIFT 0 + +/* CTRL_CFG_0 bit fields */ +#define SUPPORT_HS_MASK BIT(26) +#define SUPPORT_HS_SHIFT 26 + +#define SUPPORT_8B_MASK BIT(24) +#define SUPPORT_8B_SHIFT 24 + +/* CTRL_CFG_1 bit fields */ +#define SUPPORT_SDR50_MASK BIT(28) +#define SUPPORT_SDR50_SHIFT 28 +#define SLOT_TYPE_MASK GENMASK(27, 26) +#define SLOT_TYPE_OFFSET 26 +#define SUPPORT_64B_MASK BIT(24) +#define SUPPORT_64B_SHIFT 24 +#define SUPPORT_HS400_MASK BIT(2) +#define SUPPORT_HS400_SHIFT 2 +#define SUPPORT_DDR50_MASK BIT(1) +#define SUPPORT_DDR50_SHIFT 1 +#define SUPPORT_SDR104_MASK BIT(0) +#define SUPPORT_SDR104_SHIFT 0 + +/* PHY_CFG_0 bit fields */ +#define SEL_DLY_TXCLK_MASK BIT(29) +#define SEL_DLY_TXCLK_SHIFT 29 +#define SEL_DLY_RXCLK_MASK BIT(28) +#define SEL_DLY_RXCLK_SHIFT 28 + +#define OTAP_DLY_ENA_MASK BIT(27) +#define OTAP_DLY_ENA_SHIFT 27 +#define OTAP_DLY_SEL_MASK GENMASK(26, 23) +#define OTAP_DLY_SEL_SHIFT 23 +#define ITAP_CHG_WIN_MASK BIT(22) +#define ITAP_CHG_WIN_SHIFT 22 +#define ITAP_DLY_ENA_MASK BIT(21) +#define ITAP_DLY_ENA_SHIFT 21 +#define ITAP_DLY_SEL_MASK GENMASK(20, 16) +#define ITAP_DLY_SEL_SHIFT 16 +#define RET_ENB_MASK BIT(15) +#define RET_ENB_SHIFT 15 +#define RET_EN_MASK BIT(14) +#define RET_EN_SHIFT 14 +#define DLL_IFF_MASK GENMASK(13, 11) +#define DLL_IFF_SHIFT 11 +#define DLL_EN_MASK BIT(10) +#define DLL_EN_SHIFT 10 +#define DLL_TRIM_ICP_MASK GENMASK(9, 6) +#define DLL_TRIM_ICP_SHIFT 6 +#define RETRIM_EN_MASK BIT(5) +#define RETRIM_EN_SHIFT 5 +#define RETRIM_MASK BIT(4) +#define RETRIM_SHIFT 4 +#define DR_TY_MASK GENMASK(3, 1) +#define DR_TY_SHIFT 1 +#define PWR_DOWN_MASK BIT(0) +#define PWR_DOWN_SHIFT 0 + +/* PHY_CFG_1 bit fields */ +#define REN_DAT_MASK GENMASK(19, 12) +#define REN_DAT_SHIFT 12 +#define REN_CMD_MASK BIT(11) +#define REN_CMD_SHIFT 11 +#define REN_STRB_MASK BIT(10) +#define REN_STRB_SHIFT 10 +#define PU_STRB_MASK BIT(20) +#define PU_STRB_SHIFT 20 + +/* PHY_CFG_2 bit fields */ +#define CLKBUF_MASK GENMASK(24, 21) +#define CLKBUF_SHIFT 21 +#define SEL_STRB_MASK GENMASK(20, 13) +#define SEL_STRB_SHIFT 13 +#define SEL_FREQ_MASK GENMASK(12, 10) +#define SEL_FREQ_SHIFT 10 + +/* PHY_STAT bit fields */ +#define CAL_DONE BIT(6) +#define DLL_RDY BIT(5) + +#define OTAP_DLY 0x0 +#define ITAP_DLY 0x0 +#define STRB 0x33 + +/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */ +#define FREQSEL_200M_170M 0x0 +#define FREQSEL_170M_140M 0x1 +#define FREQSEL_140M_110M 0x2 +#define FREQSEL_110M_80M 0x3 +#define FREQSEL_80M_50M 0x4 +#define FREQSEL_275M_250M 0x5 +#define FREQSEL_250M_225M 0x6 +#define FREQSEL_225M_200M 0x7 + +/* Phy power status */ +#define PHY_UNINITIALIZED 0 +#define PHY_INITIALIZED 1 + +/* + * During init(400KHz) phy_settings will be called with 200MHZ clock + * To avoid incorrectly setting the phy for init(400KHZ) "phy_power_sts" is used. + * When actual clock is set always phy is powered off once and then powered on. + * (sdhci_arasan_set_clock). That feature will be used to identify whether the + * settings are for init phy_power_on or actual clock phy_power_on + * 0 --> init settings + * 1 --> actual settings + */ + +struct thunderbay_emmc_phy { + void __iomem *reg_base; + struct clk *emmcclk; + int phy_power_sts; +}; + +static inline void update_reg(struct thunderbay_emmc_phy *tbh_phy, u32 offset, + u32 mask, u32 shift, u32 val) +{ + u32 tmp; + + tmp = readl(tbh_phy->reg_base + offset); + tmp &= ~mask; + tmp |= val << shift; + writel(tmp, tbh_phy->reg_base + offset); +} + +static int thunderbay_emmc_phy_power(struct phy *phy, bool power_on) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + unsigned int freqsel = FREQSEL_200M_170M; + unsigned long rate; + static int lock; + u32 val; + int ret; + + /* Disable DLL */ + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 200000000: + /* lock dll only when it is used, i.e only if SEL_DLY_TXCLK/RXCLK are 0 */ + update_reg(tbh_phy, PHY_CFG_0, DLL_EN_MASK, DLL_EN_SHIFT, 0x0); + break; + + /* dll lock not required for other frequencies */ + case 50000000 ... 52000000: + case 400000: + default: + break; + } + + if (!power_on) + return 0; + + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 170000001 ... 200000000: + freqsel = FREQSEL_200M_170M; + break; + + case 140000001 ... 170000000: + freqsel = FREQSEL_170M_140M; + break; + + case 110000001 ... 140000000: + freqsel = FREQSEL_140M_110M; + break; + + case 80000001 ... 110000000: + freqsel = FREQSEL_110M_80M; + break; + + case 50000000 ... 80000000: + freqsel = FREQSEL_80M_50M; + break; + + case 250000001 ... 275000000: + freqsel = FREQSEL_275M_250M; + break; + + case 225000001 ... 250000000: + freqsel = FREQSEL_250M_225M; + break; + + case 200000001 ... 225000000: + freqsel = FREQSEL_225M_200M; + break; + default: + break; + } + /* Clock rate is checked against upper limit. It may fall low during init */ + if (rate > 200000000) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + + udelay(5); + + if (lock == 0) { + /* PDB will be done only once per boot */ + update_reg(tbh_phy, PHY_CFG_0, PWR_DOWN_MASK, + PWR_DOWN_SHIFT, 0x1); + lock = 1; + /* + * According to the user manual, it asks driver to wait 5us for + * calpad busy trimming. However it is documented that this value is + * PVT(A.K.A. process, voltage and temperature) relevant, so some + * failure cases are found which indicates we should be more tolerant + * to calpad busy trimming. + */ + ret = readl_poll_timeout(tbh_phy->reg_base + PHY_STAT, + val, (val & CAL_DONE), 10, 50); + if (ret) { + dev_err(&phy->dev, "caldone failed, ret=%d\n", ret); + return ret; + } + } + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 200000000: + /* Set frequency of the DLL operation */ + update_reg(tbh_phy, PHY_CFG_2, SEL_FREQ_MASK, SEL_FREQ_SHIFT, freqsel); + + /* Enable DLL */ + update_reg(tbh_phy, PHY_CFG_0, DLL_EN_MASK, DLL_EN_SHIFT, 0x1); + + /* + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100kHz) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. + */ + ret = readl_poll_timeout(tbh_phy->reg_base + PHY_STAT, + val, (val & DLL_RDY), 10, 50 * USEC_PER_MSEC); + if (ret) { + dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret); + return ret; + } + break; + + default: + break; + } + return 0; +} + +static int thunderbay_emmc_phy_init(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + + tbh_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + + return PTR_ERR_OR_ZERO(tbh_phy->emmcclk); +} + +static int thunderbay_emmc_phy_exit(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + + clk_put(tbh_phy->emmcclk); + + return 0; +} + +static int thunderbay_emmc_phy_power_on(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + unsigned long rate; + + /* Overwrite capability bits configurable in bootloader */ + update_reg(tbh_phy, CTRL_CFG_0, + SUPPORT_HS_MASK, SUPPORT_HS_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_0, + SUPPORT_8B_MASK, SUPPORT_8B_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_SDR50_MASK, SUPPORT_SDR50_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_DDR50_MASK, SUPPORT_DDR50_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_SDR104_MASK, SUPPORT_SDR104_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_HS400_MASK, SUPPORT_HS400_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_64B_MASK, SUPPORT_64B_SHIFT, 0x1); + + if (tbh_phy->phy_power_sts == PHY_UNINITIALIZED) { + /* Indicates initialization, settings for init, same as 400KHZ setting */ + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, OTAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, OTAP_DLY_SEL_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, DLL_TRIM_ICP_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, DR_TY_SHIFT, 0x1); + + } else if (tbh_phy->phy_power_sts == PHY_INITIALIZED) { + /* Indicates actual clock setting */ + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 200000000: + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 2); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0x8); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, + DR_TY_SHIFT, 0x1); + /* For HS400 only */ + update_reg(tbh_phy, PHY_CFG_2, SEL_STRB_MASK, + SEL_STRB_SHIFT, STRB); + break; + + case 50000000 ... 52000000: + /* For both HS and DDR52 this setting works */ + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 4); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0x8); + update_reg(tbh_phy, PHY_CFG_0, + DR_TY_MASK, DR_TY_SHIFT, 0x1); + break; + + case 400000: + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, DR_TY_SHIFT, 0x1); + break; + + default: + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 2); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0x8); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, + DR_TY_SHIFT, 0x1); + break; + } + /* Reset, init seq called without phy_power_off, this indicates init seq */ + tbh_phy->phy_power_sts = PHY_UNINITIALIZED; + } + + update_reg(tbh_phy, PHY_CFG_0, RETRIM_EN_MASK, RETRIM_EN_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, RETRIM_MASK, RETRIM_SHIFT, 0x0); + + return thunderbay_emmc_phy_power(phy, 1); +} + +static int thunderbay_emmc_phy_power_off(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + + tbh_phy->phy_power_sts = PHY_INITIALIZED; + + return thunderbay_emmc_phy_power(phy, 0); +} + +static const struct phy_ops thunderbay_emmc_phy_ops = { + .init = thunderbay_emmc_phy_init, + .exit = thunderbay_emmc_phy_exit, + .power_on = thunderbay_emmc_phy_power_on, + .power_off = thunderbay_emmc_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id thunderbay_emmc_phy_of_match[] = { + { .compatible = "intel,thunderbay-emmc-phy", + (void *)&thunderbay_emmc_phy_ops }, + {} +}; +MODULE_DEVICE_TABLE(of, thunderbay_emmc_phy_of_match); + +static int thunderbay_emmc_phy_probe(struct platform_device *pdev) +{ + struct thunderbay_emmc_phy *tbh_phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + const struct of_device_id *id; + struct phy *generic_phy; + struct resource *res; + + if (!dev->of_node) + return -ENODEV; + + tbh_phy = devm_kzalloc(dev, sizeof(*tbh_phy), GFP_KERNEL); + if (!tbh_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tbh_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tbh_phy->reg_base)) + return PTR_ERR(tbh_phy->reg_base); + + tbh_phy->phy_power_sts = PHY_UNINITIALIZED; + id = of_match_node(thunderbay_emmc_phy_of_match, pdev->dev.of_node); + if (!id) { + dev_err(dev, "failed to get match_node\n"); + return -EINVAL; + } + + generic_phy = devm_phy_create(dev, dev->of_node, id->data); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, tbh_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver thunderbay_emmc_phy_driver = { + .probe = thunderbay_emmc_phy_probe, + .driver = { + .name = "thunderbay-emmc-phy", + .of_match_table = thunderbay_emmc_phy_of_match, + }, +}; +module_platform_driver(thunderbay_emmc_phy_driver); + +MODULE_AUTHOR("Nandhini S <nandhini.srikandan@intel.com>"); +MODULE_AUTHOR("Rashmi A <rashmi.a@intel.com>"); +MODULE_DESCRIPTION("Intel Thunder Bay eMMC PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c index be09b1530ae6..29d246ea24b4 100644 --- a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c +++ b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c @@ -124,8 +124,16 @@ static int ltq_rcu_usb2_phy_power_on(struct phy *phy) reset_control_deassert(priv->phy_reset); ret = clk_prepare_enable(priv->phy_gate_clk); - if (ret) + if (ret) { dev_err(dev, "failed to enable PHY gate\n"); + return ret; + } + + /* + * at least the xrx200 usb2 phy requires some extra time to be + * operational after enabling the clock + */ + usleep_range(100, 200); return ret; } @@ -141,7 +149,7 @@ static int ltq_rcu_usb2_phy_power_off(struct phy *phy) return 0; } -static struct phy_ops ltq_rcu_usb2_phy_ops = { +static const struct phy_ops ltq_rcu_usb2_phy_ops = { .init = ltq_rcu_usb2_phy_init, .power_on = ltq_rcu_usb2_phy_power_on, .power_off = ltq_rcu_usb2_phy_power_off, diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c index 2ff9a48d833e..ef93bf2cba10 100644 --- a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c +++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c @@ -349,7 +349,7 @@ static int ltq_vrx200_pcie_phy_power_off(struct phy *phy) return 0; } -static struct phy_ops ltq_vrx200_pcie_phy_ops = { +static const struct phy_ops ltq_vrx200_pcie_phy_ops = { .init = ltq_vrx200_pcie_phy_init, .exit = ltq_vrx200_pcie_phy_exit, .power_on = ltq_vrx200_pcie_phy_power_on, @@ -402,7 +402,6 @@ static int ltq_vrx200_pcie_phy_probe(struct platform_device *pdev) struct ltq_vrx200_pcie_phy_priv *priv; struct device *dev = &pdev->dev; struct phy_provider *provider; - struct resource *res; void __iomem *base; int ret; @@ -410,8 +409,7 @@ static int ltq_vrx200_pcie_phy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 8f6273c837ec..bdb87c976243 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig @@ -3,8 +3,8 @@ # Phy drivers for Marvell platforms # config ARMADA375_USBCLUSTER_PHY - def_bool y - depends on MACH_ARMADA_375 || COMPILE_TEST + bool "Armada 375 USB cluster PHY support" if COMPILE_TEST + default y if MACH_ARMADA_375 depends on OF && HAS_IOMEM select GENERIC_PHY @@ -67,6 +67,14 @@ config PHY_MVEBU_CP110_COMPHY lanes can be used by various controllers (Ethernet, sata, usb, PCIe...). +config PHY_MVEBU_CP110_UTMI + tristate "Marvell CP110 UTMI driver" + depends on ARCH_MVEBU || COMPILE_TEST + depends on OF && USB_COMMON + select GENERIC_PHY + help + Enable this to support Marvell CP110 UTMI PHY driver. + config PHY_MVEBU_SATA def_bool y depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD @@ -116,3 +124,15 @@ config PHY_MMP3_USB The PHY driver will be used by Marvell udc/ehci/otg driver. To compile this driver as a module, choose M here. + +config PHY_MMP3_HSIC + tristate "Marvell MMP3 USB HSIC PHY Driver" + depends on MACH_MMP3_DT || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support Marvell MMP3 USB HSIC PHY driver for + Marvell MMP3 SoC. This driver will be used my the Marvell EHCI + driver to initialize the interface to internal USB HSIC + components on MMP3-based boards. + + To compile this driver as a module, choose M here. diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile index 5a106b1549f4..90862c4daa26 100644 --- a/drivers/phy/marvell/Makefile +++ b/drivers/phy/marvell/Makefile @@ -3,10 +3,12 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_MMP3_USB) += phy-mmp3-usb.o +obj-$(CONFIG_PHY_MMP3_HSIC) += phy-mmp3-hsic.o obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o +obj-$(CONFIG_PHY_MVEBU_CP110_UTMI) += phy-mvebu-cp110-utmi.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o diff --git a/drivers/phy/marvell/phy-armada375-usb2.c b/drivers/phy/marvell/phy-armada375-usb2.c index fa5dc9462d09..b141e3cd8a94 100644 --- a/drivers/phy/marvell/phy-armada375-usb2.c +++ b/drivers/phy/marvell/phy-armada375-usb2.c @@ -105,15 +105,13 @@ static int armada375_usb_phy_probe(struct platform_device *pdev) struct phy *phy; struct phy_provider *phy_provider; void __iomem *usb_cluster_base; - struct resource *res; struct armada375_cluster_phy *cluster_phy; cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); if (!cluster_phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); + usb_cluster_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(usb_cluster_base)) return PTR_ERR(usb_cluster_base); diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 6960dfd8ad8c..0fe408964334 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -41,6 +41,7 @@ struct a38x_comphy_lane { struct a38x_comphy { void __iomem *base; + void __iomem *conf; struct device *dev; struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; }; @@ -54,6 +55,21 @@ static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = { { 0, 0, 3 }, }; +static void a38x_set_conf(struct a38x_comphy_lane *lane, bool enable) +{ + struct a38x_comphy *priv = lane->priv; + u32 conf; + + if (priv->conf) { + conf = readl_relaxed(priv->conf); + if (enable) + conf |= BIT(lane->port); + else + conf &= ~BIT(lane->port); + writel(conf, priv->conf); + } +} + static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane, unsigned int offset, u32 mask, u32 value) { @@ -97,6 +113,7 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) { struct a38x_comphy_lane *lane = phy_get_drvdata(phy); unsigned int gen; + int ret; if (mode != PHY_MODE_ETHERNET) return -EINVAL; @@ -115,13 +132,20 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) return -EINVAL; } + a38x_set_conf(lane, false); + a38x_comphy_set_speed(lane, gen, gen); - return a38x_comphy_poll(lane, COMPHY_STAT1, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX); + ret = a38x_comphy_poll(lane, COMPHY_STAT1, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX); + + if (ret == 0) + a38x_set_conf(lane, true); + + return ret; } static const struct phy_ops a38x_comphy_ops = { @@ -174,14 +198,21 @@ static int a38x_comphy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); priv->dev = &pdev->dev; priv->base = base; + /* Optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); + if (res) { + priv->conf = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->conf)) + return PTR_ERR(priv->conf); + } + for_each_available_child_of_node(pdev->dev.of_node, child) { struct phy *phy; int ret; diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c index a43df63007c5..78ef6ae72a9a 100644 --- a/drivers/phy/marvell/phy-berlin-usb.c +++ b/drivers/phy/marvell/phy-berlin-usb.c @@ -165,7 +165,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev) const struct of_device_id *match = of_match_device(phy_berlin_usb_of_match, &pdev->dev); struct phy_berlin_usb_priv *priv; - struct resource *res; struct phy *phy; struct phy_provider *phy_provider; @@ -173,8 +172,7 @@ static int phy_berlin_usb_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/phy/marvell/phy-mmp3-hsic.c b/drivers/phy/marvell/phy-mmp3-hsic.c new file mode 100644 index 000000000000..7cccf01848d8 --- /dev/null +++ b/drivers/phy/marvell/phy-mmp3-hsic.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk> + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#define HSIC_CTRL 0x08 +#define HSIC_ENABLE BIT(7) +#define PLL_BYPASS BIT(4) + +static int mmp3_hsic_phy_init(struct phy *phy) +{ + void __iomem *base = (void __iomem *)phy_get_drvdata(phy); + u32 hsic_ctrl; + + hsic_ctrl = readl_relaxed(base + HSIC_CTRL); + hsic_ctrl |= HSIC_ENABLE; + hsic_ctrl |= PLL_BYPASS; + writel_relaxed(hsic_ctrl, base + HSIC_CTRL); + + return 0; +} + +static const struct phy_ops mmp3_hsic_phy_ops = { + .init = mmp3_hsic_phy_init, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mmp3_hsic_phy_of_match[] = { + { .compatible = "marvell,mmp3-hsic-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mmp3_hsic_phy_of_match); + +static int mmp3_hsic_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct resource *resource; + void __iomem *base; + struct phy *phy; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, resource); + if (IS_ERR(base)) + return PTR_ERR(base); + + phy = devm_phy_create(dev, NULL, &mmp3_hsic_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, (void *)base); + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + return 0; +} + +static struct platform_driver mmp3_hsic_phy_driver = { + .probe = mmp3_hsic_phy_probe, + .driver = { + .name = "mmp3-hsic-phy", + .of_match_table = mmp3_hsic_phy_of_match, + }, +}; +module_platform_driver(mmp3_hsic_phy_driver); + +MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); +MODULE_DESCRIPTION("Marvell MMP3 USB HSIC PHY Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/marvell/phy-mmp3-usb.c b/drivers/phy/marvell/phy-mmp3-usb.c index 499869595a58..04c0bada3519 100644 --- a/drivers/phy/marvell/phy-mmp3-usb.c +++ b/drivers/phy/marvell/phy-mmp3-usb.c @@ -246,7 +246,6 @@ MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match); static int mmp3_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *resource; struct mmp3_usb_phy *mmp3_usb_phy; struct phy_provider *provider; @@ -254,8 +253,7 @@ static int mmp3_usb_phy_probe(struct platform_device *pdev) if (!mmp3_usb_phy) return -ENOMEM; - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmp3_usb_phy->base = devm_ioremap_resource(dev, resource); + mmp3_usb_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mmp3_usb_phy->base)) { dev_err(dev, "failed to remap PHY regs\n"); return PTR_ERR(mmp3_usb_phy->base); diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c index 1a138be8bd6a..67712c77d806 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c @@ -5,12 +5,16 @@ * Authors: * Evan Wang <xswang@marvell.com> * Miquèl Raynal <miquel.raynal@bootlin.com> + * Pali Rohár <pali@kernel.org> + * Marek Behún <kabel@kernel.org> * * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. - * SMC call initial support done by Grzegorz Jaszczyk. + * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org> + * and Marek Behún <kabel@kernel.org>. */ -#include <linux/arm-smccc.h> +#include <linux/bitfield.h> +#include <linux/clk.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/mfd/syscon.h> @@ -18,107 +22,1089 @@ #include <linux/phy.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/spinlock.h> -#define MVEBU_A3700_COMPHY_LANES 3 -#define MVEBU_A3700_COMPHY_PORTS 2 - -/* COMPHY Fast SMC function identifiers */ -#define COMPHY_SIP_POWER_ON 0x82000001 -#define COMPHY_SIP_POWER_OFF 0x82000002 -#define COMPHY_SIP_PLL_LOCK 0x82000003 -#define COMPHY_FW_NOT_SUPPORTED (-1) - -#define COMPHY_FW_MODE_SATA 0x1 -#define COMPHY_FW_MODE_SGMII 0x2 -#define COMPHY_FW_MODE_HS_SGMII 0x3 -#define COMPHY_FW_MODE_USB3H 0x4 -#define COMPHY_FW_MODE_USB3D 0x5 -#define COMPHY_FW_MODE_PCIE 0x6 -#define COMPHY_FW_MODE_RXAUI 0x7 -#define COMPHY_FW_MODE_XFI 0x8 -#define COMPHY_FW_MODE_SFI 0x9 -#define COMPHY_FW_MODE_USB3 0xa - -#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ -#define COMPHY_FW_SPEED_2_5G 1 -#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */ -#define COMPHY_FW_SPEED_5G 3 -#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */ -#define COMPHY_FW_SPEED_6G 5 -#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */ -#define COMPHY_FW_SPEED_MAX 0x3F - -#define COMPHY_FW_MODE(mode) ((mode) << 12) -#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ - ((idx) << 8) | \ - ((speed) << 2)) -#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \ - ((width) << 18)) +#define PLL_SET_DELAY_US 600 +#define COMPHY_PLL_SLEEP 1000 +#define COMPHY_PLL_TIMEOUT 150000 + +/* Comphy lane2 indirect access register offset */ +#define COMPHY_LANE2_INDIR_ADDR 0x0 +#define COMPHY_LANE2_INDIR_DATA 0x4 + +/* SATA and USB3 PHY offset compared to SATA PHY */ +#define COMPHY_LANE2_REGS_BASE 0x200 + +/* + * When accessing common PHY lane registers directly, we need to shift by 1, + * since the registers are 16-bit. + */ +#define COMPHY_LANE_REG_DIRECT(reg) (((reg) & 0x7FF) << 1) + +/* COMPHY registers */ +#define COMPHY_POWER_PLL_CTRL 0x01 +#define PU_IVREF_BIT BIT(15) +#define PU_PLL_BIT BIT(14) +#define PU_RX_BIT BIT(13) +#define PU_TX_BIT BIT(12) +#define PU_TX_INTP_BIT BIT(11) +#define PU_DFE_BIT BIT(10) +#define RESET_DTL_RX_BIT BIT(9) +#define PLL_LOCK_BIT BIT(8) +#define REF_FREF_SEL_MASK GENMASK(4, 0) +#define REF_FREF_SEL_SERDES_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x1) +#define REF_FREF_SEL_SERDES_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3) +#define REF_FREF_SEL_SERDES_50MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x4) +#define REF_FREF_SEL_PCIE_USB3_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x2) +#define REF_FREF_SEL_PCIE_USB3_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3) +#define COMPHY_MODE_MASK GENMASK(7, 5) +#define COMPHY_MODE_SATA FIELD_PREP(COMPHY_MODE_MASK, 0x0) +#define COMPHY_MODE_PCIE FIELD_PREP(COMPHY_MODE_MASK, 0x3) +#define COMPHY_MODE_SERDES FIELD_PREP(COMPHY_MODE_MASK, 0x4) +#define COMPHY_MODE_USB3 FIELD_PREP(COMPHY_MODE_MASK, 0x5) + +#define COMPHY_KVCO_CAL_CTRL 0x02 +#define USE_MAX_PLL_RATE_BIT BIT(12) +#define SPEED_PLL_MASK GENMASK(7, 2) +#define SPEED_PLL_VALUE_16 FIELD_PREP(SPEED_PLL_MASK, 0x10) + +#define COMPHY_DIG_LOOPBACK_EN 0x23 +#define SEL_DATA_WIDTH_MASK GENMASK(11, 10) +#define DATA_WIDTH_10BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0) +#define DATA_WIDTH_20BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1) +#define DATA_WIDTH_40BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2) +#define PLL_READY_TX_BIT BIT(4) + +#define COMPHY_SYNC_PATTERN 0x24 +#define TXD_INVERT_BIT BIT(10) +#define RXD_INVERT_BIT BIT(11) + +#define COMPHY_SYNC_MASK_GEN 0x25 +#define PHY_GEN_MAX_MASK GENMASK(11, 10) +#define PHY_GEN_MAX_USB3_5G FIELD_PREP(PHY_GEN_MAX_MASK, 0x1) + +#define COMPHY_ISOLATION_CTRL 0x26 +#define PHY_ISOLATE_MODE BIT(15) + +#define COMPHY_GEN2_SET2 0x3e +#define GS2_TX_SSC_AMP_MASK GENMASK(15, 9) +#define GS2_TX_SSC_AMP_4128 FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20) +#define GS2_VREG_RXTX_MAS_ISET_MASK GENMASK(8, 7) +#define GS2_VREG_RXTX_MAS_ISET_60U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x0) +#define GS2_VREG_RXTX_MAS_ISET_80U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x1) +#define GS2_VREG_RXTX_MAS_ISET_100U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x2) +#define GS2_VREG_RXTX_MAS_ISET_120U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ + 0x3) +#define GS2_RSVD_6_0_MASK GENMASK(6, 0) + +#define COMPHY_GEN3_SET2 0x3f + +#define COMPHY_IDLE_SYNC_EN 0x48 +#define IDLE_SYNC_EN BIT(12) + +#define COMPHY_MISC_CTRL0 0x4F +#define CLK100M_125M_EN BIT(4) +#define TXDCLK_2X_SEL BIT(6) +#define CLK500M_EN BIT(7) +#define PHY_REF_CLK_SEL BIT(10) + +#define COMPHY_SFT_RESET 0x52 +#define SFT_RST BIT(9) +#define SFT_RST_NO_REG BIT(10) + +#define COMPHY_MISC_CTRL1 0x73 +#define SEL_BITS_PCIE_FORCE BIT(15) + +#define COMPHY_GEN2_SET3 0x112 +#define GS3_FFE_CAP_SEL_MASK GENMASK(3, 0) +#define GS3_FFE_CAP_SEL_VALUE FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF) + +/* PIPE registers */ +#define COMPHY_PIPE_LANE_CFG0 0x180 +#define PRD_TXDEEMPH0_MASK BIT(0) +#define PRD_TXMARGIN_MASK GENMASK(3, 1) +#define PRD_TXSWING_MASK BIT(4) +#define CFG_TX_ALIGN_POS_MASK GENMASK(8, 5) + +#define COMPHY_PIPE_LANE_CFG1 0x181 +#define PRD_TXDEEMPH1_MASK BIT(15) +#define USE_MAX_PLL_RATE_EN BIT(9) +#define TX_DET_RX_MODE BIT(6) +#define GEN2_TX_DATA_DLY_MASK GENMASK(4, 3) +#define GEN2_TX_DATA_DLY_DEFT FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2) +#define TX_ELEC_IDLE_MODE_EN BIT(0) + +#define COMPHY_PIPE_LANE_STAT1 0x183 +#define TXDCLK_PCLK_EN BIT(0) + +#define COMPHY_PIPE_LANE_CFG4 0x188 +#define SPREAD_SPECTRUM_CLK_EN BIT(7) + +#define COMPHY_PIPE_RST_CLK_CTRL 0x1C1 +#define PIPE_SOFT_RESET BIT(0) +#define PIPE_REG_RESET BIT(1) +#define MODE_CORE_CLK_FREQ_SEL BIT(9) +#define MODE_PIPE_WIDTH_32 BIT(3) +#define MODE_REFDIV_MASK GENMASK(5, 4) +#define MODE_REFDIV_BY_4 FIELD_PREP(MODE_REFDIV_MASK, 0x2) + +#define COMPHY_PIPE_TEST_MODE_CTRL 0x1C2 +#define MODE_MARGIN_OVERRIDE BIT(2) + +#define COMPHY_PIPE_CLK_SRC_LO 0x1C3 +#define MODE_CLK_SRC BIT(0) +#define BUNDLE_PERIOD_SEL BIT(1) +#define BUNDLE_PERIOD_SCALE_MASK GENMASK(3, 2) +#define BUNDLE_SAMPLE_CTRL BIT(4) +#define PLL_READY_DLY_MASK GENMASK(7, 5) +#define CFG_SEL_20B BIT(15) + +#define COMPHY_PIPE_PWR_MGM_TIM1 0x1D0 +#define CFG_PM_OSCCLK_WAIT_MASK GENMASK(15, 12) +#define CFG_PM_RXDEN_WAIT_MASK GENMASK(11, 8) +#define CFG_PM_RXDEN_WAIT_1_UNIT FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1) +#define CFG_PM_RXDLOZ_WAIT_MASK GENMASK(7, 0) +#define CFG_PM_RXDLOZ_WAIT_7_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7) +#define CFG_PM_RXDLOZ_WAIT_12_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC) + +/* + * This register is not from PHY lane register space. It only exists in the + * indirect register space, before the actual PHY lane 2 registers. So the + * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE. + * It is used only for SATA PHY initialization. + */ +#define COMPHY_RESERVED_REG 0x0E +#define PHYCTRL_FRM_PIN_BIT BIT(13) + +/* South Bridge PHY Configuration Registers */ +#define COMPHY_PHY_REG(lane, reg) (((1 - (lane)) * 0x28) + ((reg) & 0x3f)) + +/* + * lane0: USB3/GbE1 PHY Configuration 1 + * lane1: PCIe/GbE0 PHY Configuration 1 + * (used only by SGMII code) + */ +#define COMPHY_PHY_CFG1 0x0 +#define PIN_PU_IVREF_BIT BIT(1) +#define PIN_RESET_CORE_BIT BIT(11) +#define PIN_RESET_COMPHY_BIT BIT(12) +#define PIN_PU_PLL_BIT BIT(16) +#define PIN_PU_RX_BIT BIT(17) +#define PIN_PU_TX_BIT BIT(18) +#define PIN_TX_IDLE_BIT BIT(19) +#define GEN_RX_SEL_MASK GENMASK(25, 22) +#define GEN_RX_SEL_VALUE(val) FIELD_PREP(GEN_RX_SEL_MASK, (val)) +#define GEN_TX_SEL_MASK GENMASK(29, 26) +#define GEN_TX_SEL_VALUE(val) FIELD_PREP(GEN_TX_SEL_MASK, (val)) +#define SERDES_SPEED_1_25_G 0x6 +#define SERDES_SPEED_3_125_G 0x8 +#define PHY_RX_INIT_BIT BIT(30) + +/* + * lane0: USB3/GbE1 PHY Status 1 + * lane1: PCIe/GbE0 PHY Status 1 + * (used only by SGMII code) + */ +#define COMPHY_PHY_STAT1 0x18 +#define PHY_RX_INIT_DONE_BIT BIT(0) +#define PHY_PLL_READY_RX_BIT BIT(2) +#define PHY_PLL_READY_TX_BIT BIT(3) + +/* PHY Selector */ +#define COMPHY_SELECTOR_PHY_REG 0xFC +/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */ +#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT BIT(0) +/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */ +#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT BIT(4) +/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */ +#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT BIT(8) struct mvebu_a3700_comphy_conf { unsigned int lane; enum phy_mode mode; int submode; - unsigned int port; - u32 fw_mode; }; -#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \ +#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode) \ { \ .lane = _lane, \ .mode = _mode, \ .submode = _smode, \ - .port = _port, \ - .fw_mode = _fw, \ } -#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \ - MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw) +#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \ + MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA) -#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \ - MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw) +#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \ + MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode) static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { /* lane 0 */ - MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0, - COMPHY_FW_MODE_USB3H), - MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, - COMPHY_FW_MODE_SGMII), - MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, - COMPHY_FW_MODE_HS_SGMII), + MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX), /* lane 1 */ - MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0, - COMPHY_FW_MODE_PCIE), - MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0, - COMPHY_FW_MODE_SGMII), - MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, - COMPHY_FW_MODE_HS_SGMII), + MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX), /* lane 2 */ - MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, - COMPHY_FW_MODE_SATA), - MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0, - COMPHY_FW_MODE_USB3H), + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA), + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS), +}; + +struct mvebu_a3700_comphy_priv { + void __iomem *comphy_regs; + void __iomem *lane0_phy_regs; /* USB3 and GbE1 */ + void __iomem *lane1_phy_regs; /* PCIe and GbE0 */ + void __iomem *lane2_phy_indirect; /* SATA and USB3 */ + spinlock_t lock; /* for PHY selector access */ + bool xtal_is_40m; }; struct mvebu_a3700_comphy_lane { + struct mvebu_a3700_comphy_priv *priv; struct device *dev; unsigned int id; enum phy_mode mode; int submode; - int port; + bool invert_tx; + bool invert_rx; +}; + +struct gbe_phy_init_data_fix { + u16 addr; + u16 value; }; -static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, - unsigned long mode) +/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */ +static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = { + { 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 }, + { 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 }, + { 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 }, + { 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC }, + { 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 }, + { 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 }, + { 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 }, + { 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 }, + { 0x104, 0x0C10 } +}; + +/* 40M1G25 mode init data */ +static u16 gbe_phy_init[512] = { + /* 0 1 2 3 4 5 6 7 */ + /*-----------------------------------------------------------*/ + /* 8 9 A B C D E F */ + 0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26, /* 00 */ + 0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52, /* 08 */ + 0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000, /* 10 */ + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF, /* 18 */ + 0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000, /* 20 */ + 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 28 */ + 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */ + 0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100, /* 38 */ + 0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00, /* 40 */ + 0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A, /* 48 */ + 0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001, /* 50 */ + 0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF, /* 58 */ + 0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000, /* 60 */ + 0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002, /* 68 */ + 0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780, /* 70 */ + 0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000, /* 78 */ + 0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000, /* 80 */ + 0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210, /* 88 */ + 0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F, /* 90 */ + 0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651, /* 98 */ + 0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000, /* A0 */ + 0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* A8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* B0 */ + 0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, /* B8 */ + 0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003, /* C0 */ + 0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000, /* C8 */ + 0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00, /* D0 */ + 0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000, /* D8 */ + 0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541, /* E0 */ + 0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200, /* E8 */ + 0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000, /* F0 */ + 0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000, /* F8 */ + 0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000, /*100 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*108 */ + 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000, /*110 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*118 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*120 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*128 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*130 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*138 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*140 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*148 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*150 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*158 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*160 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*168 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*170 */ + 0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000, /*178 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*180 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*188 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*190 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*198 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E8 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1F0 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 /*1F8 */ +}; + +static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask) { - struct arm_smccc_res res; + u32 val; + + val = readl(addr); + val = (val & ~mask) | (data & mask); + writel(val, addr); +} - arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); +static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask) +{ + u16 val; - return res.a0; + val = readw(addr); + val = (val & ~mask) | (data & mask); + writew(val, addr); } -static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, +/* Used for accessing lane 2 registers (SATA/USB3 PHY) */ +static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv, + u32 offset, u16 data, u16 mask) +{ + writel(offset, + priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR); + comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA, + data, mask); +} + +static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane, + u16 reg, u16 data, u16 mask) +{ + if (lane->id == 2) { + /* lane 2 PHY registers are accessed indirectly */ + comphy_set_indirect(lane->priv, + reg + COMPHY_LANE2_REGS_BASE, + data, mask); + } else { + void __iomem *base = lane->id == 1 ? + lane->priv->lane1_phy_regs : + lane->priv->lane0_phy_regs; + + comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg), + data, mask); + } +} + +static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane, + u16 reg, u16 bits, + ulong sleep_us, ulong timeout_us) +{ + int ret; + + if (lane->id == 2) { + u32 data; + + /* lane 2 PHY registers are accessed indirectly */ + writel(reg + COMPHY_LANE2_REGS_BASE, + lane->priv->lane2_phy_indirect + + COMPHY_LANE2_INDIR_ADDR); + + ret = readl_poll_timeout(lane->priv->lane2_phy_indirect + + COMPHY_LANE2_INDIR_DATA, + data, (data & bits) == bits, + sleep_us, timeout_us); + } else { + void __iomem *base = lane->id == 1 ? + lane->priv->lane1_phy_regs : + lane->priv->lane0_phy_regs; + u16 data; + + ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg), + data, (data & bits) == bits, + sleep_us, timeout_us); + } + + return ret; +} + +static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane, + u8 reg, u32 data, u32 mask) +{ + comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg), + data, mask); +} + +static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane, + u8 reg, u32 bits, + ulong sleep_us, ulong timeout_us) +{ + u32 data; + + return readl_poll_timeout(lane->priv->comphy_regs + + COMPHY_PHY_REG(lane->id, reg), + data, (data & bits) == bits, + sleep_us, timeout_us); +} + +/* PHY selector configures with corresponding modes */ +static int +mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane) +{ + u32 old, new, clr = 0, set = 0; + unsigned long flags; + + switch (lane->mode) { + case PHY_MODE_SATA: + /* SATA must be in Lane2 */ + if (lane->id == 2) + clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT; + else + goto error; + break; + + case PHY_MODE_ETHERNET: + if (lane->id == 0) + clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT; + else if (lane->id == 1) + clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT; + else + goto error; + break; + + case PHY_MODE_USB_HOST_SS: + if (lane->id == 2) + set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT; + else if (lane->id == 0) + set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT; + else + goto error; + break; + + case PHY_MODE_PCIE: + /* PCIE must be in Lane1 */ + if (lane->id == 1) + set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT; + else + goto error; + break; + + default: + goto error; + } + + spin_lock_irqsave(&lane->priv->lock, flags); + + old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG); + new = (old & ~clr) | set; + writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG); + + spin_unlock_irqrestore(&lane->priv->lock, flags); + + dev_dbg(lane->dev, + "COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n", + lane->id, lane->mode, old, new); + + return 0; +error: + dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id, + lane->mode); + return -EINVAL; +} + +static int +mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, ref_clk; + int ret; + + /* Configure phy selector for SATA */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* Clear phy isolation mode to make it work in normal mode */ + comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL, + 0x0, PHY_ISOLATE_MODE); + + /* 0. Check the Polarity invert bits */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* 1. Select 40-bit data width */ + comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, + DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK); + + /* 2. Select reference clock(25M) and PHY mode (SATA) */ + if (lane->priv->xtal_is_40m) + ref_clk = REF_FREF_SEL_SERDES_40MHZ; + else + ref_clk = REF_FREF_SEL_SERDES_25MHZ; + + data = ref_clk | COMPHY_MODE_SATA; + mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* 3. Use maximum PLL rate (no power save) */ + comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, + USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT); + + /* 4. Reset reserved bit */ + comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG, + 0x0, PHYCTRL_FRM_PIN_BIT); + + /* 5. Set vendor-specific configuration (It is done in sata driver) */ + /* XXX: in U-Boot below sequence was executed in this place, in Linux + * not. Now it is done only in U-Boot before this comphy + * initialization - tests shows that it works ok, but in case of any + * future problem it is left for reference. + * reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff); + * reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6)); + */ + + /* Wait for > 55 us to allow PLL be enabled */ + udelay(PLL_SET_DELAY_US); + + /* Polling status */ + ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN, + PLL_READY_TX_BIT, COMPHY_PLL_SLEEP, + COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to lock SATA PLL\n"); + + return ret; +} + +static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane, + bool is_1gbps) +{ + int addr, fix_idx; + u16 val; + + fix_idx = 0; + for (addr = 0; addr < 512; addr++) { + /* + * All PHY register values are defined in full for 3.125Gbps + * SERDES speed. The values required for 1.25 Gbps are almost + * the same and only few registers should be "fixed" in + * comparison to 3.125 Gbps values. These register values are + * stored in "gbe_phy_init_fix" array. + */ + if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) { + /* Use new value */ + val = gbe_phy_init_fix[fix_idx].value; + if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix)) + fix_idx++; + } else { + val = gbe_phy_init[addr]; + } + + comphy_lane_reg_set(lane, addr, val, 0xFFFF); + } +} + +static int +mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, speed_sel; + int ret; + + /* Set selector */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* + * 1. Reset PHY by setting PHY input port PIN_RESET=1. + * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep + * PHY TXP/TXN output to idle state during PHY initialization + * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0. + */ + data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT; + mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT | + PIN_PU_TX_BIT | PHY_RX_INIT_BIT; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* 4. Release reset to the PHY by setting PIN_RESET=0. */ + data = 0x0; + mask = PIN_RESET_COMPHY_BIT; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* + * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY + * bit rate + */ + switch (lane->submode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + /* SGMII 1G, SerDes speed 1.25G */ + speed_sel = SERDES_SPEED_1_25_G; + break; + case PHY_INTERFACE_MODE_2500BASEX: + /* 2500Base-X, SerDes speed 3.125G */ + speed_sel = SERDES_SPEED_3_125_G; + break; + default: + /* Other rates are not supported */ + dev_err(lane->dev, + "unsupported phy speed %d on comphy lane%d\n", + lane->submode, lane->id); + return -EINVAL; + } + data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel); + mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* + * 6. Wait 10mS for bandgap and reference clocks to stabilize; then + * start SW programming. + */ + mdelay(10); + + /* 7. Program COMPHY register PHY_MODE */ + data = COMPHY_MODE_SERDES; + mask = COMPHY_MODE_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* + * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK + * source + */ + data = 0x0; + mask = PHY_REF_CLK_SEL; + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask); + + /* + * 9. Set correct reference clock frequency in COMPHY register + * REF_FREF_SEL. + */ + if (lane->priv->xtal_is_40m) + data = REF_FREF_SEL_SERDES_50MHZ; + else + data = REF_FREF_SEL_SERDES_25MHZ; + + mask = REF_FREF_SEL_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* + * 10. Program COMPHY register PHY_GEN_MAX[1:0] + * This step is mentioned in the flow received from verification team. + * However the PHY_GEN_MAX value is only meaningful for other interfaces + * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or + * PCIe speed 2.5/5 Gbps + */ + + /* + * 11. Program COMPHY register SEL_BITS to set correct parallel data + * bus width + */ + data = DATA_WIDTH_10BIT; + mask = SEL_DATA_WIDTH_MASK; + comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask); + + /* + * 12. As long as DFE function needs to be enabled in any mode, + * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F + * for real chip during COMPHY power on. + * The value of the DFE_UPDATE_EN already is 0x3F, because it is the + * default value after reset of the PHY. + */ + + /* + * 13. Program COMPHY GEN registers. + * These registers should be programmed based on the lab testing result + * to achieve optimal performance. Please contact the CEA group to get + * the related GEN table during real chip bring-up. We only required to + * run though the entire registers programming flow defined by + * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock + * 25 MHz the default values stored in PHY registers are OK. + */ + dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n", + lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G"); + if (lane->priv->xtal_is_40m) + comphy_gbe_phy_init(lane, + lane->submode != PHY_INTERFACE_MODE_2500BASEX); + + /* + * 14. Check the PHY Polarity invert bit + */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* + * 15. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to + * start PHY power up sequence. All the PHY register programming should + * be done before PIN_PU_PLL=1. There should be no register programming + * for normal PHY operation from this point. + */ + data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT; + mask = data; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); + + /* + * 16. Wait for PHY power up sequence to finish by checking output ports + * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1. + */ + ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, + PHY_PLL_READY_TX_BIT | + PHY_PLL_READY_RX_BIT, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) { + dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n", + lane->id); + return ret; + } + + /* + * 17. Set COMPHY input port PIN_TX_IDLE=0 + */ + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT); + + /* + * 18. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To + * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the + * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to + * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please + * refer to RX initialization part for details. + */ + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, + PHY_RX_INIT_BIT, PHY_RX_INIT_BIT); + + ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, + PHY_PLL_READY_TX_BIT | + PHY_PLL_READY_RX_BIT, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) { + dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n", + lane->id); + return ret; + } + + ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, + PHY_RX_INIT_DONE_BIT, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n", + lane->id); + + return ret; +} + +static int +mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, cfg, ref_clk; + int ret; + + /* Set phy seclector */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* + * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The + * register belong to UTMI module, so it is set in UTMI phy driver. + */ + + /* + * 1. Set PRD_TXDEEMPH (3.5db de-emph) + */ + data = PRD_TXDEEMPH0_MASK; + mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK | + CFG_TX_ALIGN_POS_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask); + + /* + * 2. Set BIT0: enable transmitter in high impedance mode + * Set BIT[3:4]: delay 2 clock cycles for HiZ off latency + * Set BIT6: Tx detect Rx at HiZ mode + * Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db + * together with bit 0 of COMPHY_PIPE_LANE_CFG0 register + */ + data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN; + mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK | + TX_ELEC_IDLE_MODE_EN; + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask); + + /* + * 3. Set Spread Spectrum Clock Enabled + */ + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4, + SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN); + + /* + * 4. Set Override Margining Controls From the MAC: + * Use margining signals from lane configuration + */ + comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL, + MODE_MARGIN_OVERRIDE, 0xFFFF); + + /* + * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles + * set Mode Clock Source = PCLK is generated from REFCLK + */ + data = 0x0; + mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK | + BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask); + + /* + * 6. Set G2 Spread Spectrum Clock Amplitude at 4K + */ + comphy_lane_reg_set(lane, COMPHY_GEN2_SET2, + GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK); + + /* + * 7. Unset G3 Spread Spectrum Clock Amplitude + * set G3 TX and RX Register Master Current Select + */ + data = GS2_VREG_RXTX_MAS_ISET_60U; + mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK | + GS2_RSVD_6_0_MASK; + comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask); + + /* + * 8. Check crystal jumper setting and program the Power and PLL Control + * accordingly Change RX wait + */ + if (lane->priv->xtal_is_40m) { + ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ; + cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT; + } else { + ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ; + cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT; + } + + data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | + PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk; + mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | + PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK | + REF_FREF_SEL_MASK; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg; + mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK | + CFG_PM_RXDLOZ_WAIT_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask); + + /* + * 9. Enable idle sync + */ + comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN, + IDLE_SYNC_EN, IDLE_SYNC_EN); + + /* + * 10. Enable the output of 500M clock + */ + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN); + + /* + * 11. Set 20-bit data width + */ + comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, + DATA_WIDTH_20BIT, 0xFFFF); + + /* + * 12. Override Speed_PLL value and use MAC PLL + */ + data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT; + mask = 0xFFFF; + comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask); + + /* + * 13. Check the Polarity invert bit + */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* + * 14. Set max speed generation to USB3.0 5Gbps + */ + comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN, + PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK); + + /* + * 15. Set capacitor value for FFE gain peaking to 0xF + */ + comphy_lane_reg_set(lane, COMPHY_GEN2_SET3, + GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK); + + /* + * 16. Release SW reset + */ + data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4; + mask = 0xFFFF; + comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); + + /* Wait for > 55 us to allow PCLK be enabled */ + udelay(PLL_SET_DELAY_US); + + ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to lock USB3 PLL\n"); + + return ret; +} + +static int +mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data, ref_clk; + int ret; + + /* Configure phy selector for PCIe */ + ret = mvebu_a3700_comphy_set_phy_selector(lane); + if (ret) + return ret; + + /* 1. Enable max PLL. */ + comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, + USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN); + + /* 2. Select 20 bit SERDES interface. */ + comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, + CFG_SEL_20B, CFG_SEL_20B); + + /* 3. Force to use reg setting for PCIe mode */ + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1, + SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE); + + /* 4. Change RX wait */ + data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT; + mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK | + CFG_PM_RXDLOZ_WAIT_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask); + + /* 5. Enable idle sync */ + comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN, + IDLE_SYNC_EN, IDLE_SYNC_EN); + + /* 6. Enable the output of 100M/125M/500M clock */ + data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN; + mask = data; + comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask); + + /* + * 7. Enable TX, PCIE global register, 0xd0074814, it is done in + * PCI-E driver + */ + + /* + * 8. Check crystal jumper setting and program the Power and PLL + * Control accordingly + */ + + if (lane->priv->xtal_is_40m) + ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ; + else + ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ; + + data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | + PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk; + mask = 0xFFFF; + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); + + /* 9. Override Speed_PLL value and use MAC PLL */ + comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, + SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT, + 0xFFFF); + + /* 10. Check the Polarity invert bit */ + data = 0x0; + if (lane->invert_tx) + data |= TXD_INVERT_BIT; + if (lane->invert_rx) + data |= RXD_INVERT_BIT; + mask = TXD_INVERT_BIT | RXD_INVERT_BIT; + comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); + + /* 11. Release SW reset */ + data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32; + mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK; + comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); + + /* Wait for > 55 us to allow PCLK be enabled */ + udelay(PLL_SET_DELAY_US); + + ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN, + COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); + if (ret) + dev_err(lane->dev, "Failed to lock PCIE PLL\n"); + + return ret; +} + +static void +mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + /* Set phy isolation mode */ + comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL, + PHY_ISOLATE_MODE, PHY_ISOLATE_MODE); + + /* Power off PLL, Tx, Rx */ + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, + 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); +} + +static void +mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + u32 mask, data; + + data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT | + PHY_RX_INIT_BIT; + mask = data; + comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); +} + +static void +mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + /* Power off PLL, Tx, Rx */ + comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, + 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); +} + +static void mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane) +{ + /* + * The USB3 MAC sets the USB3 PHY to low state, so we do not + * need to power off USB3 PHY again. + */ +} + +static bool mvebu_a3700_comphy_check_mode(int lane, enum phy_mode mode, int submode) { @@ -126,38 +1112,36 @@ static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, /* Unused PHY mux value is 0x0 */ if (mode == PHY_MODE_INVALID) - return -EINVAL; + return false; for (i = 0; i < n; i++) { if (mvebu_a3700_comphy_modes[i].lane == lane && - mvebu_a3700_comphy_modes[i].port == port && mvebu_a3700_comphy_modes[i].mode == mode && mvebu_a3700_comphy_modes[i].submode == submode) break; } if (i == n) - return -EINVAL; + return false; - return mvebu_a3700_comphy_modes[i].fw_mode; + return true; } static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); - int fw_mode; - - if (submode == PHY_INTERFACE_MODE_1000BASEX) - submode = PHY_INTERFACE_MODE_SGMII; - fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode, - submode); - if (fw_mode < 0) { + if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) { dev_err(lane->dev, "invalid COMPHY mode\n"); - return fw_mode; + return -EINVAL; } + /* Mode cannot be changed while the PHY is powered on */ + if (phy->power_count && + (lane->mode != mode || lane->submode != submode)) + return -EBUSY; + /* Just remember the mode, ->power_on() will do the real setup */ lane->mode = mode; lane->submode = submode; @@ -168,70 +1152,53 @@ static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, static int mvebu_a3700_comphy_power_on(struct phy *phy) { struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); - u32 fw_param; - int fw_mode; - int ret; - fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, - lane->mode, lane->submode); - if (fw_mode < 0) { + if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode, + lane->submode)) { dev_err(lane->dev, "invalid COMPHY mode\n"); - return fw_mode; + return -EINVAL; } switch (lane->mode) { case PHY_MODE_USB_HOST_SS: dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id); - fw_param = COMPHY_FW_MODE(fw_mode); - break; + return mvebu_a3700_comphy_usb3_power_on(lane); case PHY_MODE_SATA: dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id); - fw_param = COMPHY_FW_MODE(fw_mode); - break; + return mvebu_a3700_comphy_sata_power_on(lane); case PHY_MODE_ETHERNET: - switch (lane->submode) { - case PHY_INTERFACE_MODE_SGMII: - dev_dbg(lane->dev, "set lane %d to SGMII mode\n", - lane->id); - fw_param = COMPHY_FW_NET(fw_mode, lane->port, - COMPHY_FW_SPEED_1_25G); - break; - case PHY_INTERFACE_MODE_2500BASEX: - dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n", - lane->id); - fw_param = COMPHY_FW_NET(fw_mode, lane->port, - COMPHY_FW_SPEED_3_125G); - break; - default: - dev_err(lane->dev, "unsupported PHY submode (%d)\n", - lane->submode); - return -ENOTSUPP; - } - break; + dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id); + return mvebu_a3700_comphy_ethernet_power_on(lane); case PHY_MODE_PCIE: dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); - fw_param = COMPHY_FW_PCIE(fw_mode, lane->port, - COMPHY_FW_SPEED_5G, - phy->attrs.bus_width); - break; + return mvebu_a3700_comphy_pcie_power_on(lane); default: dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); - return -ENOTSUPP; + return -EOPNOTSUPP; } - - ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); - if (ret == COMPHY_FW_NOT_SUPPORTED) - dev_err(lane->dev, - "unsupported SMC call, try updating your firmware\n"); - - return ret; } static int mvebu_a3700_comphy_power_off(struct phy *phy) { struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); - return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); + switch (lane->id) { + case 0: + mvebu_a3700_comphy_usb3_power_off(lane); + mvebu_a3700_comphy_ethernet_power_off(lane); + return 0; + case 1: + mvebu_a3700_comphy_pcie_power_off(lane); + mvebu_a3700_comphy_ethernet_power_off(lane); + return 0; + case 2: + mvebu_a3700_comphy_usb3_power_off(lane); + mvebu_a3700_comphy_sata_power_off(lane); + return 0; + default: + dev_err(lane->dev, "invalid COMPHY mode\n"); + return -EINVAL; + } } static const struct phy_ops mvebu_a3700_comphy_ops = { @@ -245,25 +1212,90 @@ static struct phy *mvebu_a3700_comphy_xlate(struct device *dev, struct of_phandle_args *args) { struct mvebu_a3700_comphy_lane *lane; + unsigned int port; struct phy *phy; - if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS)) - return ERR_PTR(-EINVAL); - phy = of_phy_simple_xlate(dev, args); if (IS_ERR(phy)) return phy; lane = phy_get_drvdata(phy); - lane->port = args->args[0]; + + port = args->args[0]; + if (port != 0 && (port != 1 || lane->id != 0)) { + dev_err(lane->dev, "invalid port number %u\n", port); + return ERR_PTR(-EINVAL); + } + + lane->invert_tx = args->args[1] & BIT(0); + lane->invert_rx = args->args[1] & BIT(1); return phy; } static int mvebu_a3700_comphy_probe(struct platform_device *pdev) { + struct mvebu_a3700_comphy_priv *priv; struct phy_provider *provider; struct device_node *child; + struct resource *res; + struct clk *clk; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy"); + priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->comphy_regs)) + return PTR_ERR(priv->comphy_regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lane1_pcie_gbe"); + priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lane1_phy_regs)) + return PTR_ERR(priv->lane1_phy_regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lane0_usb3_gbe"); + priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lane0_phy_regs)) + return PTR_ERR(priv->lane0_phy_regs); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lane2_sata_usb3"); + priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lane2_phy_indirect)) + return PTR_ERR(priv->lane2_phy_indirect); + + /* + * Driver needs to know if reference xtal clock is 40MHz or 25MHz. + * Old DT bindings do not have xtal clk present. So do not fail here + * and expects that default 25MHz reference clock is used. + */ + clk = clk_get(&pdev->dev, "xtal"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n", + PTR_ERR(clk)); + } else { + ret = clk_prepare_enable(clk); + if (ret) { + dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n", + ret); + } else { + if (clk_get_rate(clk) == 40000000) + priv->xtal_is_40m = true; + clk_disable_unprepare(clk); + } + clk_put(clk); + } + + dev_set_drvdata(&pdev->dev, priv); for_each_available_child_of_node(pdev->dev.of_node, child) { struct mvebu_a3700_comphy_lane *lane; @@ -278,7 +1310,7 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev) continue; } - if (lane_id >= MVEBU_A3700_COMPHY_LANES) { + if (lane_id >= 3) { dev_err(&pdev->dev, "invalid 'reg' property\n"); continue; } @@ -296,16 +1328,25 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev) return PTR_ERR(phy); } + lane->priv = priv; lane->dev = &pdev->dev; lane->mode = PHY_MODE_INVALID; lane->submode = PHY_INTERFACE_MODE_NA; lane->id = lane_id; - lane->port = -1; + lane->invert_tx = false; + lane->invert_rx = false; phy_set_drvdata(phy, lane); + + /* + * To avoid relying on the bootloader/firmware configuration, + * power off all comphys. + */ + mvebu_a3700_comphy_power_off(phy); } provider = devm_of_phy_provider_register(&pdev->dev, mvebu_a3700_comphy_xlate); + return PTR_ERR_OR_ZERO(provider); } @@ -325,5 +1366,7 @@ static struct platform_driver mvebu_a3700_comphy_driver = { module_platform_driver(mvebu_a3700_comphy_driver); MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>"); +MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); +MODULE_AUTHOR("Marek Behún <kabel@kernel.org>"); MODULE_DESCRIPTION("Common PHY driver for A3700"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c index 23bc3bf5c4c0..8834436bc9db 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -72,7 +72,7 @@ struct mvebu_a3700_utmi_caps { * struct mvebu_a3700_utmi - PHY driver data * * @regs: PHY registers - * @usb_mis: Regmap with USB miscellaneous registers including PHY ones + * @usb_misc: Regmap with USB miscellaneous registers including PHY ones * @caps: PHY capabilities * @phy: PHY handle */ diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index e41367f36ee1..34672e868a1e 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -123,7 +123,6 @@ #define COMPHY_SIP_POWER_ON 0x82000001 #define COMPHY_SIP_POWER_OFF 0x82000002 -#define COMPHY_FW_NOT_SUPPORTED (-1) /* * A lane is described by the following bitfields: @@ -142,6 +141,7 @@ #define COMPHY_FW_SPEED_1250 0 #define COMPHY_FW_SPEED_3125 2 #define COMPHY_FW_SPEED_5000 3 +#define COMPHY_FW_SPEED_515625 4 #define COMPHY_FW_SPEED_103125 6 #define COMPHY_FW_PORT_OFFSET 8 #define COMPHY_FW_PORT_MASK GENMASK(11, 8) @@ -168,7 +168,7 @@ #define COMPHY_FW_MODE_SATA 0x1 #define COMPHY_FW_MODE_SGMII 0x2 /* SGMII 1G */ -#define COMPHY_FW_MODE_HS_SGMII 0x3 /* SGMII 2.5G */ +#define COMPHY_FW_MODE_2500BASEX 0x3 /* 2500BASE-X */ #define COMPHY_FW_MODE_USB3H 0x4 #define COMPHY_FW_MODE_USB3D 0x5 #define COMPHY_FW_MODE_PCIE 0x6 @@ -208,7 +208,7 @@ static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = { /* lane 0 */ GEN_CONF(0, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), ETH_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), - ETH_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX), GEN_CONF(0, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), /* lane 1 */ GEN_CONF(1, 0, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), @@ -216,11 +216,12 @@ static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = { GEN_CONF(1, 0, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), GEN_CONF(1, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), ETH_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), - ETH_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX), /* lane 2 */ ETH_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), - ETH_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX), ETH_CONF(2, 0, PHY_INTERFACE_MODE_RXAUI, 0x1, COMPHY_FW_MODE_RXAUI), + ETH_CONF(2, 0, PHY_INTERFACE_MODE_5GBASER, 0x1, COMPHY_FW_MODE_XFI), ETH_CONF(2, 0, PHY_INTERFACE_MODE_10GBASER, 0x1, COMPHY_FW_MODE_XFI), GEN_CONF(2, 0, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), GEN_CONF(2, 0, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), @@ -228,26 +229,28 @@ static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = { /* lane 3 */ GEN_CONF(3, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), ETH_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII), - ETH_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_2500BASEX), ETH_CONF(3, 1, PHY_INTERFACE_MODE_RXAUI, 0x1, COMPHY_FW_MODE_RXAUI), GEN_CONF(3, 1, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), GEN_CONF(3, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), /* lane 4 */ ETH_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII), - ETH_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_2500BASEX), + ETH_CONF(4, 0, PHY_INTERFACE_MODE_5GBASER, 0x2, COMPHY_FW_MODE_XFI), ETH_CONF(4, 0, PHY_INTERFACE_MODE_10GBASER, 0x2, COMPHY_FW_MODE_XFI), ETH_CONF(4, 0, PHY_INTERFACE_MODE_RXAUI, 0x2, COMPHY_FW_MODE_RXAUI), GEN_CONF(4, 0, PHY_MODE_USB_DEVICE_SS, COMPHY_FW_MODE_USB3D), GEN_CONF(4, 1, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), GEN_CONF(4, 1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), ETH_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), - ETH_CONF(4, 1, PHY_INTERFACE_MODE_2500BASEX, -1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(4, 1, PHY_INTERFACE_MODE_2500BASEX, -1, COMPHY_FW_MODE_2500BASEX), + ETH_CONF(4, 1, PHY_INTERFACE_MODE_5GBASER, -1, COMPHY_FW_MODE_XFI), ETH_CONF(4, 1, PHY_INTERFACE_MODE_10GBASER, -1, COMPHY_FW_MODE_XFI), /* lane 5 */ ETH_CONF(5, 1, PHY_INTERFACE_MODE_RXAUI, 0x2, COMPHY_FW_MODE_RXAUI), GEN_CONF(5, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), ETH_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), - ETH_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_2500BASEX), GEN_CONF(5, 2, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), }; @@ -273,10 +276,19 @@ static int mvebu_comphy_smc(unsigned long function, unsigned long phys, unsigned long lane, unsigned long mode) { struct arm_smccc_res res; + s32 ret; arm_smccc_smc(function, phys, lane, mode, 0, 0, 0, 0, &res); + ret = res.a0; - return res.a0; + switch (ret) { + case SMCCC_RET_SUCCESS: + return 0; + case SMCCC_RET_NOT_SUPPORTED: + return -EOPNOTSUPP; + default: + return -EINVAL; + } } static int mvebu_comphy_get_mode(bool fw_mode, int lane, int port, @@ -782,6 +794,11 @@ static int mvebu_comphy_power_on(struct phy *phy) lane->id); fw_speed = COMPHY_FW_SPEED_3125; break; + case PHY_INTERFACE_MODE_5GBASER: + dev_dbg(priv->dev, "set lane %d to 5GBASE-R mode\n", + lane->id); + fw_speed = COMPHY_FW_SPEED_515625; + break; case PHY_INTERFACE_MODE_10GBASER: dev_dbg(priv->dev, "set lane %d to 10GBASE-R mode\n", lane->id); @@ -819,7 +836,7 @@ static int mvebu_comphy_power_on(struct phy *phy) if (!ret) return ret; - if (ret == COMPHY_FW_NOT_SUPPORTED) + if (ret == -EOPNOTSUPP) dev_err(priv->dev, "unsupported SMC call, try updating your firmware\n"); diff --git a/drivers/phy/marvell/phy-mvebu-cp110-utmi.c b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c new file mode 100644 index 000000000000..aa27c7994610 --- /dev/null +++ b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Marvell + * + * Authors: + * Konstantin Porotchkin <kostap@marvell.com> + * + * Marvell CP110 UTMI PHY driver + */ + +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/usb/of.h> +#include <linux/usb/otg.h> + +#define UTMI_PHY_PORTS 2 + +/* CP110 UTMI register macro definetions */ +#define SYSCON_USB_CFG_REG 0x420 +#define USB_CFG_DEVICE_EN_MASK BIT(0) +#define USB_CFG_DEVICE_MUX_OFFSET 1 +#define USB_CFG_DEVICE_MUX_MASK BIT(1) +#define USB_CFG_PLL_MASK BIT(25) + +#define SYSCON_UTMI_CFG_REG(id) (0x440 + (id) * 4) +#define UTMI_PHY_CFG_PU_MASK BIT(5) + +#define UTMI_PLL_CTRL_REG 0x0 +#define PLL_REFDIV_OFFSET 0 +#define PLL_REFDIV_MASK GENMASK(6, 0) +#define PLL_REFDIV_VAL 0x5 +#define PLL_FBDIV_OFFSET 16 +#define PLL_FBDIV_MASK GENMASK(24, 16) +#define PLL_FBDIV_VAL 0x60 +#define PLL_SEL_LPFR_MASK GENMASK(29, 28) +#define PLL_RDY BIT(31) +#define UTMI_CAL_CTRL_REG 0x8 +#define IMPCAL_VTH_OFFSET 8 +#define IMPCAL_VTH_MASK GENMASK(10, 8) +#define IMPCAL_VTH_VAL 0x7 +#define IMPCAL_DONE BIT(23) +#define PLLCAL_DONE BIT(31) +#define UTMI_TX_CH_CTRL_REG 0xC +#define DRV_EN_LS_OFFSET 12 +#define DRV_EN_LS_MASK GENMASK(15, 12) +#define IMP_SEL_LS_OFFSET 16 +#define IMP_SEL_LS_MASK GENMASK(19, 16) +#define TX_AMP_OFFSET 20 +#define TX_AMP_MASK GENMASK(22, 20) +#define TX_AMP_VAL 0x4 +#define UTMI_RX_CH_CTRL0_REG 0x14 +#define SQ_DET_EN BIT(15) +#define SQ_ANA_DTC_SEL BIT(28) +#define UTMI_RX_CH_CTRL1_REG 0x18 +#define SQ_AMP_CAL_OFFSET 0 +#define SQ_AMP_CAL_MASK GENMASK(2, 0) +#define SQ_AMP_CAL_VAL 1 +#define SQ_AMP_CAL_EN BIT(3) +#define UTMI_CTRL_STATUS0_REG 0x24 +#define SUSPENDM BIT(22) +#define TEST_SEL BIT(25) +#define UTMI_CHGDTC_CTRL_REG 0x38 +#define VDAT_OFFSET 8 +#define VDAT_MASK GENMASK(9, 8) +#define VDAT_VAL 1 +#define VSRC_OFFSET 10 +#define VSRC_MASK GENMASK(11, 10) +#define VSRC_VAL 1 + +#define PLL_LOCK_DELAY_US 10000 +#define PLL_LOCK_TIMEOUT_US 1000000 + +#define PORT_REGS(p) ((p)->priv->regs + (p)->id * 0x1000) + +/** + * struct mvebu_cp110_utmi - PHY driver data + * + * @regs: PHY registers + * @syscon: Regmap with system controller registers + * @dev: device driver handle + * @ops: phy ops + */ +struct mvebu_cp110_utmi { + void __iomem *regs; + struct regmap *syscon; + struct device *dev; + const struct phy_ops *ops; +}; + +/** + * struct mvebu_cp110_utmi_port - PHY port data + * + * @priv: PHY driver data + * @id: PHY port ID + * @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL + */ +struct mvebu_cp110_utmi_port { + struct mvebu_cp110_utmi *priv; + u32 id; + enum usb_dr_mode dr_mode; +}; + +static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port) +{ + u32 reg; + + /* + * Setup PLL. + * The reference clock is the frequency of quartz resonator + * connected to pins REFCLK_XIN and REFCLK_XOUT of the SoC. + * Register init values are matching the 40MHz default clock. + * The crystal used for all platform boards is now 25MHz. + * See the functional specification for details. + */ + reg = readl(PORT_REGS(port) + UTMI_PLL_CTRL_REG); + reg &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK | PLL_SEL_LPFR_MASK); + reg |= (PLL_REFDIV_VAL << PLL_REFDIV_OFFSET) | + (PLL_FBDIV_VAL << PLL_FBDIV_OFFSET); + writel(reg, PORT_REGS(port) + UTMI_PLL_CTRL_REG); + + /* Impedance Calibration Threshold Setting */ + reg = readl(PORT_REGS(port) + UTMI_CAL_CTRL_REG); + reg &= ~IMPCAL_VTH_MASK; + reg |= IMPCAL_VTH_VAL << IMPCAL_VTH_OFFSET; + writel(reg, PORT_REGS(port) + UTMI_CAL_CTRL_REG); + + /* Set LS TX driver strength coarse control */ + reg = readl(PORT_REGS(port) + UTMI_TX_CH_CTRL_REG); + reg &= ~TX_AMP_MASK; + reg |= TX_AMP_VAL << TX_AMP_OFFSET; + writel(reg, PORT_REGS(port) + UTMI_TX_CH_CTRL_REG); + + /* Disable SQ and enable analog squelch detect */ + reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG); + reg &= ~SQ_DET_EN; + reg |= SQ_ANA_DTC_SEL; + writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG); + + /* + * Set External squelch calibration number and + * enable the External squelch calibration + */ + reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG); + reg &= ~SQ_AMP_CAL_MASK; + reg |= (SQ_AMP_CAL_VAL << SQ_AMP_CAL_OFFSET) | SQ_AMP_CAL_EN; + writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG); + + /* + * Set Control VDAT Reference Voltage - 0.325V and + * Control VSRC Reference Voltage - 0.6V + */ + reg = readl(PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG); + reg &= ~(VDAT_MASK | VSRC_MASK); + reg |= (VDAT_VAL << VDAT_OFFSET) | (VSRC_VAL << VSRC_OFFSET); + writel(reg, PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG); +} + +static int mvebu_cp110_utmi_phy_power_off(struct phy *phy) +{ + struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy); + struct mvebu_cp110_utmi *utmi = port->priv; + int i; + + /* Power down UTMI PHY port */ + regmap_clear_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id), + UTMI_PHY_CFG_PU_MASK); + + for (i = 0; i < UTMI_PHY_PORTS; i++) { + int test = regmap_test_bits(utmi->syscon, + SYSCON_UTMI_CFG_REG(i), + UTMI_PHY_CFG_PU_MASK); + /* skip PLL shutdown if there are active UTMI PHY ports */ + if (test != 0) + return 0; + } + + /* PLL Power down if all UTMI PHYs are down */ + regmap_clear_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK); + + return 0; +} + +static int mvebu_cp110_utmi_phy_power_on(struct phy *phy) +{ + struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy); + struct mvebu_cp110_utmi *utmi = port->priv; + struct device *dev = &phy->dev; + int ret; + u32 reg; + + /* It is necessary to power off UTMI before configuration */ + ret = mvebu_cp110_utmi_phy_power_off(phy); + if (ret) { + dev_err(dev, "UTMI power OFF before power ON failed\n"); + return ret; + } + + /* + * If UTMI port is connected to USB Device controller, + * configure the USB MUX prior to UTMI PHY initialization. + * The single USB device controller can be connected + * to UTMI0 or to UTMI1 PHY port, but not to both. + */ + if (port->dr_mode == USB_DR_MODE_PERIPHERAL) { + regmap_update_bits(utmi->syscon, SYSCON_USB_CFG_REG, + USB_CFG_DEVICE_EN_MASK | USB_CFG_DEVICE_MUX_MASK, + USB_CFG_DEVICE_EN_MASK | + (port->id << USB_CFG_DEVICE_MUX_OFFSET)); + } + + /* Set Test suspendm mode and enable Test UTMI select */ + reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); + reg |= SUSPENDM | TEST_SEL; + writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); + + /* Wait for UTMI power down */ + mdelay(1); + + /* PHY port setup first */ + mvebu_cp110_utmi_port_setup(port); + + /* Power UP UTMI PHY */ + regmap_set_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id), + UTMI_PHY_CFG_PU_MASK); + + /* Disable Test UTMI select */ + reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); + reg &= ~TEST_SEL; + writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); + + /* Wait for impedance calibration */ + ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg, + reg & IMPCAL_DONE, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + dev_err(dev, "Failed to end UTMI impedance calibration\n"); + return ret; + } + + /* Wait for PLL calibration */ + ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg, + reg & PLLCAL_DONE, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + dev_err(dev, "Failed to end UTMI PLL calibration\n"); + return ret; + } + + /* Wait for PLL ready */ + ret = readl_poll_timeout(PORT_REGS(port) + UTMI_PLL_CTRL_REG, reg, + reg & PLL_RDY, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + dev_err(dev, "PLL is not ready\n"); + return ret; + } + + /* PLL Power up */ + regmap_set_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK); + + return 0; +} + +static const struct phy_ops mvebu_cp110_utmi_phy_ops = { + .power_on = mvebu_cp110_utmi_phy_power_on, + .power_off = mvebu_cp110_utmi_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mvebu_cp110_utmi_of_match[] = { + { .compatible = "marvell,cp110-utmi-phy" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mvebu_cp110_utmi_of_match); + +static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mvebu_cp110_utmi *utmi; + struct phy_provider *provider; + struct device_node *child; + u32 usb_devices = 0; + + utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return -ENOMEM; + + utmi->dev = dev; + + /* Get system controller region */ + utmi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, + "marvell,system-controller"); + if (IS_ERR(utmi->syscon)) { + dev_err(dev, "Missing UTMI system controller\n"); + return PTR_ERR(utmi->syscon); + } + + /* Get UTMI memory region */ + utmi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(utmi->regs)) + return PTR_ERR(utmi->regs); + + for_each_available_child_of_node(dev->of_node, child) { + struct mvebu_cp110_utmi_port *port; + struct phy *phy; + int ret; + u32 port_id; + + ret = of_property_read_u32(child, "reg", &port_id); + if ((ret < 0) || (port_id >= UTMI_PHY_PORTS)) { + dev_err(dev, + "invalid 'reg' property on child %pOF\n", + child); + continue; + } + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) { + of_node_put(child); + return -ENOMEM; + } + + port->dr_mode = of_usb_get_dr_mode_by_phy(child, -1); + if ((port->dr_mode != USB_DR_MODE_HOST) && + (port->dr_mode != USB_DR_MODE_PERIPHERAL)) { + dev_err(&pdev->dev, + "Missing dual role setting of the port%d, will use HOST mode\n", + port_id); + port->dr_mode = USB_DR_MODE_HOST; + } + + if (port->dr_mode == USB_DR_MODE_PERIPHERAL) { + usb_devices++; + if (usb_devices > 1) { + dev_err(dev, + "Single USB device allowed! Port%d will use HOST mode\n", + port_id); + port->dr_mode = USB_DR_MODE_HOST; + } + } + + /* Retrieve PHY capabilities */ + utmi->ops = &mvebu_cp110_utmi_phy_ops; + + /* Instantiate the PHY */ + phy = devm_phy_create(dev, child, utmi->ops); + if (IS_ERR(phy)) { + dev_err(dev, "Failed to create the UTMI PHY\n"); + of_node_put(child); + return PTR_ERR(phy); + } + + port->priv = utmi; + port->id = port_id; + phy_set_drvdata(phy, port); + + /* Ensure the PHY is powered off */ + mvebu_cp110_utmi_phy_power_off(phy); + } + + dev_set_drvdata(dev, utmi); + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static struct platform_driver mvebu_cp110_utmi_driver = { + .probe = mvebu_cp110_utmi_phy_probe, + .driver = { + .name = "mvebu-cp110-utmi-phy", + .of_match_table = mvebu_cp110_utmi_of_match, + }, +}; +module_platform_driver(mvebu_cp110_utmi_driver); + +MODULE_AUTHOR("Konstatin Porotchkin <kostap@marvell.com>"); +MODULE_DESCRIPTION("Marvell Armada CP110 UTMI PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c index 3c01b5dceaae..51a4646e2933 100644 --- a/drivers/phy/marvell/phy-mvebu-sata.c +++ b/drivers/phy/marvell/phy-mvebu-sata.c @@ -80,7 +80,6 @@ static const struct phy_ops phy_mvebu_sata_ops = { static int phy_mvebu_sata_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; - struct resource *res; struct priv *priv; struct phy *phy; @@ -88,8 +87,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/phy/marvell/phy-pxa-28nm-hsic.c b/drivers/phy/marvell/phy-pxa-28nm-hsic.c index ae8370af59c0..c5c100563f55 100644 --- a/drivers/phy/marvell/phy-pxa-28nm-hsic.c +++ b/drivers/phy/marvell/phy-pxa-28nm-hsic.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/module.h> @@ -44,15 +45,12 @@ struct mv_hsic_phy { struct clk *clk; }; -static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms) { - timeout += jiffies; - while (time_is_after_eq_jiffies(timeout)) { - if ((readl(reg) & mask) == mask) - return true; - msleep(1); - } - return false; + u32 val; + + return readl_poll_timeout(reg, val, ((val & mask) == mask), + 1000, 1000 * ms); } static int mv_hsic_phy_init(struct phy *phy) @@ -60,6 +58,7 @@ static int mv_hsic_phy_init(struct phy *phy) struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); struct platform_device *pdev = mv_phy->pdev; void __iomem *base = mv_phy->base; + int ret; clk_prepare_enable(mv_phy->clk); @@ -75,14 +74,14 @@ static int mv_hsic_phy_init(struct phy *phy) base + PHY_28NM_HSIC_PLL_CTRL2); /* Make sure PHY PLL is locked */ - if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, - PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { + ret = wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, + PHY_28NM_HSIC_H2S_PLL_LOCK, 100); + if (ret) { dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); clk_disable_unprepare(mv_phy->clk); - return -ETIMEDOUT; } - return 0; + return ret; } static int mv_hsic_phy_power_on(struct phy *phy) @@ -91,6 +90,7 @@ static int mv_hsic_phy_power_on(struct phy *phy) struct platform_device *pdev = mv_phy->pdev; void __iomem *base = mv_phy->base; u32 reg; + int ret; reg = readl(base + PHY_28NM_HSIC_CTRL); /* Avoid SE0 state when resume for some device will take it as reset */ @@ -108,20 +108,20 @@ static int mv_hsic_phy_power_on(struct phy *phy) */ /* Make sure PHY Calibration is ready */ - if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, - PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { + ret = wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, + PHY_28NM_HSIC_H2S_IMPCAL_DONE, 100); + if (ret) { dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); - return -ETIMEDOUT; + return ret; } /* Waiting for HSIC connect int*/ - if (!wait_for_reg(base + PHY_28NM_HSIC_INT, - PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { + ret = wait_for_reg(base + PHY_28NM_HSIC_INT, + PHY_28NM_HSIC_CONNECT_INT, 200); + if (ret) dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); - return -ETIMEDOUT; - } - return 0; + return ret; } static int mv_hsic_phy_power_off(struct phy *phy) @@ -162,7 +162,6 @@ static int mv_hsic_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct mv_hsic_phy *mv_phy; - struct resource *r; mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); if (!mv_phy) @@ -176,8 +175,7 @@ static int mv_hsic_phy_probe(struct platform_device *pdev) return PTR_ERR(mv_phy->clk); } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + mv_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mv_phy->base)) return PTR_ERR(mv_phy->base); diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c index 9fd881787fa6..0b390b9d2ae1 100644 --- a/drivers/phy/marvell/phy-pxa-28nm-usb2.c +++ b/drivers/phy/marvell/phy-pxa-28nm-usb2.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/module.h> @@ -138,15 +139,12 @@ struct mv_usb2_phy { struct clk *clk; }; -static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms) { - timeout += jiffies; - while (time_is_after_eq_jiffies(timeout)) { - if ((readl(reg) & mask) == mask) - return true; - msleep(1); - } - return false; + u32 val; + + return readl_poll_timeout(reg, val, ((val & mask) == mask), + 1000, 1000 * ms); } static int mv_usb2_phy_28nm_init(struct phy *phy) @@ -208,24 +206,23 @@ static int mv_usb2_phy_28nm_init(struct phy *phy) */ /* Make sure PHY Calibration is ready */ - if (!wait_for_reg(base + PHY_28NM_CAL_REG, - PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, - HZ / 10)) { + ret = wait_for_reg(base + PHY_28NM_CAL_REG, + PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, + 100); + if (ret) { dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); - ret = -ETIMEDOUT; goto err_clk; } - if (!wait_for_reg(base + PHY_28NM_RX_REG1, - PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { + ret = wait_for_reg(base + PHY_28NM_RX_REG1, + PHY_28NM_RX_SQCAL_DONE, 100); + if (ret) { dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); - ret = -ETIMEDOUT; goto err_clk; } /* Make sure PHY PLL is ready */ - if (!wait_for_reg(base + PHY_28NM_PLL_REG0, - PHY_28NM_PLL_READY, HZ / 10)) { + ret = wait_for_reg(base + PHY_28NM_PLL_REG0, PHY_28NM_PLL_READY, 100); + if (ret) { dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); - ret = -ETIMEDOUT; goto err_clk; } @@ -297,7 +294,6 @@ static int mv_usb2_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct mv_usb2_phy *mv_phy; - struct resource *r; mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); if (!mv_phy) @@ -311,8 +307,7 @@ static int mv_usb2_phy_probe(struct platform_device *pdev) return PTR_ERR(mv_phy->clk); } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + mv_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mv_phy->base)) return PTR_ERR(mv_phy->base); diff --git a/drivers/phy/marvell/phy-pxa-usb.c b/drivers/phy/marvell/phy-pxa-usb.c index 87ff7550b912..ffe889893ff4 100644 --- a/drivers/phy/marvell/phy-pxa-usb.c +++ b/drivers/phy/marvell/phy-pxa-usb.c @@ -286,7 +286,6 @@ MODULE_DEVICE_TABLE(of, pxa_usb_phy_of_match); static int pxa_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *resource; struct pxa_usb_phy *pxa_usb_phy; struct phy_provider *provider; const struct of_device_id *of_id; @@ -301,8 +300,7 @@ static int pxa_usb_phy_probe(struct platform_device *pdev) else pxa_usb_phy->version = PXA_USB_PHY_MMP2; - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pxa_usb_phy->base = devm_ioremap_resource(dev, resource); + pxa_usb_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pxa_usb_phy->base)) { dev_err(dev, "failed to remap PHY regs\n"); return PTR_ERR(pxa_usb_phy->base); diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index dee757c957f2..3125ecb5d119 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -2,17 +2,29 @@ # # Phy drivers for Mediatek devices # +config PHY_MTK_PCIE + tristate "MediaTek PCIe-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Say 'Y' here to add support for MediaTek PCIe PHY driver. + This driver create the basic PHY instance and provides initialize + callback for PCIe GEN3 port, it supports software efuse + initialization. + config PHY_MTK_TPHY tristate "MediaTek T-PHY Driver" depends on ARCH_MEDIATEK || COMPILE_TEST - depends on OF + depends on OF && OF_ADDRESS + depends on HAS_IOMEM select GENERIC_PHY help Say 'Y' here to add support for MediaTek T-PHY driver, it supports multiple 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, + multi-ports is first version, otherwise is second version, so you can easily distinguish them by banks layout. config PHY_MTK_UFS @@ -29,9 +41,36 @@ config PHY_MTK_UFS config PHY_MTK_XSPHY tristate "MediaTek XS-PHY Driver" depends on ARCH_MEDIATEK || COMPILE_TEST - depends on OF + depends on OF && OF_ADDRESS + depends on HAS_IOMEM select GENERIC_PHY help Enable this to support the SuperSpeedPlus XS-PHY transceiver for USB3.1 GEN2 controllers on MediaTek chips. The driver supports multiple USB2.0, USB3.1 GEN2 ports. + +config PHY_MTK_HDMI + tristate "MediaTek HDMI-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on COMMON_CLK + depends on OF + select GENERIC_PHY + help + Support HDMI PHY for Mediatek SoCs. + +config PHY_MTK_MIPI_DSI + tristate "MediaTek MIPI-DSI Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on COMMON_CLK + depends on OF + select GENERIC_PHY + help + Support MIPI DSI for Mediatek SoCs. + +config PHY_MTK_DP + tristate "MediaTek DP-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Support DisplayPort PHY for MediaTek SoCs. diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 08a8e6a97b1e..fb1f8edaffa7 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -3,6 +3,18 @@ # Makefile for the phy drivers. # +obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o +obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o + +phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o +phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o +phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt8173.o +obj-$(CONFIG_PHY_MTK_HDMI) += phy-mtk-hdmi-drv.o + +phy-mtk-mipi-dsi-drv-y := phy-mtk-mipi-dsi.o +phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8173.o +phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8183.o +obj-$(CONFIG_PHY_MTK_MIPI_DSI) += phy-mtk-mipi-dsi-drv.o diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c new file mode 100644 index 000000000000..232fd3f1ff1b --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-dp.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek DisplayPort PHY driver + * + * Copyright (c) 2022, BayLibre Inc. + * Copyright (c) 2022, MediaTek Inc. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define PHY_OFFSET 0x1000 + +#define MTK_DP_PHY_DIG_PLL_CTL_1 (PHY_OFFSET + 0x14) +#define TPLL_SSC_EN BIT(3) + +#define MTK_DP_PHY_DIG_BIT_RATE (PHY_OFFSET + 0x3C) +#define BIT_RATE_RBR 0 +#define BIT_RATE_HBR 1 +#define BIT_RATE_HBR2 2 +#define BIT_RATE_HBR3 3 + +#define MTK_DP_PHY_DIG_SW_RST (PHY_OFFSET + 0x38) +#define DP_GLB_SW_RST_PHYD BIT(0) + +#define MTK_DP_LANE0_DRIVING_PARAM_3 (PHY_OFFSET + 0x138) +#define MTK_DP_LANE1_DRIVING_PARAM_3 (PHY_OFFSET + 0x238) +#define MTK_DP_LANE2_DRIVING_PARAM_3 (PHY_OFFSET + 0x338) +#define MTK_DP_LANE3_DRIVING_PARAM_3 (PHY_OFFSET + 0x438) +#define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4) +#define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12)) +#define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19) +#define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29) +#define DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT) + +#define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT GENMASK(4, 3) +#define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9) +#define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21)) +#define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29) +#define DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT) + +#define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5)) +#define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12) +#define DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \ + XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT) + +#define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0 +#define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10) +#define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19) +#define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28) +#define DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT) + +#define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT 0 +#define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9) +#define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18) +#define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0 +#define DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT) + +#define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3) +#define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0 +#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \ + XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT) + +struct mtk_dp_phy { + struct regmap *regs; +}; + +static int mtk_dp_phy_init(struct phy *phy) +{ + struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy); + static const u32 driving_params[] = { + DRIVING_PARAM_3_DEFAULT, + DRIVING_PARAM_4_DEFAULT, + DRIVING_PARAM_5_DEFAULT, + DRIVING_PARAM_6_DEFAULT, + DRIVING_PARAM_7_DEFAULT, + DRIVING_PARAM_8_DEFAULT + }; + + regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3, + driving_params, ARRAY_SIZE(driving_params)); + regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3, + driving_params, ARRAY_SIZE(driving_params)); + regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3, + driving_params, ARRAY_SIZE(driving_params)); + regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3, + driving_params, ARRAY_SIZE(driving_params)); + + return 0; +} + +static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy); + u32 val; + + if (opts->dp.set_rate) { + switch (opts->dp.link_rate) { + default: + dev_err(&phy->dev, + "Implementation error, unknown linkrate %x\n", + opts->dp.link_rate); + return -EINVAL; + case 1620: + val = BIT_RATE_RBR; + break; + case 2700: + val = BIT_RATE_HBR; + break; + case 5400: + val = BIT_RATE_HBR2; + break; + case 8100: + val = BIT_RATE_HBR3; + break; + } + regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val); + } + + regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1, + TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0); + + return 0; +} + +static int mtk_dp_phy_reset(struct phy *phy) +{ + struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy); + + regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST, + DP_GLB_SW_RST_PHYD, 0); + usleep_range(50, 200); + regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST, + DP_GLB_SW_RST_PHYD, 1); + + return 0; +} + +static const struct phy_ops mtk_dp_phy_dev_ops = { + .init = mtk_dp_phy_init, + .configure = mtk_dp_phy_configure, + .reset = mtk_dp_phy_reset, + .owner = THIS_MODULE, +}; + +static int mtk_dp_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_dp_phy *dp_phy; + struct phy *phy; + struct regmap *regs; + + regs = *(struct regmap **)dev->platform_data; + if (!regs) + return dev_err_probe(dev, EINVAL, + "No data passed, requires struct regmap**\n"); + + dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL); + if (!dp_phy) + return -ENOMEM; + + dp_phy->regs = regs; + phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), + "Failed to create DP PHY\n"); + + phy_set_drvdata(phy, dp_phy); + if (!dev->of_node) + phy_create_lookup(phy, "dp", dev_name(dev)); + + return 0; +} + +static struct platform_driver mtk_dp_phy_driver = { + .probe = mtk_dp_phy_probe, + .driver = { + .name = "mediatek-dp-phy", + }, +}; +module_platform_driver(mtk_dp_phy_driver); + +MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>"); +MODULE_DESCRIPTION("MediaTek DP PHY Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c new file mode 100644 index 000000000000..e51b2d13eab4 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunhui Dai <chunhui.dai@mediatek.com> + */ + +#include "phy-mtk-hdmi.h" +#include "phy-mtk-io.h" + +#define HDMI_CON0 0x00 +#define RG_HDMITX_DRV_IBIAS_MASK GENMASK(5, 0) +#define RG_HDMITX_EN_SER_MASK GENMASK(15, 12) +#define RG_HDMITX_EN_SLDO_MASK GENMASK(19, 16) +#define RG_HDMITX_EN_PRED_MASK GENMASK(23, 20) +#define RG_HDMITX_EN_IMP_MASK GENMASK(27, 24) +#define RG_HDMITX_EN_DRV_MASK GENMASK(31, 28) + +#define HDMI_CON1 0x04 +#define RG_HDMITX_PRED_IBIAS_MASK GENMASK(21, 18) +#define RG_HDMITX_PRED_IMP BIT(22) +#define RG_HDMITX_DRV_IMP_MASK GENMASK(31, 26) + +#define HDMI_CON2 0x08 +#define RG_HDMITX_EN_TX_CKLDO BIT(0) +#define RG_HDMITX_EN_TX_POSDIV BIT(1) +#define RG_HDMITX_TX_POSDIV_MASK GENMASK(4, 3) +#define RG_HDMITX_EN_MBIAS BIT(6) +#define RG_HDMITX_MBIAS_LPF_EN BIT(7) + +#define HDMI_CON4 0x10 +#define RG_HDMITX_RESERVE_MASK GENMASK(31, 0) + +#define HDMI_CON6 0x18 +#define RG_HTPLL_BR_MASK GENMASK(1, 0) +#define RG_HTPLL_BC_MASK GENMASK(3, 2) +#define RG_HTPLL_BP_MASK GENMASK(7, 4) +#define RG_HTPLL_IR_MASK GENMASK(11, 8) +#define RG_HTPLL_IC_MASK GENMASK(15, 12) +#define RG_HTPLL_POSDIV_MASK GENMASK(17, 16) +#define RG_HTPLL_PREDIV_MASK GENMASK(19, 18) +#define RG_HTPLL_FBKSEL_MASK GENMASK(21, 20) +#define RG_HTPLL_RLH_EN BIT(22) +#define RG_HTPLL_FBKDIV_MASK GENMASK(30, 24) +#define RG_HTPLL_EN BIT(31) + +#define HDMI_CON7 0x1c +#define RG_HTPLL_AUTOK_EN BIT(23) +#define RG_HTPLL_DIVEN_MASK GENMASK(30, 28) + +static int mtk_hdmi_pll_prepare(struct clk_hw *hw) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + void __iomem *base = hdmi_phy->regs; + + mtk_phy_set_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS); + usleep_range(80, 100); + mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_EN); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + usleep_range(80, 100); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + usleep_range(80, 100); + return 0; +} + +static void mtk_hdmi_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + void __iomem *base = hdmi_phy->regs; + + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + usleep_range(80, 100); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_EN); + usleep_range(80, 100); + mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_phy_clear_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN); + usleep_range(80, 100); +} + +static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + void __iomem *base = hdmi_phy->regs; + u32 pos_div; + + if (rate <= 64000000) + pos_div = 3; + else if (rate <= 128000000) + pos_div = 2; + else + pos_div = 1; + + mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_PREDIV_MASK); + mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_POSDIV); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_IC_MASK, 0x1); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_IR_MASK, 0x1); + mtk_phy_update_field(base + HDMI_CON2, RG_HDMITX_TX_POSDIV_MASK, pos_div); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_FBKSEL_MASK, 1); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_FBKDIV_MASK, 19); + mtk_phy_update_field(base + HDMI_CON7, RG_HTPLL_DIVEN_MASK, 0x2); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_BP_MASK, 0xc); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_BC_MASK, 0x2); + mtk_phy_update_field(base + HDMI_CON6, RG_HTPLL_BR_MASK, 0x1); + + mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PRED_IMP); + mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_PRED_IBIAS_MASK, 0x3); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_IMP_MASK); + mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_DRV_IMP_MASK, 0x28); + mtk_phy_update_field(base + HDMI_CON4, RG_HDMITX_RESERVE_MASK, 0x28); + mtk_phy_update_field(base + HDMI_CON0, RG_HDMITX_DRV_IBIAS_MASK, 0xa); + return 0; +} + +static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + unsigned long out_rate, val; + u32 tmp; + + tmp = readl(hdmi_phy->regs + HDMI_CON6); + val = FIELD_GET(RG_HTPLL_PREDIV_MASK, tmp); + switch (val) { + case 0x00: + out_rate = parent_rate; + break; + case 0x01: + out_rate = parent_rate / 2; + break; + default: + out_rate = parent_rate / 4; + break; + } + + val = FIELD_GET(RG_HTPLL_FBKDIV_MASK, tmp); + out_rate *= (val + 1) * 2; + + tmp = readl(hdmi_phy->regs + HDMI_CON2); + val = FIELD_GET(RG_HDMITX_TX_POSDIV_MASK, tmp); + out_rate >>= val; + + if (tmp & RG_HDMITX_EN_TX_POSDIV) + out_rate /= 5; + + return out_rate; +} + +static const struct clk_ops mtk_hdmi_phy_pll_ops = { + .prepare = mtk_hdmi_pll_prepare, + .unprepare = mtk_hdmi_pll_unprepare, + .set_rate = mtk_hdmi_pll_set_rate, + .round_rate = mtk_hdmi_pll_round_rate, + .recalc_rate = mtk_hdmi_pll_recalc_rate, +}; + +static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy) +{ + void __iomem *base = hdmi_phy->regs; + + mtk_phy_set_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS); + usleep_range(80, 100); + mtk_phy_set_bits(base + HDMI_CON6, RG_HTPLL_EN); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + usleep_range(80, 100); + mtk_phy_set_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + usleep_range(80, 100); +} + +static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) +{ + void __iomem *base = hdmi_phy->regs; + + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + usleep_range(80, 100); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_EN); + usleep_range(80, 100); + mtk_phy_clear_bits(base + HDMI_CON2, RG_HDMITX_EN_MBIAS); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_phy_clear_bits(base + HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_phy_clear_bits(base + HDMI_CON7, RG_HTPLL_AUTOK_EN); + usleep_range(80, 100); +} + +struct mtk_hdmi_phy_conf mtk_hdmi_phy_2701_conf = { + .flags = CLK_SET_RATE_GATE, + .pll_default_off = true, + .hdmi_phy_clk_ops = &mtk_hdmi_phy_pll_ops, + .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds, + .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds, +}; + +MODULE_AUTHOR("Chunhui Dai <chunhui.dai@mediatek.com>"); +MODULE_DESCRIPTION("MediaTek HDMI PHY Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c new file mode 100644 index 000000000000..d04758396046 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Jie Qiu <jie.qiu@mediatek.com> + */ + +#include "phy-mtk-hdmi.h" +#include "phy-mtk-io.h" + +#define HDMI_CON0 0x00 +#define RG_HDMITX_PLL_EN BIT(31) +#define RG_HDMITX_PLL_FBKDIV GENMASK(30, 24) +#define RG_HDMITX_PLL_FBKSEL GENMASK(23, 22) +#define RG_HDMITX_PLL_PREDIV GENMASK(21, 20) +#define RG_HDMITX_PLL_POSDIV GENMASK(19, 18) +#define RG_HDMITX_PLL_RST_DLY GENMASK(17, 16) +#define RG_HDMITX_PLL_IR GENMASK(15, 12) +#define RG_HDMITX_PLL_IC GENMASK(11, 8) +#define RG_HDMITX_PLL_BP GENMASK(7, 4) +#define RG_HDMITX_PLL_BR GENMASK(3, 2) +#define RG_HDMITX_PLL_BC GENMASK(1, 0) +#define HDMI_CON1 0x04 +#define RG_HDMITX_PLL_DIVEN GENMASK(31, 29) +#define RG_HDMITX_PLL_AUTOK_EN BIT(28) +#define RG_HDMITX_PLL_AUTOK_KF GENMASK(27, 26) +#define RG_HDMITX_PLL_AUTOK_KS GENMASK(25, 24) +#define RG_HDMITX_PLL_AUTOK_LOAD BIT(23) +#define RG_HDMITX_PLL_BAND GENMASK(21, 16) +#define RG_HDMITX_PLL_REF_SEL BIT(15) +#define RG_HDMITX_PLL_BIAS_EN BIT(14) +#define RG_HDMITX_PLL_BIAS_LPF_EN BIT(13) +#define RG_HDMITX_PLL_TXDIV_EN BIT(12) +#define RG_HDMITX_PLL_TXDIV GENMASK(11, 10) +#define RG_HDMITX_PLL_LVROD_EN BIT(9) +#define RG_HDMITX_PLL_MONVC_EN BIT(8) +#define RG_HDMITX_PLL_MONCK_EN BIT(7) +#define RG_HDMITX_PLL_MONREF_EN BIT(6) +#define RG_HDMITX_PLL_TST_EN BIT(5) +#define RG_HDMITX_PLL_TST_CK_EN BIT(4) +#define RG_HDMITX_PLL_TST_SEL GENMASK(3, 0) +#define HDMI_CON2 0x08 +#define RGS_HDMITX_PLL_AUTOK_BAND GENMASK(14, 8) +#define RGS_HDMITX_PLL_AUTOK_FAIL BIT(1) +#define RG_HDMITX_EN_TX_CKLDO BIT(0) +#define HDMI_CON3 0x0c +#define RG_HDMITX_SER_EN GENMASK(31, 28) +#define RG_HDMITX_PRD_EN GENMASK(27, 24) +#define RG_HDMITX_PRD_IMP_EN GENMASK(23, 20) +#define RG_HDMITX_DRV_EN GENMASK(19, 16) +#define RG_HDMITX_DRV_IMP_EN GENMASK(15, 12) +#define RG_HDMITX_MHLCK_FORCE BIT(10) +#define RG_HDMITX_MHLCK_PPIX_EN BIT(9) +#define RG_HDMITX_MHLCK_EN BIT(8) +#define RG_HDMITX_SER_DIN_SEL GENMASK(7, 4) +#define RG_HDMITX_SER_5T1_BIST_EN BIT(3) +#define RG_HDMITX_SER_BIST_TOG BIT(2) +#define RG_HDMITX_SER_DIN_TOG BIT(1) +#define RG_HDMITX_SER_CLKDIG_INV BIT(0) +#define HDMI_CON4 0x10 +#define RG_HDMITX_PRD_IBIAS_CLK GENMASK(27, 24) +#define RG_HDMITX_PRD_IBIAS_D2 GENMASK(19, 16) +#define RG_HDMITX_PRD_IBIAS_D1 GENMASK(11, 8) +#define RG_HDMITX_PRD_IBIAS_D0 GENMASK(3, 0) +#define HDMI_CON5 0x14 +#define RG_HDMITX_DRV_IBIAS_CLK GENMASK(29, 24) +#define RG_HDMITX_DRV_IBIAS_D2 GENMASK(21, 16) +#define RG_HDMITX_DRV_IBIAS_D1 GENMASK(13, 8) +#define RG_HDMITX_DRV_IBIAS_D0 GENMASK(5, 0) +#define HDMI_CON6 0x18 +#define RG_HDMITX_DRV_IMP_CLK GENMASK(29, 24) +#define RG_HDMITX_DRV_IMP_D2 GENMASK(21, 16) +#define RG_HDMITX_DRV_IMP_D1 GENMASK(13, 8) +#define RG_HDMITX_DRV_IMP_D0 GENMASK(5, 0) +#define HDMI_CON7 0x1c +#define RG_HDMITX_MHLCK_DRV_IBIAS GENMASK(31, 27) +#define RG_HDMITX_SER_DIN GENMASK(25, 16) +#define RG_HDMITX_CHLDC_TST GENMASK(15, 12) +#define RG_HDMITX_CHLCK_TST GENMASK(11, 8) +#define RG_HDMITX_RESERVE GENMASK(7, 0) +#define HDMI_CON8 0x20 +#define RGS_HDMITX_2T1_LEV GENMASK(19, 16) +#define RGS_HDMITX_2T1_EDG GENMASK(15, 12) +#define RGS_HDMITX_5T1_LEV GENMASK(11, 8) +#define RGS_HDMITX_5T1_EDG GENMASK(7, 4) +#define RGS_HDMITX_PLUG_TST BIT(0) + +static int mtk_hdmi_pll_prepare(struct clk_hw *hw) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + void __iomem *base = hdmi_phy->regs; + + mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_PLL_POSDIV); + mtk_phy_clear_bits(base + HDMI_CON3, RG_HDMITX_MHLCK_EN); + mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_EN); + usleep_range(100, 150); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_PLL_EN); + usleep_range(100, 150); + mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN); + mtk_phy_set_bits(base + HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN); + + return 0; +} + +static void mtk_hdmi_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + void __iomem *base = hdmi_phy->regs; + + mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN); + mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN); + usleep_range(100, 150); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_PLL_EN); + usleep_range(100, 150); + mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_BIAS_EN); + mtk_phy_clear_bits(base + HDMI_CON0, RG_HDMITX_PLL_POSDIV); + mtk_phy_clear_bits(base + HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN); + usleep_range(100, 150); +} + +static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + + hdmi_phy->pll_rate = rate; + if (rate <= 74250000) + *parent_rate = rate; + else + *parent_rate = rate / 2; + + return rate; +} + +static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + void __iomem *base = hdmi_phy->regs; + unsigned int pre_div; + unsigned int div; + unsigned int pre_ibias; + unsigned int hdmi_ibias; + unsigned int imp_en; + + dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__, + rate, parent_rate); + + if (rate <= 27000000) { + pre_div = 0; + div = 3; + } else if (rate <= 74250000) { + pre_div = 1; + div = 2; + } else { + pre_div = 1; + div = 1; + } + + mtk_phy_update_field(base + HDMI_CON0, RG_HDMITX_PLL_PREDIV, pre_div); + mtk_phy_set_bits(base + HDMI_CON0, RG_HDMITX_PLL_POSDIV); + mtk_phy_update_bits(base + HDMI_CON0, + RG_HDMITX_PLL_IC | RG_HDMITX_PLL_IR, + FIELD_PREP(RG_HDMITX_PLL_IC, 0x1) | + FIELD_PREP(RG_HDMITX_PLL_IR, 0x1)); + mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_PLL_TXDIV, div); + mtk_phy_update_bits(base + HDMI_CON0, + RG_HDMITX_PLL_FBKSEL | RG_HDMITX_PLL_FBKDIV, + FIELD_PREP(RG_HDMITX_PLL_FBKSEL, 0x1) | + FIELD_PREP(RG_HDMITX_PLL_FBKDIV, 19)); + mtk_phy_update_field(base + HDMI_CON1, RG_HDMITX_PLL_DIVEN, 0x2); + mtk_phy_update_bits(base + HDMI_CON0, + RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC | + RG_HDMITX_PLL_BR, + FIELD_PREP(RG_HDMITX_PLL_BP, 0xc) | + FIELD_PREP(RG_HDMITX_PLL_BC, 0x2) | + FIELD_PREP(RG_HDMITX_PLL_BR, 0x1)); + if (rate < 165000000) { + mtk_phy_clear_bits(base + HDMI_CON3, RG_HDMITX_PRD_IMP_EN); + pre_ibias = 0x3; + imp_en = 0x0; + hdmi_ibias = hdmi_phy->ibias; + } else { + mtk_phy_set_bits(base + HDMI_CON3, RG_HDMITX_PRD_IMP_EN); + pre_ibias = 0x6; + imp_en = 0xf; + hdmi_ibias = hdmi_phy->ibias_up; + } + mtk_phy_update_bits(base + HDMI_CON4, + RG_HDMITX_PRD_IBIAS_CLK | RG_HDMITX_PRD_IBIAS_D2 | + RG_HDMITX_PRD_IBIAS_D1 | RG_HDMITX_PRD_IBIAS_D0, + FIELD_PREP(RG_HDMITX_PRD_IBIAS_CLK, pre_ibias) | + FIELD_PREP(RG_HDMITX_PRD_IBIAS_D2, pre_ibias) | + FIELD_PREP(RG_HDMITX_PRD_IBIAS_D1, pre_ibias) | + FIELD_PREP(RG_HDMITX_PRD_IBIAS_D0, pre_ibias)); + mtk_phy_update_field(base + HDMI_CON3, RG_HDMITX_DRV_IMP_EN, imp_en); + mtk_phy_update_bits(base + HDMI_CON6, + RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 | + RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0, + FIELD_PREP(RG_HDMITX_DRV_IMP_CLK, hdmi_phy->drv_imp_clk) | + FIELD_PREP(RG_HDMITX_DRV_IMP_D2, hdmi_phy->drv_imp_d2) | + FIELD_PREP(RG_HDMITX_DRV_IMP_D1, hdmi_phy->drv_imp_d1) | + FIELD_PREP(RG_HDMITX_DRV_IMP_D0, hdmi_phy->drv_imp_d0)); + mtk_phy_update_bits(base + HDMI_CON5, + RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 | + RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0, + FIELD_PREP(RG_HDMITX_DRV_IBIAS_CLK, hdmi_ibias) | + FIELD_PREP(RG_HDMITX_DRV_IBIAS_D2, hdmi_ibias) | + FIELD_PREP(RG_HDMITX_DRV_IBIAS_D1, hdmi_ibias) | + FIELD_PREP(RG_HDMITX_DRV_IBIAS_D0, hdmi_ibias)); + return 0; +} + +static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + + return hdmi_phy->pll_rate; +} + +static const struct clk_ops mtk_hdmi_phy_pll_ops = { + .prepare = mtk_hdmi_pll_prepare, + .unprepare = mtk_hdmi_pll_unprepare, + .set_rate = mtk_hdmi_pll_set_rate, + .round_rate = mtk_hdmi_pll_round_rate, + .recalc_rate = mtk_hdmi_pll_recalc_rate, +}; + +static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy) +{ + mtk_phy_set_bits(hdmi_phy->regs + HDMI_CON3, + RG_HDMITX_SER_EN | RG_HDMITX_PRD_EN | + RG_HDMITX_DRV_EN); + usleep_range(100, 150); +} + +static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) +{ + mtk_phy_clear_bits(hdmi_phy->regs + HDMI_CON3, + RG_HDMITX_DRV_EN | RG_HDMITX_PRD_EN | + RG_HDMITX_SER_EN); +} + +struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf = { + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, + .hdmi_phy_clk_ops = &mtk_hdmi_phy_pll_ops, + .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds, + .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds, +}; + +MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); +MODULE_DESCRIPTION("MediaTek MT8173 HDMI PHY Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/mediatek/phy-mtk-hdmi.c b/drivers/phy/mediatek/phy-mtk-hdmi.c new file mode 100644 index 000000000000..b16d437d6721 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Jie Qiu <jie.qiu@mediatek.com> + */ + +#include "phy-mtk-hdmi.h" + +static int mtk_hdmi_phy_power_on(struct phy *phy); +static int mtk_hdmi_phy_power_off(struct phy *phy); + +static const struct phy_ops mtk_hdmi_phy_dev_ops = { + .power_on = mtk_hdmi_phy_power_on, + .power_off = mtk_hdmi_phy_power_off, + .owner = THIS_MODULE, +}; + +inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_hdmi_phy, pll_hw); +} + +static int mtk_hdmi_phy_power_on(struct phy *phy) +{ + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(hdmi_phy->pll); + if (ret < 0) + return ret; + + hdmi_phy->conf->hdmi_phy_enable_tmds(hdmi_phy); + return 0; +} + +static int mtk_hdmi_phy_power_off(struct phy *phy) +{ + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); + + hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy); + clk_disable_unprepare(hdmi_phy->pll); + + return 0; +} + +static const struct phy_ops * +mtk_hdmi_phy_dev_get_ops(const struct mtk_hdmi_phy *hdmi_phy) +{ + if (hdmi_phy && hdmi_phy->conf && + hdmi_phy->conf->hdmi_phy_enable_tmds && + hdmi_phy->conf->hdmi_phy_disable_tmds) + return &mtk_hdmi_phy_dev_ops; + + if (hdmi_phy) + dev_err(hdmi_phy->dev, "Failed to get dev ops of phy\n"); + return NULL; +} + +static void mtk_hdmi_phy_clk_get_data(struct mtk_hdmi_phy *hdmi_phy, + struct clk_init_data *clk_init) +{ + clk_init->flags = hdmi_phy->conf->flags; + clk_init->ops = hdmi_phy->conf->hdmi_phy_clk_ops; +} + +static int mtk_hdmi_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_hdmi_phy *hdmi_phy; + struct clk *ref_clk; + const char *ref_clk_name; + struct clk_init_data clk_init = { + .num_parents = 1, + .parent_names = (const char * const *)&ref_clk_name, + }; + + struct phy *phy; + struct phy_provider *phy_provider; + int ret; + + hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); + if (!hdmi_phy) + return -ENOMEM; + + hdmi_phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hdmi_phy->regs)) + return PTR_ERR(hdmi_phy->regs); + + ref_clk = devm_clk_get(dev, "pll_ref"); + if (IS_ERR(ref_clk)) + return dev_err_probe(dev, PTR_ERR(ref_clk), + "Failed to get PLL reference clock\n"); + + ref_clk_name = __clk_get_name(ref_clk); + + ret = of_property_read_string(dev->of_node, "clock-output-names", + &clk_init.name); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read clock-output-names\n"); + + hdmi_phy->dev = dev; + hdmi_phy->conf = + (struct mtk_hdmi_phy_conf *)of_device_get_match_data(dev); + mtk_hdmi_phy_clk_get_data(hdmi_phy, &clk_init); + hdmi_phy->pll_hw.init = &clk_init; + hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw); + if (IS_ERR(hdmi_phy->pll)) + return dev_err_probe(dev, PTR_ERR(hdmi_phy->pll), + "Failed to register PLL\n"); + + ret = of_property_read_u32(dev->of_node, "mediatek,ibias", + &hdmi_phy->ibias); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get ibias\n"); + + ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", + &hdmi_phy->ibias_up); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get ibias_up\n"); + + dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n"); + hdmi_phy->drv_imp_clk = 0x30; + hdmi_phy->drv_imp_d2 = 0x30; + hdmi_phy->drv_imp_d1 = 0x30; + hdmi_phy->drv_imp_d0 = 0x30; + + phy = devm_phy_create(dev, NULL, mtk_hdmi_phy_dev_get_ops(hdmi_phy)); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), "Cannot create HDMI PHY\n"); + + phy_set_drvdata(phy, hdmi_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return dev_err_probe(dev, PTR_ERR(phy_provider), + "Failed to register HDMI PHY\n"); + + if (hdmi_phy->conf->pll_default_off) + hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy); + + return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, + hdmi_phy->pll); +} + +static const struct of_device_id mtk_hdmi_phy_match[] = { + { .compatible = "mediatek,mt2701-hdmi-phy", + .data = &mtk_hdmi_phy_2701_conf, + }, + { .compatible = "mediatek,mt8173-hdmi-phy", + .data = &mtk_hdmi_phy_8173_conf, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_hdmi_phy_match); + +static struct platform_driver mtk_hdmi_phy_driver = { + .probe = mtk_hdmi_phy_probe, + .driver = { + .name = "mediatek-hdmi-phy", + .of_match_table = mtk_hdmi_phy_match, + }, +}; +module_platform_driver(mtk_hdmi_phy_driver); + +MODULE_DESCRIPTION("MediaTek HDMI PHY Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/mediatek/phy-mtk-hdmi.h b/drivers/phy/mediatek/phy-mtk-hdmi.h new file mode 100644 index 000000000000..c7fa65cff989 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunhui Dai <chunhui.dai@mediatek.com> + */ + +#ifndef _MTK_HDMI_PHY_H +#define _MTK_HDMI_PHY_H +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +struct mtk_hdmi_phy; + +struct mtk_hdmi_phy_conf { + unsigned long flags; + bool pll_default_off; + const struct clk_ops *hdmi_phy_clk_ops; + void (*hdmi_phy_enable_tmds)(struct mtk_hdmi_phy *hdmi_phy); + void (*hdmi_phy_disable_tmds)(struct mtk_hdmi_phy *hdmi_phy); +}; + +struct mtk_hdmi_phy { + void __iomem *regs; + struct device *dev; + struct mtk_hdmi_phy_conf *conf; + struct clk *pll; + struct clk_hw pll_hw; + unsigned long pll_rate; + unsigned char drv_imp_clk; + unsigned char drv_imp_d2; + unsigned char drv_imp_d1; + unsigned char drv_imp_d0; + unsigned int ibias; + unsigned int ibias_up; +}; + +struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw); + +extern struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf; +extern struct mtk_hdmi_phy_conf mtk_hdmi_phy_2701_conf; + +#endif /* _MTK_HDMI_PHY_H */ diff --git a/drivers/phy/mediatek/phy-mtk-io.h b/drivers/phy/mediatek/phy-mtk-io.h new file mode 100644 index 000000000000..d20ad5e5be81 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-io.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#ifndef __PHY_MTK_H__ +#define __PHY_MTK_H__ + +#include <linux/bitfield.h> +#include <linux/io.h> + +static inline void mtk_phy_clear_bits(void __iomem *reg, u32 bits) +{ + u32 tmp = readl(reg); + + tmp &= ~bits; + writel(tmp, reg); +} + +static inline void mtk_phy_set_bits(void __iomem *reg, u32 bits) +{ + u32 tmp = readl(reg); + + tmp |= bits; + writel(tmp, reg); +} + +static inline void mtk_phy_update_bits(void __iomem *reg, u32 mask, u32 val) +{ + u32 tmp = readl(reg); + + tmp &= ~mask; + tmp |= val & mask; + writel(tmp, reg); +} + +/* field @mask shall be constant and continuous */ +#define mtk_phy_update_field(reg, mask, val) \ +({ \ + typeof(mask) mask_ = (mask); \ + mtk_phy_update_bits(reg, mask_, FIELD_PREP(mask_, val)); \ +}) + +#endif diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c new file mode 100644 index 000000000000..673cb0f08959 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8173.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: jitao.shi <jitao.shi@mediatek.com> + */ + +#include "phy-mtk-io.h" +#include "phy-mtk-mipi-dsi.h" + +#define MIPITX_DSI_CON 0x00 +#define RG_DSI_LDOCORE_EN BIT(0) +#define RG_DSI_CKG_LDOOUT_EN BIT(1) +#define RG_DSI_BCLK_SEL GENMASK(3, 2) +#define RG_DSI_LD_IDX_SEL GENMASK(6, 4) +#define RG_DSI_PHYCLK_SEL GENMASK(9, 8) +#define RG_DSI_DSICLK_FREQ_SEL BIT(10) +#define RG_DSI_LPTX_CLMP_EN BIT(11) + +#define MIPITX_DSI_CLOCK_LANE 0x04 +#define MIPITX_DSI_DATA_LANE0 0x08 +#define MIPITX_DSI_DATA_LANE1 0x0c +#define MIPITX_DSI_DATA_LANE2 0x10 +#define MIPITX_DSI_DATA_LANE3 0x14 +#define RG_DSI_LNTx_LDOOUT_EN BIT(0) +#define RG_DSI_LNTx_CKLANE_EN BIT(1) +#define RG_DSI_LNTx_LPTX_IPLUS1 BIT(2) +#define RG_DSI_LNTx_LPTX_IPLUS2 BIT(3) +#define RG_DSI_LNTx_LPTX_IMINUS BIT(4) +#define RG_DSI_LNTx_LPCD_IPLUS BIT(5) +#define RG_DSI_LNTx_LPCD_IMINUS BIT(6) +#define RG_DSI_LNTx_RT_CODE GENMASK(11, 8) + +#define MIPITX_DSI_TOP_CON 0x40 +#define RG_DSI_LNT_INTR_EN BIT(0) +#define RG_DSI_LNT_HS_BIAS_EN BIT(1) +#define RG_DSI_LNT_IMP_CAL_EN BIT(2) +#define RG_DSI_LNT_TESTMODE_EN BIT(3) +#define RG_DSI_LNT_IMP_CAL_CODE GENMASK(7, 4) +#define RG_DSI_LNT_AIO_SEL GENMASK(10, 8) +#define RG_DSI_PAD_TIE_LOW_EN BIT(11) +#define RG_DSI_DEBUG_INPUT_EN BIT(12) +#define RG_DSI_PRESERVE GENMASK(15, 13) + +#define MIPITX_DSI_BG_CON 0x44 +#define RG_DSI_BG_CORE_EN BIT(0) +#define RG_DSI_BG_CKEN BIT(1) +#define RG_DSI_BG_DIV GENMASK(3, 2) +#define RG_DSI_BG_FAST_CHARGE BIT(4) + +#define RG_DSI_V12_SEL GENMASK(7, 5) +#define RG_DSI_V10_SEL GENMASK(10, 8) +#define RG_DSI_V072_SEL GENMASK(13, 11) +#define RG_DSI_V04_SEL GENMASK(16, 14) +#define RG_DSI_V032_SEL GENMASK(19, 17) +#define RG_DSI_V02_SEL GENMASK(22, 20) +#define RG_DSI_VOUT_MSK \ + (RG_DSI_V12_SEL | RG_DSI_V10_SEL | RG_DSI_V072_SEL | \ + RG_DSI_V04_SEL | RG_DSI_V032_SEL | RG_DSI_V02_SEL) +#define RG_DSI_BG_R1_TRIM GENMASK(27, 24) +#define RG_DSI_BG_R2_TRIM GENMASK(31, 28) + +#define MIPITX_DSI_PLL_CON0 0x50 +#define RG_DSI_MPPLL_PLL_EN BIT(0) +#define RG_DSI_MPPLL_PREDIV GENMASK(2, 1) +#define RG_DSI_MPPLL_TXDIV0 GENMASK(4, 3) +#define RG_DSI_MPPLL_TXDIV1 GENMASK(6, 5) +#define RG_DSI_MPPLL_POSDIV GENMASK(9, 7) +#define RG_DSI_MPPLL_DIV_MSK \ + (RG_DSI_MPPLL_PREDIV | RG_DSI_MPPLL_TXDIV0 | \ + RG_DSI_MPPLL_TXDIV1 | RG_DSI_MPPLL_POSDIV) +#define RG_DSI_MPPLL_MONVC_EN BIT(10) +#define RG_DSI_MPPLL_MONREF_EN BIT(11) +#define RG_DSI_MPPLL_VOD_EN BIT(12) + +#define MIPITX_DSI_PLL_CON1 0x54 +#define RG_DSI_MPPLL_SDM_FRA_EN BIT(0) +#define RG_DSI_MPPLL_SDM_SSC_PH_INIT BIT(1) +#define RG_DSI_MPPLL_SDM_SSC_EN BIT(2) +#define RG_DSI_MPPLL_SDM_SSC_PRD GENMASK(31, 16) + +#define MIPITX_DSI_PLL_CON2 0x58 + +#define MIPITX_DSI_PLL_TOP 0x64 +#define RG_DSI_MPPLL_PRESERVE GENMASK(15, 8) + +#define MIPITX_DSI_PLL_PWR 0x68 +#define RG_DSI_MPPLL_SDM_PWR_ON BIT(0) +#define RG_DSI_MPPLL_SDM_ISO_EN BIT(1) +#define RG_DSI_MPPLL_SDM_PWR_ACK BIT(8) + +#define MIPITX_DSI_SW_CTRL 0x80 +#define SW_CTRL_EN BIT(0) + +#define MIPITX_DSI_SW_CTRL_CON0 0x84 +#define SW_LNTC_LPTX_PRE_OE BIT(0) +#define SW_LNTC_LPTX_OE BIT(1) +#define SW_LNTC_LPTX_P BIT(2) +#define SW_LNTC_LPTX_N BIT(3) +#define SW_LNTC_HSTX_PRE_OE BIT(4) +#define SW_LNTC_HSTX_OE BIT(5) +#define SW_LNTC_HSTX_ZEROCLK BIT(6) +#define SW_LNT0_LPTX_PRE_OE BIT(7) +#define SW_LNT0_LPTX_OE BIT(8) +#define SW_LNT0_LPTX_P BIT(9) +#define SW_LNT0_LPTX_N BIT(10) +#define SW_LNT0_HSTX_PRE_OE BIT(11) +#define SW_LNT0_HSTX_OE BIT(12) +#define SW_LNT0_LPRX_EN BIT(13) +#define SW_LNT1_LPTX_PRE_OE BIT(14) +#define SW_LNT1_LPTX_OE BIT(15) +#define SW_LNT1_LPTX_P BIT(16) +#define SW_LNT1_LPTX_N BIT(17) +#define SW_LNT1_HSTX_PRE_OE BIT(18) +#define SW_LNT1_HSTX_OE BIT(19) +#define SW_LNT2_LPTX_PRE_OE BIT(20) +#define SW_LNT2_LPTX_OE BIT(21) +#define SW_LNT2_LPTX_P BIT(22) +#define SW_LNT2_LPTX_N BIT(23) +#define SW_LNT2_HSTX_PRE_OE BIT(24) +#define SW_LNT2_HSTX_OE BIT(25) + +static int mtk_mipi_tx_pll_prepare(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + u8 txdiv, txdiv0, txdiv1; + u64 pcw; + + dev_dbg(mipi_tx->dev, "prepare: %u Hz\n", mipi_tx->data_rate); + + if (mipi_tx->data_rate >= 500000000) { + txdiv = 1; + txdiv0 = 0; + txdiv1 = 0; + } else if (mipi_tx->data_rate >= 250000000) { + txdiv = 2; + txdiv0 = 1; + txdiv1 = 0; + } else if (mipi_tx->data_rate >= 125000000) { + txdiv = 4; + txdiv0 = 2; + txdiv1 = 0; + } else if (mipi_tx->data_rate > 62000000) { + txdiv = 8; + txdiv0 = 2; + txdiv1 = 1; + } else if (mipi_tx->data_rate >= 50000000) { + txdiv = 16; + txdiv0 = 2; + txdiv1 = 2; + } else { + return -EINVAL; + } + + mtk_phy_update_bits(base + MIPITX_DSI_BG_CON, + RG_DSI_VOUT_MSK | RG_DSI_BG_CKEN | + RG_DSI_BG_CORE_EN, + FIELD_PREP(RG_DSI_V02_SEL, 4) | + FIELD_PREP(RG_DSI_V032_SEL, 4) | + FIELD_PREP(RG_DSI_V04_SEL, 4) | + FIELD_PREP(RG_DSI_V072_SEL, 4) | + FIELD_PREP(RG_DSI_V10_SEL, 4) | + FIELD_PREP(RG_DSI_V12_SEL, 4) | + RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN); + + usleep_range(30, 100); + + mtk_phy_update_bits(base + MIPITX_DSI_TOP_CON, + RG_DSI_LNT_IMP_CAL_CODE | RG_DSI_LNT_HS_BIAS_EN, + FIELD_PREP(RG_DSI_LNT_IMP_CAL_CODE, 8) | + RG_DSI_LNT_HS_BIAS_EN); + + mtk_phy_set_bits(base + MIPITX_DSI_CON, + RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN); + + mtk_phy_update_bits(base + MIPITX_DSI_PLL_PWR, + RG_DSI_MPPLL_SDM_PWR_ON | RG_DSI_MPPLL_SDM_ISO_EN, + RG_DSI_MPPLL_SDM_PWR_ON); + + mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN); + + mtk_phy_update_bits(base + MIPITX_DSI_PLL_CON0, + RG_DSI_MPPLL_TXDIV0 | RG_DSI_MPPLL_TXDIV1 | + RG_DSI_MPPLL_PREDIV, + FIELD_PREP(RG_DSI_MPPLL_TXDIV0, txdiv0) | + FIELD_PREP(RG_DSI_MPPLL_TXDIV1, txdiv1)); + + /* + * PLL PCW config + * PCW bit 24~30 = integer part of pcw + * PCW bit 0~23 = fractional part of pcw + * pcw = data_Rate*4*txdiv/(Ref_clk*2); + * Post DIV =4, so need data_Rate*4 + * Ref_clk is 26MHz + */ + pcw = div_u64(((u64)mipi_tx->data_rate * 2 * txdiv) << 24, 26000000); + writel(pcw, base + MIPITX_DSI_PLL_CON2); + + mtk_phy_set_bits(base + MIPITX_DSI_PLL_CON1, RG_DSI_MPPLL_SDM_FRA_EN); + + mtk_phy_set_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN); + + usleep_range(20, 100); + + mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON1, RG_DSI_MPPLL_SDM_SSC_EN); + + mtk_phy_update_field(base + MIPITX_DSI_PLL_TOP, + RG_DSI_MPPLL_PRESERVE, + mipi_tx->driver_data->mppll_preserve); + + return 0; +} + +static void mtk_mipi_tx_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + + dev_dbg(mipi_tx->dev, "unprepare\n"); + + mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN); + + mtk_phy_clear_bits(base + MIPITX_DSI_PLL_TOP, RG_DSI_MPPLL_PRESERVE); + + mtk_phy_update_bits(base + MIPITX_DSI_PLL_PWR, + RG_DSI_MPPLL_SDM_ISO_EN | RG_DSI_MPPLL_SDM_PWR_ON, + RG_DSI_MPPLL_SDM_ISO_EN); + + mtk_phy_clear_bits(base + MIPITX_DSI_TOP_CON, RG_DSI_LNT_HS_BIAS_EN); + + mtk_phy_clear_bits(base + MIPITX_DSI_CON, + RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN); + + mtk_phy_clear_bits(base + MIPITX_DSI_BG_CON, + RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN); + + mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_DIV_MSK); +} + +static long mtk_mipi_tx_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clamp_val(rate, 50000000, 1250000000); +} + +static const struct clk_ops mtk_mipi_tx_pll_ops = { + .prepare = mtk_mipi_tx_pll_prepare, + .unprepare = mtk_mipi_tx_pll_unprepare, + .round_rate = mtk_mipi_tx_pll_round_rate, + .set_rate = mtk_mipi_tx_pll_set_rate, + .recalc_rate = mtk_mipi_tx_pll_recalc_rate, +}; + +static void mtk_mipi_tx_power_on_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + u32 reg; + + for (reg = MIPITX_DSI_CLOCK_LANE; + reg <= MIPITX_DSI_DATA_LANE3; reg += 4) + mtk_phy_set_bits(mipi_tx->regs + reg, RG_DSI_LNTx_LDOOUT_EN); + + mtk_phy_clear_bits(mipi_tx->regs + MIPITX_DSI_TOP_CON, + RG_DSI_PAD_TIE_LOW_EN); +} + +static void mtk_mipi_tx_power_off_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + u32 reg; + + mtk_phy_set_bits(mipi_tx->regs + MIPITX_DSI_TOP_CON, + RG_DSI_PAD_TIE_LOW_EN); + + for (reg = MIPITX_DSI_CLOCK_LANE; + reg <= MIPITX_DSI_DATA_LANE3; reg += 4) + mtk_phy_clear_bits(mipi_tx->regs + reg, RG_DSI_LNTx_LDOOUT_EN); +} + +const struct mtk_mipitx_data mt2701_mipitx_data = { + .mppll_preserve = 3, + .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops, + .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal, + .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal, +}; + +const struct mtk_mipitx_data mt8173_mipitx_data = { + .mppll_preserve = 0, + .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops, + .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal, + .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal, +}; diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c new file mode 100644 index 000000000000..f021ec5a70e5 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8183.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: jitao.shi <jitao.shi@mediatek.com> + */ + +#include "phy-mtk-io.h" +#include "phy-mtk-mipi-dsi.h" + +#define MIPITX_LANE_CON 0x000c +#define RG_DSI_CPHY_T1DRV_EN BIT(0) +#define RG_DSI_ANA_CK_SEL BIT(1) +#define RG_DSI_PHY_CK_SEL BIT(2) +#define RG_DSI_CPHY_EN BIT(3) +#define RG_DSI_PHYCK_INV_EN BIT(4) +#define RG_DSI_PWR04_EN BIT(5) +#define RG_DSI_BG_LPF_EN BIT(6) +#define RG_DSI_BG_CORE_EN BIT(7) +#define RG_DSI_PAD_TIEL_SEL BIT(8) + +#define MIPITX_VOLTAGE_SEL 0x0010 +#define RG_DSI_HSTX_LDO_REF_SEL GENMASK(9, 6) + +#define MIPITX_PLL_PWR 0x0028 +#define MIPITX_PLL_CON0 0x002c +#define MIPITX_PLL_CON1 0x0030 +#define MIPITX_PLL_CON2 0x0034 +#define MIPITX_PLL_CON3 0x0038 +#define MIPITX_PLL_CON4 0x003c +#define RG_DSI_PLL_IBIAS GENMASK(11, 10) + +#define MIPITX_D2P_RTCODE 0x0100 +#define MIPITX_D2_SW_CTL_EN 0x0144 +#define MIPITX_D0_SW_CTL_EN 0x0244 +#define MIPITX_CK_CKMODE_EN 0x0328 +#define DSI_CK_CKMODE_EN BIT(0) +#define MIPITX_CK_SW_CTL_EN 0x0344 +#define MIPITX_D1_SW_CTL_EN 0x0444 +#define MIPITX_D3_SW_CTL_EN 0x0544 +#define DSI_SW_CTL_EN BIT(0) +#define AD_DSI_PLL_SDM_PWR_ON BIT(0) +#define AD_DSI_PLL_SDM_ISO_EN BIT(1) + +#define RG_DSI_PLL_EN BIT(4) +#define RG_DSI_PLL_POSDIV GENMASK(10, 8) + +static int mtk_mipi_tx_pll_enable(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + unsigned int txdiv, txdiv0; + u64 pcw; + + dev_dbg(mipi_tx->dev, "enable: %u bps\n", mipi_tx->data_rate); + + if (mipi_tx->data_rate >= 2000000000) { + txdiv = 1; + txdiv0 = 0; + } else if (mipi_tx->data_rate >= 1000000000) { + txdiv = 2; + txdiv0 = 1; + } else if (mipi_tx->data_rate >= 500000000) { + txdiv = 4; + txdiv0 = 2; + } else if (mipi_tx->data_rate > 250000000) { + txdiv = 8; + txdiv0 = 3; + } else if (mipi_tx->data_rate >= 125000000) { + txdiv = 16; + txdiv0 = 4; + } else { + return -EINVAL; + } + + mtk_phy_clear_bits(base + MIPITX_PLL_CON4, RG_DSI_PLL_IBIAS); + + mtk_phy_set_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_PWR_ON); + mtk_phy_clear_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN); + udelay(1); + mtk_phy_clear_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_ISO_EN); + pcw = div_u64(((u64)mipi_tx->data_rate * txdiv) << 24, 26000000); + writel(pcw, base + MIPITX_PLL_CON0); + mtk_phy_update_field(base + MIPITX_PLL_CON1, RG_DSI_PLL_POSDIV, txdiv0); + mtk_phy_set_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN); + + return 0; +} + +static void mtk_mipi_tx_pll_disable(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + + mtk_phy_clear_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN); + + mtk_phy_set_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_ISO_EN); + mtk_phy_clear_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_PWR_ON); +} + +static long mtk_mipi_tx_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clamp_val(rate, 50000000, 1600000000); +} + +static const struct clk_ops mtk_mipi_tx_pll_ops = { + .enable = mtk_mipi_tx_pll_enable, + .disable = mtk_mipi_tx_pll_disable, + .round_rate = mtk_mipi_tx_pll_round_rate, + .set_rate = mtk_mipi_tx_pll_set_rate, + .recalc_rate = mtk_mipi_tx_pll_recalc_rate, +}; + +static void mtk_mipi_tx_config_calibration_data(struct mtk_mipi_tx *mipi_tx) +{ + int i, j; + + for (i = 0; i < 5; i++) { + if ((mipi_tx->rt_code[i] & 0x1f) == 0) + mipi_tx->rt_code[i] |= 0x10; + + if ((mipi_tx->rt_code[i] >> 5 & 0x1f) == 0) + mipi_tx->rt_code[i] |= 0x10 << 5; + + for (j = 0; j < 10; j++) + mtk_phy_update_bits(mipi_tx->regs + + MIPITX_D2P_RTCODE * (i + 1) + j * 4, + 1, mipi_tx->rt_code[i] >> j & 1); + } +} + +static void mtk_mipi_tx_power_on_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + void __iomem *base = mipi_tx->regs; + + /* BG_LPF_EN / BG_CORE_EN */ + writel(RG_DSI_PAD_TIEL_SEL | RG_DSI_BG_CORE_EN, base + MIPITX_LANE_CON); + usleep_range(30, 100); + writel(RG_DSI_BG_CORE_EN | RG_DSI_BG_LPF_EN, base + MIPITX_LANE_CON); + + /* Switch OFF each Lane */ + mtk_phy_clear_bits(base + MIPITX_D0_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_D1_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_D2_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_D3_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_CK_SW_CTL_EN, DSI_SW_CTL_EN); + + mtk_phy_update_field(base + MIPITX_VOLTAGE_SEL, RG_DSI_HSTX_LDO_REF_SEL, + (mipi_tx->mipitx_drive - 3000) / 200); + + mtk_mipi_tx_config_calibration_data(mipi_tx); + + mtk_phy_set_bits(base + MIPITX_CK_CKMODE_EN, DSI_CK_CKMODE_EN); +} + +static void mtk_mipi_tx_power_off_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + void __iomem *base = mipi_tx->regs; + + /* Switch ON each Lane */ + mtk_phy_set_bits(base + MIPITX_D0_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_D1_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_D2_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_D3_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_CK_SW_CTL_EN, DSI_SW_CTL_EN); + + writel(RG_DSI_PAD_TIEL_SEL | RG_DSI_BG_CORE_EN, base + MIPITX_LANE_CON); + writel(RG_DSI_PAD_TIEL_SEL, base + MIPITX_LANE_CON); +} + +const struct mtk_mipitx_data mt8183_mipitx_data = { + .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops, + .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal, + .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal, +}; diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c new file mode 100644 index 000000000000..cf9c386385bb --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 MediaTek Inc. + */ + +#include "phy-mtk-mipi-dsi.h" + +inline struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_mipi_tx, pll_hw); +} + +int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + + dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate); + + mipi_tx->data_rate = rate; + + return 0; +} + +unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + + return mipi_tx->data_rate; +} + +static int mtk_mipi_tx_power_on(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + int ret; + + /* Power up core and enable PLL */ + ret = clk_prepare_enable(mipi_tx->pll); + if (ret < 0) + return ret; + + /* Enable DSI Lane LDO outputs, disable pad tie low */ + mipi_tx->driver_data->mipi_tx_enable_signal(phy); + return 0; +} + +static int mtk_mipi_tx_power_off(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + + /* Enable pad tie low, disable DSI Lane LDO outputs */ + mipi_tx->driver_data->mipi_tx_disable_signal(phy); + + /* Disable PLL and power down core */ + clk_disable_unprepare(mipi_tx->pll); + + return 0; +} + +static const struct phy_ops mtk_mipi_tx_ops = { + .power_on = mtk_mipi_tx_power_on, + .power_off = mtk_mipi_tx_power_off, + .owner = THIS_MODULE, +}; + +static void mtk_mipi_tx_get_calibration_datal(struct mtk_mipi_tx *mipi_tx) +{ + struct nvmem_cell *cell; + size_t len; + u32 *buf; + + cell = nvmem_cell_get(mipi_tx->dev, "calibration-data"); + if (IS_ERR(cell)) { + dev_info(mipi_tx->dev, "can't get nvmem_cell_get, ignore it\n"); + return; + } + buf = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (IS_ERR(buf)) { + dev_info(mipi_tx->dev, "can't get data, ignore it\n"); + return; + } + + if (len < 3 * sizeof(u32)) { + dev_info(mipi_tx->dev, "invalid calibration data\n"); + kfree(buf); + return; + } + + mipi_tx->rt_code[0] = ((buf[0] >> 6 & 0x1f) << 5) | + (buf[0] >> 11 & 0x1f); + mipi_tx->rt_code[1] = ((buf[1] >> 27 & 0x1f) << 5) | + (buf[0] >> 1 & 0x1f); + mipi_tx->rt_code[2] = ((buf[1] >> 17 & 0x1f) << 5) | + (buf[1] >> 22 & 0x1f); + mipi_tx->rt_code[3] = ((buf[1] >> 7 & 0x1f) << 5) | + (buf[1] >> 12 & 0x1f); + mipi_tx->rt_code[4] = ((buf[2] >> 27 & 0x1f) << 5) | + (buf[1] >> 2 & 0x1f); + kfree(buf); +} + +static int mtk_mipi_tx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_mipi_tx *mipi_tx; + const char *ref_clk_name; + struct clk *ref_clk; + struct clk_init_data clk_init = { + .num_parents = 1, + .parent_names = (const char * const *)&ref_clk_name, + .flags = CLK_SET_RATE_GATE, + }; + struct phy *phy; + struct phy_provider *phy_provider; + int ret; + + mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL); + if (!mipi_tx) + return -ENOMEM; + + mipi_tx->driver_data = of_device_get_match_data(dev); + if (!mipi_tx->driver_data) + return -ENODEV; + + mipi_tx->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mipi_tx->regs)) + return PTR_ERR(mipi_tx->regs); + + ref_clk = devm_clk_get(dev, NULL); + if (IS_ERR(ref_clk)) + return dev_err_probe(dev, PTR_ERR(ref_clk), + "Failed to get reference clock\n"); + + ret = of_property_read_u32(dev->of_node, "drive-strength-microamp", + &mipi_tx->mipitx_drive); + /* If can't get the "mipi_tx->mipitx_drive", set it default 0x8 */ + if (ret < 0) + mipi_tx->mipitx_drive = 4600; + + /* check the mipitx_drive valid */ + if (mipi_tx->mipitx_drive > 6000 || mipi_tx->mipitx_drive < 3000) { + dev_warn(dev, "drive-strength-microamp is invalid %d, not in 3000 ~ 6000\n", + mipi_tx->mipitx_drive); + mipi_tx->mipitx_drive = clamp_val(mipi_tx->mipitx_drive, 3000, + 6000); + } + + ref_clk_name = __clk_get_name(ref_clk); + + ret = of_property_read_string(dev->of_node, "clock-output-names", + &clk_init.name); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read clock-output-names\n"); + + clk_init.ops = mipi_tx->driver_data->mipi_tx_clk_ops; + + mipi_tx->pll_hw.init = &clk_init; + mipi_tx->pll = devm_clk_register(dev, &mipi_tx->pll_hw); + if (IS_ERR(mipi_tx->pll)) + return dev_err_probe(dev, PTR_ERR(mipi_tx->pll), "Failed to register PLL\n"); + + phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), "Failed to create MIPI D-PHY\n"); + + phy_set_drvdata(phy, mipi_tx); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + mipi_tx->dev = dev; + + mtk_mipi_tx_get_calibration_datal(mipi_tx); + + return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, + mipi_tx->pll); +} + +static int mtk_mipi_tx_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static const struct of_device_id mtk_mipi_tx_match[] = { + { .compatible = "mediatek,mt2701-mipi-tx", + .data = &mt2701_mipitx_data }, + { .compatible = "mediatek,mt8173-mipi-tx", + .data = &mt8173_mipitx_data }, + { .compatible = "mediatek,mt8183-mipi-tx", + .data = &mt8183_mipitx_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, mtk_mipi_tx_match); + +static struct platform_driver mtk_mipi_tx_driver = { + .probe = mtk_mipi_tx_probe, + .remove = mtk_mipi_tx_remove, + .driver = { + .name = "mediatek-mipi-tx", + .of_match_table = mtk_mipi_tx_match, + }, +}; +module_platform_driver(mtk_mipi_tx_driver); + +MODULE_DESCRIPTION("MediaTek MIPI TX Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h new file mode 100644 index 000000000000..47b60b1a7226 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: Jitao Shi <jitao.shi@mediatek.com> + */ + +#ifndef _MTK_MIPI_TX_H +#define _MTK_MIPI_TX_H + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/slab.h> + +struct mtk_mipitx_data { + const u32 mppll_preserve; + const struct clk_ops *mipi_tx_clk_ops; + void (*mipi_tx_enable_signal)(struct phy *phy); + void (*mipi_tx_disable_signal)(struct phy *phy); +}; + +struct mtk_mipi_tx { + struct device *dev; + void __iomem *regs; + u32 data_rate; + u32 mipitx_drive; + u32 rt_code[5]; + const struct mtk_mipitx_data *driver_data; + struct clk_hw pll_hw; + struct clk *pll; +}; + +struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw); +int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate); + +extern const struct mtk_mipitx_data mt2701_mipitx_data; +extern const struct mtk_mipitx_data mt8173_mipitx_data; +extern const struct mtk_mipitx_data mt8183_mipitx_data; + +#endif diff --git a/drivers/phy/mediatek/phy-mtk-pcie.c b/drivers/phy/mediatek/phy-mtk-pcie.c new file mode 100644 index 000000000000..25dbd6e35722 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-pcie.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Jianjun Wang <jianjun.wang@mediatek.com> + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "phy-mtk-io.h" + +#define PEXTP_ANA_GLB_00_REG 0x9000 +/* Internal Resistor Selection of TX Bias Current */ +#define EFUSE_GLB_INTR_SEL GENMASK(28, 24) + +#define PEXTP_ANA_LN0_TRX_REG 0xa000 + +#define PEXTP_ANA_TX_REG 0x04 +/* TX PMOS impedance selection */ +#define EFUSE_LN_TX_PMOS_SEL GENMASK(5, 2) +/* TX NMOS impedance selection */ +#define EFUSE_LN_TX_NMOS_SEL GENMASK(11, 8) + +#define PEXTP_ANA_RX_REG 0x3c +/* RX impedance selection */ +#define EFUSE_LN_RX_SEL GENMASK(3, 0) + +#define PEXTP_ANA_LANE_OFFSET 0x100 + +/** + * struct mtk_pcie_lane_efuse - eFuse data for each lane + * @tx_pmos: TX PMOS impedance selection data + * @tx_nmos: TX NMOS impedance selection data + * @rx_data: RX impedance selection data + * @lane_efuse_supported: software eFuse data is supported for this lane + */ +struct mtk_pcie_lane_efuse { + u32 tx_pmos; + u32 tx_nmos; + u32 rx_data; + bool lane_efuse_supported; +}; + +/** + * struct mtk_pcie_phy_data - phy data for each SoC + * @num_lanes: supported lane numbers + * @sw_efuse_supported: support software to load eFuse data + */ +struct mtk_pcie_phy_data { + int num_lanes; + bool sw_efuse_supported; +}; + +/** + * struct mtk_pcie_phy - PCIe phy driver main structure + * @dev: pointer to device + * @phy: pointer to generic phy + * @sif_base: IO mapped register base address of system interface + * @data: pointer to SoC dependent data + * @sw_efuse_en: software eFuse enable status + * @efuse_glb_intr: internal resistor selection of TX bias current data + * @efuse: pointer to eFuse data for each lane + */ +struct mtk_pcie_phy { + struct device *dev; + struct phy *phy; + void __iomem *sif_base; + const struct mtk_pcie_phy_data *data; + + bool sw_efuse_en; + u32 efuse_glb_intr; + struct mtk_pcie_lane_efuse *efuse; +}; + +static void mtk_pcie_efuse_set_lane(struct mtk_pcie_phy *pcie_phy, + unsigned int lane) +{ + struct mtk_pcie_lane_efuse *data = &pcie_phy->efuse[lane]; + void __iomem *addr; + + if (!data->lane_efuse_supported) + return; + + addr = pcie_phy->sif_base + PEXTP_ANA_LN0_TRX_REG + + lane * PEXTP_ANA_LANE_OFFSET; + + mtk_phy_update_field(addr + PEXTP_ANA_TX_REG, EFUSE_LN_TX_PMOS_SEL, + data->tx_pmos); + + mtk_phy_update_field(addr + PEXTP_ANA_TX_REG, EFUSE_LN_TX_NMOS_SEL, + data->tx_nmos); + + mtk_phy_update_field(addr + PEXTP_ANA_RX_REG, EFUSE_LN_RX_SEL, + data->rx_data); +} + +/** + * mtk_pcie_phy_init() - Initialize the phy + * @phy: the phy to be initialized + * + * Initialize the phy by setting the efuse data. + * The hardware settings will be reset during suspend, it should be + * reinitialized when the consumer calls phy_init() again on resume. + */ +static int mtk_pcie_phy_init(struct phy *phy) +{ + struct mtk_pcie_phy *pcie_phy = phy_get_drvdata(phy); + int i; + + if (!pcie_phy->sw_efuse_en) + return 0; + + /* Set global data */ + mtk_phy_update_field(pcie_phy->sif_base + PEXTP_ANA_GLB_00_REG, + EFUSE_GLB_INTR_SEL, pcie_phy->efuse_glb_intr); + + for (i = 0; i < pcie_phy->data->num_lanes; i++) + mtk_pcie_efuse_set_lane(pcie_phy, i); + + return 0; +} + +static const struct phy_ops mtk_pcie_phy_ops = { + .init = mtk_pcie_phy_init, + .owner = THIS_MODULE, +}; + +static int mtk_pcie_efuse_read_for_lane(struct mtk_pcie_phy *pcie_phy, + unsigned int lane) +{ + struct mtk_pcie_lane_efuse *efuse = &pcie_phy->efuse[lane]; + struct device *dev = pcie_phy->dev; + char efuse_id[16]; + int ret; + + snprintf(efuse_id, sizeof(efuse_id), "tx_ln%d_pmos", lane); + ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->tx_pmos); + if (ret) + return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id); + + snprintf(efuse_id, sizeof(efuse_id), "tx_ln%d_nmos", lane); + ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->tx_nmos); + if (ret) + return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id); + + snprintf(efuse_id, sizeof(efuse_id), "rx_ln%d", lane); + ret = nvmem_cell_read_variable_le_u32(dev, efuse_id, &efuse->rx_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to read %s\n", efuse_id); + + if (!(efuse->tx_pmos || efuse->tx_nmos || efuse->rx_data)) + return dev_err_probe(dev, -EINVAL, + "No eFuse data found for lane%d, but dts enable it\n", + lane); + + efuse->lane_efuse_supported = true; + + return 0; +} + +static int mtk_pcie_read_efuse(struct mtk_pcie_phy *pcie_phy) +{ + struct device *dev = pcie_phy->dev; + bool nvmem_enabled; + int ret, i; + + /* nvmem data is optional */ + nvmem_enabled = device_property_present(dev, "nvmem-cells"); + if (!nvmem_enabled) + return 0; + + ret = nvmem_cell_read_variable_le_u32(dev, "glb_intr", + &pcie_phy->efuse_glb_intr); + if (ret) + return dev_err_probe(dev, ret, "Failed to read glb_intr\n"); + + pcie_phy->sw_efuse_en = true; + + pcie_phy->efuse = devm_kzalloc(dev, pcie_phy->data->num_lanes * + sizeof(*pcie_phy->efuse), GFP_KERNEL); + if (!pcie_phy->efuse) + return -ENOMEM; + + for (i = 0; i < pcie_phy->data->num_lanes; i++) { + ret = mtk_pcie_efuse_read_for_lane(pcie_phy, i); + if (ret) + return ret; + } + + return 0; +} + +static int mtk_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct mtk_pcie_phy *pcie_phy; + int ret; + + pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL); + if (!pcie_phy) + return -ENOMEM; + + pcie_phy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif"); + if (IS_ERR(pcie_phy->sif_base)) + return dev_err_probe(dev, PTR_ERR(pcie_phy->sif_base), + "Failed to map phy-sif base\n"); + + pcie_phy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_phy_ops); + if (IS_ERR(pcie_phy->phy)) + return dev_err_probe(dev, PTR_ERR(pcie_phy->phy), + "Failed to create PCIe phy\n"); + + pcie_phy->dev = dev; + pcie_phy->data = of_device_get_match_data(dev); + if (!pcie_phy->data) + return dev_err_probe(dev, -EINVAL, "Failed to get phy data\n"); + + if (pcie_phy->data->sw_efuse_supported) { + /* + * Failed to read the efuse data is not a fatal problem, + * ignore the failure and keep going. + */ + ret = mtk_pcie_read_efuse(pcie_phy); + if (ret == -EPROBE_DEFER || ret == -ENOMEM) + return ret; + } + + phy_set_drvdata(pcie_phy->phy, pcie_phy); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "PCIe phy probe failed\n"); + + return 0; +} + +static const struct mtk_pcie_phy_data mt8195_data = { + .num_lanes = 2, + .sw_efuse_supported = true, +}; + +static const struct of_device_id mtk_pcie_phy_of_match[] = { + { .compatible = "mediatek,mt8195-pcie-phy", .data = &mt8195_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, mtk_pcie_phy_of_match); + +static struct platform_driver mtk_pcie_phy_driver = { + .probe = mtk_pcie_phy_probe, + .driver = { + .name = "mtk-pcie-phy", + .of_match_table = mtk_pcie_phy_of_match, + }, +}; +module_platform_driver(mtk_pcie_phy_driver); + +MODULE_DESCRIPTION("MediaTek PCIe PHY driver"); +MODULE_AUTHOR("Jianjun Wang <jianjun.wang@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index cb2ed3b25068..e906a82791bd 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -8,13 +8,17 @@ #include <dt-bindings/phy/phy.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/io.h> #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> #include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "phy-mtk-io.h" /* version V1 sub-banks offset base address */ /* banks shared by multiple phys */ @@ -27,7 +31,8 @@ #define SSUSB_SIFSLV_V1_U3PHYD 0x000 #define SSUSB_SIFSLV_V1_U3PHYA 0x200 -/* version V2 sub-banks offset base address */ +/* version V2/V3 sub-banks offset base address */ +/* V3: U2FREQ is not used anymore, but reserved */ /* u2 phy banks */ #define SSUSB_SIFSLV_V2_MISC 0x000 #define SSUSB_SIFSLV_V2_U2FREQ 0x100 @@ -38,36 +43,48 @@ #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_USB20_PLL_PREDIV GENMASK(7, 6) #define PA0_RG_USB20_INTR_EN BIT(5) #define U3P_USBPHYACR1 0x004 +#define PA1_RG_INTR_CAL GENMASK(23, 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) -#define PA1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8) #define U3P_USBPHYACR2 0x008 +#define PA2_RG_U2PLL_BW GENMASK(21, 19) #define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18) #define U3P_USBPHYACR5 0x014 #define PA5_RG_U2_HSTX_SRCAL_EN BIT(15) #define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12) -#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) #define PA5_RG_U2_HS_100U_U3_EN BIT(11) #define U3P_USBPHYACR6 0x018 +#define PA6_RG_U2_PRE_EMP GENMASK(31, 30) #define PA6_RG_U2_BC11_SW_EN BIT(23) #define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20) +#define PA6_RG_U2_DISCTH GENMASK(7, 4) #define PA6_RG_U2_SQTH GENMASK(3, 0) -#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x)) #define U3P_U2PHYACR4 0x020 #define P2C_RG_USB20_GPIO_CTL BIT(9) #define P2C_USB20_GPIO_MODE BIT(8) #define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE) +#define U3P_U2PHYA_RESV 0x030 +#define P2R_RG_U2PLL_FBDIV_26M 0x1bb13b +#define P2R_RG_U2PLL_FBDIV_48M 0x3c0000 + +#define U3P_U2PHYA_RESV1 0x044 +#define P2R_RG_U2PLL_REFCLK_SEL BIT(5) +#define P2R_RG_U2PLL_FRA_EN BIT(3) + #define U3D_U2PHYDCR0 0x060 #define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24) @@ -80,11 +97,9 @@ #define P2C_FORCE_SUSPENDM BIT(18) #define P2C_FORCE_TERMSEL BIT(17) #define P2C_RG_DATAIN GENMASK(13, 10) -#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10) #define P2C_RG_DMPULLDOWN BIT(7) #define P2C_RG_DPPULLDOWN BIT(6) #define P2C_RG_XCVRSEL GENMASK(5, 4) -#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4) #define P2C_RG_SUSPENDM BIT(3) #define P2C_RG_TERMSEL BIT(2) #define P2C_DTM0_PART_MASK \ @@ -114,73 +129,66 @@ #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_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_REG6 0x018 #define P3A_RG_TX_EIDLE_CM GENMASK(31, 28) -#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28) #define U3P_U3_PHYA_REG9 0x024 #define P3A_RG_RX_DAC_MUX GENMASK(5, 1) -#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1) #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_LFPS1 0x00c #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 U3P_U3_PHYD_IMPCAL1 0x014 +#define P3D_RG_FORCE_RX_IMPEL BIT(31) +#define P3D_RG_RX_IMPEL GENMASK(28, 24) + +#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) #define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8) -#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8) #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)) #define U3P_SPLLC_XTALCTL3 0x018 #define XC3_RG_U3_XTAL_RX_PWD BIT(9) @@ -188,10 +196,8 @@ #define U3P_U2FREQ_FMCR0 0x00 #define P2F_RG_MONCLK_SEL GENMASK(27, 26) -#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26) #define P2F_RG_FREQDET_EN BIT(24) #define P2F_RG_CYCLECNT GENMASK(23, 0) -#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) #define U3P_U2FREQ_VALUE 0x0c @@ -208,69 +214,76 @@ #define PHYD_CTRL_SIGNAL_MODE4 0x1c /* CDR Charge Pump P-path current adjustment */ #define RG_CDR_BICLTD1_GEN1_MSK GENMASK(23, 20) -#define RG_CDR_BICLTD1_GEN1_VAL(x) ((0xf & (x)) << 20) #define RG_CDR_BICLTD0_GEN1_MSK GENMASK(11, 8) -#define RG_CDR_BICLTD0_GEN1_VAL(x) ((0xf & (x)) << 8) #define PHYD_DESIGN_OPTION2 0x24 /* Symbol lock count selection */ #define RG_LOCK_CNT_SEL_MSK GENMASK(5, 4) -#define RG_LOCK_CNT_SEL_VAL(x) ((0x3 & (x)) << 4) #define PHYD_DESIGN_OPTION9 0x40 /* COMWAK GAP width window */ #define RG_TG_MAX_MSK GENMASK(20, 16) -#define RG_TG_MAX_VAL(x) ((0x1f & (x)) << 16) /* COMINIT GAP width window */ #define RG_T2_MAX_MSK GENMASK(13, 8) -#define RG_T2_MAX_VAL(x) ((0x3f & (x)) << 8) /* COMWAK GAP width window */ #define RG_TG_MIN_MSK GENMASK(7, 5) -#define RG_TG_MIN_VAL(x) ((0x7 & (x)) << 5) /* COMINIT GAP width window */ #define RG_T2_MIN_MSK GENMASK(4, 0) -#define RG_T2_MIN_VAL(x) (0x1f & (x)) #define ANA_RG_CTRL_SIGNAL1 0x4c /* TX driver tail current control for 0dB de-empahsis mdoe for Gen1 speed */ #define RG_IDRV_0DB_GEN1_MSK GENMASK(13, 8) -#define RG_IDRV_0DB_GEN1_VAL(x) ((0x3f & (x)) << 8) #define ANA_RG_CTRL_SIGNAL4 0x58 #define RG_CDR_BICLTR_GEN1_MSK GENMASK(23, 20) -#define RG_CDR_BICLTR_GEN1_VAL(x) ((0xf & (x)) << 20) /* Loop filter R1 resistance adjustment for Gen1 speed */ #define RG_CDR_BR_GEN2_MSK GENMASK(10, 8) -#define RG_CDR_BR_GEN2_VAL(x) ((0x7 & (x)) << 8) #define ANA_RG_CTRL_SIGNAL6 0x60 /* I-path capacitance adjustment for Gen1 */ #define RG_CDR_BC_GEN1_MSK GENMASK(28, 24) -#define RG_CDR_BC_GEN1_VAL(x) ((0x1f & (x)) << 24) #define RG_CDR_BIRLTR_GEN1_MSK GENMASK(4, 0) -#define RG_CDR_BIRLTR_GEN1_VAL(x) (0x1f & (x)) #define ANA_EQ_EYE_CTRL_SIGNAL1 0x6c /* RX Gen1 LEQ tuning step */ #define RG_EQ_DLEQ_LFI_GEN1_MSK GENMASK(11, 8) -#define RG_EQ_DLEQ_LFI_GEN1_VAL(x) ((0xf & (x)) << 8) #define ANA_EQ_EYE_CTRL_SIGNAL4 0xd8 #define RG_CDR_BIRLTD0_GEN1_MSK GENMASK(20, 16) -#define RG_CDR_BIRLTD0_GEN1_VAL(x) ((0x1f & (x)) << 16) #define ANA_EQ_EYE_CTRL_SIGNAL5 0xdc #define RG_CDR_BIRLTD0_GEN3_MSK GENMASK(4, 0) -#define RG_CDR_BIRLTD0_GEN3_VAL(x) (0x1f & (x)) + +/* PHY switch between pcie/usb3/sgmii/sata */ +#define USB_PHY_SWITCH_CTRL 0x0 +#define RG_PHY_SW_TYPE GENMASK(3, 0) +#define RG_PHY_SW_PCIE 0x0 +#define RG_PHY_SW_USB3 0x1 +#define RG_PHY_SW_SGMII 0x2 +#define RG_PHY_SW_SATA 0x3 + +#define TPHY_CLKS_CNT 2 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; + /* + * workaround only for mt8195, HW fix it for others of V3, + * u2phy should use integer mode instead of fractional mode of + * 48M PLL, fix it by switching PLL to 26M from default 48M + */ + bool sw_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; }; @@ -294,20 +307,28 @@ struct mtk_phy_instance { struct u2phy_banks u2_banks; struct u3phy_banks u3_banks; }; - struct clk *ref_clk; /* reference clock of anolog phy */ + struct clk_bulk_data clks[TPHY_CLKS_CNT]; u32 index; - u8 type; + u32 type; + struct regmap *type_sw; + u32 type_sw_reg; + u32 type_sw_index; + u32 efuse_sw_en; + u32 efuse_intr; + u32 efuse_tx_imp; + u32 efuse_rx_imp; int eye_src; int eye_vrt; int eye_term; + int intr; + int discth; + int pre_emphasis; bool bc12_en; }; struct mtk_tphy { struct device *dev; void __iomem *sif_base; /* only shared sif */ - /* deprecated, use @ref_clk instead in phy instance */ - struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */ const struct mtk_phy_pdata *pdata; struct mtk_phy_instance **phys; int nphys; @@ -325,34 +346,32 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, int fm_out; u32 tmp; + /* HW V3 doesn't support slew rate cal anymore */ + if (tphy->pdata->version == MTK_PHY_V3) + return; + /* use force value */ if (instance->eye_src) return; /* enable USB ring oscillator */ - tmp = readl(com + U3P_USBPHYACR5); - tmp |= PA5_RG_U2_HSTX_SRCAL_EN; - writel(tmp, com + U3P_USBPHYACR5); + mtk_phy_set_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCAL_EN); udelay(1); /*enable free run clock */ - tmp = readl(fmreg + U3P_U2FREQ_FMMONR1); - tmp |= P2F_RG_FRCK_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMMONR1); + mtk_phy_set_bits(fmreg + U3P_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); /* set cycle count as 1024, and select u2 channel */ tmp = readl(fmreg + U3P_U2FREQ_FMCR0); tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL); - tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT); + tmp |= FIELD_PREP(P2F_RG_CYCLECNT, U3P_FM_DET_CYCLE_CNT); if (tphy->pdata->version == MTK_PHY_V1) - tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index >> 1); + tmp |= FIELD_PREP(P2F_RG_MONCLK_SEL, instance->index >> 1); writel(tmp, fmreg + U3P_U2FREQ_FMCR0); /* enable frequency meter */ - tmp = readl(fmreg + U3P_U2FREQ_FMCR0); - tmp |= P2F_RG_FREQDET_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMCR0); + mtk_phy_set_bits(fmreg + U3P_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* ignore return value */ readl_poll_timeout(fmreg + U3P_U2FREQ_FMMONR1, tmp, @@ -361,14 +380,10 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, fm_out = readl(fmreg + U3P_U2FREQ_VALUE); /* disable frequency meter */ - tmp = readl(fmreg + U3P_U2FREQ_FMCR0); - tmp &= ~P2F_RG_FREQDET_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMCR0); + mtk_phy_clear_bits(fmreg + U3P_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /*disable free run clock */ - tmp = readl(fmreg + U3P_U2FREQ_FMMONR1); - tmp &= ~P2F_RG_FRCK_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMMONR1); + mtk_phy_clear_bits(fmreg + U3P_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); if (fm_out) { /* ( 1024 / FM_OUT ) x reference clock frequency x coef */ @@ -384,125 +399,106 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, tphy->src_ref_clk, tphy->src_coef); /* set HS slew rate */ - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HSTX_SRCTRL; - tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val); - writel(tmp, com + U3P_USBPHYACR5); + mtk_phy_update_field(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL, + calibration_val); /* disable USB ring oscillator */ - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN; - writel(tmp, com + U3P_USBPHYACR5); + mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCAL_EN); } static void u3_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { struct u3phy_banks *u3_banks = &instance->u3_banks; - u32 tmp; + void __iomem *phya = u3_banks->phya; + void __iomem *phyd = u3_banks->phyd; /* gating PCIe Analog XTAL clock */ - tmp = readl(u3_banks->spllc + U3P_SPLLC_XTALCTL3); - tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD; - writel(tmp, u3_banks->spllc + U3P_SPLLC_XTALCTL3); + mtk_phy_set_bits(u3_banks->spllc + U3P_SPLLC_XTALCTL3, + XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD); /* gating XSQ */ - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0); - tmp &= ~P3A_RG_XTAL_EXT_EN_U3; - tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG9); - tmp &= ~P3A_RG_RX_DAC_MUX; - tmp |= P3A_RG_RX_DAC_MUX_VAL(4); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG9); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG6); - tmp &= ~P3A_RG_TX_EIDLE_CM; - tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG6); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_CDR1); - tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1); - tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_CDR1); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_LFPS1); - tmp &= ~P3D_RG_FWAKE_TH; - tmp |= P3D_RG_FWAKE_TH_VAL(0x34); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_LFPS1); - - 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); + mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG0, P3A_RG_XTAL_EXT_EN_U3, 2); + + mtk_phy_update_field(phya + U3P_U3_PHYA_REG9, P3A_RG_RX_DAC_MUX, 4); + + mtk_phy_update_field(phya + U3P_U3_PHYA_REG6, P3A_RG_TX_EIDLE_CM, 0xe); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_CDR1, + P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1, + FIELD_PREP(P3D_RG_CDR_BIR_LTD0, 0xc) | + FIELD_PREP(P3D_RG_CDR_BIR_LTD1, 0x3)); + + mtk_phy_update_field(phyd + U3P_U3_PHYD_LFPS1, P3D_RG_FWAKE_TH, 0x34); + + mtk_phy_update_field(phyd + U3P_U3_PHYD_RXDET1, P3D_RG_RXDET_STB2_SET, 0x10); + + mtk_phy_update_field(phyd + U3P_U3_PHYD_RXDET2, P3D_RG_RXDET_STB2_SET_P3, 0x10); dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); } +static void u2_phy_pll_26m_set(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + void __iomem *com = u2_banks->com; + + if (!tphy->pdata->sw_pll_48m_to_26m) + return; + + mtk_phy_update_field(com + U3P_USBPHYACR0, PA0_USB20_PLL_PREDIV, 0); + + mtk_phy_update_field(com + U3P_USBPHYACR2, PA2_RG_U2PLL_BW, 3); + + writel(P2R_RG_U2PLL_FBDIV_26M, com + U3P_U2PHYA_RESV); + + mtk_phy_set_bits(com + U3P_U2PHYA_RESV1, + P2R_RG_U2PLL_FRA_EN | P2R_RG_U2PLL_REFCLK_SEL); +} + static void u2_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; /* switch to USB function, and enable usb pll */ - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM); - tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0); - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM); - tmp = readl(com + U3P_U2PHYDTM1); - tmp &= ~P2C_RG_UART_EN; - writel(tmp, com + U3P_U2PHYDTM1); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, + P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK); - tmp = readl(com + U3P_USBPHYACR0); - tmp |= PA0_RG_USB20_INTR_EN; - writel(tmp, com + U3P_USBPHYACR0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_UART_EN); + + mtk_phy_set_bits(com + U3P_USBPHYACR0, PA0_RG_USB20_INTR_EN); /* disable switch 100uA current to SSUSB */ - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HS_100U_U3_EN; - writel(tmp, com + U3P_USBPHYACR5); - - if (!index) { - tmp = readl(com + U3P_U2PHYACR4); - tmp &= ~P2C_U2_GPIO_CTR_MSK; - writel(tmp, com + U3P_U2PHYACR4); - } + mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HS_100U_U3_EN); + + mtk_phy_clear_bits(com + U3P_U2PHYACR4, P2C_U2_GPIO_CTR_MSK); if (tphy->pdata->avoid_rx_sen_degradation) { if (!index) { - tmp = readl(com + U3P_USBPHYACR2); - tmp |= PA2_RG_SIF_U2PLL_FORCE_EN; - writel(tmp, com + U3P_USBPHYACR2); + mtk_phy_set_bits(com + U3P_USBPHYACR2, PA2_RG_SIF_U2PLL_FORCE_EN); - tmp = readl(com + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); } else { - tmp = readl(com + U3D_U2PHYDCR0); - tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_set_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); - tmp = readl(com + U3P_U2PHYDTM0); - tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_set_bits(com + U3P_U2PHYDTM0, + P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); } } - tmp = readl(com + U3P_USBPHYACR6); - tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */ - tmp &= ~PA6_RG_U2_SQTH; - tmp |= PA6_RG_U2_SQTH_VAL(2); - writel(tmp, com + U3P_USBPHYACR6); + /* DP/DM BC1.1 path Disable */ + mtk_phy_clear_bits(com + U3P_USBPHYACR6, PA6_RG_U2_BC11_SW_EN); + + mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_SQTH, 2); + + /* Workaround only for mt8195, HW fix it for others (V3) */ + u2_phy_pll_26m_set(tphy, instance); dev_dbg(tphy->dev, "%s(%d)\n", __func__, index); } @@ -513,30 +509,18 @@ static void u2_phy_instance_power_on(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; - - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK); - writel(tmp, com + U3P_U2PHYDTM0); /* OTG Enable */ - tmp = readl(com + U3P_USBPHYACR6); - tmp |= PA6_RG_U2_OTG_VBUSCMP_EN; - writel(tmp, com + U3P_USBPHYACR6); + mtk_phy_set_bits(com + U3P_USBPHYACR6, PA6_RG_U2_OTG_VBUSCMP_EN); - tmp = readl(com + U3P_U2PHYDTM1); - tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID; - tmp &= ~P2C_RG_SESSEND; - writel(tmp, com + U3P_U2PHYDTM1); + mtk_phy_set_bits(com + U3P_U2PHYDTM1, P2C_RG_VBUSVALID | P2C_RG_AVALID); + + mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_SESSEND); if (tphy->pdata->avoid_rx_sen_degradation && index) { - tmp = readl(com + U3D_U2PHYDCR0); - tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_set_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); - tmp = readl(com + U3P_U2PHYDTM0); - tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_set_bits(com + U3P_U2PHYDTM0, P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); } dev_dbg(tphy->dev, "%s(%d)\n", __func__, index); } @@ -547,30 +531,18 @@ static void u2_phy_instance_power_off(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; - - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN); - writel(tmp, com + U3P_U2PHYDTM0); /* OTG Disable */ - tmp = readl(com + U3P_USBPHYACR6); - tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN; - writel(tmp, com + U3P_USBPHYACR6); + mtk_phy_clear_bits(com + U3P_USBPHYACR6, PA6_RG_U2_OTG_VBUSCMP_EN); - tmp = readl(com + U3P_U2PHYDTM1); - tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID); - tmp |= P2C_RG_SESSEND; - writel(tmp, com + U3P_U2PHYDTM1); + mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_VBUSVALID | P2C_RG_AVALID); + + mtk_phy_set_bits(com + U3P_U2PHYDTM1, P2C_RG_SESSEND); if (tphy->pdata->avoid_rx_sen_degradation && index) { - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); - tmp = readl(com + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); } dev_dbg(tphy->dev, "%s(%d)\n", __func__, index); @@ -582,16 +554,11 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; if (tphy->pdata->avoid_rx_sen_degradation && index) { - tmp = readl(com + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~P2C_FORCE_SUSPENDM; - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_FORCE_SUSPENDM); } } @@ -624,69 +591,46 @@ 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; + void __iomem *phya = u3_banks->phya; if (tphy->pdata->version != MTK_PHY_V1) return; - 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); + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG0, + P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H, + FIELD_PREP(P3A_RG_XTAL_EXT_PE1H, 0x2) | + FIELD_PREP(P3A_RG_XTAL_EXT_PE2H, 0x2)); /* 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); + mtk_phy_update_field(phya + U3P_U3_PHYA_REG1, P3A_RG_CLKDRV_AMP, 0x4); - 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); + mtk_phy_update_field(phya + U3P_U3_PHYA_REG0, P3A_RG_CLKDRV_OFF, 0x1); /* 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); + mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG20, P3A_RG_PLL_DELTA1_PE2H, 0x3c); - 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); + mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG25, P3A_RG_PLL_DELTA_PE2H, 0x36); /* 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); + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG5, + P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H, + FIELD_PREP(P3A_RG_PLL_BR_PE2H, 0x1) | + FIELD_PREP(P3A_RG_PLL_IC_PE2H, 0x1)); + + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG4, + P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H, + FIELD_PREP(P3A_RG_PLL_BC_PE2H, 0x3)); + + mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG6, P3A_RG_PLL_IR_PE2H, 0x2); + + mtk_phy_update_field(phya + U3P_U3_PHYA_DA_REG7, P3A_RG_PLL_BP_PE2H, 0xa); /* 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); + mtk_phy_update_field(u3_banks->phyd + U3P_U3_PHYD_RXDET1, + P3D_RG_RXDET_STB2_SET, 0x10); - 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); + mtk_phy_update_field(u3_banks->phyd + U3P_U3_PHYD_RXDET2, + P3D_RG_RXDET_STB2_SET_P3, 0x10); /* wait for PCIe subsys register to active */ usleep_range(2500, 3000); @@ -697,15 +641,12 @@ 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); + mtk_phy_clear_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); - 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); + mtk_phy_clear_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); } static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, @@ -713,15 +654,12 @@ static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, { 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); + mtk_phy_set_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); - 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); + mtk_phy_set_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); } static void sata_phy_instance_init(struct mtk_tphy *tphy, @@ -729,55 +667,42 @@ static void sata_phy_instance_init(struct mtk_tphy *tphy, { struct u3phy_banks *u3_banks = &instance->u3_banks; void __iomem *phyd = u3_banks->phyd; - u32 tmp; /* charge current adjustment */ - tmp = readl(phyd + ANA_RG_CTRL_SIGNAL6); - tmp &= ~(RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK); - tmp |= RG_CDR_BIRLTR_GEN1_VAL(0x6) | RG_CDR_BC_GEN1_VAL(0x1a); - writel(tmp, phyd + ANA_RG_CTRL_SIGNAL6); - - tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL4); - tmp &= ~RG_CDR_BIRLTD0_GEN1_MSK; - tmp |= RG_CDR_BIRLTD0_GEN1_VAL(0x18); - writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL4); - - tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL5); - tmp &= ~RG_CDR_BIRLTD0_GEN3_MSK; - tmp |= RG_CDR_BIRLTD0_GEN3_VAL(0x06); - writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL5); - - tmp = readl(phyd + ANA_RG_CTRL_SIGNAL4); - tmp &= ~(RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK); - tmp |= RG_CDR_BICLTR_GEN1_VAL(0x0c) | RG_CDR_BR_GEN2_VAL(0x07); - writel(tmp, phyd + ANA_RG_CTRL_SIGNAL4); - - tmp = readl(phyd + PHYD_CTRL_SIGNAL_MODE4); - tmp &= ~(RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK); - tmp |= RG_CDR_BICLTD0_GEN1_VAL(0x08) | RG_CDR_BICLTD1_GEN1_VAL(0x02); - writel(tmp, phyd + PHYD_CTRL_SIGNAL_MODE4); - - tmp = readl(phyd + PHYD_DESIGN_OPTION2); - tmp &= ~RG_LOCK_CNT_SEL_MSK; - tmp |= RG_LOCK_CNT_SEL_VAL(0x02); - writel(tmp, phyd + PHYD_DESIGN_OPTION2); - - tmp = readl(phyd + PHYD_DESIGN_OPTION9); - tmp &= ~(RG_T2_MIN_MSK | RG_TG_MIN_MSK | - RG_T2_MAX_MSK | RG_TG_MAX_MSK); - tmp |= RG_T2_MIN_VAL(0x12) | RG_TG_MIN_VAL(0x04) | - RG_T2_MAX_VAL(0x31) | RG_TG_MAX_VAL(0x0e); - writel(tmp, phyd + PHYD_DESIGN_OPTION9); - - tmp = readl(phyd + ANA_RG_CTRL_SIGNAL1); - tmp &= ~RG_IDRV_0DB_GEN1_MSK; - tmp |= RG_IDRV_0DB_GEN1_VAL(0x20); - writel(tmp, phyd + ANA_RG_CTRL_SIGNAL1); - - tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL1); - tmp &= ~RG_EQ_DLEQ_LFI_GEN1_MSK; - tmp |= RG_EQ_DLEQ_LFI_GEN1_VAL(0x03); - writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL1); + mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL6, + RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK, + FIELD_PREP(RG_CDR_BIRLTR_GEN1_MSK, 0x6) | + FIELD_PREP(RG_CDR_BC_GEN1_MSK, 0x1a)); + + mtk_phy_update_field(phyd + ANA_EQ_EYE_CTRL_SIGNAL4, RG_CDR_BIRLTD0_GEN1_MSK, 0x18); + + mtk_phy_update_field(phyd + ANA_EQ_EYE_CTRL_SIGNAL5, RG_CDR_BIRLTD0_GEN3_MSK, 0x06); + + mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL4, + RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK, + FIELD_PREP(RG_CDR_BICLTR_GEN1_MSK, 0x0c) | + FIELD_PREP(RG_CDR_BR_GEN2_MSK, 0x07)); + + mtk_phy_update_bits(phyd + PHYD_CTRL_SIGNAL_MODE4, + RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK, + FIELD_PREP(RG_CDR_BICLTD0_GEN1_MSK, 0x08) | + FIELD_PREP(RG_CDR_BICLTD1_GEN1_MSK, 0x02)); + + mtk_phy_update_field(phyd + PHYD_DESIGN_OPTION2, RG_LOCK_CNT_SEL_MSK, 0x02); + + mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9, + RG_T2_MIN_MSK | RG_TG_MIN_MSK, + FIELD_PREP(RG_T2_MIN_MSK, 0x12) | + FIELD_PREP(RG_TG_MIN_MSK, 0x04)); + + mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9, + RG_T2_MAX_MSK | RG_TG_MAX_MSK, + FIELD_PREP(RG_T2_MAX_MSK, 0x31) | + FIELD_PREP(RG_TG_MAX_MSK, 0x0e)); + + mtk_phy_update_field(phyd + ANA_RG_CTRL_SIGNAL1, RG_IDRV_0DB_GEN1_MSK, 0x20); + + mtk_phy_update_field(phyd + ANA_EQ_EYE_CTRL_SIGNAL1, RG_EQ_DLEQ_LFI_GEN1_MSK, 0x03); dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); } @@ -850,9 +775,17 @@ static void phy_parse_property(struct mtk_tphy *tphy, &instance->eye_vrt); device_property_read_u32(dev, "mediatek,eye-term", &instance->eye_term); - dev_dbg(dev, "bc12:%d, src:%d, vrt:%d, term:%d\n", + device_property_read_u32(dev, "mediatek,intr", + &instance->intr); + device_property_read_u32(dev, "mediatek,discth", + &instance->discth); + device_property_read_u32(dev, "mediatek,pre-emphasis", + &instance->pre_emphasis); + dev_dbg(dev, "bc12:%d, src:%d, vrt:%d, term:%d, intr:%d, disc:%d\n", instance->bc12_en, instance->eye_src, - instance->eye_vrt, instance->eye_term); + instance->eye_vrt, instance->eye_term, + instance->intr, instance->discth); + dev_dbg(dev, "pre-emp:%d\n", instance->pre_emphasis); } static void u2_phy_props_set(struct mtk_tphy *tphy, @@ -860,33 +793,207 @@ static void u2_phy_props_set(struct mtk_tphy *tphy, { struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; - u32 tmp; - if (instance->bc12_en) { - tmp = readl(com + U3P_U2PHYBC12C); - tmp |= P2C_RG_CHGDT_EN; /* BC1.2 path Enable */ - writel(tmp, com + U3P_U2PHYBC12C); + if (instance->bc12_en) /* BC1.2 path Enable */ + mtk_phy_set_bits(com + U3P_U2PHYBC12C, P2C_RG_CHGDT_EN); + + if (tphy->pdata->version < MTK_PHY_V3 && instance->eye_src) + mtk_phy_update_field(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL, + instance->eye_src); + + if (instance->eye_vrt) + mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL, + instance->eye_vrt); + + if (instance->eye_term) + mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL, + instance->eye_term); + + if (instance->intr) { + if (u2_banks->misc) + mtk_phy_set_bits(u2_banks->misc + U3P_MISC_REG1, + MR1_EFUSE_AUTO_LOAD_DIS); + + mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, + instance->intr); + } + + if (instance->discth) + mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH, + instance->discth); + + if (instance->pre_emphasis) + mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_PRE_EMP, + instance->pre_emphasis); +} + +/* type switch for usb3/pcie/sgmii/sata */ +static int phy_type_syscon_get(struct mtk_phy_instance *instance, + struct device_node *dn) +{ + struct of_phandle_args args; + int ret; + + /* type switch function is optional */ + if (!of_property_read_bool(dn, "mediatek,syscon-type")) + return 0; + + ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type", + 2, 0, &args); + if (ret) + return ret; + + instance->type_sw_reg = args.args[0]; + instance->type_sw_index = args.args[1] & 0x3; /* <=3 */ + instance->type_sw = syscon_node_to_regmap(args.np); + of_node_put(args.np); + dev_info(&instance->phy->dev, "type_sw - reg %#x, index %d\n", + instance->type_sw_reg, instance->type_sw_index); + + return PTR_ERR_OR_ZERO(instance->type_sw); +} + +static int phy_type_set(struct mtk_phy_instance *instance) +{ + int type; + u32 offset; + + if (!instance->type_sw) + return 0; + + switch (instance->type) { + case PHY_TYPE_USB3: + type = RG_PHY_SW_USB3; + break; + case PHY_TYPE_PCIE: + type = RG_PHY_SW_PCIE; + break; + case PHY_TYPE_SGMII: + type = RG_PHY_SW_SGMII; + break; + case PHY_TYPE_SATA: + type = RG_PHY_SW_SATA; + break; + case PHY_TYPE_USB2: + default: + return 0; } - if (instance->eye_src) { - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HSTX_SRCTRL; - tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(instance->eye_src); - writel(tmp, com + U3P_USBPHYACR5); + offset = instance->type_sw_index * BITS_PER_BYTE; + regmap_update_bits(instance->type_sw, instance->type_sw_reg, + RG_PHY_SW_TYPE << offset, type << offset); + + 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; + + /* tphy v1 doesn't support sw efuse, skip it */ + if (!tphy->pdata->sw_efuse_supported) { + instance->efuse_sw_en = 0; + return 0; } - if (instance->eye_vrt) { - tmp = readl(com + U3P_USBPHYACR1); - tmp &= ~PA1_RG_VRT_SEL; - tmp |= PA1_RG_VRT_SEL_VAL(instance->eye_vrt); - writel(tmp, com + U3P_USBPHYACR1); + /* software efuse is optional */ + instance->efuse_sw_en = device_property_read_bool(dev, "nvmem-cells"); + if (!instance->efuse_sw_en) + return 0; + + 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_dbg(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_dbg(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; } - if (instance->eye_term) { - tmp = readl(com + U3P_USBPHYACR1); - tmp &= ~PA1_RG_TERM_SEL; - tmp |= PA1_RG_TERM_SEL_VAL(instance->eye_term); - writel(tmp, com + U3P_USBPHYACR1); + 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; + + if (!instance->efuse_sw_en) + return; + + switch (instance->type) { + case PHY_TYPE_USB2: + mtk_phy_set_bits(u2_banks->misc + U3P_MISC_REG1, MR1_EFUSE_AUTO_LOAD_DIS); + + mtk_phy_update_field(u2_banks->com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, + instance->efuse_intr); + break; + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: + mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_RSV, P3D_RG_EFUSE_AUTO_LOAD_DIS); + + mtk_phy_update_field(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL, + instance->efuse_tx_imp); + mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL); + + mtk_phy_update_field(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL, + instance->efuse_rx_imp); + mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL); + + mtk_phy_update_field(u3_banks->phya + U3P_U3_PHYA_REG0, P3A_RG_IEXT_INTR, + instance->efuse_intr); + break; + default: + dev_warn(dev, "no sw efuse for type %d\n", instance->type); + break; } } @@ -896,17 +1003,11 @@ static int mtk_phy_init(struct phy *phy) struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent); int ret; - ret = clk_prepare_enable(tphy->u3phya_ref); - if (ret) { - dev_err(tphy->dev, "failed to enable u3phya_ref\n"); + ret = clk_bulk_prepare_enable(TPHY_CLKS_CNT, instance->clks); + if (ret) return ret; - } - ret = clk_prepare_enable(instance->ref_clk); - if (ret) { - dev_err(tphy->dev, "failed to enable ref_clk\n"); - return ret; - } + phy_efuse_set(instance); switch (instance->type) { case PHY_TYPE_USB2: @@ -922,8 +1023,12 @@ static int mtk_phy_init(struct phy *phy) case PHY_TYPE_SATA: sata_phy_instance_init(tphy, instance); break; + case PHY_TYPE_SGMII: + /* nothing to do, only used to set type */ + break; default: dev_err(tphy->dev, "incompatible PHY type\n"); + clk_bulk_disable_unprepare(TPHY_CLKS_CNT, instance->clks); return -EINVAL; } @@ -966,8 +1071,7 @@ static int mtk_phy_exit(struct phy *phy) if (instance->type == PHY_TYPE_USB2) u2_phy_instance_exit(tphy, instance); - clk_disable_unprepare(instance->ref_clk); - clk_disable_unprepare(tphy->u3phya_ref); + clk_bulk_disable_unprepare(TPHY_CLKS_CNT, instance->clks); return 0; } @@ -989,6 +1093,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"); @@ -1010,21 +1115,31 @@ static struct phy *mtk_phy_xlate(struct device *dev, if (!(instance->type == PHY_TYPE_USB2 || instance->type == PHY_TYPE_USB3 || instance->type == PHY_TYPE_PCIE || - instance->type == PHY_TYPE_SATA)) { + instance->type == PHY_TYPE_SATA || + instance->type == PHY_TYPE_SGMII)) { dev_err(dev, "unsupported device type: %d\n", instance->type); return ERR_PTR(-EINVAL); } - if (tphy->pdata->version == MTK_PHY_V1) { + switch (tphy->pdata->version) { + case MTK_PHY_V1: phy_v1_banks_init(tphy, instance); - } else if (tphy->pdata->version == MTK_PHY_V2) { + break; + case MTK_PHY_V2: + case MTK_PHY_V3: phy_v2_banks_init(tphy, instance); - } else { + break; + default: dev_err(dev, "phy version is not supported\n"); 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); return instance->phy; } @@ -1045,20 +1160,34 @@ 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 = { + .sw_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 }, { .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata }, + { .compatible = "mediatek,mt8195-tphy", .data = &mt8195_pdata }, { .compatible = "mediatek,generic-tphy-v1", .data = &tphy_v1_pdata }, { .compatible = "mediatek,generic-tphy-v2", .data = &tphy_v2_pdata }, + { .compatible = "mediatek,generic-tphy-v3", .data = &tphy_v3_pdata }, { }, }; MODULE_DEVICE_TABLE(of, mtk_tphy_id_table); @@ -1102,21 +1231,21 @@ static int mtk_tphy_probe(struct platform_device *pdev) } } - /* it's deprecated, make it optional for backward compatibility */ - tphy->u3phya_ref = devm_clk_get_optional(dev, "u3phya_ref"); - if (IS_ERR(tphy->u3phya_ref)) - return PTR_ERR(tphy->u3phya_ref); - - tphy->src_ref_clk = U3P_REF_CLK; - tphy->src_coef = U3P_SLEW_RATE_COEF; - /* update parameters of slew rate calibrate if exist */ - device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", - &tphy->src_ref_clk); - device_property_read_u32(dev, "mediatek,src-coef", &tphy->src_coef); + if (tphy->pdata->version < MTK_PHY_V3) { + tphy->src_ref_clk = U3P_REF_CLK; + tphy->src_coef = U3P_SLEW_RATE_COEF; + /* update parameters of slew rate calibrate if exist */ + device_property_read_u32(dev, "mediatek,src-ref-clk-mhz", + &tphy->src_ref_clk); + device_property_read_u32(dev, "mediatek,src-coef", + &tphy->src_coef); + } port = 0; for_each_child_of_node(np, child_np) { struct mtk_phy_instance *instance; + struct clk_bulk_data *clks; + struct device *subdev; struct phy *phy; instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); @@ -1134,16 +1263,16 @@ static int mtk_tphy_probe(struct platform_device *pdev) goto put_child; } + subdev = &phy->dev; retval = of_address_to_resource(child_np, 0, &res); if (retval) { - dev_err(dev, "failed to get address resource(id-%d)\n", + dev_err(subdev, "failed to get address resource(id-%d)\n", port); goto put_child; } - instance->port_base = devm_ioremap_resource(&phy->dev, &res); + instance->port_base = devm_ioremap_resource(subdev, &res); if (IS_ERR(instance->port_base)) { - dev_err(dev, "failed to remap phy regs\n"); retval = PTR_ERR(instance->port_base); goto put_child; } @@ -1153,16 +1282,16 @@ static int mtk_tphy_probe(struct platform_device *pdev) phy_set_drvdata(phy, instance); port++; - /* if deprecated clock is provided, ignore instance's one */ - if (tphy->u3phya_ref) - continue; + clks = instance->clks; + clks[0].id = "ref"; /* digital (& analog) clock */ + clks[1].id = "da_ref"; /* analog clock */ + retval = devm_clk_bulk_get_optional(subdev, TPHY_CLKS_CNT, clks); + if (retval) + goto put_child; - instance->ref_clk = devm_clk_get(&phy->dev, "ref"); - if (IS_ERR(instance->ref_clk)) { - dev_err(dev, "failed to get ref_clk(id-%d)\n", port); - retval = PTR_ERR(instance->ref_clk); + retval = phy_type_syscon_get(instance, child_np); + if (retval) goto put_child; - } } provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); diff --git a/drivers/phy/mediatek/phy-mtk-ufs.c b/drivers/phy/mediatek/phy-mtk-ufs.c index cf94f5c35dc5..fc19e0fa8ed5 100644 --- a/drivers/phy/mediatek/phy-mtk-ufs.c +++ b/drivers/phy/mediatek/phy-mtk-ufs.c @@ -11,6 +11,8 @@ #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include "phy-mtk-io.h" + /* mphy register and offsets */ #define MP_GLB_DIG_8C 0x008C #define FRC_PLL_ISO_EN BIT(8) @@ -31,41 +33,14 @@ #define FRC_CDR_ISO_EN BIT(19) #define CDR_ISO_EN BIT(20) +#define UFSPHY_CLKS_CNT 2 + struct ufs_mtk_phy { struct device *dev; void __iomem *mmio; - struct clk *mp_clk; - struct clk *unipro_clk; + struct clk_bulk_data clks[UFSPHY_CLKS_CNT]; }; -static inline u32 mphy_readl(struct ufs_mtk_phy *phy, u32 reg) -{ - return readl(phy->mmio + reg); -} - -static inline void mphy_writel(struct ufs_mtk_phy *phy, u32 val, u32 reg) -{ - writel(val, phy->mmio + reg); -} - -static void mphy_set_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit) -{ - u32 val; - - val = mphy_readl(phy, reg); - val |= bit; - mphy_writel(phy, val, reg); -} - -static void mphy_clr_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit) -{ - u32 val; - - val = mphy_readl(phy, reg); - val &= ~bit; - mphy_writel(phy, val, reg); -} - static struct ufs_mtk_phy *get_ufs_mtk_phy(struct phy *generic_phy) { return (struct ufs_mtk_phy *)phy_get_drvdata(generic_phy); @@ -74,75 +49,70 @@ static struct ufs_mtk_phy *get_ufs_mtk_phy(struct phy *generic_phy) static int ufs_mtk_phy_clk_init(struct ufs_mtk_phy *phy) { struct device *dev = phy->dev; + struct clk_bulk_data *clks = phy->clks; - phy->unipro_clk = devm_clk_get(dev, "unipro"); - if (IS_ERR(phy->unipro_clk)) { - dev_err(dev, "failed to get clock: unipro"); - return PTR_ERR(phy->unipro_clk); - } - - phy->mp_clk = devm_clk_get(dev, "mp"); - if (IS_ERR(phy->mp_clk)) { - dev_err(dev, "failed to get clock: mp"); - return PTR_ERR(phy->mp_clk); - } - - return 0; + clks[0].id = "unipro"; + clks[1].id = "mp"; + return devm_clk_bulk_get(dev, UFSPHY_CLKS_CNT, clks); } static void ufs_mtk_phy_set_active(struct ufs_mtk_phy *phy) { + void __iomem *mmio = phy->mmio; + /* release DA_MP_PLL_PWR_ON */ - mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON); - mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON); + mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, PLL_PWR_ON); + mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON); /* release DA_MP_PLL_ISO_EN */ - mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN); - mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN); + mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, PLL_ISO_EN); + mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN); /* release DA_MP_CDR_PWR_ON */ - mphy_set_bit(phy, MP_LN_RX_44, CDR_PWR_ON); - mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON); + mtk_phy_set_bits(mmio + MP_LN_RX_44, CDR_PWR_ON); + mtk_phy_clear_bits(mmio + MP_LN_RX_44, FRC_CDR_PWR_ON); /* release DA_MP_CDR_ISO_EN */ - mphy_clr_bit(phy, MP_LN_RX_44, CDR_ISO_EN); - mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN); + mtk_phy_clear_bits(mmio + MP_LN_RX_44, CDR_ISO_EN); + mtk_phy_clear_bits(mmio + MP_LN_RX_44, FRC_CDR_ISO_EN); /* release DA_MP_RX0_SQ_EN */ - mphy_set_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN); - mphy_clr_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN); + mtk_phy_set_bits(mmio + MP_LN_DIG_RX_AC, RX_SQ_EN); + mtk_phy_clear_bits(mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN); /* delay 1us to wait DIFZ stable */ udelay(1); /* release DIFZ */ - mphy_clr_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC); + mtk_phy_clear_bits(mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC); } static void ufs_mtk_phy_set_deep_hibern(struct ufs_mtk_phy *phy) { + void __iomem *mmio = phy->mmio; + /* force DIFZ */ - mphy_set_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC); + mtk_phy_set_bits(mmio + MP_LN_DIG_RX_9C, FSM_DIFZ_FRC); /* force DA_MP_RX0_SQ_EN */ - mphy_set_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN); - mphy_clr_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN); + mtk_phy_set_bits(mmio + MP_LN_DIG_RX_AC, FRC_RX_SQ_EN); + mtk_phy_clear_bits(mmio + MP_LN_DIG_RX_AC, RX_SQ_EN); /* force DA_MP_CDR_ISO_EN */ - mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN); - mphy_set_bit(phy, MP_LN_RX_44, CDR_ISO_EN); + mtk_phy_set_bits(mmio + MP_LN_RX_44, FRC_CDR_ISO_EN); + mtk_phy_set_bits(mmio + MP_LN_RX_44, CDR_ISO_EN); /* force DA_MP_CDR_PWR_ON */ - mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON); - mphy_clr_bit(phy, MP_LN_RX_44, CDR_PWR_ON); + mtk_phy_set_bits(mmio + MP_LN_RX_44, FRC_CDR_PWR_ON); + mtk_phy_clear_bits(mmio + MP_LN_RX_44, CDR_PWR_ON); /* force DA_MP_PLL_ISO_EN */ - mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN); - mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN); + mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, FRC_PLL_ISO_EN); + mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, PLL_ISO_EN); /* force DA_MP_PLL_PWR_ON */ - mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON); - mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON); + mtk_phy_set_bits(mmio + MP_GLB_DIG_8C, FRC_FRC_PWR_ON); + mtk_phy_clear_bits(mmio + MP_GLB_DIG_8C, PLL_PWR_ON); } static int ufs_mtk_phy_power_on(struct phy *generic_phy) @@ -150,26 +120,13 @@ static int ufs_mtk_phy_power_on(struct phy *generic_phy) struct ufs_mtk_phy *phy = get_ufs_mtk_phy(generic_phy); int ret; - ret = clk_prepare_enable(phy->unipro_clk); - if (ret) { - dev_err(phy->dev, "unipro_clk enable failed %d\n", ret); - goto out; - } - - ret = clk_prepare_enable(phy->mp_clk); - if (ret) { - dev_err(phy->dev, "mp_clk enable failed %d\n", ret); - goto out_unprepare_unipro_clk; - } + ret = clk_bulk_prepare_enable(UFSPHY_CLKS_CNT, phy->clks); + if (ret) + return ret; ufs_mtk_phy_set_active(phy); return 0; - -out_unprepare_unipro_clk: - clk_disable_unprepare(phy->unipro_clk); -out: - return ret; } static int ufs_mtk_phy_power_off(struct phy *generic_phy) @@ -178,8 +135,7 @@ static int ufs_mtk_phy_power_off(struct phy *generic_phy) ufs_mtk_phy_set_deep_hibern(phy); - clk_disable_unprepare(phy->unipro_clk); - clk_disable_unprepare(phy->mp_clk); + clk_bulk_disable_unprepare(UFSPHY_CLKS_CNT, phy->clks); return 0; } @@ -195,7 +151,6 @@ static int ufs_mtk_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct phy *generic_phy; struct phy_provider *phy_provider; - struct resource *res; struct ufs_mtk_phy *phy; int ret; @@ -203,8 +158,7 @@ static int ufs_mtk_phy_probe(struct platform_device *pdev) if (!phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->mmio = devm_ioremap_resource(dev, res); + phy->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c index 8c51131945c0..b222fbbd71d1 100644 --- a/drivers/phy/mediatek/phy-mtk-xsphy.c +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -10,13 +10,14 @@ #include <dt-bindings/phy/phy.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include "phy-mtk-io.h" + /* u2 phy banks */ #define SSUSB_SIFSLV_MISC 0x000 #define SSUSB_SIFSLV_U2FREQ 0x100 @@ -36,7 +37,6 @@ #define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00) #define P2F_RG_FREQDET_EN BIT(24) #define P2F_RG_CYCLECNT GENMASK(23, 0) -#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) #define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c) @@ -49,16 +49,12 @@ #define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04) #define P2A1_RG_INTR_CAL GENMASK(23, 19) -#define P2A1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) #define P2A1_RG_VRT_SEL GENMASK(14, 12) -#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) #define P2A1_RG_TERM_SEL GENMASK(10, 8) -#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8) #define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014) #define P2A5_RG_HSTX_SRCAL_EN BIT(15) #define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12) -#define P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) #define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018) #define P2A6_RG_BC11_SW_EN BIT(23) @@ -73,15 +69,12 @@ #define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00) #define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16) -#define RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) #define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) #define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) -#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) #define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) #define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) -#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) #define XSP_REF_CLK 26 /* MHZ */ #define XSP_SLEW_RATE_COEF 17 @@ -126,26 +119,18 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, return; /* enable USB ring oscillator */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp |= P2A5_RG_HSTX_SRCAL_EN; - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_set_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); udelay(1); /* wait clock stable */ /* enable free run clock */ - tmp = readl(pbase + XSP_U2FREQ_FMMONR1); - tmp |= P2F_RG_FRCK_EN; - writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + mtk_phy_set_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); /* set cycle count as 1024 */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp &= ~(P2F_RG_CYCLECNT); - tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_update_field(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT, + XSP_FM_DET_CYCLE_CNT); /* enable frequency meter */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp |= P2F_RG_FREQDET_EN; - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_set_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* ignore return value */ readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, @@ -154,14 +139,10 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, fm_out = readl(pbase + XSP_U2FREQ_MMONR0); /* disable frequency meter */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp &= ~P2F_RG_FREQDET_EN; - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* disable free run clock */ - tmp = readl(pbase + XSP_U2FREQ_FMMONR1); - tmp &= ~P2F_RG_FRCK_EN; - writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); if (fm_out) { /* (1024 / FM_OUT) x reference clock frequency x coefficient */ @@ -177,31 +158,21 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, xsphy->src_ref_clk, xsphy->src_coef); /* set HS slew rate */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCTRL; - tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_update_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, calib_val); /* disable USB ring oscillator */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCAL_EN; - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); } static void u2_phy_instance_init(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; /* DP/DM BC1.1 path Disable */ - tmp = readl(pbase + XSP_USBPHYACR6); - tmp &= ~P2A6_RG_BC11_SW_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN); - tmp = readl(pbase + XSP_USBPHYACR0); - tmp |= P2A0_RG_INTR_EN; - writel(tmp, pbase + XSP_USBPHYACR0); + mtk_phy_set_bits(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN); } static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, @@ -209,16 +180,12 @@ static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, { void __iomem *pbase = inst->port_base; u32 index = inst->index; - u32 tmp; - tmp = readl(pbase + XSP_USBPHYACR6); - tmp |= P2A6_RG_OTG_VBUSCMP_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_set_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); - tmp = readl(pbase + XSP_U2PHYDTM1); - tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; - tmp &= ~P2D_RG_SESSEND; - writel(tmp, pbase + XSP_U2PHYDTM1); + mtk_phy_update_bits(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_VBUSVALID | P2D_RG_AVALID); dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); } @@ -228,16 +195,12 @@ static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, { void __iomem *pbase = inst->port_base; u32 index = inst->index; - u32 tmp; - tmp = readl(pbase + XSP_USBPHYACR6); - tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); - tmp = readl(pbase + XSP_U2PHYDTM1); - tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); - tmp |= P2D_RG_SESSEND; - writel(tmp, pbase + XSP_U2PHYDTM1); + mtk_phy_update_bits(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_SESSEND); dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); } @@ -306,63 +269,40 @@ static void u2_phy_props_set(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; - if (inst->efuse_intr) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_INTR_CAL; - tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->efuse_intr) + mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL, + inst->efuse_intr); - if (inst->eye_src) { - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCTRL; - tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); - writel(tmp, pbase + XSP_USBPHYACR5); - } + if (inst->eye_src) + mtk_phy_update_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, + inst->eye_src); - if (inst->eye_vrt) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_VRT_SEL; - tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->eye_vrt) + mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL, + inst->eye_vrt); - if (inst->eye_term) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_TERM_SEL; - tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->eye_term) + mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL, + inst->eye_term); } static void u3_phy_props_set(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; - if (inst->efuse_intr) { - tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); - tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; - tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); - writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); - } + if (inst->efuse_intr) + mtk_phy_update_field(xsphy->glb_base + SSPXTP_PHYA_GLB_00, + RG_XTP_GLB_BIAS_INTR_CTRL, inst->efuse_intr); - if (inst->efuse_tx_imp) { - tmp = readl(pbase + SSPXTP_PHYA_LN_04); - tmp &= ~RG_XTP_LN0_TX_IMPSEL; - tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); - writel(tmp, pbase + SSPXTP_PHYA_LN_04); - } + if (inst->efuse_tx_imp) + mtk_phy_update_field(pbase + SSPXTP_PHYA_LN_04, + RG_XTP_LN0_TX_IMPSEL, inst->efuse_tx_imp); - if (inst->efuse_rx_imp) { - tmp = readl(pbase + SSPXTP_PHYA_LN_14); - tmp &= ~RG_XTP_LN0_RX_IMPSEL; - tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); - writel(tmp, pbase + SSPXTP_PHYA_LN_14); - } + if (inst->efuse_rx_imp) + mtk_phy_update_field(pbase + SSPXTP_PHYA_LN_14, + RG_XTP_LN0_RX_IMPSEL, inst->efuse_rx_imp); } static int mtk_phy_init(struct phy *phy) diff --git a/drivers/phy/microchip/Kconfig b/drivers/phy/microchip/Kconfig new file mode 100644 index 000000000000..38039ed0754c --- /dev/null +++ b/drivers/phy/microchip/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for Microchip devices +# + +config PHY_SPARX5_SERDES + tristate "Microchip Sparx5 SerDes PHY driver" + select GENERIC_PHY + depends on ARCH_SPARX5 || COMPILE_TEST + depends on OF + depends on HAS_IOMEM + help + Enable this for support of the 10G/25G SerDes on Microchip Sparx5. + +config PHY_LAN966X_SERDES + tristate "SerDes PHY driver for Microchip LAN966X" + select GENERIC_PHY + depends on OF + depends on MFD_SYSCON + help + Enable this for supporting SerDes muxing with Microchip LAN966X diff --git a/drivers/phy/microchip/Makefile b/drivers/phy/microchip/Makefile new file mode 100644 index 000000000000..fd73b87960a5 --- /dev/null +++ b/drivers/phy/microchip/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Microchip phy drivers. +# + +obj-$(CONFIG_PHY_SPARX5_SERDES) := sparx5_serdes.o +obj-$(CONFIG_PHY_LAN966X_SERDES) := lan966x_serdes.o diff --git a/drivers/phy/microchip/lan966x_serdes.c b/drivers/phy/microchip/lan966x_serdes.c new file mode 100644 index 000000000000..c1a41b6cd29b --- /dev/null +++ b/drivers/phy/microchip/lan966x_serdes.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#include <dt-bindings/phy/phy-lan966x-serdes.h> +#include "lan966x_serdes_regs.h" + +#define PLL_CONF_MASK GENMASK(4, 3) +#define PLL_CONF_25MHZ 0 +#define PLL_CONF_125MHZ 1 +#define PLL_CONF_SERDES_125MHZ 2 +#define PLL_CONF_BYPASS 3 + +#define lan_offset_(id, tinst, tcnt, \ + gbase, ginst, gcnt, gwidth, \ + raddr, rinst, rcnt, rwidth) \ + (gbase + ((ginst) * gwidth) + raddr + ((rinst) * rwidth)) +#define lan_offset(...) lan_offset_(__VA_ARGS__) + +#define lan_rmw(val, mask, reg, off) \ + lan_rmw_(val, mask, reg, lan_offset(off)) + +#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \ + .idx = _idx, \ + .port = _port, \ + .mode = _mode, \ + .submode = _submode, \ + .mask = _mask, \ + .mux = _mux, \ +} + +#define SERDES_MUX_GMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_GMII, m, c) +#define SERDES_MUX_SGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c) +#define SERDES_MUX_QSGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c) +#define SERDES_MUX_RGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII, m, c), \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_TXID, m, c), \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_RXID, m, c), \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_ID, m, c) + +static void lan_rmw_(u32 val, u32 mask, void __iomem *mem, u32 offset) +{ + u32 v; + + v = readl(mem + offset); + v = (v & ~mask) | (val & mask); + writel(v, mem + offset); +} + +struct serdes_mux { + u8 idx; + u8 port; + enum phy_mode mode; + int submode; + u32 mask; + u32 mux; +}; + +static const struct serdes_mux lan966x_serdes_muxes[] = { + SERDES_MUX_QSGMII(SERDES6G(1), 0, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + SERDES_MUX_QSGMII(SERDES6G(1), 1, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + SERDES_MUX_QSGMII(SERDES6G(1), 2, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + SERDES_MUX_QSGMII(SERDES6G(1), 3, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + + SERDES_MUX_QSGMII(SERDES6G(2), 4, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + SERDES_MUX_QSGMII(SERDES6G(2), 5, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + SERDES_MUX_QSGMII(SERDES6G(2), 6, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + SERDES_MUX_QSGMII(SERDES6G(2), 7, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + + SERDES_MUX_GMII(CU(0), 0, HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_GMII_ENA_SET(BIT(0))), + SERDES_MUX_GMII(CU(1), 1, HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_GMII_ENA_SET(BIT(1))), + + SERDES_MUX_SGMII(SERDES6G(0), 0, HSIO_HW_CFG_SD6G_0_CFG, 0), + SERDES_MUX_SGMII(SERDES6G(1), 1, HSIO_HW_CFG_SD6G_1_CFG, 0), + SERDES_MUX_SGMII(SERDES6G(0), 2, HSIO_HW_CFG_SD6G_0_CFG, + HSIO_HW_CFG_SD6G_0_CFG_SET(1)), + SERDES_MUX_SGMII(SERDES6G(1), 3, HSIO_HW_CFG_SD6G_1_CFG, + HSIO_HW_CFG_SD6G_1_CFG_SET(1)), + + SERDES_MUX_RGMII(RGMII(0), 2, HSIO_HW_CFG_RGMII_0_CFG | + HSIO_HW_CFG_RGMII_ENA | + HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_RGMII_0_CFG_SET(0) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(0)) | + HSIO_HW_CFG_GMII_ENA_SET(BIT(2))), + SERDES_MUX_RGMII(RGMII(1), 3, HSIO_HW_CFG_RGMII_1_CFG | + HSIO_HW_CFG_RGMII_ENA | + HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_RGMII_1_CFG_SET(0) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(1)) | + HSIO_HW_CFG_GMII_ENA_SET(BIT(3))), + SERDES_MUX_RGMII(RGMII(0), 5, HSIO_HW_CFG_RGMII_0_CFG | + HSIO_HW_CFG_RGMII_ENA | + HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(0)) | + HSIO_HW_CFG_GMII_ENA_SET(BIT(5))), + SERDES_MUX_RGMII(RGMII(1), 6, HSIO_HW_CFG_RGMII_1_CFG | + HSIO_HW_CFG_RGMII_ENA | + HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(1)) | + HSIO_HW_CFG_GMII_ENA_SET(BIT(6))), +}; + +struct serdes_ctrl { + void __iomem *regs; + struct device *dev; + struct phy *phys[SERDES_MAX]; + int ref125; +}; + +struct serdes_macro { + u8 idx; + int port; + struct serdes_ctrl *ctrl; + int speed; + phy_interface_t mode; +}; + +enum lan966x_sd6g40_mode { + LAN966X_SD6G40_MODE_QSGMII, + LAN966X_SD6G40_MODE_SGMII, +}; + +enum lan966x_sd6g40_ltx2rx { + LAN966X_SD6G40_TX2RX_LOOP_NONE, + LAN966X_SD6G40_LTX2RX +}; + +struct lan966x_sd6g40_setup_args { + enum lan966x_sd6g40_mode mode; + enum lan966x_sd6g40_ltx2rx tx2rx_loop; + bool txinvert; + bool rxinvert; + bool refclk125M; + bool mute; +}; + +struct lan966x_sd6g40_mode_args { + enum lan966x_sd6g40_mode mode; + u8 lane_10bit_sel; + u8 mpll_multiplier; + u8 ref_clkdiv2; + u8 tx_rate; + u8 rx_rate; +}; + +struct lan966x_sd6g40_setup { + u8 rx_term_en; + u8 lane_10bit_sel; + u8 tx_invert; + u8 rx_invert; + u8 mpll_multiplier; + u8 lane_loopbk_en; + u8 ref_clkdiv2; + u8 tx_rate; + u8 rx_rate; +}; + +static int lan966x_sd6g40_reg_cfg(struct serdes_macro *macro, + struct lan966x_sd6g40_setup *res_struct, + u32 idx) +{ + u32 value; + + /* Note: SerDes HSIO is configured in 1G_LAN mode */ + lan_rmw(HSIO_SD_CFG_LANE_10BIT_SEL_SET(res_struct->lane_10bit_sel) | + HSIO_SD_CFG_RX_RATE_SET(res_struct->rx_rate) | + HSIO_SD_CFG_TX_RATE_SET(res_struct->tx_rate) | + HSIO_SD_CFG_TX_INVERT_SET(res_struct->tx_invert) | + HSIO_SD_CFG_RX_INVERT_SET(res_struct->rx_invert) | + HSIO_SD_CFG_LANE_LOOPBK_EN_SET(res_struct->lane_loopbk_en) | + HSIO_SD_CFG_RX_RESET_SET(0) | + HSIO_SD_CFG_TX_RESET_SET(0), + HSIO_SD_CFG_LANE_10BIT_SEL | + HSIO_SD_CFG_RX_RATE | + HSIO_SD_CFG_TX_RATE | + HSIO_SD_CFG_TX_INVERT | + HSIO_SD_CFG_RX_INVERT | + HSIO_SD_CFG_LANE_LOOPBK_EN | + HSIO_SD_CFG_RX_RESET | + HSIO_SD_CFG_TX_RESET, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + lan_rmw(HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(res_struct->mpll_multiplier) | + HSIO_MPLL_CFG_REF_CLKDIV2_SET(res_struct->ref_clkdiv2), + HSIO_MPLL_CFG_MPLL_MULTIPLIER | + HSIO_MPLL_CFG_REF_CLKDIV2, + macro->ctrl->regs, HSIO_MPLL_CFG(idx)); + + lan_rmw(HSIO_SD_CFG_RX_TERM_EN_SET(res_struct->rx_term_en), + HSIO_SD_CFG_RX_TERM_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + lan_rmw(HSIO_MPLL_CFG_REF_SSP_EN_SET(1), + HSIO_MPLL_CFG_REF_SSP_EN, + macro->ctrl->regs, HSIO_MPLL_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + lan_rmw(HSIO_SD_CFG_PHY_RESET_SET(0), + HSIO_SD_CFG_PHY_RESET, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + lan_rmw(HSIO_MPLL_CFG_MPLL_EN_SET(1), + HSIO_MPLL_CFG_MPLL_EN, + macro->ctrl->regs, HSIO_MPLL_CFG(idx)); + + usleep_range(7 * USEC_PER_MSEC, 8 * USEC_PER_MSEC); + + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_MPLL_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] mpll_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + lan_rmw(HSIO_SD_CFG_TX_CM_EN_SET(1), + HSIO_SD_CFG_TX_CM_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_TX_CM_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] tx_cm_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + lan_rmw(HSIO_SD_CFG_RX_PLL_EN_SET(1) | + HSIO_SD_CFG_TX_EN_SET(1), + HSIO_SD_CFG_RX_PLL_EN | + HSIO_SD_CFG_TX_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + /* Waiting for serdes 0 rx DPLL to lock... */ + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_RX_PLL_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] rx_pll_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + /* Waiting for serdes 0 tx operational... */ + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_TX_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] tx_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + lan_rmw(HSIO_SD_CFG_TX_DATA_EN_SET(1) | + HSIO_SD_CFG_RX_DATA_EN_SET(1), + HSIO_SD_CFG_TX_DATA_EN | + HSIO_SD_CFG_RX_DATA_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + return 0; +} + +static int lan966x_sd6g40_get_conf_from_mode(struct serdes_macro *macro, + enum lan966x_sd6g40_mode f_mode, + bool ref125M, + struct lan966x_sd6g40_mode_args *ret_val) +{ + switch (f_mode) { + case LAN966X_SD6G40_MODE_QSGMII: + ret_val->lane_10bit_sel = 0; + if (ref125M) { + ret_val->mpll_multiplier = 40; + ret_val->ref_clkdiv2 = 0x1; + ret_val->tx_rate = 0x0; + ret_val->rx_rate = 0x0; + } else { + ret_val->mpll_multiplier = 100; + ret_val->ref_clkdiv2 = 0x0; + ret_val->tx_rate = 0x0; + ret_val->rx_rate = 0x0; + } + break; + + case LAN966X_SD6G40_MODE_SGMII: + ret_val->lane_10bit_sel = 1; + if (ref125M) { + ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 50 : 40; + ret_val->ref_clkdiv2 = 0x1; + ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + } else { + ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 125 : 100; + ret_val->ref_clkdiv2 = 0x0; + ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + } + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int lan966x_calc_sd6g40_setup_lane(struct serdes_macro *macro, + struct lan966x_sd6g40_setup_args config, + struct lan966x_sd6g40_setup *ret_val) +{ + struct lan966x_sd6g40_mode_args sd6g40_mode; + struct lan966x_sd6g40_mode_args *mode_args = &sd6g40_mode; + int ret; + + ret = lan966x_sd6g40_get_conf_from_mode(macro, config.mode, + config.refclk125M, mode_args); + if (ret) + return ret; + + ret_val->lane_10bit_sel = mode_args->lane_10bit_sel; + ret_val->rx_rate = mode_args->rx_rate; + ret_val->tx_rate = mode_args->tx_rate; + ret_val->mpll_multiplier = mode_args->mpll_multiplier; + ret_val->ref_clkdiv2 = mode_args->ref_clkdiv2; + ret_val->rx_term_en = 0; + + if (config.tx2rx_loop == LAN966X_SD6G40_LTX2RX) + ret_val->lane_loopbk_en = 1; + else + ret_val->lane_loopbk_en = 0; + + ret_val->tx_invert = !!config.txinvert; + ret_val->rx_invert = !!config.rxinvert; + + return 0; +} + +static int lan966x_sd6g40_setup_lane(struct serdes_macro *macro, + struct lan966x_sd6g40_setup_args config, + u32 idx) +{ + struct lan966x_sd6g40_setup calc_results = {}; + int ret; + + ret = lan966x_calc_sd6g40_setup_lane(macro, config, &calc_results); + if (ret) + return ret; + + return lan966x_sd6g40_reg_cfg(macro, &calc_results, idx); +} + +static int lan966x_sd6g40_setup(struct serdes_macro *macro, u32 idx, int mode) +{ + struct lan966x_sd6g40_setup_args conf = {}; + + conf.refclk125M = macro->ctrl->ref125; + + if (mode == PHY_INTERFACE_MODE_QSGMII) + conf.mode = LAN966X_SD6G40_MODE_QSGMII; + else + conf.mode = LAN966X_SD6G40_MODE_SGMII; + + return lan966x_sd6g40_setup_lane(macro, conf, idx); +} + +static int lan966x_rgmii_setup(struct serdes_macro *macro, u32 idx, int mode) +{ + bool tx_delay = false; + bool rx_delay = false; + + /* Configure RGMII */ + lan_rmw(HSIO_RGMII_CFG_RGMII_RX_RST_SET(0) | + HSIO_RGMII_CFG_RGMII_TX_RST_SET(0) | + HSIO_RGMII_CFG_TX_CLK_CFG_SET(macro->speed == SPEED_1000 ? 1 : + macro->speed == SPEED_100 ? 2 : + macro->speed == SPEED_10 ? 3 : 0), + HSIO_RGMII_CFG_RGMII_RX_RST | + HSIO_RGMII_CFG_RGMII_TX_RST | + HSIO_RGMII_CFG_TX_CLK_CFG, + macro->ctrl->regs, HSIO_RGMII_CFG(idx)); + + if (mode == PHY_INTERFACE_MODE_RGMII || + mode == PHY_INTERFACE_MODE_RGMII_TXID) + rx_delay = true; + + if (mode == PHY_INTERFACE_MODE_RGMII || + mode == PHY_INTERFACE_MODE_RGMII_RXID) + tx_delay = true; + + /* Setup DLL configuration */ + lan_rmw(HSIO_DLL_CFG_DLL_RST_SET(0) | + HSIO_DLL_CFG_DLL_ENA_SET(rx_delay), + HSIO_DLL_CFG_DLL_RST | + HSIO_DLL_CFG_DLL_ENA, + macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x0 : 0x2)); + + lan_rmw(HSIO_DLL_CFG_DELAY_ENA_SET(rx_delay), + HSIO_DLL_CFG_DELAY_ENA, + macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x0 : 0x2)); + + lan_rmw(HSIO_DLL_CFG_DLL_RST_SET(0) | + HSIO_DLL_CFG_DLL_ENA_SET(tx_delay), + HSIO_DLL_CFG_DLL_RST | + HSIO_DLL_CFG_DLL_ENA, + macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x1 : 0x3)); + + lan_rmw(HSIO_DLL_CFG_DELAY_ENA_SET(tx_delay), + HSIO_DLL_CFG_DELAY_ENA, + macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x1 : 0x3)); + + return 0; +} + +static int serdes_set_speed(struct phy *phy, int speed) +{ + struct serdes_macro *macro = phy_get_drvdata(phy); + + if (!phy_interface_mode_is_rgmii(macro->mode)) + return 0; + + macro->speed = speed; + lan966x_rgmii_setup(macro, macro->idx - (SERDES6G_MAX + 1), macro->mode); + + return 0; +} + +static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct serdes_macro *macro = phy_get_drvdata(phy); + unsigned int i; + int val; + + /* As of now only PHY_MODE_ETHERNET is supported */ + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + if (submode == PHY_INTERFACE_MODE_2500BASEX) + macro->speed = SPEED_2500; + else + macro->speed = SPEED_1000; + + if (submode == PHY_INTERFACE_MODE_1000BASEX || + submode == PHY_INTERFACE_MODE_2500BASEX) + submode = PHY_INTERFACE_MODE_SGMII; + + if (submode == PHY_INTERFACE_MODE_QUSGMII) + submode = PHY_INTERFACE_MODE_QSGMII; + + for (i = 0; i < ARRAY_SIZE(lan966x_serdes_muxes); i++) { + if (macro->idx != lan966x_serdes_muxes[i].idx || + mode != lan966x_serdes_muxes[i].mode || + submode != lan966x_serdes_muxes[i].submode || + macro->port != lan966x_serdes_muxes[i].port) + continue; + + val = readl(macro->ctrl->regs + lan_offset(HSIO_HW_CFG)); + val |= lan966x_serdes_muxes[i].mux; + lan_rmw(val, lan966x_serdes_muxes[i].mask, + macro->ctrl->regs, HSIO_HW_CFG); + + macro->mode = lan966x_serdes_muxes[i].submode; + + if (macro->idx < CU_MAX) + return 0; + + if (macro->idx < SERDES6G_MAX) + return lan966x_sd6g40_setup(macro, + macro->idx - (CU_MAX + 1), + macro->mode); + + if (macro->idx < RGMII_MAX) + return lan966x_rgmii_setup(macro, + macro->idx - (SERDES6G_MAX + 1), + macro->mode); + + return -EOPNOTSUPP; + } + + return -EINVAL; +} + +static const struct phy_ops serdes_ops = { + .set_mode = serdes_set_mode, + .set_speed = serdes_set_speed, + .owner = THIS_MODULE, +}; + +static struct phy *serdes_simple_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct serdes_ctrl *ctrl = dev_get_drvdata(dev); + unsigned int port, idx, i; + + if (args->args_count != 2) + return ERR_PTR(-EINVAL); + + port = args->args[0]; + idx = args->args[1]; + + for (i = 0; i < SERDES_MAX; i++) { + struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]); + + if (idx != macro->idx) + continue; + + macro->port = port; + return ctrl->phys[i]; + } + + return ERR_PTR(-ENODEV); +} + +static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy) +{ + struct serdes_macro *macro; + + *phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops); + if (IS_ERR(*phy)) + return PTR_ERR(*phy); + + macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL); + if (!macro) + return -ENOMEM; + + macro->idx = idx; + macro->ctrl = ctrl; + macro->port = -1; + + phy_set_drvdata(*phy, macro); + + return 0; +} + +static int serdes_probe(struct platform_device *pdev) +{ + struct phy_provider *provider; + struct serdes_ctrl *ctrl; + void __iomem *hw_stat; + unsigned int i; + u32 val; + int ret; + + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->dev = &pdev->dev; + ctrl->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(ctrl->regs)) + return PTR_ERR(ctrl->regs); + + hw_stat = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); + if (IS_ERR(hw_stat)) + return PTR_ERR(hw_stat); + + for (i = 0; i < SERDES_MAX; i++) { + ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]); + if (ret) + return ret; + } + + val = readl(hw_stat); + val = FIELD_GET(PLL_CONF_MASK, val); + ctrl->ref125 = (val == PLL_CONF_125MHZ || + val == PLL_CONF_SERDES_125MHZ); + + dev_set_drvdata(&pdev->dev, ctrl); + + provider = devm_of_phy_provider_register(ctrl->dev, + serdes_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id serdes_ids[] = { + { .compatible = "microchip,lan966x-serdes", }, + {}, +}; +MODULE_DEVICE_TABLE(of, serdes_ids); + +static struct platform_driver mscc_lan966x_serdes = { + .probe = serdes_probe, + .driver = { + .name = "microchip,lan966x-serdes", + .of_match_table = of_match_ptr(serdes_ids), + }, +}; + +module_platform_driver(mscc_lan966x_serdes); + +MODULE_DESCRIPTION("Microchip lan966x switch serdes driver"); +MODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/microchip/lan966x_serdes_regs.h b/drivers/phy/microchip/lan966x_serdes_regs.h new file mode 100644 index 000000000000..ac54cd01fea6 --- /dev/null +++ b/drivers/phy/microchip/lan966x_serdes_regs.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _LAN966X_SERDES_REGS_H_ +#define _LAN966X_SERDES_REGS_H_ + +#include <linux/bitfield.h> +#include <linux/types.h> +#include <linux/bug.h> + +enum lan966x_target { + TARGET_HSIO = 32, + NUM_TARGETS = 66 +}; + +#define __REG(...) __VA_ARGS__ + +/* HSIO:SD:SD_CFG */ +#define HSIO_SD_CFG(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 0, 0, 1, 4) + +#define HSIO_SD_CFG_PHY_RESET BIT(27) +#define HSIO_SD_CFG_PHY_RESET_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_PHY_RESET, x) +#define HSIO_SD_CFG_PHY_RESET_GET(x)\ + FIELD_GET(HSIO_SD_CFG_PHY_RESET, x) + +#define HSIO_SD_CFG_TX_RESET BIT(18) +#define HSIO_SD_CFG_TX_RESET_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_RESET, x) +#define HSIO_SD_CFG_TX_RESET_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_RESET, x) + +#define HSIO_SD_CFG_TX_RATE GENMASK(17, 16) +#define HSIO_SD_CFG_TX_RATE_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_RATE, x) +#define HSIO_SD_CFG_TX_RATE_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_RATE, x) + +#define HSIO_SD_CFG_TX_INVERT BIT(15) +#define HSIO_SD_CFG_TX_INVERT_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_INVERT, x) +#define HSIO_SD_CFG_TX_INVERT_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_INVERT, x) + +#define HSIO_SD_CFG_TX_EN BIT(14) +#define HSIO_SD_CFG_TX_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_EN, x) +#define HSIO_SD_CFG_TX_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_EN, x) + +#define HSIO_SD_CFG_TX_DATA_EN BIT(12) +#define HSIO_SD_CFG_TX_DATA_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_DATA_EN, x) +#define HSIO_SD_CFG_TX_DATA_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_DATA_EN, x) + +#define HSIO_SD_CFG_TX_CM_EN BIT(11) +#define HSIO_SD_CFG_TX_CM_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_CM_EN, x) +#define HSIO_SD_CFG_TX_CM_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_CM_EN, x) + +#define HSIO_SD_CFG_LANE_10BIT_SEL BIT(10) +#define HSIO_SD_CFG_LANE_10BIT_SEL_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_LANE_10BIT_SEL, x) +#define HSIO_SD_CFG_LANE_10BIT_SEL_GET(x)\ + FIELD_GET(HSIO_SD_CFG_LANE_10BIT_SEL, x) + +#define HSIO_SD_CFG_RX_TERM_EN BIT(9) +#define HSIO_SD_CFG_RX_TERM_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_TERM_EN, x) +#define HSIO_SD_CFG_RX_TERM_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_TERM_EN, x) + +#define HSIO_SD_CFG_RX_RESET BIT(8) +#define HSIO_SD_CFG_RX_RESET_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_RESET, x) +#define HSIO_SD_CFG_RX_RESET_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_RESET, x) + +#define HSIO_SD_CFG_RX_RATE GENMASK(7, 6) +#define HSIO_SD_CFG_RX_RATE_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_RATE, x) +#define HSIO_SD_CFG_RX_RATE_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_RATE, x) + +#define HSIO_SD_CFG_RX_PLL_EN BIT(5) +#define HSIO_SD_CFG_RX_PLL_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_PLL_EN, x) +#define HSIO_SD_CFG_RX_PLL_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_PLL_EN, x) + +#define HSIO_SD_CFG_RX_INVERT BIT(3) +#define HSIO_SD_CFG_RX_INVERT_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_INVERT, x) +#define HSIO_SD_CFG_RX_INVERT_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_INVERT, x) + +#define HSIO_SD_CFG_RX_DATA_EN BIT(2) +#define HSIO_SD_CFG_RX_DATA_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_DATA_EN, x) +#define HSIO_SD_CFG_RX_DATA_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_DATA_EN, x) + +#define HSIO_SD_CFG_LANE_LOOPBK_EN BIT(0) +#define HSIO_SD_CFG_LANE_LOOPBK_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_LANE_LOOPBK_EN, x) +#define HSIO_SD_CFG_LANE_LOOPBK_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_LANE_LOOPBK_EN, x) + +/* HSIO:SD:MPLL_CFG */ +#define HSIO_MPLL_CFG(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 8, 0, 1, 4) + +#define HSIO_MPLL_CFG_REF_SSP_EN BIT(18) +#define HSIO_MPLL_CFG_REF_SSP_EN_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_REF_SSP_EN, x) +#define HSIO_MPLL_CFG_REF_SSP_EN_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_REF_SSP_EN, x) + +#define HSIO_MPLL_CFG_REF_CLKDIV2 BIT(17) +#define HSIO_MPLL_CFG_REF_CLKDIV2_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_REF_CLKDIV2, x) +#define HSIO_MPLL_CFG_REF_CLKDIV2_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_REF_CLKDIV2, x) + +#define HSIO_MPLL_CFG_MPLL_EN BIT(16) +#define HSIO_MPLL_CFG_MPLL_EN_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_MPLL_EN, x) +#define HSIO_MPLL_CFG_MPLL_EN_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_MPLL_EN, x) + +#define HSIO_MPLL_CFG_MPLL_MULTIPLIER GENMASK(6, 0) +#define HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_MPLL_MULTIPLIER, x) +#define HSIO_MPLL_CFG_MPLL_MULTIPLIER_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_MPLL_MULTIPLIER, x) + +/* HSIO:SD:SD_STAT */ +#define HSIO_SD_STAT(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 12, 0, 1, 4) + +#define HSIO_SD_STAT_MPLL_STATE BIT(6) +#define HSIO_SD_STAT_MPLL_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_MPLL_STATE, x) +#define HSIO_SD_STAT_MPLL_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_MPLL_STATE, x) + +#define HSIO_SD_STAT_TX_STATE BIT(5) +#define HSIO_SD_STAT_TX_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_TX_STATE, x) +#define HSIO_SD_STAT_TX_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_TX_STATE, x) + +#define HSIO_SD_STAT_TX_CM_STATE BIT(2) +#define HSIO_SD_STAT_TX_CM_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_TX_CM_STATE, x) +#define HSIO_SD_STAT_TX_CM_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_TX_CM_STATE, x) + +#define HSIO_SD_STAT_RX_PLL_STATE BIT(0) +#define HSIO_SD_STAT_RX_PLL_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_RX_PLL_STATE, x) +#define HSIO_SD_STAT_RX_PLL_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_RX_PLL_STATE, x) + +/* HSIO:HW_CFGSTAT:HW_CFG */ +#define HSIO_HW_CFG __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 0, 0, 1, 4) + +#define HSIO_HW_CFG_RGMII_1_CFG BIT(15) +#define HSIO_HW_CFG_RGMII_1_CFG_SET(x)\ + (((x) << 15) & GENMASK(15, 15)) +#define HSIO_HW_CFG_RGMII_1_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_RGMII_1_CFG, x) + +#define HSIO_HW_CFG_RGMII_0_CFG BIT(14) +#define HSIO_HW_CFG_RGMII_0_CFG_SET(x)\ + (((x) << 14) & GENMASK(14, 14)) +#define HSIO_HW_CFG_RGMII_0_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_RGMII_0_CFG, x) + +#define HSIO_HW_CFG_RGMII_ENA GENMASK(13, 12) +#define HSIO_HW_CFG_RGMII_ENA_SET(x)\ + (((x) << 12) & GENMASK(13, 12)) +#define HSIO_HW_CFG_RGMII_ENA_GET(x)\ + FIELD_GET(HSIO_HW_CFG_RGMII_ENA, x) + +#define HSIO_HW_CFG_SD6G_0_CFG BIT(11) +#define HSIO_HW_CFG_SD6G_0_CFG_SET(x)\ + (((x) << 11) & GENMASK(11, 11)) +#define HSIO_HW_CFG_SD6G_0_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_SD6G_0_CFG, x) + +#define HSIO_HW_CFG_SD6G_1_CFG BIT(10) +#define HSIO_HW_CFG_SD6G_1_CFG_SET(x)\ + (((x) << 10) & GENMASK(10, 10)) +#define HSIO_HW_CFG_SD6G_1_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_SD6G_1_CFG, x) + +#define HSIO_HW_CFG_GMII_ENA GENMASK(9, 2) +#define HSIO_HW_CFG_GMII_ENA_SET(x)\ + (((x) << 2) & GENMASK(9, 2)) +#define HSIO_HW_CFG_GMII_ENA_GET(x)\ + FIELD_GET(HSIO_HW_CFG_GMII_ENA, x) + +#define HSIO_HW_CFG_QSGMII_ENA GENMASK(1, 0) +#define HSIO_HW_CFG_QSGMII_ENA_SET(x)\ + ((x) & GENMASK(1, 0)) +#define HSIO_HW_CFG_QSGMII_ENA_GET(x)\ + FIELD_GET(HSIO_HW_CFG_QSGMII_ENA, x) + +/* HSIO:HW_CFGSTAT:RGMII_CFG */ +#define HSIO_RGMII_CFG(r) __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 20, r, 2, 4) + +#define HSIO_RGMII_CFG_TX_CLK_CFG GENMASK(4, 2) +#define HSIO_RGMII_CFG_TX_CLK_CFG_SET(x)\ + FIELD_PREP(HSIO_RGMII_CFG_TX_CLK_CFG, x) +#define HSIO_RGMII_CFG_TX_CLK_CFG_GET(x)\ + FIELD_GET(HSIO_RGMII_CFG_TX_CLK_CFG, x) + +#define HSIO_RGMII_CFG_RGMII_TX_RST BIT(1) +#define HSIO_RGMII_CFG_RGMII_TX_RST_SET(x)\ + FIELD_PREP(HSIO_RGMII_CFG_RGMII_TX_RST, x) +#define HSIO_RGMII_CFG_RGMII_TX_RST_GET(x)\ + FIELD_GET(HSIO_RGMII_CFG_RGMII_TX_RST, x) + +#define HSIO_RGMII_CFG_RGMII_RX_RST BIT(0) +#define HSIO_RGMII_CFG_RGMII_RX_RST_SET(x)\ + FIELD_PREP(HSIO_RGMII_CFG_RGMII_RX_RST, x) +#define HSIO_RGMII_CFG_RGMII_RX_RST_GET(x)\ + FIELD_GET(HSIO_RGMII_CFG_RGMII_RX_RST, x) + +/* HSIO:HW_CFGSTAT:DLL_CFG */ +#define HSIO_DLL_CFG(r) __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 36, r, 4, 4) + +#define HSIO_DLL_CFG_DELAY_ENA BIT(2) +#define HSIO_DLL_CFG_DELAY_ENA_SET(x)\ + FIELD_PREP(HSIO_DLL_CFG_DELAY_ENA, x) +#define HSIO_DLL_CFG_DELAY_ENA_GET(x)\ + FIELD_GET(HSIO_DLL_CFG_DELAY_ENA, x) + +#define HSIO_DLL_CFG_DLL_ENA BIT(1) +#define HSIO_DLL_CFG_DLL_ENA_SET(x)\ + FIELD_PREP(HSIO_DLL_CFG_DLL_ENA, x) +#define HSIO_DLL_CFG_DLL_ENA_GET(x)\ + FIELD_GET(HSIO_DLL_CFG_DLL_ENA, x) + +#define HSIO_DLL_CFG_DLL_RST BIT(0) +#define HSIO_DLL_CFG_DLL_RST_SET(x)\ + FIELD_PREP(HSIO_DLL_CFG_DLL_RST, x) +#define HSIO_DLL_CFG_DLL_RST_GET(x)\ + FIELD_GET(HSIO_DLL_CFG_DLL_RST, x) + +#endif /* _LAN966X_HSIO_REGS_H_ */ diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c new file mode 100644 index 000000000000..ab1b0986aa67 --- /dev/null +++ b/drivers/phy/microchip/sparx5_serdes.c @@ -0,0 +1,2517 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Microchip Sparx5 Switch SerDes driver + * + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + * and the datasheet is available here: + * https://ww1.microchip.com/downloads/en/DeviceDoc/SparX-5_Family_L2L3_Enterprise_10G_Ethernet_Switches_Datasheet_00003822B.pdf + */ +#include <linux/printk.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> + +#include "sparx5_serdes.h" + +#define SPX5_CMU_MAX 14 + +#define SPX5_SERDES_10G_START 13 +#define SPX5_SERDES_25G_START 25 + +enum sparx5_10g28cmu_mode { + SPX5_SD10G28_CMU_MAIN = 0, + SPX5_SD10G28_CMU_AUX1 = 1, + SPX5_SD10G28_CMU_AUX2 = 3, + SPX5_SD10G28_CMU_NONE = 4, +}; + +enum sparx5_sd25g28_mode_preset_type { + SPX5_SD25G28_MODE_PRESET_25000, + SPX5_SD25G28_MODE_PRESET_10000, + SPX5_SD25G28_MODE_PRESET_5000, + SPX5_SD25G28_MODE_PRESET_SD_2G5, + SPX5_SD25G28_MODE_PRESET_1000BASEX, +}; + +enum sparx5_sd10g28_mode_preset_type { + SPX5_SD10G28_MODE_PRESET_10000, + SPX5_SD10G28_MODE_PRESET_SFI_5000_6G, + SPX5_SD10G28_MODE_PRESET_SFI_5000_10G, + SPX5_SD10G28_MODE_PRESET_QSGMII, + SPX5_SD10G28_MODE_PRESET_SD_2G5, + SPX5_SD10G28_MODE_PRESET_1000BASEX, +}; + +struct sparx5_serdes_io_resource { + enum sparx5_serdes_target id; + phys_addr_t offset; +}; + +struct sparx5_sd25g28_mode_preset { + u8 bitwidth; + u8 tx_pre_div; + u8 fifo_ck_div; + u8 pre_divsel; + u8 vco_div_mode; + u8 sel_div; + u8 ck_bitwidth; + u8 subrate; + u8 com_txcal_en; + u8 com_tx_reserve_msb; + u8 com_tx_reserve_lsb; + u8 cfg_itx_ipcml_base; + u8 tx_reserve_lsb; + u8 tx_reserve_msb; + u8 bw; + u8 rxterm; + u8 dfe_tap; + u8 dfe_enable; + bool txmargin; + u8 cfg_ctle_rstn; + u8 r_dfe_rstn; + u8 cfg_pi_bw_3_0; + u8 tx_tap_dly; + u8 tx_tap_adv; +}; + +struct sparx5_sd25g28_media_preset { + u8 cfg_eq_c_force_3_0; + u8 cfg_vga_ctrl_byp_4_0; + u8 cfg_eq_r_force_3_0; + u8 cfg_en_adv; + u8 cfg_en_main; + u8 cfg_en_dly; + u8 cfg_tap_adv_3_0; + u8 cfg_tap_main; + u8 cfg_tap_dly_4_0; + u8 cfg_alos_thr_2_0; +}; + +struct sparx5_sd25g28_args { + u8 if_width; /* UDL if-width: 10/16/20/32/64 */ + bool skip_cmu_cfg:1; /* Enable/disable CMU cfg */ + enum sparx5_10g28cmu_mode cmu_sel; /* Device/Mode serdes uses */ + bool no_pwrcycle:1; /* Omit initial power-cycle */ + bool txinvert:1; /* Enable inversion of output data */ + bool rxinvert:1; /* Enable inversion of input data */ + u16 txswing; /* Set output level */ + u8 rate; /* Rate of network interface */ + u8 pi_bw_gen1; + u8 duty_cycle; /* Set output level to half/full */ + bool mute:1; /* Mute Output Buffer */ + bool reg_rst:1; + u8 com_pll_reserve; +}; + +struct sparx5_sd25g28_params { + u8 reg_rst; + u8 cfg_jc_byp; + u8 cfg_common_reserve_7_0; + u8 r_reg_manual; + u8 r_d_width_ctrl_from_hwt; + u8 r_d_width_ctrl_2_0; + u8 r_txfifo_ck_div_pmad_2_0; + u8 r_rxfifo_ck_div_pmad_2_0; + u8 cfg_pll_lol_set; + u8 cfg_vco_div_mode_1_0; + u8 cfg_pre_divsel_1_0; + u8 cfg_sel_div_3_0; + u8 cfg_vco_start_code_3_0; + u8 cfg_pma_tx_ck_bitwidth_2_0; + u8 cfg_tx_prediv_1_0; + u8 cfg_rxdiv_sel_2_0; + u8 cfg_tx_subrate_2_0; + u8 cfg_rx_subrate_2_0; + u8 r_multi_lane_mode; + u8 cfg_cdrck_en; + u8 cfg_dfeck_en; + u8 cfg_dfe_pd; + u8 cfg_dfedmx_pd; + u8 cfg_dfetap_en_5_1; + u8 cfg_dmux_pd; + u8 cfg_dmux_clk_pd; + u8 cfg_erramp_pd; + u8 cfg_pi_dfe_en; + u8 cfg_pi_en; + u8 cfg_pd_ctle; + u8 cfg_summer_en; + u8 cfg_pmad_ck_pd; + u8 cfg_pd_clk; + u8 cfg_pd_cml; + u8 cfg_pd_driver; + u8 cfg_rx_reg_pu; + u8 cfg_pd_rms_det; + u8 cfg_dcdr_pd; + u8 cfg_ecdr_pd; + u8 cfg_pd_sq; + u8 cfg_itx_ipdriver_base_2_0; + u8 cfg_tap_dly_4_0; + u8 cfg_tap_main; + u8 cfg_en_main; + u8 cfg_tap_adv_3_0; + u8 cfg_en_adv; + u8 cfg_en_dly; + u8 cfg_iscan_en; + u8 l1_pcs_en_fast_iscan; + u8 l0_cfg_bw_1_0; + u8 l0_cfg_txcal_en; + u8 cfg_en_dummy; + u8 cfg_pll_reserve_3_0; + u8 l0_cfg_tx_reserve_15_8; + u8 l0_cfg_tx_reserve_7_0; + u8 cfg_tx_reserve_15_8; + u8 cfg_tx_reserve_7_0; + u8 cfg_bw_1_0; + u8 cfg_txcal_man_en; + u8 cfg_phase_man_4_0; + u8 cfg_quad_man_1_0; + u8 cfg_txcal_shift_code_5_0; + u8 cfg_txcal_valid_sel_3_0; + u8 cfg_txcal_en; + u8 cfg_cdr_kf_2_0; + u8 cfg_cdr_m_7_0; + u8 cfg_pi_bw_3_0; + u8 cfg_pi_steps_1_0; + u8 cfg_dis_2ndorder; + u8 cfg_ctle_rstn; + u8 r_dfe_rstn; + u8 cfg_alos_thr_2_0; + u8 cfg_itx_ipcml_base_1_0; + u8 cfg_rx_reserve_7_0; + u8 cfg_rx_reserve_15_8; + u8 cfg_rxterm_2_0; + u8 cfg_fom_selm; + u8 cfg_rx_sp_ctle_1_0; + u8 cfg_isel_ctle_1_0; + u8 cfg_vga_ctrl_byp_4_0; + u8 cfg_vga_byp; + u8 cfg_agc_adpt_byp; + u8 cfg_eqr_byp; + u8 cfg_eqr_force_3_0; + u8 cfg_eqc_force_3_0; + u8 cfg_sum_setcm_en; + u8 cfg_init_pos_iscan_6_0; + u8 cfg_init_pos_ipi_6_0; + u8 cfg_dfedig_m_2_0; + u8 cfg_en_dfedig; + u8 cfg_pi_DFE_en; + u8 cfg_tx2rx_lp_en; + u8 cfg_txlb_en; + u8 cfg_rx2tx_lp_en; + u8 cfg_rxlb_en; + u8 r_tx_pol_inv; + u8 r_rx_pol_inv; +}; + +struct sparx5_sd10g28_media_preset { + u8 cfg_en_adv; + u8 cfg_en_main; + u8 cfg_en_dly; + u8 cfg_tap_adv_3_0; + u8 cfg_tap_main; + u8 cfg_tap_dly_4_0; + u8 cfg_vga_ctrl_3_0; + u8 cfg_vga_cp_2_0; + u8 cfg_eq_res_3_0; + u8 cfg_eq_r_byp; + u8 cfg_eq_c_force_3_0; + u8 cfg_alos_thr_3_0; +}; + +struct sparx5_sd10g28_mode_preset { + u8 bwidth; /* interface width: 10/16/20/32/64 */ + enum sparx5_10g28cmu_mode cmu_sel; /* Device/Mode serdes uses */ + u8 rate; /* Rate of network interface */ + u8 dfe_tap; + u8 dfe_enable; + u8 pi_bw_gen1; + u8 duty_cycle; /* Set output level to half/full */ +}; + +struct sparx5_sd10g28_args { + bool skip_cmu_cfg:1; /* Enable/disable CMU cfg */ + bool no_pwrcycle:1; /* Omit initial power-cycle */ + bool txinvert:1; /* Enable inversion of output data */ + bool rxinvert:1; /* Enable inversion of input data */ + bool txmargin:1; /* Set output level to half/full */ + u16 txswing; /* Set output level */ + bool mute:1; /* Mute Output Buffer */ + bool is_6g:1; + bool reg_rst:1; +}; + +struct sparx5_sd10g28_params { + u8 cmu_sel; + u8 is_6g; + u8 skip_cmu_cfg; + u8 cfg_lane_reserve_7_0; + u8 cfg_ssc_rtl_clk_sel; + u8 cfg_lane_reserve_15_8; + u8 cfg_txrate_1_0; + u8 cfg_rxrate_1_0; + u8 r_d_width_ctrl_2_0; + u8 cfg_pma_tx_ck_bitwidth_2_0; + u8 cfg_rxdiv_sel_2_0; + u8 r_pcs2pma_phymode_4_0; + u8 cfg_lane_id_2_0; + u8 cfg_cdrck_en; + u8 cfg_dfeck_en; + u8 cfg_dfe_pd; + u8 cfg_dfetap_en_5_1; + u8 cfg_erramp_pd; + u8 cfg_pi_DFE_en; + u8 cfg_pi_en; + u8 cfg_pd_ctle; + u8 cfg_summer_en; + u8 cfg_pd_rx_cktree; + u8 cfg_pd_clk; + u8 cfg_pd_cml; + u8 cfg_pd_driver; + u8 cfg_rx_reg_pu; + u8 cfg_d_cdr_pd; + u8 cfg_pd_sq; + u8 cfg_rxdet_en; + u8 cfg_rxdet_str; + u8 r_multi_lane_mode; + u8 cfg_en_adv; + u8 cfg_en_main; + u8 cfg_en_dly; + u8 cfg_tap_adv_3_0; + u8 cfg_tap_main; + u8 cfg_tap_dly_4_0; + u8 cfg_vga_ctrl_3_0; + u8 cfg_vga_cp_2_0; + u8 cfg_eq_res_3_0; + u8 cfg_eq_r_byp; + u8 cfg_eq_c_force_3_0; + u8 cfg_en_dfedig; + u8 cfg_sum_setcm_en; + u8 cfg_en_preemph; + u8 cfg_itx_ippreemp_base_1_0; + u8 cfg_itx_ipdriver_base_2_0; + u8 cfg_ibias_tune_reserve_5_0; + u8 cfg_txswing_half; + u8 cfg_dis_2nd_order; + u8 cfg_rx_ssc_lh; + u8 cfg_pi_floop_steps_1_0; + u8 cfg_pi_ext_dac_23_16; + u8 cfg_pi_ext_dac_15_8; + u8 cfg_iscan_ext_dac_7_0; + u8 cfg_cdr_kf_gen1_2_0; + u8 cfg_cdr_kf_gen2_2_0; + u8 cfg_cdr_kf_gen3_2_0; + u8 cfg_cdr_kf_gen4_2_0; + u8 r_cdr_m_gen1_7_0; + u8 cfg_pi_bw_gen1_3_0; + u8 cfg_pi_bw_gen2; + u8 cfg_pi_bw_gen3; + u8 cfg_pi_bw_gen4; + u8 cfg_pi_ext_dac_7_0; + u8 cfg_pi_steps; + u8 cfg_mp_max_3_0; + u8 cfg_rstn_dfedig; + u8 cfg_alos_thr_3_0; + u8 cfg_predrv_slewrate_1_0; + u8 cfg_itx_ipcml_base_1_0; + u8 cfg_ip_pre_base_1_0; + u8 r_cdr_m_gen2_7_0; + u8 r_cdr_m_gen3_7_0; + u8 r_cdr_m_gen4_7_0; + u8 r_en_auto_cdr_rstn; + u8 cfg_oscal_afe; + u8 cfg_pd_osdac_afe; + u8 cfg_resetb_oscal_afe[2]; + u8 cfg_center_spreading; + u8 cfg_m_cnt_maxval_4_0; + u8 cfg_ncnt_maxval_7_0; + u8 cfg_ncnt_maxval_10_8; + u8 cfg_ssc_en; + u8 cfg_tx2rx_lp_en; + u8 cfg_txlb_en; + u8 cfg_rx2tx_lp_en; + u8 cfg_rxlb_en; + u8 r_tx_pol_inv; + u8 r_rx_pol_inv; + u8 fx_100; +}; + +static struct sparx5_sd25g28_media_preset media_presets_25g[] = { + { /* ETH_MEDIA_DEFAULT */ + .cfg_en_adv = 0, + .cfg_en_main = 1, + .cfg_en_dly = 0, + .cfg_tap_adv_3_0 = 0, + .cfg_tap_main = 1, + .cfg_tap_dly_4_0 = 0, + .cfg_eq_c_force_3_0 = 0xf, + .cfg_vga_ctrl_byp_4_0 = 4, + .cfg_eq_r_force_3_0 = 12, + .cfg_alos_thr_2_0 = 7, + }, + { /* ETH_MEDIA_SR */ + .cfg_en_adv = 1, + .cfg_en_main = 1, + .cfg_en_dly = 1, + .cfg_tap_adv_3_0 = 0, + .cfg_tap_main = 1, + .cfg_tap_dly_4_0 = 0x10, + .cfg_eq_c_force_3_0 = 0xf, + .cfg_vga_ctrl_byp_4_0 = 8, + .cfg_eq_r_force_3_0 = 4, + .cfg_alos_thr_2_0 = 0, + }, + { /* ETH_MEDIA_DAC */ + .cfg_en_adv = 0, + .cfg_en_main = 1, + .cfg_en_dly = 0, + .cfg_tap_adv_3_0 = 0, + .cfg_tap_main = 1, + .cfg_tap_dly_4_0 = 0, + .cfg_eq_c_force_3_0 = 0xf, + .cfg_vga_ctrl_byp_4_0 = 8, + .cfg_eq_r_force_3_0 = 0xc, + .cfg_alos_thr_2_0 = 0, + }, +}; + +static struct sparx5_sd25g28_mode_preset mode_presets_25g[] = { + { /* SPX5_SD25G28_MODE_PRESET_25000 */ + .bitwidth = 40, + .tx_pre_div = 0, + .fifo_ck_div = 0, + .pre_divsel = 1, + .vco_div_mode = 0, + .sel_div = 15, + .ck_bitwidth = 3, + .subrate = 0, + .com_txcal_en = 0, + .com_tx_reserve_msb = (0x26 << 1), + .com_tx_reserve_lsb = 0xf0, + .cfg_itx_ipcml_base = 0, + .tx_reserve_msb = 0xcc, + .tx_reserve_lsb = 0xfe, + .bw = 3, + .rxterm = 0, + .dfe_enable = 1, + .dfe_tap = 0x1f, + .txmargin = 1, + .cfg_ctle_rstn = 1, + .r_dfe_rstn = 1, + .cfg_pi_bw_3_0 = 0, + .tx_tap_dly = 8, + .tx_tap_adv = 0xc, + }, + { /* SPX5_SD25G28_MODE_PRESET_10000 */ + .bitwidth = 64, + .tx_pre_div = 0, + .fifo_ck_div = 2, + .pre_divsel = 0, + .vco_div_mode = 1, + .sel_div = 9, + .ck_bitwidth = 0, + .subrate = 0, + .com_txcal_en = 1, + .com_tx_reserve_msb = (0x20 << 1), + .com_tx_reserve_lsb = 0x40, + .cfg_itx_ipcml_base = 0, + .tx_reserve_msb = 0x4c, + .tx_reserve_lsb = 0x44, + .bw = 3, + .cfg_pi_bw_3_0 = 0, + .rxterm = 3, + .dfe_enable = 1, + .dfe_tap = 0x1f, + .txmargin = 0, + .cfg_ctle_rstn = 1, + .r_dfe_rstn = 1, + .tx_tap_dly = 0, + .tx_tap_adv = 0, + }, + { /* SPX5_SD25G28_MODE_PRESET_5000 */ + .bitwidth = 64, + .tx_pre_div = 0, + .fifo_ck_div = 2, + .pre_divsel = 0, + .vco_div_mode = 2, + .sel_div = 9, + .ck_bitwidth = 0, + .subrate = 0, + .com_txcal_en = 1, + .com_tx_reserve_msb = (0x20 << 1), + .com_tx_reserve_lsb = 0, + .cfg_itx_ipcml_base = 0, + .tx_reserve_msb = 0xe, + .tx_reserve_lsb = 0x80, + .bw = 0, + .rxterm = 0, + .cfg_pi_bw_3_0 = 6, + .dfe_enable = 0, + .dfe_tap = 0, + .tx_tap_dly = 0, + .tx_tap_adv = 0, + }, + { /* SPX5_SD25G28_MODE_PRESET_SD_2G5 */ + .bitwidth = 10, + .tx_pre_div = 0, + .fifo_ck_div = 0, + .pre_divsel = 0, + .vco_div_mode = 1, + .sel_div = 6, + .ck_bitwidth = 3, + .subrate = 2, + .com_txcal_en = 1, + .com_tx_reserve_msb = (0x26 << 1), + .com_tx_reserve_lsb = (0xf << 4), + .cfg_itx_ipcml_base = 2, + .tx_reserve_msb = 0x8, + .tx_reserve_lsb = 0x8a, + .bw = 0, + .cfg_pi_bw_3_0 = 0, + .rxterm = (1 << 2), + .dfe_enable = 0, + .dfe_tap = 0, + .tx_tap_dly = 0, + .tx_tap_adv = 0, + }, + { /* SPX5_SD25G28_MODE_PRESET_1000BASEX */ + .bitwidth = 10, + .tx_pre_div = 0, + .fifo_ck_div = 1, + .pre_divsel = 0, + .vco_div_mode = 1, + .sel_div = 8, + .ck_bitwidth = 3, + .subrate = 3, + .com_txcal_en = 1, + .com_tx_reserve_msb = (0x26 << 1), + .com_tx_reserve_lsb = 0xf0, + .cfg_itx_ipcml_base = 0, + .tx_reserve_msb = 0x8, + .tx_reserve_lsb = 0xce, + .bw = 0, + .rxterm = 0, + .cfg_pi_bw_3_0 = 0, + .dfe_enable = 0, + .dfe_tap = 0, + .tx_tap_dly = 0, + .tx_tap_adv = 0, + }, +}; + +static struct sparx5_sd10g28_media_preset media_presets_10g[] = { + { /* ETH_MEDIA_DEFAULT */ + .cfg_en_adv = 0, + .cfg_en_main = 1, + .cfg_en_dly = 0, + .cfg_tap_adv_3_0 = 0, + .cfg_tap_main = 1, + .cfg_tap_dly_4_0 = 0, + .cfg_vga_ctrl_3_0 = 5, + .cfg_vga_cp_2_0 = 0, + .cfg_eq_res_3_0 = 0xa, + .cfg_eq_r_byp = 1, + .cfg_eq_c_force_3_0 = 0x8, + .cfg_alos_thr_3_0 = 0x3, + }, + { /* ETH_MEDIA_SR */ + .cfg_en_adv = 1, + .cfg_en_main = 1, + .cfg_en_dly = 1, + .cfg_tap_adv_3_0 = 0, + .cfg_tap_main = 1, + .cfg_tap_dly_4_0 = 0xc, + .cfg_vga_ctrl_3_0 = 0xa, + .cfg_vga_cp_2_0 = 0x4, + .cfg_eq_res_3_0 = 0xa, + .cfg_eq_r_byp = 1, + .cfg_eq_c_force_3_0 = 0xF, + .cfg_alos_thr_3_0 = 0x3, + }, + { /* ETH_MEDIA_DAC */ + .cfg_en_adv = 1, + .cfg_en_main = 1, + .cfg_en_dly = 1, + .cfg_tap_adv_3_0 = 12, + .cfg_tap_main = 1, + .cfg_tap_dly_4_0 = 8, + .cfg_vga_ctrl_3_0 = 0xa, + .cfg_vga_cp_2_0 = 4, + .cfg_eq_res_3_0 = 0xa, + .cfg_eq_r_byp = 1, + .cfg_eq_c_force_3_0 = 0xf, + .cfg_alos_thr_3_0 = 0x0, + } +}; + +static struct sparx5_sd10g28_mode_preset mode_presets_10g[] = { + { /* SPX5_SD10G28_MODE_PRESET_10000 */ + .bwidth = 64, + .cmu_sel = SPX5_SD10G28_CMU_MAIN, + .rate = 0x0, + .dfe_enable = 1, + .dfe_tap = 0x1f, + .pi_bw_gen1 = 0x0, + .duty_cycle = 0x2, + }, + { /* SPX5_SD10G28_MODE_PRESET_SFI_5000_6G */ + .bwidth = 16, + .cmu_sel = SPX5_SD10G28_CMU_MAIN, + .rate = 0x1, + .dfe_enable = 0, + .dfe_tap = 0, + .pi_bw_gen1 = 0x5, + .duty_cycle = 0x0, + }, + { /* SPX5_SD10G28_MODE_PRESET_SFI_5000_10G */ + .bwidth = 64, + .cmu_sel = SPX5_SD10G28_CMU_MAIN, + .rate = 0x1, + .dfe_enable = 0, + .dfe_tap = 0, + .pi_bw_gen1 = 0x5, + .duty_cycle = 0x0, + }, + { /* SPX5_SD10G28_MODE_PRESET_QSGMII */ + .bwidth = 20, + .cmu_sel = SPX5_SD10G28_CMU_AUX1, + .rate = 0x1, + .dfe_enable = 0, + .dfe_tap = 0, + .pi_bw_gen1 = 0x5, + .duty_cycle = 0x0, + }, + { /* SPX5_SD10G28_MODE_PRESET_SD_2G5 */ + .bwidth = 10, + .cmu_sel = SPX5_SD10G28_CMU_AUX2, + .rate = 0x2, + .dfe_enable = 0, + .dfe_tap = 0, + .pi_bw_gen1 = 0x7, + .duty_cycle = 0x0, + }, + { /* SPX5_SD10G28_MODE_PRESET_1000BASEX */ + .bwidth = 10, + .cmu_sel = SPX5_SD10G28_CMU_AUX1, + .rate = 0x3, + .dfe_enable = 0, + .dfe_tap = 0, + .pi_bw_gen1 = 0x7, + .duty_cycle = 0x0, + }, +}; + +/* map from SD25G28 interface width to configuration value */ +static u8 sd25g28_get_iw_setting(struct device *dev, const u8 interface_width) +{ + switch (interface_width) { + case 10: return 0; + case 16: return 1; + case 32: return 3; + case 40: return 4; + case 64: return 5; + default: + dev_err(dev, "%s: Illegal value %d for interface width\n", + __func__, interface_width); + } + return 0; +} + +/* map from SD10G28 interface width to configuration value */ +static u8 sd10g28_get_iw_setting(struct device *dev, const u8 interface_width) +{ + switch (interface_width) { + case 10: return 0; + case 16: return 1; + case 20: return 2; + case 32: return 3; + case 40: return 4; + case 64: return 7; + default: + dev_err(dev, "%s: Illegal value %d for interface width\n", __func__, + interface_width); + return 0; + } +} + +static int sparx5_sd10g25_get_mode_preset(struct sparx5_serdes_macro *macro, + struct sparx5_sd25g28_mode_preset *mode) +{ + switch (macro->serdesmode) { + case SPX5_SD_MODE_SFI: + if (macro->speed == SPEED_25000) + *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_25000]; + else if (macro->speed == SPEED_10000) + *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_10000]; + else if (macro->speed == SPEED_5000) + *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_5000]; + break; + case SPX5_SD_MODE_2G5: + *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_SD_2G5]; + break; + case SPX5_SD_MODE_1000BASEX: + *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_1000BASEX]; + break; + case SPX5_SD_MODE_100FX: + /* Not supported */ + return -EINVAL; + default: + *mode = mode_presets_25g[SPX5_SD25G28_MODE_PRESET_25000]; + break; + } + return 0; +} + +static int sparx5_sd10g28_get_mode_preset(struct sparx5_serdes_macro *macro, + struct sparx5_sd10g28_mode_preset *mode, + struct sparx5_sd10g28_args *args) +{ + switch (macro->serdesmode) { + case SPX5_SD_MODE_SFI: + if (macro->speed == SPEED_10000) { + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_10000]; + } else if (macro->speed == SPEED_5000) { + if (args->is_6g) + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_SFI_5000_6G]; + else + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_SFI_5000_10G]; + } else { + dev_err(macro->priv->dev, "%s: Illegal speed: %02u, sidx: %02u, mode (%u)", + __func__, macro->speed, macro->sidx, + macro->serdesmode); + return -EINVAL; + } + break; + case SPX5_SD_MODE_QSGMII: + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_QSGMII]; + break; + case SPX5_SD_MODE_2G5: + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_SD_2G5]; + break; + case SPX5_SD_MODE_100FX: + case SPX5_SD_MODE_1000BASEX: + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_1000BASEX]; + break; + default: + *mode = mode_presets_10g[SPX5_SD10G28_MODE_PRESET_10000]; + break; + } + return 0; +} + +static void sparx5_sd25g28_get_params(struct sparx5_serdes_macro *macro, + struct sparx5_sd25g28_media_preset *media, + struct sparx5_sd25g28_mode_preset *mode, + struct sparx5_sd25g28_args *args, + struct sparx5_sd25g28_params *params) +{ + u8 iw = sd25g28_get_iw_setting(macro->priv->dev, mode->bitwidth); + struct sparx5_sd25g28_params init = { + .r_d_width_ctrl_2_0 = iw, + .r_txfifo_ck_div_pmad_2_0 = mode->fifo_ck_div, + .r_rxfifo_ck_div_pmad_2_0 = mode->fifo_ck_div, + .cfg_vco_div_mode_1_0 = mode->vco_div_mode, + .cfg_pre_divsel_1_0 = mode->pre_divsel, + .cfg_sel_div_3_0 = mode->sel_div, + .cfg_vco_start_code_3_0 = 0, + .cfg_pma_tx_ck_bitwidth_2_0 = mode->ck_bitwidth, + .cfg_tx_prediv_1_0 = mode->tx_pre_div, + .cfg_rxdiv_sel_2_0 = mode->ck_bitwidth, + .cfg_tx_subrate_2_0 = mode->subrate, + .cfg_rx_subrate_2_0 = mode->subrate, + .r_multi_lane_mode = 0, + .cfg_cdrck_en = 1, + .cfg_dfeck_en = mode->dfe_enable, + .cfg_dfe_pd = mode->dfe_enable == 1 ? 0 : 1, + .cfg_dfedmx_pd = 1, + .cfg_dfetap_en_5_1 = mode->dfe_tap, + .cfg_dmux_pd = 0, + .cfg_dmux_clk_pd = 1, + .cfg_erramp_pd = mode->dfe_enable == 1 ? 0 : 1, + .cfg_pi_DFE_en = mode->dfe_enable, + .cfg_pi_en = 1, + .cfg_pd_ctle = 0, + .cfg_summer_en = 1, + .cfg_pmad_ck_pd = 0, + .cfg_pd_clk = 0, + .cfg_pd_cml = 0, + .cfg_pd_driver = 0, + .cfg_rx_reg_pu = 1, + .cfg_pd_rms_det = 1, + .cfg_dcdr_pd = 0, + .cfg_ecdr_pd = 1, + .cfg_pd_sq = 1, + .cfg_itx_ipdriver_base_2_0 = mode->txmargin, + .cfg_tap_dly_4_0 = media->cfg_tap_dly_4_0, + .cfg_tap_main = media->cfg_tap_main, + .cfg_en_main = media->cfg_en_main, + .cfg_tap_adv_3_0 = media->cfg_tap_adv_3_0, + .cfg_en_adv = media->cfg_en_adv, + .cfg_en_dly = media->cfg_en_dly, + .cfg_iscan_en = 0, + .l1_pcs_en_fast_iscan = 0, + .l0_cfg_bw_1_0 = 0, + .cfg_en_dummy = 0, + .cfg_pll_reserve_3_0 = args->com_pll_reserve, + .l0_cfg_txcal_en = mode->com_txcal_en, + .l0_cfg_tx_reserve_15_8 = mode->com_tx_reserve_msb, + .l0_cfg_tx_reserve_7_0 = mode->com_tx_reserve_lsb, + .cfg_tx_reserve_15_8 = mode->tx_reserve_msb, + .cfg_tx_reserve_7_0 = mode->tx_reserve_lsb, + .cfg_bw_1_0 = mode->bw, + .cfg_txcal_man_en = 1, + .cfg_phase_man_4_0 = 0, + .cfg_quad_man_1_0 = 0, + .cfg_txcal_shift_code_5_0 = 2, + .cfg_txcal_valid_sel_3_0 = 4, + .cfg_txcal_en = 0, + .cfg_cdr_kf_2_0 = 1, + .cfg_cdr_m_7_0 = 6, + .cfg_pi_bw_3_0 = mode->cfg_pi_bw_3_0, + .cfg_pi_steps_1_0 = 0, + .cfg_dis_2ndorder = 1, + .cfg_ctle_rstn = mode->cfg_ctle_rstn, + .r_dfe_rstn = mode->r_dfe_rstn, + .cfg_alos_thr_2_0 = media->cfg_alos_thr_2_0, + .cfg_itx_ipcml_base_1_0 = mode->cfg_itx_ipcml_base, + .cfg_rx_reserve_7_0 = 0xbf, + .cfg_rx_reserve_15_8 = 0x61, + .cfg_rxterm_2_0 = mode->rxterm, + .cfg_fom_selm = 0, + .cfg_rx_sp_ctle_1_0 = 0, + .cfg_isel_ctle_1_0 = 0, + .cfg_vga_ctrl_byp_4_0 = media->cfg_vga_ctrl_byp_4_0, + .cfg_vga_byp = 1, + .cfg_agc_adpt_byp = 1, + .cfg_eqr_byp = 1, + .cfg_eqr_force_3_0 = media->cfg_eq_r_force_3_0, + .cfg_eqc_force_3_0 = media->cfg_eq_c_force_3_0, + .cfg_sum_setcm_en = 1, + .cfg_pi_dfe_en = 1, + .cfg_init_pos_iscan_6_0 = 6, + .cfg_init_pos_ipi_6_0 = 9, + .cfg_dfedig_m_2_0 = 6, + .cfg_en_dfedig = mode->dfe_enable, + .r_d_width_ctrl_from_hwt = 0, + .r_reg_manual = 1, + .reg_rst = args->reg_rst, + .cfg_jc_byp = 1, + .cfg_common_reserve_7_0 = 1, + .cfg_pll_lol_set = 1, + .cfg_tx2rx_lp_en = 0, + .cfg_txlb_en = 0, + .cfg_rx2tx_lp_en = 0, + .cfg_rxlb_en = 0, + .r_tx_pol_inv = args->txinvert, + .r_rx_pol_inv = args->rxinvert, + }; + + *params = init; +} + +static void sparx5_sd10g28_get_params(struct sparx5_serdes_macro *macro, + struct sparx5_sd10g28_media_preset *media, + struct sparx5_sd10g28_mode_preset *mode, + struct sparx5_sd10g28_args *args, + struct sparx5_sd10g28_params *params) +{ + u8 iw = sd10g28_get_iw_setting(macro->priv->dev, mode->bwidth); + struct sparx5_sd10g28_params init = { + .skip_cmu_cfg = args->skip_cmu_cfg, + .is_6g = args->is_6g, + .cmu_sel = mode->cmu_sel, + .cfg_lane_reserve_7_0 = (mode->cmu_sel % 2) << 6, + .cfg_ssc_rtl_clk_sel = (mode->cmu_sel / 2), + .cfg_lane_reserve_15_8 = mode->duty_cycle, + .cfg_txrate_1_0 = mode->rate, + .cfg_rxrate_1_0 = mode->rate, + .fx_100 = macro->serdesmode == SPX5_SD_MODE_100FX, + .r_d_width_ctrl_2_0 = iw, + .cfg_pma_tx_ck_bitwidth_2_0 = iw, + .cfg_rxdiv_sel_2_0 = iw, + .r_pcs2pma_phymode_4_0 = 0, + .cfg_lane_id_2_0 = 0, + .cfg_cdrck_en = 1, + .cfg_dfeck_en = mode->dfe_enable, + .cfg_dfe_pd = (mode->dfe_enable == 1) ? 0 : 1, + .cfg_dfetap_en_5_1 = mode->dfe_tap, + .cfg_erramp_pd = (mode->dfe_enable == 1) ? 0 : 1, + .cfg_pi_DFE_en = mode->dfe_enable, + .cfg_pi_en = 1, + .cfg_pd_ctle = 0, + .cfg_summer_en = 1, + .cfg_pd_rx_cktree = 0, + .cfg_pd_clk = 0, + .cfg_pd_cml = 0, + .cfg_pd_driver = 0, + .cfg_rx_reg_pu = 1, + .cfg_d_cdr_pd = 0, + .cfg_pd_sq = mode->dfe_enable, + .cfg_rxdet_en = 0, + .cfg_rxdet_str = 0, + .r_multi_lane_mode = 0, + .cfg_en_adv = media->cfg_en_adv, + .cfg_en_main = 1, + .cfg_en_dly = media->cfg_en_dly, + .cfg_tap_adv_3_0 = media->cfg_tap_adv_3_0, + .cfg_tap_main = media->cfg_tap_main, + .cfg_tap_dly_4_0 = media->cfg_tap_dly_4_0, + .cfg_vga_ctrl_3_0 = media->cfg_vga_ctrl_3_0, + .cfg_vga_cp_2_0 = media->cfg_vga_cp_2_0, + .cfg_eq_res_3_0 = media->cfg_eq_res_3_0, + .cfg_eq_r_byp = media->cfg_eq_r_byp, + .cfg_eq_c_force_3_0 = media->cfg_eq_c_force_3_0, + .cfg_en_dfedig = mode->dfe_enable, + .cfg_sum_setcm_en = 1, + .cfg_en_preemph = 0, + .cfg_itx_ippreemp_base_1_0 = 0, + .cfg_itx_ipdriver_base_2_0 = (args->txswing >> 6), + .cfg_ibias_tune_reserve_5_0 = (args->txswing & 63), + .cfg_txswing_half = (args->txmargin), + .cfg_dis_2nd_order = 0x1, + .cfg_rx_ssc_lh = 0x0, + .cfg_pi_floop_steps_1_0 = 0x0, + .cfg_pi_ext_dac_23_16 = (1 << 5), + .cfg_pi_ext_dac_15_8 = (0 << 6), + .cfg_iscan_ext_dac_7_0 = (1 << 7) + 9, + .cfg_cdr_kf_gen1_2_0 = 1, + .cfg_cdr_kf_gen2_2_0 = 1, + .cfg_cdr_kf_gen3_2_0 = 1, + .cfg_cdr_kf_gen4_2_0 = 1, + .r_cdr_m_gen1_7_0 = 4, + .cfg_pi_bw_gen1_3_0 = mode->pi_bw_gen1, + .cfg_pi_bw_gen2 = mode->pi_bw_gen1, + .cfg_pi_bw_gen3 = mode->pi_bw_gen1, + .cfg_pi_bw_gen4 = mode->pi_bw_gen1, + .cfg_pi_ext_dac_7_0 = 3, + .cfg_pi_steps = 0, + .cfg_mp_max_3_0 = 1, + .cfg_rstn_dfedig = mode->dfe_enable, + .cfg_alos_thr_3_0 = media->cfg_alos_thr_3_0, + .cfg_predrv_slewrate_1_0 = 3, + .cfg_itx_ipcml_base_1_0 = 0, + .cfg_ip_pre_base_1_0 = 0, + .r_cdr_m_gen2_7_0 = 2, + .r_cdr_m_gen3_7_0 = 2, + .r_cdr_m_gen4_7_0 = 2, + .r_en_auto_cdr_rstn = 0, + .cfg_oscal_afe = 1, + .cfg_pd_osdac_afe = 0, + .cfg_resetb_oscal_afe[0] = 0, + .cfg_resetb_oscal_afe[1] = 1, + .cfg_center_spreading = 0, + .cfg_m_cnt_maxval_4_0 = 15, + .cfg_ncnt_maxval_7_0 = 32, + .cfg_ncnt_maxval_10_8 = 6, + .cfg_ssc_en = 1, + .cfg_tx2rx_lp_en = 0, + .cfg_txlb_en = 0, + .cfg_rx2tx_lp_en = 0, + .cfg_rxlb_en = 0, + .r_tx_pol_inv = args->txinvert, + .r_rx_pol_inv = args->rxinvert, + }; + + *params = init; +} + +static void sparx5_sd25g28_reset(void __iomem *regs[], + struct sparx5_sd25g28_params *params, + u32 sd_index) +{ + if (params->reg_rst == 1) { + sdx5_rmw_addr(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(1), + SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, + sdx5_addr(regs, SD_LANE_25G_SD_LANE_CFG(sd_index))); + + usleep_range(1000, 2000); + + sdx5_rmw_addr(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(0), + SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, + sdx5_addr(regs, SD_LANE_25G_SD_LANE_CFG(sd_index))); + } +} + +static int sparx5_sd25g28_apply_params(struct sparx5_serdes_macro *macro, + struct sparx5_sd25g28_params *params) +{ + struct sparx5_serdes_private *priv = macro->priv; + void __iomem **regs = priv->regs; + struct device *dev = priv->dev; + u32 sd_index = macro->stpidx; + u32 value; + + sdx5_rmw(SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(1), + SD_LANE_25G_SD_LANE_CFG_MACRO_RST, + priv, + SD_LANE_25G_SD_LANE_CFG(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0xFF), + SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, + priv, + SD25G_LANE_CMU_FF(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT_SET + (params->r_d_width_ctrl_from_hwt) | + SD25G_LANE_CMU_1A_R_REG_MANUAL_SET(params->r_reg_manual), + SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT | + SD25G_LANE_CMU_1A_R_REG_MANUAL, + priv, + SD25G_LANE_CMU_1A(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_SET + (params->cfg_common_reserve_7_0), + SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0, + priv, + SD25G_LANE_CMU_31(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_09_CFG_EN_DUMMY_SET(params->cfg_en_dummy), + SD25G_LANE_CMU_09_CFG_EN_DUMMY, + priv, + SD25G_LANE_CMU_09(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_SET + (params->cfg_pll_reserve_3_0), + SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0, + priv, + SD25G_LANE_CMU_13(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN_SET(params->l0_cfg_txcal_en), + SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN, + priv, + SD25G_LANE_CMU_40(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_SET + (params->l0_cfg_tx_reserve_15_8), + SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8, + priv, + SD25G_LANE_CMU_46(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_SET + (params->l0_cfg_tx_reserve_7_0), + SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0, + priv, + SD25G_LANE_CMU_45(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_SET(0), + SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN, + priv, + SD25G_LANE_CMU_0B(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_SET(1), + SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN, + priv, + SD25G_LANE_CMU_0B(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_19_R_CK_RESETB_SET(0), + SD25G_LANE_CMU_19_R_CK_RESETB, + priv, + SD25G_LANE_CMU_19(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_19_R_CK_RESETB_SET(1), + SD25G_LANE_CMU_19_R_CK_RESETB, + priv, + SD25G_LANE_CMU_19(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_18_R_PLL_RSTN_SET(0), + SD25G_LANE_CMU_18_R_PLL_RSTN, + priv, + SD25G_LANE_CMU_18(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_18_R_PLL_RSTN_SET(1), + SD25G_LANE_CMU_18_R_PLL_RSTN, + priv, + SD25G_LANE_CMU_18(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_SET(params->r_d_width_ctrl_2_0), + SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0, + priv, + SD25G_LANE_CMU_1A(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_SET + (params->r_txfifo_ck_div_pmad_2_0) | + SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_SET + (params->r_rxfifo_ck_div_pmad_2_0), + SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0 | + SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0, + priv, + SD25G_LANE_CMU_30(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_SET(params->cfg_pll_lol_set) | + SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_SET + (params->cfg_vco_div_mode_1_0), + SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET | + SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0, + priv, + SD25G_LANE_CMU_0C(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_SET + (params->cfg_pre_divsel_1_0), + SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0, + priv, + SD25G_LANE_CMU_0D(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_SET(params->cfg_sel_div_3_0), + SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0, + priv, + SD25G_LANE_CMU_0E(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0x00), + SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, + priv, + SD25G_LANE_CMU_FF(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_SET + (params->cfg_pma_tx_ck_bitwidth_2_0), + SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0, + priv, + SD25G_LANE_LANE_0C(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_SET + (params->cfg_tx_prediv_1_0), + SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0, + priv, + SD25G_LANE_LANE_01(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_SET + (params->cfg_rxdiv_sel_2_0), + SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0, + priv, + SD25G_LANE_LANE_18(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_SET + (params->cfg_tx_subrate_2_0), + SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0, + priv, + SD25G_LANE_LANE_2C(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_SET + (params->cfg_rx_subrate_2_0), + SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0, + priv, + SD25G_LANE_LANE_28(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_SET(params->cfg_cdrck_en), + SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN, + priv, + SD25G_LANE_LANE_18(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_SET + (params->cfg_dfetap_en_5_1), + SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1, + priv, + SD25G_LANE_LANE_0F(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_SET(params->cfg_erramp_pd), + SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD, + priv, + SD25G_LANE_LANE_18(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN_SET(params->cfg_pi_dfe_en), + SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN, + priv, + SD25G_LANE_LANE_1D(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_19_LN_CFG_ECDR_PD_SET(params->cfg_ecdr_pd), + SD25G_LANE_LANE_19_LN_CFG_ECDR_PD, + priv, + SD25G_LANE_LANE_19(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_SET + (params->cfg_itx_ipdriver_base_2_0), + SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0, + priv, + SD25G_LANE_LANE_01(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_SET(params->cfg_tap_dly_4_0), + SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0, + priv, + SD25G_LANE_LANE_03(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_SET(params->cfg_tap_adv_3_0), + SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0, + priv, + SD25G_LANE_LANE_06(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_07_LN_CFG_EN_ADV_SET(params->cfg_en_adv) | + SD25G_LANE_LANE_07_LN_CFG_EN_DLY_SET(params->cfg_en_dly), + SD25G_LANE_LANE_07_LN_CFG_EN_ADV | + SD25G_LANE_LANE_07_LN_CFG_EN_DLY, + priv, + SD25G_LANE_LANE_07(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_SET + (params->cfg_tx_reserve_15_8), + SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8, + priv, + SD25G_LANE_LANE_43(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_SET + (params->cfg_tx_reserve_7_0), + SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0, + priv, + SD25G_LANE_LANE_42(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_05_LN_CFG_BW_1_0_SET(params->cfg_bw_1_0), + SD25G_LANE_LANE_05_LN_CFG_BW_1_0, + priv, + SD25G_LANE_LANE_05(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_SET + (params->cfg_txcal_man_en), + SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN, + priv, + SD25G_LANE_LANE_0B(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_SET + (params->cfg_txcal_shift_code_5_0), + SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0, + priv, + SD25G_LANE_LANE_0A(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_SET + (params->cfg_txcal_valid_sel_3_0), + SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0, + priv, + SD25G_LANE_LANE_09(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_SET(params->cfg_cdr_kf_2_0), + SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0, + priv, + SD25G_LANE_LANE_1A(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_SET(params->cfg_cdr_m_7_0), + SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0, + priv, + SD25G_LANE_LANE_1B(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_SET(params->cfg_pi_bw_3_0), + SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0, + priv, + SD25G_LANE_LANE_2B(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_SET + (params->cfg_dis_2ndorder), + SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER, + priv, + SD25G_LANE_LANE_2C(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_SET(params->cfg_ctle_rstn), + SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN, + priv, + SD25G_LANE_LANE_2E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_SET + (params->cfg_itx_ipcml_base_1_0), + SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0, + priv, + SD25G_LANE_LANE_00(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_SET + (params->cfg_rx_reserve_7_0), + SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0, + priv, + SD25G_LANE_LANE_44(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_SET + (params->cfg_rx_reserve_15_8), + SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8, + priv, + SD25G_LANE_LANE_45(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_SET(params->cfg_dfeck_en) | + SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_SET(params->cfg_rxterm_2_0), + SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN | + SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0, + priv, + SD25G_LANE_LANE_0D(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_SET + (params->cfg_vga_ctrl_byp_4_0), + SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0, + priv, + SD25G_LANE_LANE_21(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_SET + (params->cfg_eqr_force_3_0), + SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0, + priv, + SD25G_LANE_LANE_22(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_SET + (params->cfg_eqc_force_3_0) | + SD25G_LANE_LANE_1C_LN_CFG_DFE_PD_SET(params->cfg_dfe_pd), + SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0 | + SD25G_LANE_LANE_1C_LN_CFG_DFE_PD, + priv, + SD25G_LANE_LANE_1C(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN_SET + (params->cfg_sum_setcm_en), + SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN, + priv, + SD25G_LANE_LANE_1E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_SET + (params->cfg_init_pos_iscan_6_0), + SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0, + priv, + SD25G_LANE_LANE_25(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_SET + (params->cfg_init_pos_ipi_6_0), + SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0, + priv, + SD25G_LANE_LANE_26(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_SET(params->cfg_erramp_pd), + SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD, + priv, + SD25G_LANE_LANE_18(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_SET + (params->cfg_dfedig_m_2_0), + SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0, + priv, + SD25G_LANE_LANE_0E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG_SET(params->cfg_en_dfedig), + SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG, + priv, + SD25G_LANE_LANE_0E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_40_LN_R_TX_POL_INV_SET(params->r_tx_pol_inv) | + SD25G_LANE_LANE_40_LN_R_RX_POL_INV_SET(params->r_rx_pol_inv), + SD25G_LANE_LANE_40_LN_R_TX_POL_INV | + SD25G_LANE_LANE_40_LN_R_RX_POL_INV, + priv, + SD25G_LANE_LANE_40(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN_SET(params->cfg_rx2tx_lp_en) | + SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_SET(params->cfg_tx2rx_lp_en), + SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN | + SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN, + priv, + SD25G_LANE_LANE_04(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN_SET(params->cfg_rxlb_en), + SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN, + priv, + SD25G_LANE_LANE_1E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_19_LN_CFG_TXLB_EN_SET(params->cfg_txlb_en), + SD25G_LANE_LANE_19_LN_CFG_TXLB_EN, + priv, + SD25G_LANE_LANE_19(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_SET(0), + SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG, + priv, + SD25G_LANE_LANE_2E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_SET(1), + SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG, + priv, + SD25G_LANE_LANE_2E(sd_index)); + + sdx5_rmw(SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(0), + SD_LANE_25G_SD_LANE_CFG_MACRO_RST, + priv, + SD_LANE_25G_SD_LANE_CFG(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(0), + SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN, + priv, + SD25G_LANE_LANE_1C(sd_index)); + + usleep_range(1000, 2000); + + sdx5_rmw(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(1), + SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN, + priv, + SD25G_LANE_LANE_1C(sd_index)); + + usleep_range(10000, 20000); + + sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0xff), + SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, + priv, + SD25G_LANE_CMU_FF(sd_index)); + + value = readl(sdx5_addr(regs, SD25G_LANE_CMU_C0(sd_index))); + value = SD25G_LANE_CMU_C0_PLL_LOL_UDL_GET(value); + + if (value) { + dev_err(dev, "25G PLL Loss of Lock: 0x%x\n", value); + return -EINVAL; + } + + value = readl(sdx5_addr(regs, SD_LANE_25G_SD_LANE_STAT(sd_index))); + value = SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_GET(value); + + if (value != 0x1) { + dev_err(dev, "25G PMA Reset failed: 0x%x\n", value); + return -EINVAL; + } + sdx5_rmw(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_SET(0x1), + SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS, + priv, + SD25G_LANE_CMU_2A(sd_index)); + + sdx5_rmw(SD_LANE_25G_SD_SER_RST_SER_RST_SET(0x0), + SD_LANE_25G_SD_SER_RST_SER_RST, + priv, + SD_LANE_25G_SD_SER_RST(sd_index)); + + sdx5_rmw(SD_LANE_25G_SD_DES_RST_DES_RST_SET(0x0), + SD_LANE_25G_SD_DES_RST_DES_RST, + priv, + SD_LANE_25G_SD_DES_RST(sd_index)); + + sdx5_rmw(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(0), + SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, + priv, + SD25G_LANE_CMU_FF(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_SET + (params->cfg_alos_thr_2_0), + SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0, + priv, + SD25G_LANE_LANE_2D(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ_SET(0), + SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ, + priv, + SD25G_LANE_LANE_2E(sd_index)); + + sdx5_rmw(SD25G_LANE_LANE_2E_LN_CFG_PD_SQ_SET(0), + SD25G_LANE_LANE_2E_LN_CFG_PD_SQ, + priv, + SD25G_LANE_LANE_2E(sd_index)); + + return 0; +} + +static void sparx5_sd10g28_reset(void __iomem *regs[], u32 lane_index) +{ + /* Note: SerDes SD10G_LANE_1 is configured in 10G_LAN mode */ + sdx5_rmw_addr(SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(1), + SD_LANE_SD_LANE_CFG_EXT_CFG_RST, + sdx5_addr(regs, SD_LANE_SD_LANE_CFG(lane_index))); + + usleep_range(1000, 2000); + + sdx5_rmw_addr(SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(0), + SD_LANE_SD_LANE_CFG_EXT_CFG_RST, + sdx5_addr(regs, SD_LANE_SD_LANE_CFG(lane_index))); +} + +static int sparx5_sd10g28_apply_params(struct sparx5_serdes_macro *macro, + struct sparx5_sd10g28_params *params) +{ + struct sparx5_serdes_private *priv = macro->priv; + void __iomem **regs = priv->regs; + struct device *dev = priv->dev; + u32 lane_index = macro->sidx; + u32 sd_index = macro->stpidx; + void __iomem *sd_inst; + u32 value; + + if (params->is_6g) + sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, sd_index); + else + sd_inst = sdx5_inst_get(priv, TARGET_SD10G_LANE, sd_index); + + sdx5_rmw(SD_LANE_SD_LANE_CFG_MACRO_RST_SET(1), + SD_LANE_SD_LANE_CFG_MACRO_RST, + priv, + SD_LANE_SD_LANE_CFG(lane_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT_SET(0x0) | + SD10G_LANE_LANE_93_R_REG_MANUAL_SET(0x1) | + SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT_SET(0x1) | + SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT_SET(0x1) | + SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL_SET(0x0), + SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT | + SD10G_LANE_LANE_93_R_REG_MANUAL | + SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT | + SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT | + SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL, + sd_inst, + SD10G_LANE_LANE_93(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_94_R_ISCAN_REG_SET(0x1) | + SD10G_LANE_LANE_94_R_TXEQ_REG_SET(0x1) | + SD10G_LANE_LANE_94_R_MISC_REG_SET(0x1) | + SD10G_LANE_LANE_94_R_SWING_REG_SET(0x1), + SD10G_LANE_LANE_94_R_ISCAN_REG | + SD10G_LANE_LANE_94_R_TXEQ_REG | + SD10G_LANE_LANE_94_R_MISC_REG | + SD10G_LANE_LANE_94_R_SWING_REG, + sd_inst, + SD10G_LANE_LANE_94(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_9E_R_RXEQ_REG_SET(0x1), + SD10G_LANE_LANE_9E_R_RXEQ_REG, + sd_inst, + SD10G_LANE_LANE_9E(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_A1_R_SSC_FROM_HWT_SET(0x0) | + SD10G_LANE_LANE_A1_R_CDR_FROM_HWT_SET(0x0) | + SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT_SET(0x1), + SD10G_LANE_LANE_A1_R_SSC_FROM_HWT | + SD10G_LANE_LANE_A1_R_CDR_FROM_HWT | + SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT, + sd_inst, + SD10G_LANE_LANE_A1(sd_index)); + + sdx5_rmw(SD_LANE_SD_LANE_CFG_RX_REF_SEL_SET(params->cmu_sel) | + SD_LANE_SD_LANE_CFG_TX_REF_SEL_SET(params->cmu_sel), + SD_LANE_SD_LANE_CFG_RX_REF_SEL | + SD_LANE_SD_LANE_CFG_TX_REF_SEL, + priv, + SD_LANE_SD_LANE_CFG(lane_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_SET + (params->cfg_lane_reserve_7_0), + SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0, + sd_inst, + SD10G_LANE_LANE_40(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL_SET + (params->cfg_ssc_rtl_clk_sel), + SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL, + sd_inst, + SD10G_LANE_LANE_50(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_35_CFG_TXRATE_1_0_SET + (params->cfg_txrate_1_0) | + SD10G_LANE_LANE_35_CFG_RXRATE_1_0_SET + (params->cfg_rxrate_1_0), + SD10G_LANE_LANE_35_CFG_TXRATE_1_0 | + SD10G_LANE_LANE_35_CFG_RXRATE_1_0, + sd_inst, + SD10G_LANE_LANE_35(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_SET + (params->r_d_width_ctrl_2_0), + SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0, + sd_inst, + SD10G_LANE_LANE_94(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_SET + (params->cfg_pma_tx_ck_bitwidth_2_0), + SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0, + sd_inst, + SD10G_LANE_LANE_01(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_SET + (params->cfg_rxdiv_sel_2_0), + SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0, + sd_inst, + SD10G_LANE_LANE_30(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_SET + (params->r_pcs2pma_phymode_4_0), + SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0, + sd_inst, + SD10G_LANE_LANE_A2(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_13_CFG_CDRCK_EN_SET(params->cfg_cdrck_en), + SD10G_LANE_LANE_13_CFG_CDRCK_EN, + sd_inst, + SD10G_LANE_LANE_13(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_23_CFG_DFECK_EN_SET + (params->cfg_dfeck_en) | + SD10G_LANE_LANE_23_CFG_DFE_PD_SET(params->cfg_dfe_pd) | + SD10G_LANE_LANE_23_CFG_ERRAMP_PD_SET + (params->cfg_erramp_pd), + SD10G_LANE_LANE_23_CFG_DFECK_EN | + SD10G_LANE_LANE_23_CFG_DFE_PD | + SD10G_LANE_LANE_23_CFG_ERRAMP_PD, + sd_inst, + SD10G_LANE_LANE_23(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_SET + (params->cfg_dfetap_en_5_1), + SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1, + sd_inst, + SD10G_LANE_LANE_22(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_1A_CFG_PI_DFE_EN_SET + (params->cfg_pi_DFE_en), + SD10G_LANE_LANE_1A_CFG_PI_DFE_EN, + sd_inst, + SD10G_LANE_LANE_1A(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_02_CFG_EN_ADV_SET(params->cfg_en_adv) | + SD10G_LANE_LANE_02_CFG_EN_MAIN_SET(params->cfg_en_main) | + SD10G_LANE_LANE_02_CFG_EN_DLY_SET(params->cfg_en_dly) | + SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_SET + (params->cfg_tap_adv_3_0), + SD10G_LANE_LANE_02_CFG_EN_ADV | + SD10G_LANE_LANE_02_CFG_EN_MAIN | + SD10G_LANE_LANE_02_CFG_EN_DLY | + SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0, + sd_inst, + SD10G_LANE_LANE_02(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_03_CFG_TAP_MAIN_SET(params->cfg_tap_main), + SD10G_LANE_LANE_03_CFG_TAP_MAIN, + sd_inst, + SD10G_LANE_LANE_03(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_SET + (params->cfg_tap_dly_4_0), + SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0, + sd_inst, + SD10G_LANE_LANE_04(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_SET + (params->cfg_vga_ctrl_3_0), + SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0, + sd_inst, + SD10G_LANE_LANE_2F(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_SET + (params->cfg_vga_cp_2_0), + SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0, + sd_inst, + SD10G_LANE_LANE_2F(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_SET + (params->cfg_eq_res_3_0), + SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0, + sd_inst, + SD10G_LANE_LANE_0B(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0D_CFG_EQR_BYP_SET(params->cfg_eq_r_byp), + SD10G_LANE_LANE_0D_CFG_EQR_BYP, + sd_inst, + SD10G_LANE_LANE_0D(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_SET + (params->cfg_eq_c_force_3_0) | + SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_SET + (params->cfg_sum_setcm_en), + SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0 | + SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN, + sd_inst, + SD10G_LANE_LANE_0E(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_23_CFG_EN_DFEDIG_SET + (params->cfg_en_dfedig), + SD10G_LANE_LANE_23_CFG_EN_DFEDIG, + sd_inst, + SD10G_LANE_LANE_23(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_EN_PREEMPH_SET + (params->cfg_en_preemph), + SD10G_LANE_LANE_06_CFG_EN_PREEMPH, + sd_inst, + SD10G_LANE_LANE_06(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_SET + (params->cfg_itx_ippreemp_base_1_0) | + SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_SET + (params->cfg_itx_ipdriver_base_2_0), + SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0 | + SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0, + sd_inst, + SD10G_LANE_LANE_33(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_SET + (params->cfg_ibias_tune_reserve_5_0), + SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0, + sd_inst, + SD10G_LANE_LANE_52(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_37_CFG_TXSWING_HALF_SET + (params->cfg_txswing_half), + SD10G_LANE_LANE_37_CFG_TXSWING_HALF, + sd_inst, + SD10G_LANE_LANE_37(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_SET + (params->cfg_dis_2nd_order), + SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER, + sd_inst, + SD10G_LANE_LANE_3C(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_39_CFG_RX_SSC_LH_SET + (params->cfg_rx_ssc_lh), + SD10G_LANE_LANE_39_CFG_RX_SSC_LH, + sd_inst, + SD10G_LANE_LANE_39(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_SET + (params->cfg_pi_floop_steps_1_0), + SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0, + sd_inst, + SD10G_LANE_LANE_1A(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_SET + (params->cfg_pi_ext_dac_23_16), + SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16, + sd_inst, + SD10G_LANE_LANE_16(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_SET + (params->cfg_pi_ext_dac_15_8), + SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8, + sd_inst, + SD10G_LANE_LANE_15(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_SET + (params->cfg_iscan_ext_dac_7_0), + SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0, + sd_inst, + SD10G_LANE_LANE_26(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_SET + (params->cfg_cdr_kf_gen1_2_0), + SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0, + sd_inst, + SD10G_LANE_LANE_42(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_SET + (params->r_cdr_m_gen1_7_0), + SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0, + sd_inst, + SD10G_LANE_LANE_0F(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_SET + (params->cfg_pi_bw_gen1_3_0), + SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0, + sd_inst, + SD10G_LANE_LANE_24(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_SET + (params->cfg_pi_ext_dac_7_0), + SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0, + sd_inst, + SD10G_LANE_LANE_14(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_1A_CFG_PI_STEPS_SET(params->cfg_pi_steps), + SD10G_LANE_LANE_1A_CFG_PI_STEPS, + sd_inst, + SD10G_LANE_LANE_1A(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_SET + (params->cfg_mp_max_3_0), + SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0, + sd_inst, + SD10G_LANE_LANE_3A(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG_SET + (params->cfg_rstn_dfedig), + SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG, + sd_inst, + SD10G_LANE_LANE_31(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_SET + (params->cfg_alos_thr_3_0), + SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0, + sd_inst, + SD10G_LANE_LANE_48(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_SET + (params->cfg_predrv_slewrate_1_0), + SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0, + sd_inst, + SD10G_LANE_LANE_36(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_SET + (params->cfg_itx_ipcml_base_1_0), + SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0, + sd_inst, + SD10G_LANE_LANE_32(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_SET + (params->cfg_ip_pre_base_1_0), + SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0, + sd_inst, + SD10G_LANE_LANE_37(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_SET + (params->cfg_lane_reserve_15_8), + SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8, + sd_inst, + SD10G_LANE_LANE_41(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_SET + (params->r_en_auto_cdr_rstn), + SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN, + sd_inst, + SD10G_LANE_LANE_9E(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_SET + (params->cfg_oscal_afe) | + SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE_SET + (params->cfg_pd_osdac_afe), + SD10G_LANE_LANE_0C_CFG_OSCAL_AFE | + SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE, + sd_inst, + SD10G_LANE_LANE_0C(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_SET + (params->cfg_resetb_oscal_afe[0]), + SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE, + sd_inst, + SD10G_LANE_LANE_0B(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_SET + (params->cfg_resetb_oscal_afe[1]), + SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE, + sd_inst, + SD10G_LANE_LANE_0B(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_83_R_TX_POL_INV_SET + (params->r_tx_pol_inv) | + SD10G_LANE_LANE_83_R_RX_POL_INV_SET + (params->r_rx_pol_inv), + SD10G_LANE_LANE_83_R_TX_POL_INV | + SD10G_LANE_LANE_83_R_RX_POL_INV, + sd_inst, + SD10G_LANE_LANE_83(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN_SET + (params->cfg_rx2tx_lp_en) | + SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN_SET + (params->cfg_tx2rx_lp_en), + SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN | + SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN, + sd_inst, + SD10G_LANE_LANE_06(sd_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_0E_CFG_RXLB_EN_SET(params->cfg_rxlb_en) | + SD10G_LANE_LANE_0E_CFG_TXLB_EN_SET(params->cfg_txlb_en), + SD10G_LANE_LANE_0E_CFG_RXLB_EN | + SD10G_LANE_LANE_0E_CFG_TXLB_EN, + sd_inst, + SD10G_LANE_LANE_0E(sd_index)); + + sdx5_rmw(SD_LANE_SD_LANE_CFG_MACRO_RST_SET(0), + SD_LANE_SD_LANE_CFG_MACRO_RST, + priv, + SD_LANE_SD_LANE_CFG(lane_index)); + + sdx5_inst_rmw(SD10G_LANE_LANE_50_CFG_SSC_RESETB_SET(1), + SD10G_LANE_LANE_50_CFG_SSC_RESETB, + sd_inst, + SD10G_LANE_LANE_50(sd_index)); + + sdx5_rmw(SD10G_LANE_LANE_50_CFG_SSC_RESETB_SET(1), + SD10G_LANE_LANE_50_CFG_SSC_RESETB, + priv, + SD10G_LANE_LANE_50(sd_index)); + + sdx5_rmw(SD_LANE_MISC_SD_125_RST_DIS_SET(params->fx_100), + SD_LANE_MISC_SD_125_RST_DIS, + priv, + SD_LANE_MISC(lane_index)); + + sdx5_rmw(SD_LANE_MISC_RX_ENA_SET(params->fx_100), + SD_LANE_MISC_RX_ENA, + priv, + SD_LANE_MISC(lane_index)); + + sdx5_rmw(SD_LANE_MISC_MUX_ENA_SET(params->fx_100), + SD_LANE_MISC_MUX_ENA, + priv, + SD_LANE_MISC(lane_index)); + + usleep_range(3000, 6000); + + value = readl(sdx5_addr(regs, SD_LANE_SD_LANE_STAT(lane_index))); + value = SD_LANE_SD_LANE_STAT_PMA_RST_DONE_GET(value); + if (value != 1) { + dev_err(dev, "10G PMA Reset failed: 0x%x\n", value); + return -EINVAL; + } + + sdx5_rmw(SD_LANE_SD_SER_RST_SER_RST_SET(0x0), + SD_LANE_SD_SER_RST_SER_RST, + priv, + SD_LANE_SD_SER_RST(lane_index)); + + sdx5_rmw(SD_LANE_SD_DES_RST_DES_RST_SET(0x0), + SD_LANE_SD_DES_RST_DES_RST, + priv, + SD_LANE_SD_DES_RST(lane_index)); + + return 0; +} + +static int sparx5_sd25g28_config(struct sparx5_serdes_macro *macro, bool reset) +{ + struct sparx5_sd25g28_media_preset media = media_presets_25g[macro->media]; + struct sparx5_sd25g28_mode_preset mode; + struct sparx5_sd25g28_args args = { + .rxinvert = 1, + .txinvert = 0, + .txswing = 240, + .com_pll_reserve = 0xf, + .reg_rst = reset, + }; + struct sparx5_sd25g28_params params; + int err; + + err = sparx5_sd10g25_get_mode_preset(macro, &mode); + if (err) + return err; + sparx5_sd25g28_get_params(macro, &media, &mode, &args, ¶ms); + sparx5_sd25g28_reset(macro->priv->regs, ¶ms, macro->stpidx); + return sparx5_sd25g28_apply_params(macro, ¶ms); +} + +static int sparx5_sd10g28_config(struct sparx5_serdes_macro *macro, bool reset) +{ + struct sparx5_sd10g28_media_preset media = media_presets_10g[macro->media]; + struct sparx5_sd10g28_mode_preset mode; + struct sparx5_sd10g28_params params; + struct sparx5_sd10g28_args args = { + .is_6g = (macro->serdestype == SPX5_SDT_6G), + .txinvert = 0, + .rxinvert = 1, + .txswing = 240, + .reg_rst = reset, + }; + int err; + + err = sparx5_sd10g28_get_mode_preset(macro, &mode, &args); + if (err) + return err; + sparx5_sd10g28_get_params(macro, &media, &mode, &args, ¶ms); + sparx5_sd10g28_reset(macro->priv->regs, macro->sidx); + return sparx5_sd10g28_apply_params(macro, ¶ms); +} + +/* Power down serdes TX driver */ +static int sparx5_serdes_power_save(struct sparx5_serdes_macro *macro, u32 pwdn) +{ + struct sparx5_serdes_private *priv = macro->priv; + void __iomem *sd_inst; + + if (macro->serdestype == SPX5_SDT_6G) + sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, macro->stpidx); + else if (macro->serdestype == SPX5_SDT_10G) + sd_inst = sdx5_inst_get(priv, TARGET_SD10G_LANE, macro->stpidx); + else + sd_inst = sdx5_inst_get(priv, TARGET_SD25G_LANE, macro->stpidx); + + if (macro->serdestype == SPX5_SDT_25G) { + sdx5_inst_rmw(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_SET(pwdn), + SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER, + sd_inst, + SD25G_LANE_LANE_04(0)); + } else { + /* 6G and 10G */ + sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(pwdn), + SD10G_LANE_LANE_06_CFG_PD_DRIVER, + sd_inst, + SD10G_LANE_LANE_06(0)); + } + return 0; +} + +static int sparx5_serdes_clock_config(struct sparx5_serdes_macro *macro) +{ + struct sparx5_serdes_private *priv = macro->priv; + + if (macro->serdesmode == SPX5_SD_MODE_100FX) { + u32 freq = priv->coreclock == 250000000 ? 2 : + priv->coreclock == 500000000 ? 1 : 0; + + sdx5_rmw(SD_LANE_MISC_CORE_CLK_FREQ_SET(freq), + SD_LANE_MISC_CORE_CLK_FREQ, + priv, + SD_LANE_MISC(macro->sidx)); + } + return 0; +} + +static int sparx5_cmu_apply_cfg(struct sparx5_serdes_private *priv, + u32 cmu_idx, + void __iomem *cmu_tgt, + void __iomem *cmu_cfg_tgt, + u32 spd10g) +{ + void __iomem **regs = priv->regs; + struct device *dev = priv->dev; + int value; + + cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx); + cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx); + + if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 || + cmu_idx == 10 || cmu_idx == 13) { + spd10g = 0; + } + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(1), + SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0), + SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(1), + SD_CMU_CFG_SD_CMU_CFG_CMU_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(0x0), + SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT | + SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT | + SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT | + SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT | + SD_CMU_CMU_45_R_EN_RATECHG_CTRL, + cmu_tgt, + SD_CMU_CMU_45(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(0), + SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, + cmu_tgt, + SD_CMU_CMU_47(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(0), + SD_CMU_CMU_1B_CFG_RESERVE_7_0, + cmu_tgt, + SD_CMU_CMU_1B(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_JC_BYP_SET(0x1), + SD_CMU_CMU_0D_CFG_JC_BYP, + cmu_tgt, + SD_CMU_CMU_0D(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(1), + SD_CMU_CMU_1F_CFG_VTUNE_SEL, + cmu_tgt, + SD_CMU_CMU_1F(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(3), + SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, + cmu_tgt, + SD_CMU_CMU_00(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(3), + SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, + cmu_tgt, + SD_CMU_CMU_05(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(1), + SD_CMU_CMU_30_R_PLL_DLOL_EN, + cmu_tgt, + SD_CMU_CMU_30(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_09_CFG_SW_10G_SET(spd10g), + SD_CMU_CMU_09_CFG_SW_10G, + cmu_tgt, + SD_CMU_CMU_09(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(0), + SD_CMU_CFG_SD_CMU_CFG_CMU_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + msleep(20); + + sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(0), + SD_CMU_CMU_44_R_PLL_RSTN, + cmu_tgt, + SD_CMU_CMU_44(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(1), + SD_CMU_CMU_44_R_PLL_RSTN, + cmu_tgt, + SD_CMU_CMU_44(cmu_idx)); + + msleep(20); + + value = readl(sdx5_addr(regs, SD_CMU_CMU_E0(cmu_idx))); + value = SD_CMU_CMU_E0_PLL_LOL_UDL_GET(value); + + if (value) { + dev_err(dev, "CMU PLL Loss of Lock: 0x%x\n", value); + return -EINVAL; + } + sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(0), + SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, + cmu_tgt, + SD_CMU_CMU_0D(cmu_idx)); + return 0; +} + +static int sparx5_cmu_cfg(struct sparx5_serdes_private *priv, u32 cmu_idx) +{ + void __iomem *cmu_tgt, *cmu_cfg_tgt; + u32 spd10g = 1; + + if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 || + cmu_idx == 10 || cmu_idx == 13) { + spd10g = 0; + } + + cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx); + cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx); + + return sparx5_cmu_apply_cfg(priv, cmu_idx, cmu_tgt, cmu_cfg_tgt, spd10g); +} + +static int sparx5_serdes_cmu_enable(struct sparx5_serdes_private *priv) +{ + int idx, err = 0; + + if (!priv->cmu_enabled) { + for (idx = 0; idx < SPX5_CMU_MAX; idx++) { + err = sparx5_cmu_cfg(priv, idx); + if (err) { + dev_err(priv->dev, "CMU %u, error: %d\n", idx, err); + goto leave; + } + } + priv->cmu_enabled = true; + } +leave: + return err; +} + +static int sparx5_serdes_get_serdesmode(phy_interface_t portmode, int speed) +{ + switch (portmode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + if (speed == SPEED_2500) + return SPX5_SD_MODE_2G5; + if (speed == SPEED_100) + return SPX5_SD_MODE_100FX; + return SPX5_SD_MODE_1000BASEX; + case PHY_INTERFACE_MODE_SGMII: + /* The same Serdes mode is used for both SGMII and 1000BaseX */ + return SPX5_SD_MODE_1000BASEX; + case PHY_INTERFACE_MODE_QSGMII: + return SPX5_SD_MODE_QSGMII; + case PHY_INTERFACE_MODE_10GBASER: + return SPX5_SD_MODE_SFI; + default: + return -EINVAL; + } +} + +static int sparx5_serdes_config(struct sparx5_serdes_macro *macro) +{ + struct device *dev = macro->priv->dev; + int serdesmode; + int err; + + err = sparx5_serdes_cmu_enable(macro->priv); + if (err) + return err; + + serdesmode = sparx5_serdes_get_serdesmode(macro->portmode, macro->speed); + if (serdesmode < 0) { + dev_err(dev, "SerDes %u, interface not supported: %s\n", + macro->sidx, + phy_modes(macro->portmode)); + return serdesmode; + } + macro->serdesmode = serdesmode; + + sparx5_serdes_clock_config(macro); + + if (macro->serdestype == SPX5_SDT_25G) + err = sparx5_sd25g28_config(macro, false); + else + err = sparx5_sd10g28_config(macro, false); + if (err) { + dev_err(dev, "SerDes %u, config error: %d\n", + macro->sidx, err); + } + return err; +} + +static int sparx5_serdes_power_on(struct phy *phy) +{ + struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); + + return sparx5_serdes_power_save(macro, false); +} + +static int sparx5_serdes_power_off(struct phy *phy) +{ + struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); + + return sparx5_serdes_power_save(macro, true); +} + +static int sparx5_serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct sparx5_serdes_macro *macro; + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + switch (submode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_10GBASER: + macro = phy_get_drvdata(phy); + macro->portmode = submode; + sparx5_serdes_config(macro); + return 0; + default: + return -EINVAL; + } +} + +static int sparx5_serdes_set_media(struct phy *phy, enum phy_media media) +{ + struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); + + if (media != macro->media) { + macro->media = media; + if (macro->serdesmode != SPX5_SD_MODE_NONE) + sparx5_serdes_config(macro); + } + return 0; +} + +static int sparx5_serdes_set_speed(struct phy *phy, int speed) +{ + struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); + + if (macro->sidx < SPX5_SERDES_10G_START && speed > SPEED_5000) + return -EINVAL; + if (macro->sidx < SPX5_SERDES_25G_START && speed > SPEED_10000) + return -EINVAL; + if (speed != macro->speed) { + macro->speed = speed; + if (macro->serdesmode != SPX5_SD_MODE_NONE) + sparx5_serdes_config(macro); + } + return 0; +} + +static int sparx5_serdes_reset(struct phy *phy) +{ + struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); + int err; + + err = sparx5_serdes_cmu_enable(macro->priv); + if (err) + return err; + if (macro->serdestype == SPX5_SDT_25G) + err = sparx5_sd25g28_config(macro, true); + else + err = sparx5_sd10g28_config(macro, true); + if (err) { + dev_err(&phy->dev, "SerDes %u, reset error: %d\n", + macro->sidx, err); + } + return err; +} + +static int sparx5_serdes_validate(struct phy *phy, enum phy_mode mode, + int submode, + union phy_configure_opts *opts) +{ + struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + if (macro->speed == 0) + return -EINVAL; + + if (macro->sidx < SPX5_SERDES_10G_START && macro->speed > SPEED_5000) + return -EINVAL; + if (macro->sidx < SPX5_SERDES_25G_START && macro->speed > SPEED_10000) + return -EINVAL; + + switch (submode) { + case PHY_INTERFACE_MODE_1000BASEX: + if (macro->speed != SPEED_100 && /* This is for 100BASE-FX */ + macro->speed != SPEED_1000) + return -EINVAL; + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_QSGMII: + if (macro->speed >= SPEED_5000) + return -EINVAL; + break; + case PHY_INTERFACE_MODE_10GBASER: + if (macro->speed < SPEED_5000) + return -EINVAL; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct phy_ops sparx5_serdes_ops = { + .power_on = sparx5_serdes_power_on, + .power_off = sparx5_serdes_power_off, + .set_mode = sparx5_serdes_set_mode, + .set_media = sparx5_serdes_set_media, + .set_speed = sparx5_serdes_set_speed, + .reset = sparx5_serdes_reset, + .validate = sparx5_serdes_validate, + .owner = THIS_MODULE, +}; + +static int sparx5_phy_create(struct sparx5_serdes_private *priv, + int idx, struct phy **phy) +{ + struct sparx5_serdes_macro *macro; + + *phy = devm_phy_create(priv->dev, NULL, &sparx5_serdes_ops); + if (IS_ERR(*phy)) + return PTR_ERR(*phy); + + macro = devm_kzalloc(priv->dev, sizeof(*macro), GFP_KERNEL); + if (!macro) + return -ENOMEM; + + macro->sidx = idx; + macro->priv = priv; + macro->speed = SPEED_UNKNOWN; + if (idx < SPX5_SERDES_10G_START) { + macro->serdestype = SPX5_SDT_6G; + macro->stpidx = macro->sidx; + } else if (idx < SPX5_SERDES_25G_START) { + macro->serdestype = SPX5_SDT_10G; + macro->stpidx = macro->sidx - SPX5_SERDES_10G_START; + } else { + macro->serdestype = SPX5_SDT_25G; + macro->stpidx = macro->sidx - SPX5_SERDES_25G_START; + } + + phy_set_drvdata(*phy, macro); + + return 0; +} + +static struct sparx5_serdes_io_resource sparx5_serdes_iomap[] = { + { TARGET_SD_CMU, 0x0 }, /* 0x610808000: sd_cmu_0 */ + { TARGET_SD_CMU + 1, 0x8000 }, /* 0x610810000: sd_cmu_1 */ + { TARGET_SD_CMU + 2, 0x10000 }, /* 0x610818000: sd_cmu_2 */ + { TARGET_SD_CMU + 3, 0x18000 }, /* 0x610820000: sd_cmu_3 */ + { TARGET_SD_CMU + 4, 0x20000 }, /* 0x610828000: sd_cmu_4 */ + { TARGET_SD_CMU + 5, 0x28000 }, /* 0x610830000: sd_cmu_5 */ + { TARGET_SD_CMU + 6, 0x30000 }, /* 0x610838000: sd_cmu_6 */ + { TARGET_SD_CMU + 7, 0x38000 }, /* 0x610840000: sd_cmu_7 */ + { TARGET_SD_CMU + 8, 0x40000 }, /* 0x610848000: sd_cmu_8 */ + { TARGET_SD_CMU_CFG, 0x48000 }, /* 0x610850000: sd_cmu_cfg_0 */ + { TARGET_SD_CMU_CFG + 1, 0x50000 }, /* 0x610858000: sd_cmu_cfg_1 */ + { TARGET_SD_CMU_CFG + 2, 0x58000 }, /* 0x610860000: sd_cmu_cfg_2 */ + { TARGET_SD_CMU_CFG + 3, 0x60000 }, /* 0x610868000: sd_cmu_cfg_3 */ + { TARGET_SD_CMU_CFG + 4, 0x68000 }, /* 0x610870000: sd_cmu_cfg_4 */ + { TARGET_SD_CMU_CFG + 5, 0x70000 }, /* 0x610878000: sd_cmu_cfg_5 */ + { TARGET_SD_CMU_CFG + 6, 0x78000 }, /* 0x610880000: sd_cmu_cfg_6 */ + { TARGET_SD_CMU_CFG + 7, 0x80000 }, /* 0x610888000: sd_cmu_cfg_7 */ + { TARGET_SD_CMU_CFG + 8, 0x88000 }, /* 0x610890000: sd_cmu_cfg_8 */ + { TARGET_SD6G_LANE, 0x90000 }, /* 0x610898000: sd6g_lane_0 */ + { TARGET_SD6G_LANE + 1, 0x98000 }, /* 0x6108a0000: sd6g_lane_1 */ + { TARGET_SD6G_LANE + 2, 0xa0000 }, /* 0x6108a8000: sd6g_lane_2 */ + { TARGET_SD6G_LANE + 3, 0xa8000 }, /* 0x6108b0000: sd6g_lane_3 */ + { TARGET_SD6G_LANE + 4, 0xb0000 }, /* 0x6108b8000: sd6g_lane_4 */ + { TARGET_SD6G_LANE + 5, 0xb8000 }, /* 0x6108c0000: sd6g_lane_5 */ + { TARGET_SD6G_LANE + 6, 0xc0000 }, /* 0x6108c8000: sd6g_lane_6 */ + { TARGET_SD6G_LANE + 7, 0xc8000 }, /* 0x6108d0000: sd6g_lane_7 */ + { TARGET_SD6G_LANE + 8, 0xd0000 }, /* 0x6108d8000: sd6g_lane_8 */ + { TARGET_SD6G_LANE + 9, 0xd8000 }, /* 0x6108e0000: sd6g_lane_9 */ + { TARGET_SD6G_LANE + 10, 0xe0000 }, /* 0x6108e8000: sd6g_lane_10 */ + { TARGET_SD6G_LANE + 11, 0xe8000 }, /* 0x6108f0000: sd6g_lane_11 */ + { TARGET_SD6G_LANE + 12, 0xf0000 }, /* 0x6108f8000: sd6g_lane_12 */ + { TARGET_SD10G_LANE, 0xf8000 }, /* 0x610900000: sd10g_lane_0 */ + { TARGET_SD10G_LANE + 1, 0x100000 }, /* 0x610908000: sd10g_lane_1 */ + { TARGET_SD10G_LANE + 2, 0x108000 }, /* 0x610910000: sd10g_lane_2 */ + { TARGET_SD10G_LANE + 3, 0x110000 }, /* 0x610918000: sd10g_lane_3 */ + { TARGET_SD_LANE, 0x1a0000 }, /* 0x6109a8000: sd_lane_0 */ + { TARGET_SD_LANE + 1, 0x1a8000 }, /* 0x6109b0000: sd_lane_1 */ + { TARGET_SD_LANE + 2, 0x1b0000 }, /* 0x6109b8000: sd_lane_2 */ + { TARGET_SD_LANE + 3, 0x1b8000 }, /* 0x6109c0000: sd_lane_3 */ + { TARGET_SD_LANE + 4, 0x1c0000 }, /* 0x6109c8000: sd_lane_4 */ + { TARGET_SD_LANE + 5, 0x1c8000 }, /* 0x6109d0000: sd_lane_5 */ + { TARGET_SD_LANE + 6, 0x1d0000 }, /* 0x6109d8000: sd_lane_6 */ + { TARGET_SD_LANE + 7, 0x1d8000 }, /* 0x6109e0000: sd_lane_7 */ + { TARGET_SD_LANE + 8, 0x1e0000 }, /* 0x6109e8000: sd_lane_8 */ + { TARGET_SD_LANE + 9, 0x1e8000 }, /* 0x6109f0000: sd_lane_9 */ + { TARGET_SD_LANE + 10, 0x1f0000 }, /* 0x6109f8000: sd_lane_10 */ + { TARGET_SD_LANE + 11, 0x1f8000 }, /* 0x610a00000: sd_lane_11 */ + { TARGET_SD_LANE + 12, 0x200000 }, /* 0x610a08000: sd_lane_12 */ + { TARGET_SD_LANE + 13, 0x208000 }, /* 0x610a10000: sd_lane_13 */ + { TARGET_SD_LANE + 14, 0x210000 }, /* 0x610a18000: sd_lane_14 */ + { TARGET_SD_LANE + 15, 0x218000 }, /* 0x610a20000: sd_lane_15 */ + { TARGET_SD_LANE + 16, 0x220000 }, /* 0x610a28000: sd_lane_16 */ + { TARGET_SD_CMU + 9, 0x400000 }, /* 0x610c08000: sd_cmu_9 */ + { TARGET_SD_CMU + 10, 0x408000 }, /* 0x610c10000: sd_cmu_10 */ + { TARGET_SD_CMU + 11, 0x410000 }, /* 0x610c18000: sd_cmu_11 */ + { TARGET_SD_CMU + 12, 0x418000 }, /* 0x610c20000: sd_cmu_12 */ + { TARGET_SD_CMU + 13, 0x420000 }, /* 0x610c28000: sd_cmu_13 */ + { TARGET_SD_CMU_CFG + 9, 0x428000 }, /* 0x610c30000: sd_cmu_cfg_9 */ + { TARGET_SD_CMU_CFG + 10, 0x430000 }, /* 0x610c38000: sd_cmu_cfg_10 */ + { TARGET_SD_CMU_CFG + 11, 0x438000 }, /* 0x610c40000: sd_cmu_cfg_11 */ + { TARGET_SD_CMU_CFG + 12, 0x440000 }, /* 0x610c48000: sd_cmu_cfg_12 */ + { TARGET_SD_CMU_CFG + 13, 0x448000 }, /* 0x610c50000: sd_cmu_cfg_13 */ + { TARGET_SD10G_LANE + 4, 0x450000 }, /* 0x610c58000: sd10g_lane_4 */ + { TARGET_SD10G_LANE + 5, 0x458000 }, /* 0x610c60000: sd10g_lane_5 */ + { TARGET_SD10G_LANE + 6, 0x460000 }, /* 0x610c68000: sd10g_lane_6 */ + { TARGET_SD10G_LANE + 7, 0x468000 }, /* 0x610c70000: sd10g_lane_7 */ + { TARGET_SD10G_LANE + 8, 0x470000 }, /* 0x610c78000: sd10g_lane_8 */ + { TARGET_SD10G_LANE + 9, 0x478000 }, /* 0x610c80000: sd10g_lane_9 */ + { TARGET_SD10G_LANE + 10, 0x480000 }, /* 0x610c88000: sd10g_lane_10 */ + { TARGET_SD10G_LANE + 11, 0x488000 }, /* 0x610c90000: sd10g_lane_11 */ + { TARGET_SD25G_LANE, 0x490000 }, /* 0x610c98000: sd25g_lane_0 */ + { TARGET_SD25G_LANE + 1, 0x498000 }, /* 0x610ca0000: sd25g_lane_1 */ + { TARGET_SD25G_LANE + 2, 0x4a0000 }, /* 0x610ca8000: sd25g_lane_2 */ + { TARGET_SD25G_LANE + 3, 0x4a8000 }, /* 0x610cb0000: sd25g_lane_3 */ + { TARGET_SD25G_LANE + 4, 0x4b0000 }, /* 0x610cb8000: sd25g_lane_4 */ + { TARGET_SD25G_LANE + 5, 0x4b8000 }, /* 0x610cc0000: sd25g_lane_5 */ + { TARGET_SD25G_LANE + 6, 0x4c0000 }, /* 0x610cc8000: sd25g_lane_6 */ + { TARGET_SD25G_LANE + 7, 0x4c8000 }, /* 0x610cd0000: sd25g_lane_7 */ + { TARGET_SD_LANE + 17, 0x550000 }, /* 0x610d58000: sd_lane_17 */ + { TARGET_SD_LANE + 18, 0x558000 }, /* 0x610d60000: sd_lane_18 */ + { TARGET_SD_LANE + 19, 0x560000 }, /* 0x610d68000: sd_lane_19 */ + { TARGET_SD_LANE + 20, 0x568000 }, /* 0x610d70000: sd_lane_20 */ + { TARGET_SD_LANE + 21, 0x570000 }, /* 0x610d78000: sd_lane_21 */ + { TARGET_SD_LANE + 22, 0x578000 }, /* 0x610d80000: sd_lane_22 */ + { TARGET_SD_LANE + 23, 0x580000 }, /* 0x610d88000: sd_lane_23 */ + { TARGET_SD_LANE + 24, 0x588000 }, /* 0x610d90000: sd_lane_24 */ + { TARGET_SD_LANE_25G, 0x590000 }, /* 0x610d98000: sd_lane_25g_25 */ + { TARGET_SD_LANE_25G + 1, 0x598000 }, /* 0x610da0000: sd_lane_25g_26 */ + { TARGET_SD_LANE_25G + 2, 0x5a0000 }, /* 0x610da8000: sd_lane_25g_27 */ + { TARGET_SD_LANE_25G + 3, 0x5a8000 }, /* 0x610db0000: sd_lane_25g_28 */ + { TARGET_SD_LANE_25G + 4, 0x5b0000 }, /* 0x610db8000: sd_lane_25g_29 */ + { TARGET_SD_LANE_25G + 5, 0x5b8000 }, /* 0x610dc0000: sd_lane_25g_30 */ + { TARGET_SD_LANE_25G + 6, 0x5c0000 }, /* 0x610dc8000: sd_lane_25g_31 */ + { TARGET_SD_LANE_25G + 7, 0x5c8000 }, /* 0x610dd0000: sd_lane_25g_32 */ +}; + +/* Client lookup function, uses serdes index */ +static struct phy *sparx5_serdes_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct sparx5_serdes_private *priv = dev_get_drvdata(dev); + int idx; + unsigned int sidx; + + if (args->args_count != 1) + return ERR_PTR(-EINVAL); + + sidx = args->args[0]; + + /* Check validity: ERR_PTR(-ENODEV) if not valid */ + for (idx = 0; idx < SPX5_SERDES_MAX; idx++) { + struct sparx5_serdes_macro *macro = + phy_get_drvdata(priv->phys[idx]); + + if (sidx != macro->sidx) + continue; + + return priv->phys[idx]; + } + return ERR_PTR(-ENODEV); +} + +static int sparx5_serdes_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct sparx5_serdes_private *priv; + struct phy_provider *provider; + struct resource *iores; + void __iomem *iomem; + unsigned long clock; + struct clk *clk; + int idx; + int err; + + if (!np && !pdev->dev.platform_data) + return -ENODEV; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->dev = &pdev->dev; + + /* Get coreclock */ + clk = devm_clk_get(priv->dev, NULL); + if (IS_ERR(clk)) { + dev_err(priv->dev, "Failed to get coreclock\n"); + return PTR_ERR(clk); + } + clock = clk_get_rate(clk); + if (clock == 0) { + dev_err(priv->dev, "Invalid coreclock %lu\n", clock); + return -EINVAL; + } + priv->coreclock = clock; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) { + dev_err(priv->dev, "Invalid resource\n"); + return -EINVAL; + } + iomem = devm_ioremap(priv->dev, iores->start, resource_size(iores)); + if (!iomem) { + dev_err(priv->dev, "Unable to get serdes registers: %s\n", + iores->name); + return -ENOMEM; + } + for (idx = 0; idx < ARRAY_SIZE(sparx5_serdes_iomap); idx++) { + struct sparx5_serdes_io_resource *iomap = &sparx5_serdes_iomap[idx]; + + priv->regs[iomap->id] = iomem + iomap->offset; + } + for (idx = 0; idx < SPX5_SERDES_MAX; idx++) { + err = sparx5_phy_create(priv, idx, &priv->phys[idx]); + if (err) + return err; + } + + provider = devm_of_phy_provider_register(priv->dev, sparx5_serdes_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id sparx5_serdes_match[] = { + { .compatible = "microchip,sparx5-serdes" }, + { } +}; +MODULE_DEVICE_TABLE(of, sparx5_serdes_match); + +static struct platform_driver sparx5_serdes_driver = { + .probe = sparx5_serdes_probe, + .driver = { + .name = "sparx5-serdes", + .of_match_table = sparx5_serdes_match, + }, +}; + +module_platform_driver(sparx5_serdes_driver); + +MODULE_DESCRIPTION("Microchip Sparx5 switch serdes driver"); +MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/microchip/sparx5_serdes.h b/drivers/phy/microchip/sparx5_serdes.h new file mode 100644 index 000000000000..0a3e496e6210 --- /dev/null +++ b/drivers/phy/microchip/sparx5_serdes.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * Microchip Sparx5 SerDes driver + * + * Copyright (c) 2020 Microchip Technology Inc. + */ + +#ifndef _SPARX5_SERDES_H_ +#define _SPARX5_SERDES_H_ + +#include "sparx5_serdes_regs.h" + +#define SPX5_SERDES_MAX 33 + +enum sparx5_serdes_type { + SPX5_SDT_6G = 6, + SPX5_SDT_10G = 10, + SPX5_SDT_25G = 25, +}; + +enum sparx5_serdes_mode { + SPX5_SD_MODE_NONE, + SPX5_SD_MODE_2G5, + SPX5_SD_MODE_QSGMII, + SPX5_SD_MODE_100FX, + SPX5_SD_MODE_1000BASEX, + SPX5_SD_MODE_SFI, +}; + +struct sparx5_serdes_private { + struct device *dev; + void __iomem *regs[NUM_TARGETS]; + struct phy *phys[SPX5_SERDES_MAX]; + bool cmu_enabled; + unsigned long coreclock; +}; + +struct sparx5_serdes_macro { + struct sparx5_serdes_private *priv; + u32 sidx; + u32 stpidx; + enum sparx5_serdes_type serdestype; + enum sparx5_serdes_mode serdesmode; + phy_interface_t portmode; + int speed; + enum phy_media media; +}; + +/* Read, Write and modify registers content. + * The register definition macros start at the id + */ +static inline void __iomem *sdx5_addr(void __iomem *base[], + int id, int tinst, int tcnt, + int gbase, int ginst, + int gcnt, int gwidth, + int raddr, int rinst, + int rcnt, int rwidth) +{ + WARN_ON((tinst) >= tcnt); + WARN_ON((ginst) >= gcnt); + WARN_ON((rinst) >= rcnt); + return base[id + (tinst)] + + gbase + ((ginst) * gwidth) + + raddr + ((rinst) * rwidth); +} + +static inline void __iomem *sdx5_inst_baseaddr(void __iomem *base, + int gbase, int ginst, + int gcnt, int gwidth, + int raddr, int rinst, + int rcnt, int rwidth) +{ + WARN_ON((ginst) >= gcnt); + WARN_ON((rinst) >= rcnt); + return base + + gbase + ((ginst) * gwidth) + + raddr + ((rinst) * rwidth); +} + +static inline void sdx5_rmw(u32 val, u32 mask, struct sparx5_serdes_private *priv, + int id, int tinst, int tcnt, + int gbase, int ginst, int gcnt, int gwidth, + int raddr, int rinst, int rcnt, int rwidth) +{ + u32 nval; + void __iomem *addr = + sdx5_addr(priv->regs, id, tinst, tcnt, + gbase, ginst, gcnt, gwidth, + raddr, rinst, rcnt, rwidth); + nval = readl(addr); + nval = (nval & ~mask) | (val & mask); + writel(nval, addr); +} + +static inline void sdx5_inst_rmw(u32 val, u32 mask, void __iomem *iomem, + int id, int tinst, int tcnt, + int gbase, int ginst, int gcnt, int gwidth, + int raddr, int rinst, int rcnt, int rwidth) +{ + u32 nval; + void __iomem *addr = + sdx5_inst_baseaddr(iomem, + gbase, ginst, gcnt, gwidth, + raddr, rinst, rcnt, rwidth); + nval = readl(addr); + nval = (nval & ~mask) | (val & mask); + writel(nval, addr); +} + +static inline void sdx5_rmw_addr(u32 val, u32 mask, void __iomem *addr) +{ + u32 nval; + + nval = readl(addr); + nval = (nval & ~mask) | (val & mask); + writel(nval, addr); +} + +static inline void __iomem *sdx5_inst_get(struct sparx5_serdes_private *priv, + int id, int tinst) +{ + return priv->regs[id + tinst]; +} + +static inline void __iomem *sdx5_inst_addr(void __iomem *iomem, + int id, int tinst, int tcnt, + int gbase, + int ginst, int gcnt, int gwidth, + int raddr, + int rinst, int rcnt, int rwidth) +{ + return sdx5_inst_baseaddr(iomem, gbase, ginst, gcnt, gwidth, + raddr, rinst, rcnt, rwidth); +} + + +#endif /* _SPARX5_SERDES_REGS_H_ */ diff --git a/drivers/phy/microchip/sparx5_serdes_regs.h b/drivers/phy/microchip/sparx5_serdes_regs.h new file mode 100644 index 000000000000..b96386a4df5a --- /dev/null +++ b/drivers/phy/microchip/sparx5_serdes_regs.h @@ -0,0 +1,2695 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * Microchip Sparx5 SerDes driver + * + * Copyright (c) 2020 Microchip Technology Inc. + */ + +/* This file is autogenerated by cml-utils 2020-11-16 13:11:27 +0100. + * Commit ID: 13bdf073131d8bf40c54901df6988ae4e9c8f29f + */ + +#ifndef _SPARX5_SERDES_REGS_H_ +#define _SPARX5_SERDES_REGS_H_ + +#include <linux/bitfield.h> +#include <linux/types.h> +#include <linux/bug.h> + +enum sparx5_serdes_target { + TARGET_SD10G_LANE = 200, + TARGET_SD25G_LANE = 212, + TARGET_SD6G_LANE = 233, + TARGET_SD_CMU = 248, + TARGET_SD_CMU_CFG = 262, + TARGET_SD_LANE = 276, + TARGET_SD_LANE_25G = 301, + NUM_TARGETS = 332 +}; + +#define __REG(...) __VA_ARGS__ + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_01 */ +#define SD10G_LANE_LANE_01(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 4, 0, 1, 4) + +#define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0 GENMASK(2, 0) +#define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0, x) +#define SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_01_CFG_PMA_TX_CK_BITWIDTH_2_0, x) + +#define SD10G_LANE_LANE_01_CFG_RXDET_EN BIT(4) +#define SD10G_LANE_LANE_01_CFG_RXDET_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_01_CFG_RXDET_EN, x) +#define SD10G_LANE_LANE_01_CFG_RXDET_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_01_CFG_RXDET_EN, x) + +#define SD10G_LANE_LANE_01_CFG_RXDET_STR BIT(5) +#define SD10G_LANE_LANE_01_CFG_RXDET_STR_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_01_CFG_RXDET_STR, x) +#define SD10G_LANE_LANE_01_CFG_RXDET_STR_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_01_CFG_RXDET_STR, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_02 */ +#define SD10G_LANE_LANE_02(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 8, 0, 1, 4) + +#define SD10G_LANE_LANE_02_CFG_EN_ADV BIT(0) +#define SD10G_LANE_LANE_02_CFG_EN_ADV_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_ADV, x) +#define SD10G_LANE_LANE_02_CFG_EN_ADV_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_ADV, x) + +#define SD10G_LANE_LANE_02_CFG_EN_MAIN BIT(1) +#define SD10G_LANE_LANE_02_CFG_EN_MAIN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_MAIN, x) +#define SD10G_LANE_LANE_02_CFG_EN_MAIN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_MAIN, x) + +#define SD10G_LANE_LANE_02_CFG_EN_DLY BIT(2) +#define SD10G_LANE_LANE_02_CFG_EN_DLY_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_DLY, x) +#define SD10G_LANE_LANE_02_CFG_EN_DLY_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_DLY, x) + +#define SD10G_LANE_LANE_02_CFG_EN_DLY2 BIT(3) +#define SD10G_LANE_LANE_02_CFG_EN_DLY2_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_02_CFG_EN_DLY2, x) +#define SD10G_LANE_LANE_02_CFG_EN_DLY2_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_02_CFG_EN_DLY2, x) + +#define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0 GENMASK(7, 4) +#define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0, x) +#define SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_02_CFG_TAP_ADV_3_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_03 */ +#define SD10G_LANE_LANE_03(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 12, 0, 1, 4) + +#define SD10G_LANE_LANE_03_CFG_TAP_MAIN BIT(0) +#define SD10G_LANE_LANE_03_CFG_TAP_MAIN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_03_CFG_TAP_MAIN, x) +#define SD10G_LANE_LANE_03_CFG_TAP_MAIN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_03_CFG_TAP_MAIN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_04 */ +#define SD10G_LANE_LANE_04(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 16, 0, 1, 4) + +#define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0 GENMASK(4, 0) +#define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0, x) +#define SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_04_CFG_TAP_DLY_4_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_06 */ +#define SD10G_LANE_LANE_06(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 24, 0, 1, 4) + +#define SD10G_LANE_LANE_06_CFG_PD_DRIVER BIT(0) +#define SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_06_CFG_PD_DRIVER, x) +#define SD10G_LANE_LANE_06_CFG_PD_DRIVER_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_06_CFG_PD_DRIVER, x) + +#define SD10G_LANE_LANE_06_CFG_PD_CLK BIT(1) +#define SD10G_LANE_LANE_06_CFG_PD_CLK_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_06_CFG_PD_CLK, x) +#define SD10G_LANE_LANE_06_CFG_PD_CLK_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_06_CFG_PD_CLK, x) + +#define SD10G_LANE_LANE_06_CFG_PD_CML BIT(2) +#define SD10G_LANE_LANE_06_CFG_PD_CML_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_06_CFG_PD_CML, x) +#define SD10G_LANE_LANE_06_CFG_PD_CML_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_06_CFG_PD_CML, x) + +#define SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN BIT(3) +#define SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN, x) +#define SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_06_CFG_TX2RX_LP_EN, x) + +#define SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN BIT(4) +#define SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN, x) +#define SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_06_CFG_RX2TX_LP_EN, x) + +#define SD10G_LANE_LANE_06_CFG_EN_PREEMPH BIT(5) +#define SD10G_LANE_LANE_06_CFG_EN_PREEMPH_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_06_CFG_EN_PREEMPH, x) +#define SD10G_LANE_LANE_06_CFG_EN_PREEMPH_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_06_CFG_EN_PREEMPH, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0B */ +#define SD10G_LANE_LANE_0B(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 44, 0, 1, 4) + +#define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0 GENMASK(3, 0) +#define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0, x) +#define SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0B_CFG_EQ_RES_3_0, x) + +#define SD10G_LANE_LANE_0B_CFG_PD_CTLE BIT(4) +#define SD10G_LANE_LANE_0B_CFG_PD_CTLE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0B_CFG_PD_CTLE, x) +#define SD10G_LANE_LANE_0B_CFG_PD_CTLE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0B_CFG_PD_CTLE, x) + +#define SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN BIT(5) +#define SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN, x) +#define SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0B_CFG_CTLE_TP_EN, x) + +#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE BIT(6) +#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE, x) +#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_AFE, x) + +#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ BIT(7) +#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ, x) +#define SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0B_CFG_RESETB_OSCAL_SQ, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0C */ +#define SD10G_LANE_LANE_0C(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 48, 0, 1, 4) + +#define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE BIT(0) +#define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSCAL_AFE, x) +#define SD10G_LANE_LANE_0C_CFG_OSCAL_AFE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSCAL_AFE, x) + +#define SD10G_LANE_LANE_0C_CFG_OSCAL_SQ BIT(1) +#define SD10G_LANE_LANE_0C_CFG_OSCAL_SQ_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSCAL_SQ, x) +#define SD10G_LANE_LANE_0C_CFG_OSCAL_SQ_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSCAL_SQ, x) + +#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE BIT(2) +#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE, x) +#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_AFE, x) + +#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ BIT(3) +#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ, x) +#define SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_OSDAC_2X_SQ, x) + +#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE BIT(4) +#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE, x) +#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_AFE, x) + +#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ BIT(5) +#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ, x) +#define SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_PD_OSDAC_SQ, x) + +#define SD10G_LANE_LANE_0C_CFG_PD_RX_LS BIT(6) +#define SD10G_LANE_LANE_0C_CFG_PD_RX_LS_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_PD_RX_LS, x) +#define SD10G_LANE_LANE_0C_CFG_PD_RX_LS_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_PD_RX_LS, x) + +#define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12 BIT(7) +#define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12, x) +#define SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0C_CFG_RX_PCIE_GEN12, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0D */ +#define SD10G_LANE_LANE_0D(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 52, 0, 1, 4) + +#define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0 GENMASK(1, 0) +#define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0, x) +#define SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0D_CFG_CTLE_M_THR_1_0, x) + +#define SD10G_LANE_LANE_0D_CFG_EQR_BYP BIT(4) +#define SD10G_LANE_LANE_0D_CFG_EQR_BYP_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0D_CFG_EQR_BYP, x) +#define SD10G_LANE_LANE_0D_CFG_EQR_BYP_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0D_CFG_EQR_BYP, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0E */ +#define SD10G_LANE_LANE_0E(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 56, 0, 1, 4) + +#define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0 GENMASK(3, 0) +#define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0, x) +#define SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0E_CFG_EQC_FORCE_3_0, x) + +#define SD10G_LANE_LANE_0E_CFG_RXLB_EN BIT(4) +#define SD10G_LANE_LANE_0E_CFG_RXLB_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0E_CFG_RXLB_EN, x) +#define SD10G_LANE_LANE_0E_CFG_RXLB_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0E_CFG_RXLB_EN, x) + +#define SD10G_LANE_LANE_0E_CFG_TXLB_EN BIT(5) +#define SD10G_LANE_LANE_0E_CFG_TXLB_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0E_CFG_TXLB_EN, x) +#define SD10G_LANE_LANE_0E_CFG_TXLB_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0E_CFG_TXLB_EN, x) + +#define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN BIT(6) +#define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN, x) +#define SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0E_CFG_SUM_SETCM_EN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_0F */ +#define SD10G_LANE_LANE_0F(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 60, 0, 1, 4) + +#define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0 GENMASK(7, 0) +#define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0, x) +#define SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_0F_R_CDR_M_GEN1_7_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_13 */ +#define SD10G_LANE_LANE_13(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 76, 0, 1, 4) + +#define SD10G_LANE_LANE_13_CFG_DCDR_PD BIT(0) +#define SD10G_LANE_LANE_13_CFG_DCDR_PD_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_13_CFG_DCDR_PD, x) +#define SD10G_LANE_LANE_13_CFG_DCDR_PD_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_13_CFG_DCDR_PD, x) + +#define SD10G_LANE_LANE_13_CFG_PHID_1T BIT(1) +#define SD10G_LANE_LANE_13_CFG_PHID_1T_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_13_CFG_PHID_1T, x) +#define SD10G_LANE_LANE_13_CFG_PHID_1T_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_13_CFG_PHID_1T, x) + +#define SD10G_LANE_LANE_13_CFG_CDRCK_EN BIT(2) +#define SD10G_LANE_LANE_13_CFG_CDRCK_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_13_CFG_CDRCK_EN, x) +#define SD10G_LANE_LANE_13_CFG_CDRCK_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_13_CFG_CDRCK_EN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_14 */ +#define SD10G_LANE_LANE_14(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 80, 0, 1, 4) + +#define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0 GENMASK(7, 0) +#define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0, x) +#define SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_14_CFG_PI_EXT_DAC_7_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_15 */ +#define SD10G_LANE_LANE_15(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 84, 0, 1, 4) + +#define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8 GENMASK(7, 0) +#define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8, x) +#define SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_15_CFG_PI_EXT_DAC_15_8, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_16 */ +#define SD10G_LANE_LANE_16(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 88, 0, 1, 4) + +#define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16 GENMASK(7, 0) +#define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16, x) +#define SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_16_CFG_PI_EXT_DAC_23_16, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_1A */ +#define SD10G_LANE_LANE_1A(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 104, 0, 1, 4) + +#define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN BIT(0) +#define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN, x) +#define SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_R_SCAN_EN, x) + +#define SD10G_LANE_LANE_1A_CFG_PI_EN BIT(1) +#define SD10G_LANE_LANE_1A_CFG_PI_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_EN, x) +#define SD10G_LANE_LANE_1A_CFG_PI_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_EN, x) + +#define SD10G_LANE_LANE_1A_CFG_PI_DFE_EN BIT(2) +#define SD10G_LANE_LANE_1A_CFG_PI_DFE_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_DFE_EN, x) +#define SD10G_LANE_LANE_1A_CFG_PI_DFE_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_DFE_EN, x) + +#define SD10G_LANE_LANE_1A_CFG_PI_STEPS BIT(3) +#define SD10G_LANE_LANE_1A_CFG_PI_STEPS_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_STEPS, x) +#define SD10G_LANE_LANE_1A_CFG_PI_STEPS_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_STEPS, x) + +#define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0 GENMASK(5, 4) +#define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0, x) +#define SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_1A_CFG_PI_FLOOP_STEPS_1_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_22 */ +#define SD10G_LANE_LANE_22(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 136, 0, 1, 4) + +#define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1 GENMASK(4, 0) +#define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1, x) +#define SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_22_CFG_DFETAP_EN_5_1, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_23 */ +#define SD10G_LANE_LANE_23(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 140, 0, 1, 4) + +#define SD10G_LANE_LANE_23_CFG_DFE_PD BIT(0) +#define SD10G_LANE_LANE_23_CFG_DFE_PD_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_23_CFG_DFE_PD, x) +#define SD10G_LANE_LANE_23_CFG_DFE_PD_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_23_CFG_DFE_PD, x) + +#define SD10G_LANE_LANE_23_CFG_EN_DFEDIG BIT(1) +#define SD10G_LANE_LANE_23_CFG_EN_DFEDIG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_23_CFG_EN_DFEDIG, x) +#define SD10G_LANE_LANE_23_CFG_EN_DFEDIG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_23_CFG_EN_DFEDIG, x) + +#define SD10G_LANE_LANE_23_CFG_DFECK_EN BIT(2) +#define SD10G_LANE_LANE_23_CFG_DFECK_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_23_CFG_DFECK_EN, x) +#define SD10G_LANE_LANE_23_CFG_DFECK_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_23_CFG_DFECK_EN, x) + +#define SD10G_LANE_LANE_23_CFG_ERRAMP_PD BIT(3) +#define SD10G_LANE_LANE_23_CFG_ERRAMP_PD_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_23_CFG_ERRAMP_PD, x) +#define SD10G_LANE_LANE_23_CFG_ERRAMP_PD_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_23_CFG_ERRAMP_PD, x) + +#define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0 GENMASK(6, 4) +#define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0, x) +#define SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_23_CFG_DFEDIG_M_2_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_24 */ +#define SD10G_LANE_LANE_24(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 144, 0, 1, 4) + +#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0 GENMASK(3, 0) +#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0, x) +#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_24_CFG_PI_BW_GEN1_3_0, x) + +#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0 GENMASK(7, 4) +#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0, x) +#define SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_24_CFG_PI_BW_GEN2_3_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_26 */ +#define SD10G_LANE_LANE_26(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 152, 0, 1, 4) + +#define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0 GENMASK(7, 0) +#define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0, x) +#define SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_26_CFG_ISCAN_EXT_DAC_7_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_2F */ +#define SD10G_LANE_LANE_2F(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 188, 0, 1, 4) + +#define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0 GENMASK(2, 0) +#define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0, x) +#define SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_2F_CFG_VGA_CP_2_0, x) + +#define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0 GENMASK(7, 4) +#define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0, x) +#define SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_2F_CFG_VGA_CTRL_3_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_30 */ +#define SD10G_LANE_LANE_30(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 192, 0, 1, 4) + +#define SD10G_LANE_LANE_30_CFG_SUMMER_EN BIT(0) +#define SD10G_LANE_LANE_30_CFG_SUMMER_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_30_CFG_SUMMER_EN, x) +#define SD10G_LANE_LANE_30_CFG_SUMMER_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_30_CFG_SUMMER_EN, x) + +#define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0 GENMASK(6, 4) +#define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0, x) +#define SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_30_CFG_RXDIV_SEL_2_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_31 */ +#define SD10G_LANE_LANE_31(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 196, 0, 1, 4) + +#define SD10G_LANE_LANE_31_CFG_PI_RSTN BIT(0) +#define SD10G_LANE_LANE_31_CFG_PI_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_31_CFG_PI_RSTN, x) +#define SD10G_LANE_LANE_31_CFG_PI_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_31_CFG_PI_RSTN, x) + +#define SD10G_LANE_LANE_31_CFG_CDR_RSTN BIT(1) +#define SD10G_LANE_LANE_31_CFG_CDR_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_31_CFG_CDR_RSTN, x) +#define SD10G_LANE_LANE_31_CFG_CDR_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_31_CFG_CDR_RSTN, x) + +#define SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG BIT(2) +#define SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG, x) +#define SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_31_CFG_RSTN_DFEDIG, x) + +#define SD10G_LANE_LANE_31_CFG_CTLE_RSTN BIT(3) +#define SD10G_LANE_LANE_31_CFG_CTLE_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_31_CFG_CTLE_RSTN, x) +#define SD10G_LANE_LANE_31_CFG_CTLE_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_31_CFG_CTLE_RSTN, x) + +#define SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8 BIT(4) +#define SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8, x) +#define SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_31_CFG_RSTN_DIV5_8, x) + +#define SD10G_LANE_LANE_31_CFG_R50_EN BIT(5) +#define SD10G_LANE_LANE_31_CFG_R50_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_31_CFG_R50_EN, x) +#define SD10G_LANE_LANE_31_CFG_R50_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_31_CFG_R50_EN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_32 */ +#define SD10G_LANE_LANE_32(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 200, 0, 1, 4) + +#define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0 GENMASK(1, 0) +#define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0, x) +#define SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_32_CFG_ITX_IPCLK_BASE_1_0, x) + +#define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0 GENMASK(5, 4) +#define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0, x) +#define SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_32_CFG_ITX_IPCML_BASE_1_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_33 */ +#define SD10G_LANE_LANE_33(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 204, 0, 1, 4) + +#define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0 GENMASK(2, 0) +#define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0, x) +#define SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_33_CFG_ITX_IPDRIVER_BASE_2_0, x) + +#define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0 GENMASK(5, 4) +#define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0, x) +#define SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_33_CFG_ITX_IPPREEMP_BASE_1_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_35 */ +#define SD10G_LANE_LANE_35(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 212, 0, 1, 4) + +#define SD10G_LANE_LANE_35_CFG_TXRATE_1_0 GENMASK(1, 0) +#define SD10G_LANE_LANE_35_CFG_TXRATE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_35_CFG_TXRATE_1_0, x) +#define SD10G_LANE_LANE_35_CFG_TXRATE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_35_CFG_TXRATE_1_0, x) + +#define SD10G_LANE_LANE_35_CFG_RXRATE_1_0 GENMASK(5, 4) +#define SD10G_LANE_LANE_35_CFG_RXRATE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_35_CFG_RXRATE_1_0, x) +#define SD10G_LANE_LANE_35_CFG_RXRATE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_35_CFG_RXRATE_1_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_36 */ +#define SD10G_LANE_LANE_36(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 216, 0, 1, 4) + +#define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0 GENMASK(1, 0) +#define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0, x) +#define SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_36_CFG_PREDRV_SLEWRATE_1_0, x) + +#define SD10G_LANE_LANE_36_CFG_EID_LP BIT(4) +#define SD10G_LANE_LANE_36_CFG_EID_LP_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_36_CFG_EID_LP, x) +#define SD10G_LANE_LANE_36_CFG_EID_LP_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_36_CFG_EID_LP, x) + +#define SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH BIT(5) +#define SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH, x) +#define SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_36_CFG_EN_PREDRV_EMPH, x) + +#define SD10G_LANE_LANE_36_CFG_PRBS_SEL BIT(6) +#define SD10G_LANE_LANE_36_CFG_PRBS_SEL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_36_CFG_PRBS_SEL, x) +#define SD10G_LANE_LANE_36_CFG_PRBS_SEL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_36_CFG_PRBS_SEL, x) + +#define SD10G_LANE_LANE_36_CFG_PRBS_SETB BIT(7) +#define SD10G_LANE_LANE_36_CFG_PRBS_SETB_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_36_CFG_PRBS_SETB, x) +#define SD10G_LANE_LANE_36_CFG_PRBS_SETB_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_36_CFG_PRBS_SETB, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_37 */ +#define SD10G_LANE_LANE_37(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 220, 0, 1, 4) + +#define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD BIT(0) +#define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD, x) +#define SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_37_CFG_RXDET_COMP_PD, x) + +#define SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE BIT(1) +#define SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE, x) +#define SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_37_CFG_PD_RX_CKTREE, x) + +#define SD10G_LANE_LANE_37_CFG_TXSWING_HALF BIT(2) +#define SD10G_LANE_LANE_37_CFG_TXSWING_HALF_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_37_CFG_TXSWING_HALF, x) +#define SD10G_LANE_LANE_37_CFG_TXSWING_HALF_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_37_CFG_TXSWING_HALF, x) + +#define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0 GENMASK(5, 4) +#define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0, x) +#define SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_37_CFG_IP_PRE_BASE_1_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_39 */ +#define SD10G_LANE_LANE_39(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 228, 0, 1, 4) + +#define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0 GENMASK(2, 0) +#define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0, x) +#define SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_39_CFG_RXFILT_Y_2_0, x) + +#define SD10G_LANE_LANE_39_CFG_RX_SSC_LH BIT(4) +#define SD10G_LANE_LANE_39_CFG_RX_SSC_LH_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_39_CFG_RX_SSC_LH, x) +#define SD10G_LANE_LANE_39_CFG_RX_SSC_LH_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_39_CFG_RX_SSC_LH, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3A */ +#define SD10G_LANE_LANE_3A(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 232, 0, 1, 4) + +#define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0 GENMASK(3, 0) +#define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0, x) +#define SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_3A_CFG_MP_MIN_3_0, x) + +#define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0 GENMASK(7, 4) +#define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0, x) +#define SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_3A_CFG_MP_MAX_3_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_3C */ +#define SD10G_LANE_LANE_3C(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 240, 0, 1, 4) + +#define SD10G_LANE_LANE_3C_CFG_DIS_ACC BIT(0) +#define SD10G_LANE_LANE_3C_CFG_DIS_ACC_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_3C_CFG_DIS_ACC, x) +#define SD10G_LANE_LANE_3C_CFG_DIS_ACC_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_3C_CFG_DIS_ACC, x) + +#define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER BIT(1) +#define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER, x) +#define SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_3C_CFG_DIS_2NDORDER, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_40 */ +#define SD10G_LANE_LANE_40(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 256, 0, 1, 4) + +#define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0 GENMASK(7, 0) +#define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0, x) +#define SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_40_CFG_LANE_RESERVE_7_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_41 */ +#define SD10G_LANE_LANE_41(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 260, 0, 1, 4) + +#define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8 GENMASK(7, 0) +#define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8, x) +#define SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_41_CFG_LANE_RESERVE_15_8, x) + +/* SD10G_LANE_TARGET:LANE_GRP_0:LANE_42 */ +#define SD10G_LANE_LANE_42(t) __REG(TARGET_SD10G_LANE, t, 12, 0, 0, 1, 288, 264, 0, 1, 4) + +#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0 GENMASK(2, 0) +#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0, x) +#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN1_2_0, x) + +#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0 GENMASK(6, 4) +#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0, x) +#define SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_42_CFG_CDR_KF_GEN2_2_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_48 */ +#define SD10G_LANE_LANE_48(t) __REG(TARGET_SD10G_LANE, t, 12, 288, 0, 1, 40, 0, 0, 1, 4) + +#define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0 GENMASK(3, 0) +#define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0, x) +#define SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_48_CFG_ALOS_THR_3_0, x) + +#define SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL BIT(4) +#define SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL, x) +#define SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_48_CFG_AUX_RXCK_SEL, x) + +#define SD10G_LANE_LANE_48_CFG_CLK_ENQ BIT(5) +#define SD10G_LANE_LANE_48_CFG_CLK_ENQ_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_48_CFG_CLK_ENQ, x) +#define SD10G_LANE_LANE_48_CFG_CLK_ENQ_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_48_CFG_CLK_ENQ, x) + +/* SD10G_LANE_TARGET:LANE_GRP_1:LANE_50 */ +#define SD10G_LANE_LANE_50(t) __REG(TARGET_SD10G_LANE, t, 12, 288, 0, 1, 40, 32, 0, 1, 4) + +#define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0 GENMASK(1, 0) +#define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0, x) +#define SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_50_CFG_SSC_PI_STEP_1_0, x) + +#define SD10G_LANE_LANE_50_CFG_SSC_RESETB BIT(4) +#define SD10G_LANE_LANE_50_CFG_SSC_RESETB_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_50_CFG_SSC_RESETB, x) +#define SD10G_LANE_LANE_50_CFG_SSC_RESETB_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_50_CFG_SSC_RESETB, x) + +#define SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL BIT(5) +#define SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL, x) +#define SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_50_CFG_SSC_RTL_CLK_SEL, x) + +#define SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL BIT(6) +#define SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL, x) +#define SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_50_CFG_AUX_TXCK_SEL, x) + +#define SD10G_LANE_LANE_50_CFG_JT_EN BIT(7) +#define SD10G_LANE_LANE_50_CFG_JT_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_50_CFG_JT_EN, x) +#define SD10G_LANE_LANE_50_CFG_JT_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_50_CFG_JT_EN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_2:LANE_52 */ +#define SD10G_LANE_LANE_52(t) __REG(TARGET_SD10G_LANE, t, 12, 328, 0, 1, 24, 0, 0, 1, 4) + +#define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0 GENMASK(5, 0) +#define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0, x) +#define SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_52_CFG_IBIAS_TUNE_RESERVE_5_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_4:LANE_83 */ +#define SD10G_LANE_LANE_83(t) __REG(TARGET_SD10G_LANE, t, 12, 464, 0, 1, 112, 60, 0, 1, 4) + +#define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE BIT(0) +#define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_TX_BIT_REVERSE, x) +#define SD10G_LANE_LANE_83_R_TX_BIT_REVERSE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_TX_BIT_REVERSE, x) + +#define SD10G_LANE_LANE_83_R_TX_POL_INV BIT(1) +#define SD10G_LANE_LANE_83_R_TX_POL_INV_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_TX_POL_INV, x) +#define SD10G_LANE_LANE_83_R_TX_POL_INV_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_TX_POL_INV, x) + +#define SD10G_LANE_LANE_83_R_RX_BIT_REVERSE BIT(2) +#define SD10G_LANE_LANE_83_R_RX_BIT_REVERSE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_RX_BIT_REVERSE, x) +#define SD10G_LANE_LANE_83_R_RX_BIT_REVERSE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_RX_BIT_REVERSE, x) + +#define SD10G_LANE_LANE_83_R_RX_POL_INV BIT(3) +#define SD10G_LANE_LANE_83_R_RX_POL_INV_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_RX_POL_INV, x) +#define SD10G_LANE_LANE_83_R_RX_POL_INV_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_RX_POL_INV, x) + +#define SD10G_LANE_LANE_83_R_DFE_RSTN BIT(4) +#define SD10G_LANE_LANE_83_R_DFE_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_DFE_RSTN, x) +#define SD10G_LANE_LANE_83_R_DFE_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_DFE_RSTN, x) + +#define SD10G_LANE_LANE_83_R_CDR_RSTN BIT(5) +#define SD10G_LANE_LANE_83_R_CDR_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_CDR_RSTN, x) +#define SD10G_LANE_LANE_83_R_CDR_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_CDR_RSTN, x) + +#define SD10G_LANE_LANE_83_R_CTLE_RSTN BIT(6) +#define SD10G_LANE_LANE_83_R_CTLE_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_83_R_CTLE_RSTN, x) +#define SD10G_LANE_LANE_83_R_CTLE_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_83_R_CTLE_RSTN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_93 */ +#define SD10G_LANE_LANE_93(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 12, 0, 1, 4) + +#define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN BIT(0) +#define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN, x) +#define SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_RXEI_FIFO_RST_EN, x) + +#define SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT BIT(1) +#define SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT, x) +#define SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_DWIDTHCTRL_FROM_HWT, x) + +#define SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE BIT(2) +#define SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE, x) +#define SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_DIS_RESTORE_DFE, x) + +#define SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL BIT(3) +#define SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL, x) +#define SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_EN_RATECHG_CTRL, x) + +#define SD10G_LANE_LANE_93_R_REG_MANUAL BIT(4) +#define SD10G_LANE_LANE_93_R_REG_MANUAL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_REG_MANUAL, x) +#define SD10G_LANE_LANE_93_R_REG_MANUAL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_REG_MANUAL, x) + +#define SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT BIT(5) +#define SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT, x) +#define SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_AUXCKSEL_FROM_HWT, x) + +#define SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT BIT(6) +#define SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT, x) +#define SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_LANE_ID_FROM_HWT, x) + +#define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT BIT(7) +#define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT, x) +#define SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_93_R_RX_PCIE_GEN12_FROM_HWT, x) + +/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_94 */ +#define SD10G_LANE_LANE_94(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 16, 0, 1, 4) + +#define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0 GENMASK(2, 0) +#define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0, x) +#define SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_94_R_DWIDTHCTRL_2_0, x) + +#define SD10G_LANE_LANE_94_R_ISCAN_REG BIT(4) +#define SD10G_LANE_LANE_94_R_ISCAN_REG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_94_R_ISCAN_REG, x) +#define SD10G_LANE_LANE_94_R_ISCAN_REG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_94_R_ISCAN_REG, x) + +#define SD10G_LANE_LANE_94_R_TXEQ_REG BIT(5) +#define SD10G_LANE_LANE_94_R_TXEQ_REG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_94_R_TXEQ_REG, x) +#define SD10G_LANE_LANE_94_R_TXEQ_REG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_94_R_TXEQ_REG, x) + +#define SD10G_LANE_LANE_94_R_MISC_REG BIT(6) +#define SD10G_LANE_LANE_94_R_MISC_REG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_94_R_MISC_REG, x) +#define SD10G_LANE_LANE_94_R_MISC_REG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_94_R_MISC_REG, x) + +#define SD10G_LANE_LANE_94_R_SWING_REG BIT(7) +#define SD10G_LANE_LANE_94_R_SWING_REG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_94_R_SWING_REG, x) +#define SD10G_LANE_LANE_94_R_SWING_REG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_94_R_SWING_REG, x) + +/* SD10G_LANE_TARGET:LANE_GRP_5:LANE_9E */ +#define SD10G_LANE_LANE_9E(t) __REG(TARGET_SD10G_LANE, t, 12, 576, 0, 1, 64, 56, 0, 1, 4) + +#define SD10G_LANE_LANE_9E_R_RXEQ_REG BIT(0) +#define SD10G_LANE_LANE_9E_R_RXEQ_REG_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_9E_R_RXEQ_REG, x) +#define SD10G_LANE_LANE_9E_R_RXEQ_REG_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_9E_R_RXEQ_REG, x) + +#define SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN BIT(1) +#define SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN, x) +#define SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_9E_R_AUTO_RST_TREE_PD_MAN, x) + +#define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN BIT(2) +#define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN, x) +#define SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_9E_R_EN_AUTO_CDR_RSTN, x) + +/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A1 */ +#define SD10G_LANE_LANE_A1(t) __REG(TARGET_SD10G_LANE, t, 12, 640, 0, 1, 128, 4, 0, 1, 4) + +#define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0 GENMASK(1, 0) +#define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0, x) +#define SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_A1_R_PMA_TXCK_DIV_SEL_1_0, x) + +#define SD10G_LANE_LANE_A1_R_SSC_FROM_HWT BIT(4) +#define SD10G_LANE_LANE_A1_R_SSC_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_A1_R_SSC_FROM_HWT, x) +#define SD10G_LANE_LANE_A1_R_SSC_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_A1_R_SSC_FROM_HWT, x) + +#define SD10G_LANE_LANE_A1_R_CDR_FROM_HWT BIT(5) +#define SD10G_LANE_LANE_A1_R_CDR_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_A1_R_CDR_FROM_HWT, x) +#define SD10G_LANE_LANE_A1_R_CDR_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_A1_R_CDR_FROM_HWT, x) + +#define SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT BIT(6) +#define SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT, x) +#define SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_A1_R_PCLK_GATING_FROM_HWT, x) + +#define SD10G_LANE_LANE_A1_R_PCLK_GATING BIT(7) +#define SD10G_LANE_LANE_A1_R_PCLK_GATING_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_A1_R_PCLK_GATING, x) +#define SD10G_LANE_LANE_A1_R_PCLK_GATING_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_A1_R_PCLK_GATING, x) + +/* SD10G_LANE_TARGET:LANE_GRP_6:LANE_A2 */ +#define SD10G_LANE_LANE_A2(t) __REG(TARGET_SD10G_LANE, t, 12, 640, 0, 1, 128, 8, 0, 1, 4) + +#define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0 GENMASK(4, 0) +#define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0, x) +#define SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_A2_R_PCS2PMA_PHYMODE_4_0, x) + +/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */ +#define SD10G_LANE_LANE_DF(t) __REG(TARGET_SD10G_LANE, t, 12, 832, 0, 1, 84, 60, 0, 1, 4) + +#define SD10G_LANE_LANE_DF_LOL_UDL BIT(0) +#define SD10G_LANE_LANE_DF_LOL_UDL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_DF_LOL_UDL, x) +#define SD10G_LANE_LANE_DF_LOL_UDL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_DF_LOL_UDL, x) + +#define SD10G_LANE_LANE_DF_LOL BIT(1) +#define SD10G_LANE_LANE_DF_LOL_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_DF_LOL, x) +#define SD10G_LANE_LANE_DF_LOL_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_DF_LOL, x) + +#define SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED BIT(2) +#define SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x) +#define SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x) + +#define SD10G_LANE_LANE_DF_SQUELCH BIT(3) +#define SD10G_LANE_LANE_DF_SQUELCH_SET(x)\ + FIELD_PREP(SD10G_LANE_LANE_DF_SQUELCH, x) +#define SD10G_LANE_LANE_DF_SQUELCH_GET(x)\ + FIELD_GET(SD10G_LANE_LANE_DF_SQUELCH, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_09 */ +#define SD25G_LANE_CMU_09(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 36, 0, 1, 4) + +#define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN BIT(0) +#define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN, x) +#define SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_09_CFG_REFCK_TERM_EN, x) + +#define SD25G_LANE_CMU_09_CFG_EN_DUMMY BIT(1) +#define SD25G_LANE_CMU_09_CFG_EN_DUMMY_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_09_CFG_EN_DUMMY, x) +#define SD25G_LANE_CMU_09_CFG_EN_DUMMY_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_09_CFG_EN_DUMMY, x) + +#define SD25G_LANE_CMU_09_CFG_PLL_LOS_SET BIT(2) +#define SD25G_LANE_CMU_09_CFG_PLL_LOS_SET_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_09_CFG_PLL_LOS_SET, x) +#define SD25G_LANE_CMU_09_CFG_PLL_LOS_SET_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_09_CFG_PLL_LOS_SET, x) + +#define SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD BIT(3) +#define SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD, x) +#define SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_09_CFG_CTRL_LOGIC_PD, x) + +#define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0 GENMASK(5, 4) +#define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0, x) +#define SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_09_CFG_PLL_TP_SEL_1_0, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_0B */ +#define SD25G_LANE_CMU_0B(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 44, 0, 1, 4) + +#define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT BIT(0) +#define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT, x) +#define SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_FORCE_RX_FILT, x) + +#define SD25G_LANE_CMU_0B_CFG_DISLOL BIT(1) +#define SD25G_LANE_CMU_0B_CFG_DISLOL_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_DISLOL, x) +#define SD25G_LANE_CMU_0B_CFG_DISLOL_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_DISLOL, x) + +#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN BIT(2) +#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN, x) +#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_EN, x) + +#define SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN BIT(3) +#define SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN, x) +#define SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_VCO_CAL_RESETN, x) + +#define SD25G_LANE_CMU_0B_CFG_VFILT2PAD BIT(4) +#define SD25G_LANE_CMU_0B_CFG_VFILT2PAD_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_VFILT2PAD, x) +#define SD25G_LANE_CMU_0B_CFG_VFILT2PAD_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_VFILT2PAD, x) + +#define SD25G_LANE_CMU_0B_CFG_DISLOS BIT(5) +#define SD25G_LANE_CMU_0B_CFG_DISLOS_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_DISLOS, x) +#define SD25G_LANE_CMU_0B_CFG_DISLOS_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_DISLOS, x) + +#define SD25G_LANE_CMU_0B_CFG_DCLOL BIT(6) +#define SD25G_LANE_CMU_0B_CFG_DCLOL_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_DCLOL, x) +#define SD25G_LANE_CMU_0B_CFG_DCLOL_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_DCLOL, x) + +#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN BIT(7) +#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN, x) +#define SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0B_CFG_RST_TREE_PD_MAN, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_0C */ +#define SD25G_LANE_CMU_0C(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 48, 0, 1, 4) + +#define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET BIT(0) +#define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET, x) +#define SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0C_CFG_PLL_LOL_SET, x) + +#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN BIT(1) +#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN, x) +#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_DN, x) + +#define SD25G_LANE_CMU_0C_CFG_VCO_PD BIT(2) +#define SD25G_LANE_CMU_0C_CFG_VCO_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0C_CFG_VCO_PD, x) +#define SD25G_LANE_CMU_0C_CFG_VCO_PD_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0C_CFG_VCO_PD, x) + +#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP BIT(3) +#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP, x) +#define SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0C_CFG_EN_TX_CK_UP, x) + +#define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0 GENMASK(5, 4) +#define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0, x) +#define SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0C_CFG_VCO_DIV_MODE_1_0, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_0D */ +#define SD25G_LANE_CMU_0D(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 52, 0, 1, 4) + +#define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD BIT(0) +#define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0D_CFG_CK_TREE_PD, x) +#define SD25G_LANE_CMU_0D_CFG_CK_TREE_PD_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0D_CFG_CK_TREE_PD, x) + +#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN BIT(1) +#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN, x) +#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_DN, x) + +#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP BIT(2) +#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP, x) +#define SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0D_CFG_EN_RX_CK_UP, x) + +#define SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP BIT(3) +#define SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP, x) +#define SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0D_CFG_VCO_CAL_BYP, x) + +#define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0 GENMASK(5, 4) +#define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0, x) +#define SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0D_CFG_PRE_DIVSEL_1_0, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_0E */ +#define SD25G_LANE_CMU_0E(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 56, 0, 1, 4) + +#define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0 GENMASK(3, 0) +#define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0, x) +#define SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0E_CFG_SEL_DIV_3_0, x) + +#define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD BIT(4) +#define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD, x) +#define SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_0E_CFG_PMAA_CENTR_CK_PD, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_13 */ +#define SD25G_LANE_CMU_13(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 76, 0, 1, 4) + +#define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0 GENMASK(3, 0) +#define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0, x) +#define SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_13_CFG_PLL_RESERVE_3_0, x) + +#define SD25G_LANE_CMU_13_CFG_JT_EN BIT(4) +#define SD25G_LANE_CMU_13_CFG_JT_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_13_CFG_JT_EN, x) +#define SD25G_LANE_CMU_13_CFG_JT_EN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_13_CFG_JT_EN, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_18 */ +#define SD25G_LANE_CMU_18(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 96, 0, 1, 4) + +#define SD25G_LANE_CMU_18_R_PLL_RSTN BIT(0) +#define SD25G_LANE_CMU_18_R_PLL_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_RSTN, x) +#define SD25G_LANE_CMU_18_R_PLL_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_18_R_PLL_RSTN, x) + +#define SD25G_LANE_CMU_18_R_PLL_LOL_SET BIT(1) +#define SD25G_LANE_CMU_18_R_PLL_LOL_SET_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_LOL_SET, x) +#define SD25G_LANE_CMU_18_R_PLL_LOL_SET_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_18_R_PLL_LOL_SET, x) + +#define SD25G_LANE_CMU_18_R_PLL_LOS_SET BIT(2) +#define SD25G_LANE_CMU_18_R_PLL_LOS_SET_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_LOS_SET, x) +#define SD25G_LANE_CMU_18_R_PLL_LOS_SET_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_18_R_PLL_LOS_SET, x) + +#define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0 GENMASK(5, 4) +#define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0, x) +#define SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_18_R_PLL_TP_SEL_1_0, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_19 */ +#define SD25G_LANE_CMU_19(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 100, 0, 1, 4) + +#define SD25G_LANE_CMU_19_R_CK_RESETB BIT(0) +#define SD25G_LANE_CMU_19_R_CK_RESETB_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_19_R_CK_RESETB, x) +#define SD25G_LANE_CMU_19_R_CK_RESETB_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_19_R_CK_RESETB, x) + +#define SD25G_LANE_CMU_19_R_PLL_DLOL_EN BIT(1) +#define SD25G_LANE_CMU_19_R_PLL_DLOL_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_19_R_PLL_DLOL_EN, x) +#define SD25G_LANE_CMU_19_R_PLL_DLOL_EN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_19_R_PLL_DLOL_EN, x) + +/* SD25G_TARGET:CMU_GRP_0:CMU_1A */ +#define SD25G_LANE_CMU_1A(t) __REG(TARGET_SD25G_LANE, t, 8, 0, 0, 1, 132, 104, 0, 1, 4) + +#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0 GENMASK(2, 0) +#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0, x) +#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_2_0, x) + +#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT BIT(4) +#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT, x) +#define SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_1A_R_DWIDTHCTRL_FROM_HWT, x) + +#define SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE BIT(5) +#define SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE, x) +#define SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_1A_R_MASK_EI_SOURCE, x) + +#define SD25G_LANE_CMU_1A_R_REG_MANUAL BIT(6) +#define SD25G_LANE_CMU_1A_R_REG_MANUAL_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_1A_R_REG_MANUAL, x) +#define SD25G_LANE_CMU_1A_R_REG_MANUAL_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_1A_R_REG_MANUAL, x) + +/* SD25G_TARGET:CMU_GRP_1:CMU_2A */ +#define SD25G_LANE_CMU_2A(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 36, 0, 1, 4) + +#define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0 GENMASK(1, 0) +#define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_2A_R_DBG_SEL_1_0, x) +#define SD25G_LANE_CMU_2A_R_DBG_SEL_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_SEL_1_0, x) + +#define SD25G_LANE_CMU_2A_R_DBG_LINK_LANE BIT(4) +#define SD25G_LANE_CMU_2A_R_DBG_LINK_LANE_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_2A_R_DBG_LINK_LANE, x) +#define SD25G_LANE_CMU_2A_R_DBG_LINK_LANE_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_LINK_LANE, x) + +#define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS BIT(5) +#define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS, x) +#define SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_2A_R_DBG_LOL_STATUS, x) + +/* SD25G_TARGET:CMU_GRP_1:CMU_30 */ +#define SD25G_LANE_CMU_30(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 60, 0, 1, 4) + +#define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0 GENMASK(2, 0) +#define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0, x) +#define SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_30_R_TXFIFO_CK_DIV_PMAD_2_0, x) + +#define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0 GENMASK(6, 4) +#define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0, x) +#define SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_30_R_RXFIFO_CK_DIV_PMAD_2_0, x) + +/* SD25G_TARGET:CMU_GRP_1:CMU_31 */ +#define SD25G_LANE_CMU_31(t) __REG(TARGET_SD25G_LANE, t, 8, 132, 0, 1, 124, 64, 0, 1, 4) + +#define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0 GENMASK(7, 0) +#define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0, x) +#define SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_31_CFG_COMMON_RESERVE_7_0, x) + +/* SD25G_TARGET:CMU_GRP_2:CMU_40 */ +#define SD25G_LANE_CMU_40(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 0, 0, 1, 4) + +#define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL BIT(0) +#define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL, x) +#define SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_CKSKEW_CTRL, x) + +#define SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD BIT(1) +#define SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD, x) +#define SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_ISCAN_HOLD, x) + +#define SD25G_LANE_CMU_40_L0_CFG_PD_CLK BIT(2) +#define SD25G_LANE_CMU_40_L0_CFG_PD_CLK_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_PD_CLK, x) +#define SD25G_LANE_CMU_40_L0_CFG_PD_CLK_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_PD_CLK, x) + +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN BIT(3) +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN, x) +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_EN, x) + +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN BIT(4) +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN, x) +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_MAN_EN, x) + +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST BIT(5) +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST, x) +#define SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_40_L0_CFG_TXCAL_RST, x) + +/* SD25G_TARGET:CMU_GRP_2:CMU_45 */ +#define SD25G_LANE_CMU_45(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 20, 0, 1, 4) + +#define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0 GENMASK(7, 0) +#define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0, x) +#define SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_45_L0_CFG_TX_RESERVE_7_0, x) + +/* SD25G_TARGET:CMU_GRP_2:CMU_46 */ +#define SD25G_LANE_CMU_46(t) __REG(TARGET_SD25G_LANE, t, 8, 256, 0, 1, 512, 24, 0, 1, 4) + +#define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8 GENMASK(7, 0) +#define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8, x) +#define SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_46_L0_CFG_TX_RESERVE_15_8, x) + +/* SD25G_TARGET:CMU_GRP_3:CMU_C0 */ +#define SD25G_LANE_CMU_C0(t) __REG(TARGET_SD25G_LANE, t, 8, 768, 0, 1, 252, 0, 0, 1, 4) + +#define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0 GENMASK(3, 0) +#define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0, x) +#define SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_C0_READ_VCO_CTUNE_3_0, x) + +#define SD25G_LANE_CMU_C0_PLL_LOL_UDL BIT(4) +#define SD25G_LANE_CMU_C0_PLL_LOL_UDL_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_C0_PLL_LOL_UDL, x) +#define SD25G_LANE_CMU_C0_PLL_LOL_UDL_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_C0_PLL_LOL_UDL, x) + +/* SD25G_TARGET:CMU_GRP_4:CMU_FF */ +#define SD25G_LANE_CMU_FF(t) __REG(TARGET_SD25G_LANE, t, 8, 1020, 0, 1, 4, 0, 0, 1, 4) + +#define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX GENMASK(7, 0) +#define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_SET(x)\ + FIELD_PREP(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, x) +#define SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX_GET(x)\ + FIELD_GET(SD25G_LANE_CMU_FF_REGISTER_TABLE_INDEX, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_00 */ +#define SD25G_LANE_LANE_00(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 0, 0, 1, 4) + +#define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0 GENMASK(3, 0) +#define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0, x) +#define SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_00_LN_CFG_ITX_VC_DRIVER_3_0, x) + +#define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0 GENMASK(5, 4) +#define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0, x) +#define SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_00_LN_CFG_ITX_IPCML_BASE_1_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_01 */ +#define SD25G_LANE_LANE_01(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 4, 0, 1, 4) + +#define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0 GENMASK(2, 0) +#define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0, x) +#define SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_01_LN_CFG_ITX_IPDRIVER_BASE_2_0, x) + +#define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0 GENMASK(5, 4) +#define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0, x) +#define SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_01_LN_CFG_TX_PREDIV_1_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_03 */ +#define SD25G_LANE_LANE_03(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 12, 0, 1, 4) + +#define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0 GENMASK(4, 0) +#define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0, x) +#define SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_03_LN_CFG_TAP_DLY_4_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_04 */ +#define SD25G_LANE_LANE_04(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 16, 0, 1, 4) + +#define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN BIT(0) +#define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN, x) +#define SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_TX2RX_LP_EN, x) + +#define SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN BIT(1) +#define SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN, x) +#define SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_RX2TX_LP_EN, x) + +#define SD25G_LANE_LANE_04_LN_CFG_PD_CML BIT(2) +#define SD25G_LANE_LANE_04_LN_CFG_PD_CML_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_PD_CML, x) +#define SD25G_LANE_LANE_04_LN_CFG_PD_CML_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_PD_CML, x) + +#define SD25G_LANE_LANE_04_LN_CFG_PD_CLK BIT(3) +#define SD25G_LANE_LANE_04_LN_CFG_PD_CLK_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_PD_CLK, x) +#define SD25G_LANE_LANE_04_LN_CFG_PD_CLK_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_PD_CLK, x) + +#define SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER BIT(4) +#define SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER, x) +#define SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER, x) + +#define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN BIT(5) +#define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN, x) +#define SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_04_LN_CFG_TAP_MAIN, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_05 */ +#define SD25G_LANE_LANE_05(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 20, 0, 1, 4) + +#define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0 GENMASK(3, 0) +#define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0, x) +#define SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_05_LN_CFG_TAP_DLY2_3_0, x) + +#define SD25G_LANE_LANE_05_LN_CFG_BW_1_0 GENMASK(5, 4) +#define SD25G_LANE_LANE_05_LN_CFG_BW_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_05_LN_CFG_BW_1_0, x) +#define SD25G_LANE_LANE_05_LN_CFG_BW_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_05_LN_CFG_BW_1_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_06 */ +#define SD25G_LANE_LANE_06(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 24, 0, 1, 4) + +#define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN BIT(0) +#define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_06_LN_CFG_EN_MAIN, x) +#define SD25G_LANE_LANE_06_LN_CFG_EN_MAIN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_06_LN_CFG_EN_MAIN, x) + +#define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0 GENMASK(7, 4) +#define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0, x) +#define SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_06_LN_CFG_TAP_ADV_3_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_07 */ +#define SD25G_LANE_LANE_07(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 28, 0, 1, 4) + +#define SD25G_LANE_LANE_07_LN_CFG_EN_ADV BIT(0) +#define SD25G_LANE_LANE_07_LN_CFG_EN_ADV_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_07_LN_CFG_EN_ADV, x) +#define SD25G_LANE_LANE_07_LN_CFG_EN_ADV_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_ADV, x) + +#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY2 BIT(1) +#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY2_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_07_LN_CFG_EN_DLY2, x) +#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY2_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_DLY2, x) + +#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY BIT(2) +#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_07_LN_CFG_EN_DLY, x) +#define SD25G_LANE_LANE_07_LN_CFG_EN_DLY_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_07_LN_CFG_EN_DLY, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_09 */ +#define SD25G_LANE_LANE_09(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 36, 0, 1, 4) + +#define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0 GENMASK(3, 0) +#define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0, x) +#define SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_09_LN_CFG_TXCAL_VALID_SEL_3_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_0A */ +#define SD25G_LANE_LANE_0A(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 40, 0, 1, 4) + +#define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0 GENMASK(5, 0) +#define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0, x) +#define SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0A_LN_CFG_TXCAL_SHIFT_CODE_5_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_0B */ +#define SD25G_LANE_LANE_0B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 44, 0, 1, 4) + +#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN BIT(0) +#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN, x) +#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_MAN_EN, x) + +#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST BIT(1) +#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST, x) +#define SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_TXCAL_RST, x) + +#define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0 GENMASK(5, 4) +#define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0, x) +#define SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0B_LN_CFG_QUAD_MAN_1_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_0C */ +#define SD25G_LANE_LANE_0C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 48, 0, 1, 4) + +#define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0 GENMASK(2, 0) +#define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0, x) +#define SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_PMA_TX_CK_BITWIDTH_2_0, x) + +#define SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN BIT(4) +#define SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN, x) +#define SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_TXCAL_EN, x) + +#define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD BIT(5) +#define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD, x) +#define SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0C_LN_CFG_RXTERM_PD, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_0D */ +#define SD25G_LANE_LANE_0D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 52, 0, 1, 4) + +#define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0 GENMASK(2, 0) +#define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0, x) +#define SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_RXTERM_2_0, x) + +#define SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8 BIT(4) +#define SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8, x) +#define SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_RSTN_DIV5_8, x) + +#define SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN BIT(5) +#define SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN, x) +#define SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_SUMMER_EN, x) + +#define SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD BIT(6) +#define SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD, x) +#define SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_DMUX_PD, x) + +#define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN BIT(7) +#define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN, x) +#define SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0D_LN_CFG_DFECK_EN, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_0E */ +#define SD25G_LANE_LANE_0E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 56, 0, 1, 4) + +#define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN BIT(0) +#define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN, x) +#define SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_ISCAN_EN, x) + +#define SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD BIT(1) +#define SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD, x) +#define SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_DMUX_CLK_PD, x) + +#define SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG BIT(2) +#define SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG, x) +#define SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_EN_DFEDIG, x) + +#define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0 GENMASK(6, 4) +#define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0, x) +#define SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0E_LN_CFG_DFEDIG_M_2_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_0F */ +#define SD25G_LANE_LANE_0F(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 60, 0, 1, 4) + +#define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1 GENMASK(4, 0) +#define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1, x) +#define SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_0F_LN_CFG_DFETAP_EN_5_1, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_18 */ +#define SD25G_LANE_LANE_18(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 96, 0, 1, 4) + +#define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN BIT(0) +#define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN, x) +#define SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_CDRCK_EN, x) + +#define SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT BIT(1) +#define SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT, x) +#define SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_ADD_VOLT, x) + +#define SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN BIT(2) +#define SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN, x) +#define SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_MAN_VOLT_EN, x) + +#define SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD BIT(3) +#define SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD, x) +#define SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_ERRAMP_PD, x) + +#define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0 GENMASK(6, 4) +#define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0, x) +#define SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_18_LN_CFG_RXDIV_SEL_2_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_19 */ +#define SD25G_LANE_LANE_19(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 100, 0, 1, 4) + +#define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD BIT(0) +#define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_DCDR_PD, x) +#define SD25G_LANE_LANE_19_LN_CFG_DCDR_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_DCDR_PD, x) + +#define SD25G_LANE_LANE_19_LN_CFG_ECDR_PD BIT(1) +#define SD25G_LANE_LANE_19_LN_CFG_ECDR_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_ECDR_PD, x) +#define SD25G_LANE_LANE_19_LN_CFG_ECDR_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_ECDR_PD, x) + +#define SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL BIT(2) +#define SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL, x) +#define SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_ISCAN_SEL, x) + +#define SD25G_LANE_LANE_19_LN_CFG_TXLB_EN BIT(3) +#define SD25G_LANE_LANE_19_LN_CFG_TXLB_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_TXLB_EN, x) +#define SD25G_LANE_LANE_19_LN_CFG_TXLB_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_TXLB_EN, x) + +#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU BIT(4) +#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU, x) +#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_RX_REG_PU, x) + +#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP BIT(5) +#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP, x) +#define SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_RX_REG_BYP, x) + +#define SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET BIT(6) +#define SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET, x) +#define SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_PD_RMS_DET, x) + +#define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE BIT(7) +#define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_19_LN_CFG_PD_CTLE, x) +#define SD25G_LANE_LANE_19_LN_CFG_PD_CTLE_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_19_LN_CFG_PD_CTLE, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_1A */ +#define SD25G_LANE_LANE_1A(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 104, 0, 1, 4) + +#define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN BIT(0) +#define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN, x) +#define SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1A_LN_CFG_CTLE_TP_EN, x) + +#define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0 GENMASK(6, 4) +#define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0, x) +#define SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1A_LN_CFG_CDR_KF_2_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_1B */ +#define SD25G_LANE_LANE_1B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 108, 0, 1, 4) + +#define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0 GENMASK(7, 0) +#define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0, x) +#define SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1B_LN_CFG_CDR_M_7_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_1C */ +#define SD25G_LANE_LANE_1C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 112, 0, 1, 4) + +#define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN BIT(0) +#define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN, x) +#define SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_CDR_RSTN, x) + +#define SD25G_LANE_LANE_1C_LN_CFG_DFE_PD BIT(1) +#define SD25G_LANE_LANE_1C_LN_CFG_DFE_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_DFE_PD, x) +#define SD25G_LANE_LANE_1C_LN_CFG_DFE_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_DFE_PD, x) + +#define SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD BIT(2) +#define SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD, x) +#define SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_DFEDMX_PD, x) + +#define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0 GENMASK(7, 4) +#define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0, x) +#define SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1C_LN_CFG_EQC_FORCE_3_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_1D */ +#define SD25G_LANE_LANE_1D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 116, 0, 1, 4) + +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR BIT(0) +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR, x) +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_EXT_OVR, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD BIT(1) +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD, x) +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_HOLD, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN BIT(2) +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN, x) +#define SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_ISCAN_RSTN, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP BIT(3) +#define SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP, x) +#define SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_AGC_ADPT_BYP, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_PHID_1T BIT(4) +#define SD25G_LANE_LANE_1D_LN_CFG_PHID_1T_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PHID_1T, x) +#define SD25G_LANE_LANE_1D_LN_CFG_PHID_1T_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PHID_1T, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN BIT(5) +#define SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN, x) +#define SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_DFE_EN, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR BIT(6) +#define SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR, x) +#define SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_EXT_OVR, x) + +#define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD BIT(7) +#define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD, x) +#define SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1D_LN_CFG_PI_HOLD, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_1E */ +#define SD25G_LANE_LANE_1E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 120, 0, 1, 4) + +#define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0 GENMASK(1, 0) +#define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0, x) +#define SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_PI_STEPS_1_0, x) + +#define SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN BIT(4) +#define SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN, x) +#define SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_RXLB_EN, x) + +#define SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN BIT(5) +#define SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN, x) +#define SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_SUM_SETCM_EN, x) + +#define SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR BIT(6) +#define SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR, x) +#define SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_R_OFFSET_DIR, x) + +#define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD BIT(7) +#define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD, x) +#define SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_1E_LN_CFG_PMAD_CK_PD, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_21 */ +#define SD25G_LANE_LANE_21(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 132, 0, 1, 4) + +#define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0 GENMASK(4, 0) +#define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0, x) +#define SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_21_LN_CFG_VGA_CTRL_BYP_4_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_22 */ +#define SD25G_LANE_LANE_22(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 136, 0, 1, 4) + +#define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0 GENMASK(3, 0) +#define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0, x) +#define SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_22_LN_CFG_EQR_FORCE_3_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_25 */ +#define SD25G_LANE_LANE_25(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 148, 0, 1, 4) + +#define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0 GENMASK(6, 0) +#define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0, x) +#define SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_25_LN_CFG_INIT_POS_ISCAN_6_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_26 */ +#define SD25G_LANE_LANE_26(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 152, 0, 1, 4) + +#define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0 GENMASK(6, 0) +#define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0, x) +#define SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_26_LN_CFG_INIT_POS_IPI_6_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_28 */ +#define SD25G_LANE_LANE_28(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 160, 0, 1, 4) + +#define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN BIT(0) +#define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN, x) +#define SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_ISCAN_MODE_EN, x) + +#define SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH BIT(1) +#define SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH, x) +#define SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_RX_SSC_LH, x) + +#define SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL BIT(2) +#define SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL, x) +#define SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_FIGMERIT_SEL, x) + +#define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0 GENMASK(6, 4) +#define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0, x) +#define SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_28_LN_CFG_RX_SUBRATE_2_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_2B */ +#define SD25G_LANE_LANE_2B(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 172, 0, 1, 4) + +#define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0 GENMASK(3, 0) +#define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0, x) +#define SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_PI_BW_3_0, x) + +#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR BIT(4) +#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR, x) +#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_RSTN_DMUX_SUBR, x) + +#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU BIT(5) +#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU, x) +#define SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2B_LN_CFG_RSTN_TXDUPU, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_2C */ +#define SD25G_LANE_LANE_2C(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 176, 0, 1, 4) + +#define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0 GENMASK(2, 0) +#define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0, x) +#define SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2C_LN_CFG_TX_SUBRATE_2_0, x) + +#define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER BIT(4) +#define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER, x) +#define SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2C_LN_CFG_DIS_2NDORDER, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_2D */ +#define SD25G_LANE_LANE_2D(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 180, 0, 1, 4) + +#define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0 GENMASK(2, 0) +#define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0, x) +#define SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2D_LN_CFG_ALOS_THR_2_0, x) + +#define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0 GENMASK(6, 4) +#define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0, x) +#define SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2D_LN_CFG_SAT_CNTSEL_2_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_2E */ +#define SD25G_LANE_LANE_2E(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 184, 0, 1, 4) + +#define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN BIT(0) +#define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN, x) +#define SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_EN_FAST_ISCAN, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ BIT(1) +#define SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ, x) +#define SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_DIS_SQ, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_PD_SQ BIT(2) +#define SD25G_LANE_LANE_2E_LN_CFG_PD_SQ_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_PD_SQ, x) +#define SD25G_LANE_LANE_2E_LN_CFG_PD_SQ_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_PD_SQ, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS BIT(3) +#define SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS, x) +#define SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_DIS_ALOS, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC BIT(4) +#define SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC, x) +#define SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_RESETN_AGC, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG BIT(5) +#define SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG, x) +#define SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_RSTN_DFEDIG, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN BIT(6) +#define SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN, x) +#define SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_PI_RSTN, x) + +#define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN BIT(7) +#define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN, x) +#define SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_2E_LN_CFG_CTLE_RSTN, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_40 */ +#define SD25G_LANE_LANE_40(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 256, 0, 1, 4) + +#define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE BIT(0) +#define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE, x) +#define SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_TX_BIT_REVERSE, x) + +#define SD25G_LANE_LANE_40_LN_R_TX_POL_INV BIT(1) +#define SD25G_LANE_LANE_40_LN_R_TX_POL_INV_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_TX_POL_INV, x) +#define SD25G_LANE_LANE_40_LN_R_TX_POL_INV_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_TX_POL_INV, x) + +#define SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE BIT(2) +#define SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE, x) +#define SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_RX_BIT_REVERSE, x) + +#define SD25G_LANE_LANE_40_LN_R_RX_POL_INV BIT(3) +#define SD25G_LANE_LANE_40_LN_R_RX_POL_INV_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_RX_POL_INV, x) +#define SD25G_LANE_LANE_40_LN_R_RX_POL_INV_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_RX_POL_INV, x) + +#define SD25G_LANE_LANE_40_LN_R_CDR_RSTN BIT(4) +#define SD25G_LANE_LANE_40_LN_R_CDR_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_CDR_RSTN, x) +#define SD25G_LANE_LANE_40_LN_R_CDR_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_CDR_RSTN, x) + +#define SD25G_LANE_LANE_40_LN_R_DFE_RSTN BIT(5) +#define SD25G_LANE_LANE_40_LN_R_DFE_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_DFE_RSTN, x) +#define SD25G_LANE_LANE_40_LN_R_DFE_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_DFE_RSTN, x) + +#define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN BIT(6) +#define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_40_LN_R_CTLE_RSTN, x) +#define SD25G_LANE_LANE_40_LN_R_CTLE_RSTN_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_40_LN_R_CTLE_RSTN, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_42 */ +#define SD25G_LANE_LANE_42(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 264, 0, 1, 4) + +#define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0 GENMASK(7, 0) +#define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0, x) +#define SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_42_LN_CFG_TX_RESERVE_7_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_43 */ +#define SD25G_LANE_LANE_43(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 268, 0, 1, 4) + +#define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8 GENMASK(7, 0) +#define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8, x) +#define SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_43_LN_CFG_TX_RESERVE_15_8, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_44 */ +#define SD25G_LANE_LANE_44(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 272, 0, 1, 4) + +#define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0 GENMASK(7, 0) +#define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0, x) +#define SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_44_LN_CFG_RX_RESERVE_7_0, x) + +/* SD25G_TARGET:LANE_GRP_0:LANE_45 */ +#define SD25G_LANE_LANE_45(t) __REG(TARGET_SD25G_LANE, t, 8, 1024, 0, 1, 768, 276, 0, 1, 4) + +#define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8 GENMASK(7, 0) +#define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8, x) +#define SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_45_LN_CFG_RX_RESERVE_15_8, x) + +/* SD25G_TARGET:LANE_GRP_1:LANE_DE */ +#define SD25G_LANE_LANE_DE(t) __REG(TARGET_SD25G_LANE, t, 8, 1792, 0, 1, 128, 120, 0, 1, 4) + +#define SD25G_LANE_LANE_DE_LN_LOL_UDL BIT(0) +#define SD25G_LANE_LANE_DE_LN_LOL_UDL_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_DE_LN_LOL_UDL, x) +#define SD25G_LANE_LANE_DE_LN_LOL_UDL_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_DE_LN_LOL_UDL, x) + +#define SD25G_LANE_LANE_DE_LN_LOL BIT(1) +#define SD25G_LANE_LANE_DE_LN_LOL_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_DE_LN_LOL, x) +#define SD25G_LANE_LANE_DE_LN_LOL_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_DE_LN_LOL, x) + +#define SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED BIT(2) +#define SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED, x) +#define SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_DE_LN_PMA2PCS_RXEI_FILTERED, x) + +#define SD25G_LANE_LANE_DE_LN_PMA_RXEI BIT(3) +#define SD25G_LANE_LANE_DE_LN_PMA_RXEI_SET(x)\ + FIELD_PREP(SD25G_LANE_LANE_DE_LN_PMA_RXEI, x) +#define SD25G_LANE_LANE_DE_LN_PMA_RXEI_GET(x)\ + FIELD_GET(SD25G_LANE_LANE_DE_LN_PMA_RXEI, x) + +/* SD10G_LANE_TARGET:LANE_GRP_8:LANE_DF */ +#define SD6G_LANE_LANE_DF(t) __REG(TARGET_SD6G_LANE, t, 13, 832, 0, 1, 84, 60, 0, 1, 4) + +#define SD6G_LANE_LANE_DF_LOL_UDL BIT(0) +#define SD6G_LANE_LANE_DF_LOL_UDL_SET(x)\ + FIELD_PREP(SD6G_LANE_LANE_DF_LOL_UDL, x) +#define SD6G_LANE_LANE_DF_LOL_UDL_GET(x)\ + FIELD_GET(SD6G_LANE_LANE_DF_LOL_UDL, x) + +#define SD6G_LANE_LANE_DF_LOL BIT(1) +#define SD6G_LANE_LANE_DF_LOL_SET(x)\ + FIELD_PREP(SD6G_LANE_LANE_DF_LOL, x) +#define SD6G_LANE_LANE_DF_LOL_GET(x)\ + FIELD_GET(SD6G_LANE_LANE_DF_LOL, x) + +#define SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED BIT(2) +#define SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_SET(x)\ + FIELD_PREP(SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x) +#define SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED_GET(x)\ + FIELD_GET(SD6G_LANE_LANE_DF_PMA2PCS_RXEI_FILTERED, x) + +#define SD6G_LANE_LANE_DF_SQUELCH BIT(3) +#define SD6G_LANE_LANE_DF_SQUELCH_SET(x)\ + FIELD_PREP(SD6G_LANE_LANE_DF_SQUELCH, x) +#define SD6G_LANE_LANE_DF_SQUELCH_GET(x)\ + FIELD_GET(SD6G_LANE_LANE_DF_SQUELCH, x) + +/* SD10G_CMU_TARGET:CMU_GRP_0:CMU_00 */ +#define SD_CMU_CMU_00(t) __REG(TARGET_SD_CMU, t, 14, 0, 0, 1, 20, 0, 0, 1, 4) + +#define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE BIT(0) +#define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE_SET(x)\ + FIELD_PREP(SD_CMU_CMU_00_R_HWT_SIMULATION_MODE, x) +#define SD_CMU_CMU_00_R_HWT_SIMULATION_MODE_GET(x)\ + FIELD_GET(SD_CMU_CMU_00_R_HWT_SIMULATION_MODE, x) + +#define SD_CMU_CMU_00_CFG_PLL_LOL_SET BIT(1) +#define SD_CMU_CMU_00_CFG_PLL_LOL_SET_SET(x)\ + FIELD_PREP(SD_CMU_CMU_00_CFG_PLL_LOL_SET, x) +#define SD_CMU_CMU_00_CFG_PLL_LOL_SET_GET(x)\ + FIELD_GET(SD_CMU_CMU_00_CFG_PLL_LOL_SET, x) + +#define SD_CMU_CMU_00_CFG_PLL_LOS_SET BIT(2) +#define SD_CMU_CMU_00_CFG_PLL_LOS_SET_SET(x)\ + FIELD_PREP(SD_CMU_CMU_00_CFG_PLL_LOS_SET, x) +#define SD_CMU_CMU_00_CFG_PLL_LOS_SET_GET(x)\ + FIELD_GET(SD_CMU_CMU_00_CFG_PLL_LOS_SET, x) + +#define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0 GENMASK(5, 4) +#define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(x)\ + FIELD_PREP(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, x) +#define SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_GET(x)\ + FIELD_GET(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, x) + +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_05 */ +#define SD_CMU_CMU_05(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 0, 0, 1, 4) + +#define SD_CMU_CMU_05_CFG_REFCK_TERM_EN BIT(0) +#define SD_CMU_CMU_05_CFG_REFCK_TERM_EN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_05_CFG_REFCK_TERM_EN, x) +#define SD_CMU_CMU_05_CFG_REFCK_TERM_EN_GET(x)\ + FIELD_GET(SD_CMU_CMU_05_CFG_REFCK_TERM_EN, x) + +#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0 GENMASK(5, 4) +#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(x)\ + FIELD_PREP(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x) +#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_GET(x)\ + FIELD_GET(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x) + +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_09 */ +#define SD_CMU_CMU_09(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 16, 0, 1, 4) + +#define SD_CMU_CMU_09_CFG_EN_TX_CK_UP BIT(0) +#define SD_CMU_CMU_09_CFG_EN_TX_CK_UP_SET(x)\ + FIELD_PREP(SD_CMU_CMU_09_CFG_EN_TX_CK_UP, x) +#define SD_CMU_CMU_09_CFG_EN_TX_CK_UP_GET(x)\ + FIELD_GET(SD_CMU_CMU_09_CFG_EN_TX_CK_UP, x) + +#define SD_CMU_CMU_09_CFG_EN_TX_CK_DN BIT(1) +#define SD_CMU_CMU_09_CFG_EN_TX_CK_DN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_09_CFG_EN_TX_CK_DN, x) +#define SD_CMU_CMU_09_CFG_EN_TX_CK_DN_GET(x)\ + FIELD_GET(SD_CMU_CMU_09_CFG_EN_TX_CK_DN, x) + +#define SD_CMU_CMU_09_CFG_SW_8G BIT(4) +#define SD_CMU_CMU_09_CFG_SW_8G_SET(x)\ + FIELD_PREP(SD_CMU_CMU_09_CFG_SW_8G, x) +#define SD_CMU_CMU_09_CFG_SW_8G_GET(x)\ + FIELD_GET(SD_CMU_CMU_09_CFG_SW_8G, x) + +#define SD_CMU_CMU_09_CFG_SW_10G BIT(5) +#define SD_CMU_CMU_09_CFG_SW_10G_SET(x)\ + FIELD_PREP(SD_CMU_CMU_09_CFG_SW_10G, x) +#define SD_CMU_CMU_09_CFG_SW_10G_GET(x)\ + FIELD_GET(SD_CMU_CMU_09_CFG_SW_10G, x) + +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_0D */ +#define SD_CMU_CMU_0D(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 32, 0, 1, 4) + +#define SD_CMU_CMU_0D_CFG_PD_DIV64 BIT(0) +#define SD_CMU_CMU_0D_CFG_PD_DIV64_SET(x)\ + FIELD_PREP(SD_CMU_CMU_0D_CFG_PD_DIV64, x) +#define SD_CMU_CMU_0D_CFG_PD_DIV64_GET(x)\ + FIELD_GET(SD_CMU_CMU_0D_CFG_PD_DIV64, x) + +#define SD_CMU_CMU_0D_CFG_PD_DIV66 BIT(1) +#define SD_CMU_CMU_0D_CFG_PD_DIV66_SET(x)\ + FIELD_PREP(SD_CMU_CMU_0D_CFG_PD_DIV66, x) +#define SD_CMU_CMU_0D_CFG_PD_DIV66_GET(x)\ + FIELD_GET(SD_CMU_CMU_0D_CFG_PD_DIV66, x) + +#define SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD BIT(2) +#define SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(x)\ + FIELD_PREP(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, x) +#define SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_GET(x)\ + FIELD_GET(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, x) + +#define SD_CMU_CMU_0D_CFG_JC_BYP BIT(3) +#define SD_CMU_CMU_0D_CFG_JC_BYP_SET(x)\ + FIELD_PREP(SD_CMU_CMU_0D_CFG_JC_BYP, x) +#define SD_CMU_CMU_0D_CFG_JC_BYP_GET(x)\ + FIELD_GET(SD_CMU_CMU_0D_CFG_JC_BYP, x) + +#define SD_CMU_CMU_0D_CFG_REFCK_PD BIT(4) +#define SD_CMU_CMU_0D_CFG_REFCK_PD_SET(x)\ + FIELD_PREP(SD_CMU_CMU_0D_CFG_REFCK_PD, x) +#define SD_CMU_CMU_0D_CFG_REFCK_PD_GET(x)\ + FIELD_GET(SD_CMU_CMU_0D_CFG_REFCK_PD, x) + +/* SD10G_CMU_TARGET:CMU_GRP_3:CMU_1B */ +#define SD_CMU_CMU_1B(t) __REG(TARGET_SD_CMU, t, 14, 104, 0, 1, 20, 4, 0, 1, 4) + +#define SD_CMU_CMU_1B_CFG_RESERVE_7_0 GENMASK(7, 0) +#define SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(x)\ + FIELD_PREP(SD_CMU_CMU_1B_CFG_RESERVE_7_0, x) +#define SD_CMU_CMU_1B_CFG_RESERVE_7_0_GET(x)\ + FIELD_GET(SD_CMU_CMU_1B_CFG_RESERVE_7_0, x) + +/* SD10G_CMU_TARGET:CMU_GRP_4:CMU_1F */ +#define SD_CMU_CMU_1F(t) __REG(TARGET_SD_CMU, t, 14, 124, 0, 1, 68, 0, 0, 1, 4) + +#define SD_CMU_CMU_1F_CFG_BIAS_DN_EN BIT(0) +#define SD_CMU_CMU_1F_CFG_BIAS_DN_EN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_1F_CFG_BIAS_DN_EN, x) +#define SD_CMU_CMU_1F_CFG_BIAS_DN_EN_GET(x)\ + FIELD_GET(SD_CMU_CMU_1F_CFG_BIAS_DN_EN, x) + +#define SD_CMU_CMU_1F_CFG_BIAS_UP_EN BIT(1) +#define SD_CMU_CMU_1F_CFG_BIAS_UP_EN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_1F_CFG_BIAS_UP_EN, x) +#define SD_CMU_CMU_1F_CFG_BIAS_UP_EN_GET(x)\ + FIELD_GET(SD_CMU_CMU_1F_CFG_BIAS_UP_EN, x) + +#define SD_CMU_CMU_1F_CFG_IC2IP_N BIT(2) +#define SD_CMU_CMU_1F_CFG_IC2IP_N_SET(x)\ + FIELD_PREP(SD_CMU_CMU_1F_CFG_IC2IP_N, x) +#define SD_CMU_CMU_1F_CFG_IC2IP_N_GET(x)\ + FIELD_GET(SD_CMU_CMU_1F_CFG_IC2IP_N, x) + +#define SD_CMU_CMU_1F_CFG_VTUNE_SEL BIT(3) +#define SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(x)\ + FIELD_PREP(SD_CMU_CMU_1F_CFG_VTUNE_SEL, x) +#define SD_CMU_CMU_1F_CFG_VTUNE_SEL_GET(x)\ + FIELD_GET(SD_CMU_CMU_1F_CFG_VTUNE_SEL, x) + +/* SD10G_CMU_TARGET:CMU_GRP_5:CMU_30 */ +#define SD_CMU_CMU_30(t) __REG(TARGET_SD_CMU, t, 14, 192, 0, 1, 72, 0, 0, 1, 4) + +#define SD_CMU_CMU_30_R_PLL_DLOL_EN BIT(0) +#define SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_30_R_PLL_DLOL_EN, x) +#define SD_CMU_CMU_30_R_PLL_DLOL_EN_GET(x)\ + FIELD_GET(SD_CMU_CMU_30_R_PLL_DLOL_EN, x) + +/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_44 */ +#define SD_CMU_CMU_44(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 8, 0, 1, 4) + +#define SD_CMU_CMU_44_R_PLL_RSTN BIT(0) +#define SD_CMU_CMU_44_R_PLL_RSTN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_44_R_PLL_RSTN, x) +#define SD_CMU_CMU_44_R_PLL_RSTN_GET(x)\ + FIELD_GET(SD_CMU_CMU_44_R_PLL_RSTN, x) + +#define SD_CMU_CMU_44_R_CK_RESETB BIT(1) +#define SD_CMU_CMU_44_R_CK_RESETB_SET(x)\ + FIELD_PREP(SD_CMU_CMU_44_R_CK_RESETB, x) +#define SD_CMU_CMU_44_R_CK_RESETB_GET(x)\ + FIELD_GET(SD_CMU_CMU_44_R_CK_RESETB, x) + +/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_45 */ +#define SD_CMU_CMU_45(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 12, 0, 1, 4) + +#define SD_CMU_CMU_45_R_EN_RATECHG_CTRL BIT(0) +#define SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_R_EN_RATECHG_CTRL, x) +#define SD_CMU_CMU_45_R_EN_RATECHG_CTRL_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_R_EN_RATECHG_CTRL, x) + +#define SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT BIT(1) +#define SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT, x) +#define SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT, x) + +#define SD_CMU_CMU_45_RESERVED BIT(2) +#define SD_CMU_CMU_45_RESERVED_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_RESERVED, x) +#define SD_CMU_CMU_45_RESERVED_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_RESERVED, x) + +#define SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT BIT(3) +#define SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT, x) +#define SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT, x) + +#define SD_CMU_CMU_45_RESERVED_2 BIT(4) +#define SD_CMU_CMU_45_RESERVED_2_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_RESERVED_2, x) +#define SD_CMU_CMU_45_RESERVED_2_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_RESERVED_2, x) + +#define SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT BIT(5) +#define SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT, x) +#define SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT, x) + +#define SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT BIT(6) +#define SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT, x) +#define SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT, x) + +#define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN BIT(7) +#define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN, x) +#define SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN_GET(x)\ + FIELD_GET(SD_CMU_CMU_45_R_AUTO_RST_TREE_PD_MAN, x) + +/* SD10G_CMU_TARGET:CMU_GRP_6:CMU_47 */ +#define SD_CMU_CMU_47(t) __REG(TARGET_SD_CMU, t, 14, 264, 0, 1, 632, 20, 0, 1, 4) + +#define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0 GENMASK(4, 0) +#define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(x)\ + FIELD_PREP(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, x) +#define SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_GET(x)\ + FIELD_GET(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, x) + +/* SD10G_CMU_TARGET:CMU_GRP_7:CMU_E0 */ +#define SD_CMU_CMU_E0(t) __REG(TARGET_SD_CMU, t, 14, 896, 0, 1, 8, 0, 0, 1, 4) + +#define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0 GENMASK(3, 0) +#define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0_SET(x)\ + FIELD_PREP(SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0, x) +#define SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0_GET(x)\ + FIELD_GET(SD_CMU_CMU_E0_READ_VCO_CTUNE_3_0, x) + +#define SD_CMU_CMU_E0_PLL_LOL_UDL BIT(4) +#define SD_CMU_CMU_E0_PLL_LOL_UDL_SET(x)\ + FIELD_PREP(SD_CMU_CMU_E0_PLL_LOL_UDL, x) +#define SD_CMU_CMU_E0_PLL_LOL_UDL_GET(x)\ + FIELD_GET(SD_CMU_CMU_E0_PLL_LOL_UDL, x) + +/* SD_CMU_TARGET:SD_CMU_CFG:SD_CMU_CFG */ +#define SD_CMU_CFG_SD_CMU_CFG(t) __REG(TARGET_SD_CMU_CFG, t, 14, 0, 0, 1, 8, 0, 0, 1, 4) + +#define SD_CMU_CFG_SD_CMU_CFG_CMU_RST BIT(0) +#define SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(x)\ + FIELD_PREP(SD_CMU_CFG_SD_CMU_CFG_CMU_RST, x) +#define SD_CMU_CFG_SD_CMU_CFG_CMU_RST_GET(x)\ + FIELD_GET(SD_CMU_CFG_SD_CMU_CFG_CMU_RST, x) + +#define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST BIT(1) +#define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(x)\ + FIELD_PREP(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, x) +#define SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_GET(x)\ + FIELD_GET(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, x) + +/* SD_LANE_TARGET:SD_RESET:SD_SER_RST */ +#define SD_LANE_SD_SER_RST(t) __REG(TARGET_SD_LANE, t, 25, 0, 0, 1, 8, 0, 0, 1, 4) + +#define SD_LANE_SD_SER_RST_SER_RST BIT(0) +#define SD_LANE_SD_SER_RST_SER_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_SER_RST_SER_RST, x) +#define SD_LANE_SD_SER_RST_SER_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_SER_RST_SER_RST, x) + +/* SD_LANE_TARGET:SD_RESET:SD_DES_RST */ +#define SD_LANE_SD_DES_RST(t) __REG(TARGET_SD_LANE, t, 25, 0, 0, 1, 8, 4, 0, 1, 4) + +#define SD_LANE_SD_DES_RST_DES_RST BIT(0) +#define SD_LANE_SD_DES_RST_DES_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_DES_RST_DES_RST, x) +#define SD_LANE_SD_DES_RST_DES_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_DES_RST_DES_RST, x) + +/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */ +#define SD_LANE_SD_LANE_CFG(t) __REG(TARGET_SD_LANE, t, 25, 8, 0, 1, 8, 0, 0, 1, 4) + +#define SD_LANE_SD_LANE_CFG_MACRO_RST BIT(0) +#define SD_LANE_SD_LANE_CFG_MACRO_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_MACRO_RST, x) +#define SD_LANE_SD_LANE_CFG_MACRO_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_MACRO_RST, x) + +#define SD_LANE_SD_LANE_CFG_EXT_CFG_RST BIT(1) +#define SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_EXT_CFG_RST, x) +#define SD_LANE_SD_LANE_CFG_EXT_CFG_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_EXT_CFG_RST, x) + +#define SD_LANE_SD_LANE_CFG_TX_REF_SEL GENMASK(5, 4) +#define SD_LANE_SD_LANE_CFG_TX_REF_SEL_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_TX_REF_SEL, x) +#define SD_LANE_SD_LANE_CFG_TX_REF_SEL_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_TX_REF_SEL, x) + +#define SD_LANE_SD_LANE_CFG_RX_REF_SEL GENMASK(7, 6) +#define SD_LANE_SD_LANE_CFG_RX_REF_SEL_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_RX_REF_SEL, x) +#define SD_LANE_SD_LANE_CFG_RX_REF_SEL_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_RX_REF_SEL, x) + +#define SD_LANE_SD_LANE_CFG_LANE_RST BIT(8) +#define SD_LANE_SD_LANE_CFG_LANE_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_LANE_RST, x) +#define SD_LANE_SD_LANE_CFG_LANE_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_RST, x) + +#define SD_LANE_SD_LANE_CFG_LANE_TX_RST BIT(9) +#define SD_LANE_SD_LANE_CFG_LANE_TX_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_LANE_TX_RST, x) +#define SD_LANE_SD_LANE_CFG_LANE_TX_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_TX_RST, x) + +#define SD_LANE_SD_LANE_CFG_LANE_RX_RST BIT(10) +#define SD_LANE_SD_LANE_CFG_LANE_RX_RST_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_CFG_LANE_RX_RST, x) +#define SD_LANE_SD_LANE_CFG_LANE_RX_RST_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_CFG_LANE_RX_RST, x) + +/* SD_LANE_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */ +#define SD_LANE_SD_LANE_STAT(t) __REG(TARGET_SD_LANE, t, 25, 8, 0, 1, 8, 4, 0, 1, 4) + +#define SD_LANE_SD_LANE_STAT_PMA_RST_DONE BIT(0) +#define SD_LANE_SD_LANE_STAT_PMA_RST_DONE_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_STAT_PMA_RST_DONE, x) +#define SD_LANE_SD_LANE_STAT_PMA_RST_DONE_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_STAT_PMA_RST_DONE, x) + +#define SD_LANE_SD_LANE_STAT_DFE_RST_DONE BIT(1) +#define SD_LANE_SD_LANE_STAT_DFE_RST_DONE_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_STAT_DFE_RST_DONE, x) +#define SD_LANE_SD_LANE_STAT_DFE_RST_DONE_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_STAT_DFE_RST_DONE, x) + +#define SD_LANE_SD_LANE_STAT_DBG_OBS GENMASK(31, 16) +#define SD_LANE_SD_LANE_STAT_DBG_OBS_SET(x)\ + FIELD_PREP(SD_LANE_SD_LANE_STAT_DBG_OBS, x) +#define SD_LANE_SD_LANE_STAT_DBG_OBS_GET(x)\ + FIELD_GET(SD_LANE_SD_LANE_STAT_DBG_OBS, x) + +/* SD_LANE_TARGET:CFG_STAT_FX100:MISC */ +#define SD_LANE_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 0, 0, 1, 4) + +#define SD_LANE_MISC_SD_125_RST_DIS BIT(0) +#define SD_LANE_MISC_SD_125_RST_DIS_SET(x)\ + FIELD_PREP(SD_LANE_MISC_SD_125_RST_DIS, x) +#define SD_LANE_MISC_SD_125_RST_DIS_GET(x)\ + FIELD_GET(SD_LANE_MISC_SD_125_RST_DIS, x) + +#define SD_LANE_MISC_RX_ENA BIT(1) +#define SD_LANE_MISC_RX_ENA_SET(x)\ + FIELD_PREP(SD_LANE_MISC_RX_ENA, x) +#define SD_LANE_MISC_RX_ENA_GET(x)\ + FIELD_GET(SD_LANE_MISC_RX_ENA, x) + +#define SD_LANE_MISC_MUX_ENA BIT(2) +#define SD_LANE_MISC_MUX_ENA_SET(x)\ + FIELD_PREP(SD_LANE_MISC_MUX_ENA, x) +#define SD_LANE_MISC_MUX_ENA_GET(x)\ + FIELD_GET(SD_LANE_MISC_MUX_ENA, x) + +#define SD_LANE_MISC_CORE_CLK_FREQ GENMASK(5, 4) +#define SD_LANE_MISC_CORE_CLK_FREQ_SET(x)\ + FIELD_PREP(SD_LANE_MISC_CORE_CLK_FREQ, x) +#define SD_LANE_MISC_CORE_CLK_FREQ_GET(x)\ + FIELD_GET(SD_LANE_MISC_CORE_CLK_FREQ, x) + +/* SD_LANE_TARGET:CFG_STAT_FX100:M_STAT_MISC */ +#define SD_LANE_M_STAT_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 36, 0, 1, 4) + +#define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM GENMASK(21, 0) +#define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM_SET(x)\ + FIELD_PREP(SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM, x) +#define SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM_GET(x)\ + FIELD_GET(SD_LANE_M_STAT_MISC_M_RIS_EDGE_PTR_ADJ_SUM, x) + +#define SD_LANE_M_STAT_MISC_M_LOCK_CNT GENMASK(31, 24) +#define SD_LANE_M_STAT_MISC_M_LOCK_CNT_SET(x)\ + FIELD_PREP(SD_LANE_M_STAT_MISC_M_LOCK_CNT, x) +#define SD_LANE_M_STAT_MISC_M_LOCK_CNT_GET(x)\ + FIELD_GET(SD_LANE_M_STAT_MISC_M_LOCK_CNT, x) + +/* SD25G_CFG_TARGET:SD_RESET:SD_SER_RST */ +#define SD_LANE_25G_SD_SER_RST(t) __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 0, 0, 1, 4) + +#define SD_LANE_25G_SD_SER_RST_SER_RST BIT(0) +#define SD_LANE_25G_SD_SER_RST_SER_RST_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_SER_RST_SER_RST, x) +#define SD_LANE_25G_SD_SER_RST_SER_RST_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_SER_RST_SER_RST, x) + +/* SD25G_CFG_TARGET:SD_RESET:SD_DES_RST */ +#define SD_LANE_25G_SD_DES_RST(t) __REG(TARGET_SD_LANE_25G, t, 8, 0, 0, 1, 8, 4, 0, 1, 4) + +#define SD_LANE_25G_SD_DES_RST_DES_RST BIT(0) +#define SD_LANE_25G_SD_DES_RST_DES_RST_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_DES_RST_DES_RST, x) +#define SD_LANE_25G_SD_DES_RST_DES_RST_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_DES_RST_DES_RST, x) + +/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG */ +#define SD_LANE_25G_SD_LANE_CFG(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 0, 0, 1, 4) + +#define SD_LANE_25G_SD_LANE_CFG_MACRO_RST BIT(0) +#define SD_LANE_25G_SD_LANE_CFG_MACRO_RST_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_MACRO_RST, x) +#define SD_LANE_25G_SD_LANE_CFG_MACRO_RST_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_MACRO_RST, x) + +#define SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST BIT(1) +#define SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, x) +#define SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, x) + +#define SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE BIT(4) +#define SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE, x) +#define SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_HWT_MULTI_LANE_MODE, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE GENMASK(7, 5) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_PHYMODE, x) + +#define SD_LANE_25G_SD_LANE_CFG_LANE_RST BIT(8) +#define SD_LANE_25G_SD_LANE_CFG_LANE_RST_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_LANE_RST, x) +#define SD_LANE_25G_SD_LANE_CFG_LANE_RST_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_LANE_RST, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV BIT(9) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_ADV, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN BIT(10) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_MAIN, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY BIT(11) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_DLY, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV GENMASK(15, 12) +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_ADV, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN BIT(16) +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_MAIN, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY GENMASK(21, 17) +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_TAP_DLY, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN BIT(22) +#define SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_ISCAN_EN, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN BIT(23) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS_EN_FAST_ISCAN, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING BIT(24) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXSWING, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI BIT(25) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXEI, x) + +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN GENMASK(28, 26) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN, x) +#define SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG_PCS2PMA_TXMARGIN, x) + +/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_CFG2 */ +#define SD_LANE_25G_SD_LANE_CFG2(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 4, 0, 1, 4) + +#define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL GENMASK(2, 0) +#define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_DATA_WIDTH_SEL, x) + +#define SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL GENMASK(5, 3) +#define SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_PMA_TXCK_SEL, x) + +#define SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL GENMASK(8, 6) +#define SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_PMA_RXDIV_SEL, x) + +#define SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED GENMASK(10, 9) +#define SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED, x) +#define SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_PCS2PMA_TX_SPEED, x) + +#define SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV GENMASK(13, 11) +#define SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV, x) +#define SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_TXFIFO_CK_DIV, x) + +#define SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV GENMASK(16, 14) +#define SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV, x) +#define SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_RXFIFO_CK_DIV, x) + +#define SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL GENMASK(19, 17) +#define SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_HWT_VCO_DIV_SEL, x) + +#define SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV GENMASK(23, 20) +#define SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV, x) +#define SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_HWT_CFG_SEL_DIV, x) + +#define SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL GENMASK(25, 24) +#define SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_HWT_PRE_DIVSEL, x) + +#define SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL GENMASK(28, 26) +#define SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_TXRATE_SEL, x) + +#define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL GENMASK(31, 29) +#define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL, x) +#define SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_CFG2_RXRATE_SEL, x) + +/* SD25G_CFG_TARGET:SD_LANE_CFG_STAT:SD_LANE_STAT */ +#define SD_LANE_25G_SD_LANE_STAT(t) __REG(TARGET_SD_LANE_25G, t, 8, 8, 0, 1, 12, 8, 0, 1, 4) + +#define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE BIT(0) +#define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE, x) +#define SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_STAT_PMA_RST_DONE, x) + +#define SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE BIT(1) +#define SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE, x) +#define SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_STAT_LANE_RST_DONE, x) + +#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS GENMASK(31, 16) +#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_SET(x)\ + FIELD_PREP(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x) +#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_GET(x)\ + FIELD_GET(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x) + +#endif /* _SPARX5_SERDES_REGS_H_ */ diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 12e71a315a2c..2f8210167b77 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Motorola CPCAP PMIC USB PHY driver * Copyright (C) 2017 Tony Lindgren <tony@atomide.com> @@ -5,15 +6,6 @@ * Some parts based on earlier Motorola Linux kernel tree code in * board-mapphone-usb.c and cpcap-usb-det.c: * Copyright (C) 2007 - 2011 Motorola, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/atomic.h> @@ -122,7 +114,6 @@ enum cpcap_gpio_mode { struct cpcap_phy_ddata { struct regmap *reg; struct device *dev; - struct clk *refclk; struct usb_phy phy; struct delayed_work detect_work; struct pinctrl *pins; @@ -144,7 +135,7 @@ static bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata) error = iio_read_channel_processed(ddata->vbus, &value); if (error >= 0) - return value > 3900 ? true : false; + return value > 3900; dev_err(ddata->dev, "error reading VBUS: %i\n", error); @@ -365,7 +356,8 @@ static int cpcap_usb_init_irq(struct platform_device *pdev, error = devm_request_threaded_irq(ddata->dev, irq, NULL, cpcap_phy_irq_thread, - IRQF_SHARED, + IRQF_SHARED | + IRQF_ONESHOT, name, ddata); if (error) { dev_err(ddata->dev, "could not get irq %s: %i\n", @@ -662,35 +654,42 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev) generic_phy = devm_phy_create(ddata->dev, NULL, &ops); if (IS_ERR(generic_phy)) { error = PTR_ERR(generic_phy); - return PTR_ERR(generic_phy); + goto out_reg_disable; } phy_set_drvdata(generic_phy, ddata); phy_provider = devm_of_phy_provider_register(ddata->dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); + if (IS_ERR(phy_provider)) { + error = PTR_ERR(phy_provider); + goto out_reg_disable; + } error = cpcap_usb_init_optional_pins(ddata); if (error) - return error; + goto out_reg_disable; cpcap_usb_init_optional_gpios(ddata); error = cpcap_usb_init_iio(ddata); if (error) - return error; + goto out_reg_disable; error = cpcap_usb_init_interrupts(pdev, ddata); if (error) - return error; + goto out_reg_disable; usb_add_phy_dev(&ddata->phy); atomic_set(&ddata->active, 1); schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1)); return 0; + +out_reg_disable: + regulator_disable(ddata->vusb); + + return error; } static int cpcap_usb_phy_remove(struct platform_device *pdev) @@ -707,7 +706,6 @@ static int cpcap_usb_phy_remove(struct platform_device *pdev) usb_remove_phy(&ddata->phy); cancel_delayed_work_sync(&ddata->detect_work); - clk_unprepare(ddata->refclk); regulator_disable(ddata->vusb); return 0; diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 94a34cf75eb3..3cd4d51c247c 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -178,6 +178,7 @@ static const struct phy_ops gpio_usb_ops = { /** * phy_mdm6600_cmd() - send a command request to mdm6600 * @ddata: device driver data + * @val: value of cmd to be set * * Configures the three command request GPIOs to the specified value. */ @@ -194,7 +195,7 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) /** * phy_mdm6600_status() - read mdm6600 status lines - * @ddata: device driver data + * @work: work structure */ static void phy_mdm6600_status(struct work_struct *work) { @@ -628,7 +629,8 @@ idle: cleanup: if (error < 0) phy_mdm6600_device_power_off(ddata); - + pm_runtime_disable(ddata->dev); + pm_runtime_dont_use_autosuspend(ddata->dev); return error; } diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c new file mode 100644 index 000000000000..95c6dbb52da7 --- /dev/null +++ b/drivers/phy/phy-can-transceiver.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-can-transceiver.c - phy driver for CAN transceivers + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com + * + */ +#include<linux/phy/phy.h> +#include<linux/platform_device.h> +#include<linux/module.h> +#include<linux/gpio.h> +#include<linux/gpio/consumer.h> +#include <linux/mux/consumer.h> + +struct can_transceiver_data { + u32 flags; +#define CAN_TRANSCEIVER_STB_PRESENT BIT(0) +#define CAN_TRANSCEIVER_EN_PRESENT BIT(1) +}; + +struct can_transceiver_phy { + struct phy *generic_phy; + struct gpio_desc *standby_gpio; + struct gpio_desc *enable_gpio; + struct mux_state *mux_state; +}; + +/* Power on function */ +static int can_transceiver_phy_power_on(struct phy *phy) +{ + struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + int ret; + + if (can_transceiver_phy->mux_state) { + ret = mux_state_select(can_transceiver_phy->mux_state); + if (ret) { + dev_err(&phy->dev, "Failed to select CAN mux: %d\n", ret); + return ret; + } + } + if (can_transceiver_phy->standby_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0); + if (can_transceiver_phy->enable_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1); + + return 0; +} + +/* Power off function */ +static int can_transceiver_phy_power_off(struct phy *phy) +{ + struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + + if (can_transceiver_phy->standby_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); + if (can_transceiver_phy->enable_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); + if (can_transceiver_phy->mux_state) + mux_state_deselect(can_transceiver_phy->mux_state); + + return 0; +} + +static const struct phy_ops can_transceiver_phy_ops = { + .power_on = can_transceiver_phy_power_on, + .power_off = can_transceiver_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct can_transceiver_data tcan1042_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT, +}; + +static const struct can_transceiver_data tcan1043_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT, +}; + +static const struct of_device_id can_transceiver_phy_ids[] = { + { + .compatible = "ti,tcan1042", + .data = &tcan1042_drvdata + }, + { + .compatible = "ti,tcan1043", + .data = &tcan1043_drvdata + }, + { } +}; +MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); + +static int can_transceiver_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct can_transceiver_phy *can_transceiver_phy; + const struct can_transceiver_data *drvdata; + const struct of_device_id *match; + struct phy *phy; + struct gpio_desc *standby_gpio; + struct gpio_desc *enable_gpio; + u32 max_bitrate = 0; + + can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL); + if (!can_transceiver_phy) + return -ENOMEM; + + match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); + drvdata = match->data; + + if (of_property_read_bool(dev->of_node, "mux-states")) { + struct mux_state *mux_state; + + mux_state = devm_mux_state_get(dev, NULL); + if (IS_ERR(mux_state)) + return dev_err_probe(&pdev->dev, PTR_ERR(mux_state), + "failed to get mux\n"); + can_transceiver_phy->mux_state = mux_state; + } + + phy = devm_phy_create(dev, dev->of_node, + &can_transceiver_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create can transceiver phy\n"); + return PTR_ERR(phy); + } + + device_property_read_u32(dev, "max-bitrate", &max_bitrate); + if (!max_bitrate) + dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n"); + phy->attrs.max_link_rate = max_bitrate; + + can_transceiver_phy->generic_phy = phy; + + if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) { + standby_gpio = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH); + if (IS_ERR(standby_gpio)) + return PTR_ERR(standby_gpio); + can_transceiver_phy->standby_gpio = standby_gpio; + } + + if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) { + enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(enable_gpio)) + return PTR_ERR(enable_gpio); + can_transceiver_phy->enable_gpio = enable_gpio; + } + + phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver can_transceiver_phy_driver = { + .probe = can_transceiver_phy_probe, + .driver = { + .name = "can-transceiver-phy", + .of_match_table = can_transceiver_phy_ids, + }, +}; + +module_platform_driver(can_transceiver_phy_driver); + +MODULE_AUTHOR("Faiz Abbas <faiz_abbas@ti.com>"); +MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>"); +MODULE_DESCRIPTION("CAN TRANSCEIVER PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index 14e0551cd319..929e86d6558e 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -12,12 +12,10 @@ #include <linux/phy/phy.h> #include <linux/phy/phy-mipi-dphy.h> -#define PSEC_PER_SEC 1000000000000LL - /* * Minimum D-PHY timings based on MIPI D-PHY specification. Derived * from the valid ranges specified in Section 6.9, Table 14, Page 41 - * of the D-PHY specification (v2.1). + * of the D-PHY specification (v1.2). */ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, unsigned int bpp, @@ -38,7 +36,7 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, cfg->clk_miss = 0; cfg->clk_post = 60000 + 52 * ui; - cfg->clk_pre = 8000; + cfg->clk_pre = 8; cfg->clk_prepare = 38000; cfg->clk_settle = 95000; cfg->clk_term_en = 0; @@ -66,10 +64,10 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui); cfg->init = 100; - cfg->lpx = 60000; + cfg->lpx = 50000; cfg->ta_get = 5 * cfg->lpx; cfg->ta_go = 4 * cfg->lpx; - cfg->ta_sure = 2 * cfg->lpx; + cfg->ta_sure = cfg->lpx; cfg->wakeup = 1000; cfg->hs_clk_rate = hs_clk_rate; @@ -99,7 +97,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg) if (cfg->clk_post < (60000 + 52 * ui)) return -EINVAL; - if (cfg->clk_pre < 8000) + if (cfg->clk_pre < 8) return -EINVAL; if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a27b8d578d7f..d93ddf1262c5 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -229,6 +229,17 @@ void phy_pm_runtime_forbid(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid); +/** + * phy_init - phy internal initialization before phy operation + * @phy: the phy returned by phy_get() + * + * Used to allow phy's driver to perform phy internal initialization, + * such as PLL block powering, clock initialization or anything that's + * is required by the phy to perform the start of operation. + * Must be called before phy_power_on(). + * + * Return: %0 if successful, a negative error code otherwise + */ int phy_init(struct phy *phy) { int ret; @@ -242,6 +253,9 @@ int phy_init(struct phy *phy) ret = 0; /* Override possible ret == -ENOTSUPP */ mutex_lock(&phy->mutex); + if (phy->power_count > phy->init_count) + dev_warn(&phy->dev, "phy_power_on was called before phy_init\n"); + if (phy->init_count == 0 && phy->ops->init) { ret = phy->ops->init(phy); if (ret < 0) { @@ -258,6 +272,14 @@ out: } EXPORT_SYMBOL_GPL(phy_init); +/** + * phy_exit - Phy internal un-initialization + * @phy: the phy returned by phy_get() + * + * Must be called after phy_power_off(). + * + * Return: %0 if successful, a negative error code otherwise + */ int phy_exit(struct phy *phy) { int ret; @@ -287,6 +309,14 @@ out: } EXPORT_SYMBOL_GPL(phy_exit); +/** + * phy_power_on - Enable the phy and enter proper operation + * @phy: the phy returned by phy_get() + * + * Must be called after phy_init(). + * + * Return: %0 if successful, a negative error code otherwise + */ int phy_power_on(struct phy *phy) { int ret = 0; @@ -329,6 +359,14 @@ out: } EXPORT_SYMBOL_GPL(phy_power_on); +/** + * phy_power_off - Disable the phy. + * @phy: the phy returned by phy_get() + * + * Must be called before phy_exit(). + * + * Return: %0 if successful, a negative error code otherwise + */ int phy_power_off(struct phy *phy) { int ret; @@ -373,6 +411,36 @@ int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode) } EXPORT_SYMBOL_GPL(phy_set_mode_ext); +int phy_set_media(struct phy *phy, enum phy_media media) +{ + int ret; + + if (!phy || !phy->ops->set_media) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->set_media(phy, media); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_set_media); + +int phy_set_speed(struct phy *phy, int speed) +{ + int ret; + + if (!phy || !phy->ops->set_speed) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->set_speed(phy, speed); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_set_speed); + int phy_reset(struct phy *phy) { int ret; @@ -402,7 +470,7 @@ EXPORT_SYMBOL_GPL(phy_reset); * runtime, which are otherwise lost after host controller reset and cannot * be applied in phy_init() or phy_power_on(). * - * Returns: 0 if successful, an negative error code otherwise + * Return: %0 if successful, a negative error code otherwise */ int phy_calibrate(struct phy *phy) { @@ -428,7 +496,7 @@ EXPORT_SYMBOL_GPL(phy_calibrate); * on the phy. The configuration will be applied on the current phy * mode, that can be changed using phy_set_mode(). * - * Returns: 0 if successful, an negative error code otherwise + * Return: %0 if successful, a negative error code otherwise */ int phy_configure(struct phy *phy, union phy_configure_opts *opts) { @@ -462,7 +530,7 @@ EXPORT_SYMBOL_GPL(phy_configure); * PHY, so calling it as many times as deemed fit will have no side * effect. * - * Returns: 0 if successful, an negative error code otherwise + * Return: %0 if successful, a negative error code otherwise */ int phy_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts) @@ -667,16 +735,18 @@ struct phy *phy_get(struct device *dev, const char *string) struct phy *phy; struct device_link *link; - if (string == NULL) { - dev_WARN(dev, "missing string\n"); - return ERR_PTR(-EINVAL); - } - if (dev->of_node) { - index = of_property_match_string(dev->of_node, "phy-names", - string); + if (string) + index = of_property_match_string(dev->of_node, "phy-names", + string); + else + index = 0; phy = _of_phy_get(dev->of_node, index); } else { + if (string == NULL) { + dev_WARN(dev, "missing string\n"); + return ERR_PTR(-EINVAL); + } phy = phy_find(dev, string); } if (IS_ERR(phy)) @@ -1062,6 +1132,7 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register); * __devm_of_phy_provider_register() - create/register phy provider with the * framework * @dev: struct device of the phy provider + * @children: device node containing children (if different from dev->of_node) * @owner: the module owner containing of_xlate * @of_xlate: function pointer to obtain phy instance from phy provider * @@ -1117,12 +1188,14 @@ EXPORT_SYMBOL_GPL(of_phy_provider_unregister); /** * devm_of_phy_provider_unregister() - remove phy provider from the framework * @dev: struct device of the phy provider + * @phy_provider: phy provider returned by of_phy_provider_register() * * destroys the devres associated with this phy provider and invokes * of_phy_provider_unregister to unregister the phy provider. */ void devm_of_phy_provider_unregister(struct device *dev, - struct phy_provider *phy_provider) { + struct phy_provider *phy_provider) +{ int r; r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match, diff --git a/drivers/phy/phy-lgm-usb.c b/drivers/phy/phy-lgm-usb.c new file mode 100644 index 000000000000..309c8f0e0724 --- /dev/null +++ b/drivers/phy/phy-lgm-usb.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel LGM USB PHY driver + * + * Copyright (C) 2020 Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/usb/phy.h> +#include <linux/workqueue.h> + +#define CTRL1_OFFSET 0x14 +#define SRAM_EXT_LD_DONE BIT(25) +#define SRAM_INIT_DONE BIT(26) + +#define TCPC_OFFSET 0x1014 +#define TCPC_MUX_CTL GENMASK(1, 0) +#define MUX_NC 0 +#define MUX_USB 1 +#define MUX_DP 2 +#define MUX_USBDP 3 +#define TCPC_FLIPPED BIT(2) +#define TCPC_LOW_POWER_EN BIT(3) +#define TCPC_VALID BIT(4) +#define TCPC_CONN \ + (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_USB)) +#define TCPC_DISCONN \ + (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_NC) | TCPC_LOW_POWER_EN) + +static const char *const PHY_RESETS[] = { "phy31", "phy", }; +static const char *const CTL_RESETS[] = { "apb", "ctrl", }; + +struct tca_apb { + struct reset_control *resets[ARRAY_SIZE(PHY_RESETS)]; + struct regulator *vbus; + struct work_struct wk; + struct usb_phy phy; + + bool regulator_enabled; + bool phy_initialized; + bool connected; +}; + +static int get_flipped(struct tca_apb *ta, bool *flipped) +{ + union extcon_property_value property; + int ret; + + ret = extcon_get_property(ta->phy.edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY, &property); + if (ret) { + dev_err(ta->phy.dev, "no polarity property from extcon\n"); + return ret; + } + + *flipped = property.intval; + + return 0; +} + +static int phy_init(struct usb_phy *phy) +{ + struct tca_apb *ta = container_of(phy, struct tca_apb, phy); + void __iomem *ctrl1 = phy->io_priv + CTRL1_OFFSET; + int val, ret, i; + + if (ta->phy_initialized) + return 0; + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) + reset_control_deassert(ta->resets[i]); + + ret = readl_poll_timeout(ctrl1, val, val & SRAM_INIT_DONE, 10, 10 * 1000); + if (ret) { + dev_err(ta->phy.dev, "SRAM init failed, 0x%x\n", val); + return ret; + } + + writel(readl(ctrl1) | SRAM_EXT_LD_DONE, ctrl1); + + ta->phy_initialized = true; + if (!ta->phy.edev) { + writel(TCPC_CONN, ta->phy.io_priv + TCPC_OFFSET); + return phy->set_vbus(phy, true); + } + + schedule_work(&ta->wk); + + return ret; +} + +static void phy_shutdown(struct usb_phy *phy) +{ + struct tca_apb *ta = container_of(phy, struct tca_apb, phy); + int i; + + if (!ta->phy_initialized) + return; + + ta->phy_initialized = false; + flush_work(&ta->wk); + ta->phy.set_vbus(&ta->phy, false); + + ta->connected = false; + writel(TCPC_DISCONN, ta->phy.io_priv + TCPC_OFFSET); + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) + reset_control_assert(ta->resets[i]); +} + +static int phy_set_vbus(struct usb_phy *phy, int on) +{ + struct tca_apb *ta = container_of(phy, struct tca_apb, phy); + int ret; + + if (!!on == ta->regulator_enabled) + return 0; + + if (on) + ret = regulator_enable(ta->vbus); + else + ret = regulator_disable(ta->vbus); + + if (!ret) + ta->regulator_enabled = on; + + dev_dbg(ta->phy.dev, "set vbus: %d\n", on); + return ret; +} + +static void tca_work(struct work_struct *work) +{ + struct tca_apb *ta = container_of(work, struct tca_apb, wk); + bool connected; + bool flipped = false; + u32 val; + int ret; + + ret = get_flipped(ta, &flipped); + if (ret) + return; + + connected = extcon_get_state(ta->phy.edev, EXTCON_USB_HOST); + if (connected == ta->connected) + return; + + ta->connected = connected; + if (connected) { + val = TCPC_CONN; + if (flipped) + val |= TCPC_FLIPPED; + dev_dbg(ta->phy.dev, "connected%s\n", flipped ? " flipped" : ""); + } else { + val = TCPC_DISCONN; + dev_dbg(ta->phy.dev, "disconnected\n"); + } + + writel(val, ta->phy.io_priv + TCPC_OFFSET); + + ret = ta->phy.set_vbus(&ta->phy, connected); + if (ret) + dev_err(ta->phy.dev, "failed to set VBUS\n"); +} + +static int id_notifier(struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct tca_apb *ta = container_of(nb, struct tca_apb, phy.id_nb); + + if (ta->phy_initialized) + schedule_work(&ta->wk); + + return NOTIFY_DONE; +} + +static int vbus_notifier(struct notifier_block *nb, unsigned long evnt, void *ptr) +{ + return NOTIFY_DONE; +} + +static int phy_probe(struct platform_device *pdev) +{ + struct reset_control *resets[ARRAY_SIZE(CTL_RESETS)]; + struct device *dev = &pdev->dev; + struct usb_phy *phy; + struct tca_apb *ta; + int i; + + ta = devm_kzalloc(dev, sizeof(*ta), GFP_KERNEL); + if (!ta) + return -ENOMEM; + + platform_set_drvdata(pdev, ta); + INIT_WORK(&ta->wk, tca_work); + + phy = &ta->phy; + phy->dev = dev; + phy->label = dev_name(dev); + phy->type = USB_PHY_TYPE_USB3; + phy->init = phy_init; + phy->shutdown = phy_shutdown; + phy->set_vbus = phy_set_vbus; + phy->id_nb.notifier_call = id_notifier; + phy->vbus_nb.notifier_call = vbus_notifier; + + phy->io_priv = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->io_priv)) + return PTR_ERR(phy->io_priv); + + ta->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(ta->vbus)) + return PTR_ERR(ta->vbus); + + for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) { + resets[i] = devm_reset_control_get_exclusive(dev, CTL_RESETS[i]); + if (IS_ERR(resets[i])) { + dev_err(dev, "%s reset not found\n", CTL_RESETS[i]); + return PTR_ERR(resets[i]); + } + } + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) { + ta->resets[i] = devm_reset_control_get_exclusive(dev, PHY_RESETS[i]); + if (IS_ERR(ta->resets[i])) { + dev_err(dev, "%s reset not found\n", PHY_RESETS[i]); + return PTR_ERR(ta->resets[i]); + } + } + + for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) + reset_control_assert(resets[i]); + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) + reset_control_assert(ta->resets[i]); + /* + * Out-of-band reset of the controller after PHY reset will cause + * controller malfunctioning, so we should use in-band controller + * reset only and leave the controller de-asserted here. + */ + for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) + reset_control_deassert(resets[i]); + + /* Need to wait at least 20us after de-assert the controller */ + usleep_range(20, 100); + + return usb_add_phy_dev(phy); +} + +static int phy_remove(struct platform_device *pdev) +{ + struct tca_apb *ta = platform_get_drvdata(pdev); + + usb_remove_phy(&ta->phy); + + return 0; +} + +static const struct of_device_id intel_usb_phy_dt_ids[] = { + { .compatible = "intel,lgm-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, intel_usb_phy_dt_ids); + +static struct platform_driver lgm_phy_driver = { + .driver = { + .name = "lgm-usb-phy", + .of_match_table = intel_usb_phy_dt_ids, + }, + .probe = phy_probe, + .remove = phy_remove, +}; + +module_platform_driver(lgm_phy_driver); + +MODULE_DESCRIPTION("Intel LGM USB PHY driver"); +MODULE_AUTHOR("Li Yin <yin1.li@intel.com>"); +MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 7a33ec12f71b..d0f4546648f0 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -961,7 +961,8 @@ static void xgene_phy_sata_cfg_lanes(struct xgene_phy_ctx *ctx) serdes_wr(ctx, lane, RXTX_REG1, val); /* Latch VTT value based on the termination to ground and - enable TX FIFO */ + * enable TX FIFO + */ serdes_rd(ctx, lane, RXTX_REG2, &val); val = RXTX_REG2_VTT_ENA_SET(val, 0x1); val = RXTX_REG2_VTT_SEL_SET(val, 0x1); @@ -1615,7 +1616,7 @@ static struct phy *xgene_phy_xlate(struct device *dev, if (args->args_count <= 0) return ERR_PTR(-EINVAL); - if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX) + if (args->args[0] >= MODE_MAX) return ERR_PTR(-EINVAL); ctx->mode = args->args[0]; @@ -1644,7 +1645,6 @@ static int xgene_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct xgene_phy_ctx *ctx; - struct resource *res; u32 default_spd[] = DEFAULT_SATA_SPD_SEL; u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN; u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION; @@ -1661,8 +1661,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->sds_base = devm_ioremap_resource(&pdev->dev, res); + ctx->sds_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->sds_base)) return PTR_ERR(ctx->sds_base); diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index e46824da29f6..5c98850f5a36 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -18,6 +18,23 @@ config PHY_QCOM_APQ8064_SATA depends on OF select GENERIC_PHY +config PHY_QCOM_EDP + tristate "Qualcomm eDP PHY driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on OF + depends on COMMON_CLK + select GENERIC_PHY + help + Enable this driver to support the Qualcomm eDP PHY found in various + Qualcomm chipsets. + +config PHY_QCOM_IPQ4019_USB + tristate "Qualcomm IPQ4019 USB PHY driver" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s. + config PHY_QCOM_IPQ806X_SATA tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" depends on ARCH_QCOM @@ -52,30 +69,6 @@ config PHY_QCOM_QUSB2 PHY which is usually paired with either the ChipIdea or Synopsys DWC3 USB IPs on MSM SOCs. -config PHY_QCOM_UFS - tristate "Qualcomm UFS PHY driver" - depends on OF && ARCH_QCOM - select GENERIC_PHY - help - Support for UFS PHY on QCOM chipsets. - -if PHY_QCOM_UFS - -config PHY_QCOM_UFS_14NM - tristate - default PHY_QCOM_UFS - help - Support for 14nm UFS QMP phy present on QCOM chipsets. - -config PHY_QCOM_UFS_20NM - tristate - default PHY_QCOM_UFS - depends on BROKEN - help - Support for 20nm UFS QMP phy present on QCOM chipsets. - -endif - config PHY_QCOM_USB_HS tristate "Qualcomm USB HS PHY module" depends on USB_ULPI_BUS @@ -85,9 +78,49 @@ config PHY_QCOM_USB_HS Support for the USB high-speed ULPI compliant phy on Qualcomm chipsets. +config PHY_QCOM_USB_SNPS_FEMTO_V2 + tristate "Qualcomm SNPS FEMTO USB HS PHY V2 module" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + Enable support for the USB high-speed SNPS Femto phy on Qualcomm + chipsets. This PHY has differences in the register map compared + to the V1 variants. The PHY is paired with a Synopsys DWC3 USB + controller on Qualcomm SOCs. + config PHY_QCOM_USB_HSIC tristate "Qualcomm USB HSIC ULPI PHY module" depends on USB_ULPI_BUS select GENERIC_PHY help Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. + +config PHY_QCOM_USB_HS_28NM + tristate "Qualcomm 28nm High-Speed PHY" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in + select GENERIC_PHY + help + Enable this to support the Qualcomm Synopsys DesignWare Core 28nm + High-Speed PHY driver. This driver supports the Hi-Speed PHY which + is usually paired with either the ChipIdea or Synopsys DWC3 USB + IPs on MSM SOCs. + +config PHY_QCOM_USB_SS + tristate "Qualcomm USB Super-Speed PHY driver" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in + select GENERIC_PHY + help + Enable this to support the Super-Speed USB transceiver on various + Qualcomm chipsets. + +config PHY_QCOM_IPQ806X_USB + tristate "Qualcomm IPQ806x DWC3 USB PHY driver" + depends on HAS_IOMEM + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + This option enables support for the Synopsis PHYs present inside the + Qualcomm USB3.0 DWC3 controller on ipq806x SoC. This driver supports + both HS and SS PHY controllers. diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index 283251d6a5d9..65f6c30a3e93 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -1,12 +1,21 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_QCOM_EDP) += phy-qcom-edp.o +obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o -obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o +obj-$(CONFIG_PHY_QCOM_QMP) += \ + phy-qcom-qmp-combo.o \ + phy-qcom-qmp-pcie.o \ + phy-qcom-qmp-pcie-msm8996.o \ + phy-qcom-qmp-ufs.o \ + phy-qcom-qmp-usb.o + obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o -obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o -obj-$(CONFIG_PHY_QCOM_UFS_14NM) += phy-qcom-ufs-qmp-14nm.o -obj-$(CONFIG_PHY_QCOM_UFS_20NM) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o +obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o +obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o +obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c index febe0aef68d4..d437a249cd73 100644 --- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -4,6 +4,7 @@ */ #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -72,18 +73,12 @@ struct qcom_apq8064_sata_phy { }; /* Helper function to do poll and timeout */ -static int read_poll_timeout(void __iomem *addr, u32 mask) +static int poll_timeout(void __iomem *addr, u32 mask) { - unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); + u32 val; - do { - if (readl_relaxed(addr) & mask) - return 0; - - usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); - } while (!time_after(jiffies, timeout)); - - return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; + return readl_relaxed_poll_timeout(addr, val, (val & mask), + DELAY_INTERVAL_US, TIMEOUT_MS * 1000); } static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) @@ -137,21 +132,21 @@ static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); /* PLL Lock wait */ - ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); + ret = poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); if (ret) { dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); return ret; } /* TX Calibration */ - ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); + ret = poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); if (ret) { dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); return ret; } /* RX Calibration */ - ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); + ret = poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); if (ret) { dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); return ret; @@ -206,7 +201,6 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) { struct qcom_apq8064_sata_phy *phy; struct device *dev = &pdev->dev; - struct resource *res; struct phy_provider *phy_provider; struct phy *generic_phy; int ret; @@ -215,8 +209,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) if (!phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->mmio = devm_ioremap_resource(dev, res); + phy->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c new file mode 100644 index 000000000000..fc8ca0f3018d --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -0,0 +1,864 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Ltd. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +#include "phy-qcom-qmp.h" + +/* EDP_PHY registers */ +#define DP_PHY_CFG 0x0010 +#define DP_PHY_CFG_1 0x0014 +#define DP_PHY_PD_CTL 0x001c +#define DP_PHY_MODE 0x0020 + +#define DP_PHY_AUX_CFG0 0x0024 +#define DP_PHY_AUX_CFG1 0x0028 +#define DP_PHY_AUX_CFG2 0x002C +#define DP_PHY_AUX_CFG3 0x0030 +#define DP_PHY_AUX_CFG4 0x0034 +#define DP_PHY_AUX_CFG5 0x0038 +#define DP_PHY_AUX_CFG6 0x003C +#define DP_PHY_AUX_CFG7 0x0040 +#define DP_PHY_AUX_CFG8 0x0044 +#define DP_PHY_AUX_CFG9 0x0048 + +#define DP_PHY_AUX_INTERRUPT_MASK 0x0058 + +#define DP_PHY_VCO_DIV 0x0074 +#define DP_PHY_TX0_TX1_LANE_CTL 0x007c +#define DP_PHY_TX2_TX3_LANE_CTL 0x00a0 + +#define DP_PHY_STATUS 0x00e0 + +/* LANE_TXn registers */ +#define TXn_CLKBUF_ENABLE 0x0000 +#define TXn_TX_EMP_POST1_LVL 0x0004 + +#define TXn_TX_DRV_LVL 0x0014 +#define TXn_TX_DRV_LVL_OFFSET 0x0018 +#define TXn_RESET_TSYNC_EN 0x001c +#define TXn_LDO_CONFIG 0x0084 +#define TXn_TX_BAND 0x0028 + +#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044 +#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048 + +#define TXn_TRANSCEIVER_BIAS_EN 0x0054 +#define TXn_HIGHZ_DRVR_EN 0x0058 +#define TXn_TX_POL_INV 0x005c +#define TXn_LANE_MODE_1 0x0064 + +#define TXn_TRAN_DRVR_EMP_EN 0x0078 + +struct qcom_edp_cfg { + bool is_dp; + + /* DP PHY swing and pre_emphasis tables */ + const u8 (*swing_hbr_rbr)[4][4]; + const u8 (*swing_hbr3_hbr2)[4][4]; + const u8 (*pre_emphasis_hbr_rbr)[4][4]; + const u8 (*pre_emphasis_hbr3_hbr2)[4][4]; +}; + +struct qcom_edp { + struct device *dev; + const struct qcom_edp_cfg *cfg; + + struct phy *phy; + + void __iomem *edp; + void __iomem *tx0; + void __iomem *tx1; + void __iomem *pll; + + struct clk_hw dp_link_hw; + struct clk_hw dp_pixel_hw; + + struct phy_configure_opts_dp dp_opts; + + struct clk_bulk_data clks[2]; + struct regulator_bulk_data supplies[2]; +}; + +static const u8 dp_swing_hbr_rbr[4][4] = { + { 0x08, 0x0f, 0x16, 0x1f }, + { 0x11, 0x1e, 0x1f, 0xff }, + { 0x16, 0x1f, 0xff, 0xff }, + { 0x1f, 0xff, 0xff, 0xff } +}; + +static const u8 dp_pre_emp_hbr_rbr[4][4] = { + { 0x00, 0x0d, 0x14, 0x1a }, + { 0x00, 0x0e, 0x15, 0xff }, + { 0x00, 0x0e, 0xff, 0xff }, + { 0x03, 0xff, 0xff, 0xff } +}; + +static const u8 dp_swing_hbr2_hbr3[4][4] = { + { 0x02, 0x12, 0x16, 0x1a }, + { 0x09, 0x19, 0x1f, 0xff }, + { 0x10, 0x1f, 0xff, 0xff }, + { 0x1f, 0xff, 0xff, 0xff } +}; + +static const u8 dp_pre_emp_hbr2_hbr3[4][4] = { + { 0x00, 0x0c, 0x15, 0x1b }, + { 0x02, 0x0e, 0x16, 0xff }, + { 0x02, 0x11, 0xff, 0xff }, + { 0x04, 0xff, 0xff, 0xff } +}; + +static const struct qcom_edp_cfg dp_phy_cfg = { + .is_dp = true, + .swing_hbr_rbr = &dp_swing_hbr_rbr, + .swing_hbr3_hbr2 = &dp_swing_hbr2_hbr3, + .pre_emphasis_hbr_rbr = &dp_pre_emp_hbr_rbr, + .pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3, +}; + +static const u8 edp_swing_hbr_rbr[4][4] = { + { 0x07, 0x0f, 0x16, 0x1f }, + { 0x0d, 0x16, 0x1e, 0xff }, + { 0x11, 0x1b, 0xff, 0xff }, + { 0x16, 0xff, 0xff, 0xff } +}; + +static const u8 edp_pre_emp_hbr_rbr[4][4] = { + { 0x05, 0x12, 0x17, 0x1d }, + { 0x05, 0x11, 0x18, 0xff }, + { 0x06, 0x11, 0xff, 0xff }, + { 0x00, 0xff, 0xff, 0xff } +}; + +static const u8 edp_swing_hbr2_hbr3[4][4] = { + { 0x0b, 0x11, 0x17, 0x1c }, + { 0x10, 0x19, 0x1f, 0xff }, + { 0x19, 0x1f, 0xff, 0xff }, + { 0x1f, 0xff, 0xff, 0xff } +}; + +static const u8 edp_pre_emp_hbr2_hbr3[4][4] = { + { 0x08, 0x11, 0x17, 0x1b }, + { 0x00, 0x0c, 0x13, 0xff }, + { 0x05, 0x10, 0xff, 0xff }, + { 0x00, 0xff, 0xff, 0xff } +}; + +static const struct qcom_edp_cfg edp_phy_cfg = { + .is_dp = false, + .swing_hbr_rbr = &edp_swing_hbr_rbr, + .swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3, + .pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr, + .pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3, +}; + +static int qcom_edp_phy_init(struct phy *phy) +{ + struct qcom_edp *edp = phy_get_drvdata(phy); + const struct qcom_edp_cfg *cfg = edp->cfg; + int ret; + u8 cfg8; + + ret = regulator_bulk_enable(ARRAY_SIZE(edp->supplies), edp->supplies); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(edp->clks), edp->clks); + if (ret) + goto out_disable_supplies; + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + + /* Turn on BIAS current for PHY/PLL */ + writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + + writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL); + msleep(20); + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + + if (cfg && cfg->is_dp) + cfg8 = 0xb7; + else + cfg8 = 0x37; + + writel(0xfc, edp->edp + DP_PHY_MODE); + + writel(0x00, edp->edp + DP_PHY_AUX_CFG0); + writel(0x13, edp->edp + DP_PHY_AUX_CFG1); + writel(0x24, edp->edp + DP_PHY_AUX_CFG2); + writel(0x00, edp->edp + DP_PHY_AUX_CFG3); + writel(0x0a, edp->edp + DP_PHY_AUX_CFG4); + writel(0x26, edp->edp + DP_PHY_AUX_CFG5); + writel(0x0a, edp->edp + DP_PHY_AUX_CFG6); + writel(0x03, edp->edp + DP_PHY_AUX_CFG7); + writel(cfg8, edp->edp + DP_PHY_AUX_CFG8); + writel(0x03, edp->edp + DP_PHY_AUX_CFG9); + + writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | + PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | + PHY_AUX_REQ_ERR_MASK, edp->edp + DP_PHY_AUX_INTERRUPT_MASK); + + msleep(20); + + return 0; + +out_disable_supplies: + regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies); + + return ret; +} + +static int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configure_opts_dp *dp_opts) +{ + const struct qcom_edp_cfg *cfg = edp->cfg; + unsigned int v_level = 0; + unsigned int p_level = 0; + u8 ldo_config; + u8 swing; + u8 emph; + int i; + + if (!cfg) + return 0; + + for (i = 0; i < dp_opts->lanes; i++) { + v_level = max(v_level, dp_opts->voltage[i]); + p_level = max(p_level, dp_opts->pre[i]); + } + + if (dp_opts->link_rate <= 2700) { + swing = (*cfg->swing_hbr_rbr)[v_level][p_level]; + emph = (*cfg->pre_emphasis_hbr_rbr)[v_level][p_level]; + } else { + swing = (*cfg->swing_hbr3_hbr2)[v_level][p_level]; + emph = (*cfg->pre_emphasis_hbr3_hbr2)[v_level][p_level]; + } + + if (swing == 0xff || emph == 0xff) + return -EINVAL; + + ldo_config = (cfg && cfg->is_dp) ? 0x1 : 0x0; + + writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG); + writel(swing, edp->tx0 + TXn_TX_DRV_LVL); + writel(emph, edp->tx0 + TXn_TX_EMP_POST1_LVL); + + writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG); + writel(swing, edp->tx1 + TXn_TX_DRV_LVL); + writel(emph, edp->tx1 + TXn_TX_EMP_POST1_LVL); + + return 0; +} + +static int qcom_edp_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + const struct phy_configure_opts_dp *dp_opts = &opts->dp; + struct qcom_edp *edp = phy_get_drvdata(phy); + int ret = 0; + + memcpy(&edp->dp_opts, dp_opts, sizeof(*dp_opts)); + + if (dp_opts->set_voltages) + ret = qcom_edp_set_voltages(edp, dp_opts); + + return ret; +} + +static int qcom_edp_configure_ssc(const struct qcom_edp *edp) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 step1; + u32 step2; + + switch (dp_opts->link_rate) { + case 1620: + case 2700: + case 8100: + step1 = 0x45; + step2 = 0x06; + break; + + case 5400: + step1 = 0x5c; + step2 = 0x08; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(0x01, edp->pll + QSERDES_V4_COM_SSC_EN_CENTER); + writel(0x00, edp->pll + QSERDES_V4_COM_SSC_ADJ_PER1); + writel(0x36, edp->pll + QSERDES_V4_COM_SSC_PER1); + writel(0x01, edp->pll + QSERDES_V4_COM_SSC_PER2); + writel(step1, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0); + writel(step2, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0); + + return 0; +} + +static int qcom_edp_configure_pll(const struct qcom_edp *edp) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 div_frac_start2_mode0; + u32 div_frac_start3_mode0; + u32 dec_start_mode0; + u32 lock_cmp1_mode0; + u32 lock_cmp2_mode0; + u32 hsclk_sel; + + switch (dp_opts->link_rate) { + case 1620: + hsclk_sel = 0x5; + dec_start_mode0 = 0x69; + div_frac_start2_mode0 = 0x80; + div_frac_start3_mode0 = 0x07; + lock_cmp1_mode0 = 0x6f; + lock_cmp2_mode0 = 0x08; + break; + + case 2700: + hsclk_sel = 0x3; + dec_start_mode0 = 0x69; + div_frac_start2_mode0 = 0x80; + div_frac_start3_mode0 = 0x07; + lock_cmp1_mode0 = 0x0f; + lock_cmp2_mode0 = 0x0e; + break; + + case 5400: + hsclk_sel = 0x1; + dec_start_mode0 = 0x8c; + div_frac_start2_mode0 = 0x00; + div_frac_start3_mode0 = 0x0a; + lock_cmp1_mode0 = 0x1f; + lock_cmp2_mode0 = 0x1c; + break; + + case 8100: + hsclk_sel = 0x0; + dec_start_mode0 = 0x69; + div_frac_start2_mode0 = 0x80; + div_frac_start3_mode0 = 0x07; + lock_cmp1_mode0 = 0x2f; + lock_cmp2_mode0 = 0x2a; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(0x01, edp->pll + QSERDES_V4_COM_SVS_MODE_CLK_SEL); + writel(0x0b, edp->pll + QSERDES_V4_COM_SYSCLK_EN_SEL); + writel(0x02, edp->pll + QSERDES_V4_COM_SYS_CLK_CTRL); + writel(0x0c, edp->pll + QSERDES_V4_COM_CLK_ENABLE1); + writel(0x06, edp->pll + QSERDES_V4_COM_SYSCLK_BUF_ENABLE); + writel(0x30, edp->pll + QSERDES_V4_COM_CLK_SELECT); + writel(hsclk_sel, edp->pll + QSERDES_V4_COM_HSCLK_SEL); + writel(0x0f, edp->pll + QSERDES_V4_COM_PLL_IVCO); + writel(0x08, edp->pll + QSERDES_V4_COM_LOCK_CMP_EN); + writel(0x36, edp->pll + QSERDES_V4_COM_PLL_CCTRL_MODE0); + writel(0x16, edp->pll + QSERDES_V4_COM_PLL_RCTRL_MODE0); + writel(0x06, edp->pll + QSERDES_V4_COM_CP_CTRL_MODE0); + writel(dec_start_mode0, edp->pll + QSERDES_V4_COM_DEC_START_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_DIV_FRAC_START1_MODE0); + writel(div_frac_start2_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START2_MODE0); + writel(div_frac_start3_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START3_MODE0); + writel(0x02, edp->pll + QSERDES_V4_COM_CMN_CONFIG); + writel(0x3f, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_MAP); + writel(lock_cmp1_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP1_MODE0); + writel(lock_cmp2_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP2_MODE0); + + writel(0x0a, edp->pll + QSERDES_V4_COM_BG_TIMER); + writel(0x14, edp->pll + QSERDES_V4_COM_CORECLK_DIV_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_CTRL); + writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + writel(0x0f, edp->pll + QSERDES_V4_COM_CORE_CLK_EN); + writel(0xa0, edp->pll + QSERDES_V4_COM_VCO_TUNE1_MODE0); + writel(0x03, edp->pll + QSERDES_V4_COM_VCO_TUNE2_MODE0); + + return 0; +} + +static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel_freq) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 vco_div; + + switch (dp_opts->link_rate) { + case 1620: + vco_div = 0x1; + *pixel_freq = 1620000000UL / 2; + break; + + case 2700: + vco_div = 0x1; + *pixel_freq = 2700000000UL / 2; + break; + + case 5400: + vco_div = 0x2; + *pixel_freq = 5400000000UL / 4; + break; + + case 8100: + vco_div = 0x0; + *pixel_freq = 8100000000UL / 6; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(vco_div, edp->edp + DP_PHY_VCO_DIV); + + return 0; +} + +static int qcom_edp_phy_power_on(struct phy *phy) +{ + const struct qcom_edp *edp = phy_get_drvdata(phy); + const struct qcom_edp_cfg *cfg = edp->cfg; + u32 bias0_en, drvr0_en, bias1_en, drvr1_en; + unsigned long pixel_freq; + u8 ldo_config; + int timeout; + int ret; + u32 val; + u8 cfg1; + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + writel(0xfc, edp->edp + DP_PHY_MODE); + + timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS, + val, val & BIT(7), 5, 200); + if (timeout) + return timeout; + + + ldo_config = (cfg && cfg->is_dp) ? 0x1 : 0x0; + + writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG); + writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG); + writel(0x00, edp->tx0 + TXn_LANE_MODE_1); + writel(0x00, edp->tx1 + TXn_LANE_MODE_1); + + if (edp->dp_opts.ssc) { + ret = qcom_edp_configure_ssc(edp); + if (ret) + return ret; + } + + ret = qcom_edp_configure_pll(edp); + if (ret) + return ret; + + /* TX Lane configuration */ + writel(0x05, edp->edp + DP_PHY_TX0_TX1_LANE_CTL); + writel(0x05, edp->edp + DP_PHY_TX2_TX3_LANE_CTL); + + /* TX-0 register configuration */ + writel(0x03, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN); + writel(0x0f, edp->tx0 + TXn_CLKBUF_ENABLE); + writel(0x03, edp->tx0 + TXn_RESET_TSYNC_EN); + writel(0x01, edp->tx0 + TXn_TRAN_DRVR_EMP_EN); + writel(0x04, edp->tx0 + TXn_TX_BAND); + + /* TX-1 register configuration */ + writel(0x03, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN); + writel(0x0f, edp->tx1 + TXn_CLKBUF_ENABLE); + writel(0x03, edp->tx1 + TXn_RESET_TSYNC_EN); + writel(0x01, edp->tx1 + TXn_TRAN_DRVR_EMP_EN); + writel(0x04, edp->tx1 + TXn_TX_BAND); + + ret = qcom_edp_set_vco_div(edp, &pixel_freq); + if (ret) + return ret; + + writel(0x01, edp->edp + DP_PHY_CFG); + writel(0x05, edp->edp + DP_PHY_CFG); + writel(0x01, edp->edp + DP_PHY_CFG); + writel(0x09, edp->edp + DP_PHY_CFG); + + writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL); + + timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS, + val, val & BIT(0), 500, 10000); + if (timeout) + return timeout; + + writel(0x19, edp->edp + DP_PHY_CFG); + writel(0x1f, edp->tx0 + TXn_HIGHZ_DRVR_EN); + writel(0x04, edp->tx0 + TXn_HIGHZ_DRVR_EN); + writel(0x00, edp->tx0 + TXn_TX_POL_INV); + writel(0x1f, edp->tx1 + TXn_HIGHZ_DRVR_EN); + writel(0x04, edp->tx1 + TXn_HIGHZ_DRVR_EN); + writel(0x00, edp->tx1 + TXn_TX_POL_INV); + writel(0x10, edp->tx0 + TXn_TX_DRV_LVL_OFFSET); + writel(0x10, edp->tx1 + TXn_TX_DRV_LVL_OFFSET); + writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX0); + writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX1); + writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX0); + writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX1); + + writel(0x10, edp->tx0 + TXn_TX_EMP_POST1_LVL); + writel(0x10, edp->tx1 + TXn_TX_EMP_POST1_LVL); + writel(0x1f, edp->tx0 + TXn_TX_DRV_LVL); + writel(0x1f, edp->tx1 + TXn_TX_DRV_LVL); + + if (edp->dp_opts.lanes == 1) { + bias0_en = 0x01; + bias1_en = 0x00; + drvr0_en = 0x06; + drvr1_en = 0x07; + cfg1 = 0x1; + } else if (edp->dp_opts.lanes == 2) { + bias0_en = 0x03; + bias1_en = 0x00; + drvr0_en = 0x04; + drvr1_en = 0x07; + cfg1 = 0x3; + } else { + bias0_en = 0x03; + bias1_en = 0x03; + drvr0_en = 0x04; + drvr1_en = 0x04; + cfg1 = 0xf; + } + + writel(drvr0_en, edp->tx0 + TXn_HIGHZ_DRVR_EN); + writel(bias0_en, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN); + writel(drvr1_en, edp->tx1 + TXn_HIGHZ_DRVR_EN); + writel(bias1_en, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN); + writel(cfg1, edp->edp + DP_PHY_CFG_1); + + writel(0x18, edp->edp + DP_PHY_CFG); + usleep_range(100, 1000); + + writel(0x19, edp->edp + DP_PHY_CFG); + + ret = readl_poll_timeout(edp->edp + DP_PHY_STATUS, + val, val & BIT(1), 500, 10000); + if (ret) + return ret; + + clk_set_rate(edp->dp_link_hw.clk, edp->dp_opts.link_rate * 100000); + clk_set_rate(edp->dp_pixel_hw.clk, pixel_freq); + + return 0; +} + +static int qcom_edp_phy_power_off(struct phy *phy) +{ + const struct qcom_edp *edp = phy_get_drvdata(phy); + + writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL); + + return 0; +} + +static int qcom_edp_phy_exit(struct phy *phy) +{ + struct qcom_edp *edp = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(ARRAY_SIZE(edp->clks), edp->clks); + regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies); + + return 0; +} + +static const struct phy_ops qcom_edp_ops = { + .init = qcom_edp_phy_init, + .configure = qcom_edp_phy_configure, + .power_on = qcom_edp_phy_power_on, + .power_off = qcom_edp_phy_power_off, + .exit = qcom_edp_phy_exit, + .owner = THIS_MODULE, +}; + +/* + * Embedded Display Port PLL driver block diagram for branch clocks + * + * +------------------------------+ + * | EDP_VCO_CLK | + * | | + * | +-------------------+ | + * | | (EDP PLL/VCO) | | + * | +---------+---------+ | + * | v | + * | +----------+-----------+ | + * | | hsclk_divsel_clk_src | | + * | +----------+-----------+ | + * +------------------------------+ + * | + * +---------<---------v------------>----------+ + * | | + * +--------v----------------+ | + * | edp_phy_pll_link_clk | | + * | link_clk | | + * +--------+----------------+ | + * | | + * | | + * v v + * Input to DISPCC block | + * for link clk, crypto clk | + * and interface clock | + * | + * | + * +--------<------------+-----------------+---<---+ + * | | | + * +----v---------+ +--------v-----+ +--------v------+ + * | vco_divided | | vco_divided | | vco_divided | + * | _clk_src | | _clk_src | | _clk_src | + * | | | | | | + * |divsel_six | | divsel_two | | divsel_four | + * +-------+------+ +-----+--------+ +--------+------+ + * | | | + * v---->----------v-------------<------v + * | + * +----------+-----------------+ + * | edp_phy_pll_vco_div_clk | + * +---------+------------------+ + * | + * v + * Input to DISPCC block + * for EDP pixel clock + * + */ +static int qcom_edp_dp_pixel_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + switch (req->rate) { + case 1620000000UL / 2: + case 2700000000UL / 2: + /* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */ + return 0; + + default: + return -EINVAL; + } +} + +static unsigned long +qcom_edp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_pixel_hw); + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + + switch (dp_opts->link_rate) { + case 1620: + return 1620000000UL / 2; + case 2700: + return 2700000000UL / 2; + case 5400: + return 5400000000UL / 4; + case 8100: + return 8100000000UL / 6; + default: + return 0; + } +} + +static const struct clk_ops qcom_edp_dp_pixel_clk_ops = { + .determine_rate = qcom_edp_dp_pixel_clk_determine_rate, + .recalc_rate = qcom_edp_dp_pixel_clk_recalc_rate, +}; + +static int qcom_edp_dp_link_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + switch (req->rate) { + case 162000000: + case 270000000: + case 540000000: + case 810000000: + return 0; + + default: + return -EINVAL; + } +} + +static unsigned long +qcom_edp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_link_hw); + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + + switch (dp_opts->link_rate) { + case 1620: + case 2700: + case 5400: + case 8100: + return dp_opts->link_rate * 100000; + + default: + return 0; + } +} + +static const struct clk_ops qcom_edp_dp_link_clk_ops = { + .determine_rate = qcom_edp_dp_link_clk_determine_rate, + .recalc_rate = qcom_edp_dp_link_clk_recalc_rate, +}; + +static int qcom_edp_clks_register(struct qcom_edp *edp, struct device_node *np) +{ + struct clk_hw_onecell_data *data; + struct clk_init_data init = { }; + char name[64]; + int ret; + + data = devm_kzalloc(edp->dev, struct_size(data, hws, 2), GFP_KERNEL); + if (!data) + return -ENOMEM; + + snprintf(name, sizeof(name), "%s::link_clk", dev_name(edp->dev)); + init.ops = &qcom_edp_dp_link_clk_ops; + init.name = name; + edp->dp_link_hw.init = &init; + ret = devm_clk_hw_register(edp->dev, &edp->dp_link_hw); + if (ret) + return ret; + + snprintf(name, sizeof(name), "%s::vco_div_clk", dev_name(edp->dev)); + init.ops = &qcom_edp_dp_pixel_clk_ops; + init.name = name; + edp->dp_pixel_hw.init = &init; + ret = devm_clk_hw_register(edp->dev, &edp->dp_pixel_hw); + if (ret) + return ret; + + data->hws[0] = &edp->dp_link_hw; + data->hws[1] = &edp->dp_pixel_hw; + data->num = 2; + + return devm_of_clk_add_hw_provider(edp->dev, of_clk_hw_onecell_get, data); +} + +static int qcom_edp_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct qcom_edp *edp; + int ret; + + edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL); + if (!edp) + return -ENOMEM; + + edp->dev = dev; + edp->cfg = of_device_get_match_data(&pdev->dev); + + edp->edp = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(edp->edp)) + return PTR_ERR(edp->edp); + + edp->tx0 = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(edp->tx0)) + return PTR_ERR(edp->tx0); + + edp->tx1 = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(edp->tx1)) + return PTR_ERR(edp->tx1); + + edp->pll = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(edp->pll)) + return PTR_ERR(edp->pll); + + edp->clks[0].id = "aux"; + edp->clks[1].id = "cfg_ahb"; + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(edp->clks), edp->clks); + if (ret) + return ret; + + edp->supplies[0].supply = "vdda-phy"; + edp->supplies[1].supply = "vdda-pll"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(edp->supplies), edp->supplies); + if (ret) + return ret; + + ret = regulator_set_load(edp->supplies[0].consumer, 21800); /* 1.2 V vdda-phy */ + if (ret) { + dev_err(dev, "failed to set load at %s\n", edp->supplies[0].supply); + return ret; + } + + ret = regulator_set_load(edp->supplies[1].consumer, 36000); /* 0.9 V vdda-pll */ + if (ret) { + dev_err(dev, "failed to set load at %s\n", edp->supplies[1].supply); + return ret; + } + + ret = qcom_edp_clks_register(edp, pdev->dev.of_node); + if (ret) + return ret; + + edp->phy = devm_phy_create(dev, pdev->dev.of_node, &qcom_edp_ops); + if (IS_ERR(edp->phy)) { + dev_err(dev, "failed to register phy\n"); + return PTR_ERR(edp->phy); + } + + phy_set_drvdata(edp->phy, edp); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id qcom_edp_phy_match_table[] = { + { .compatible = "qcom,sc7280-edp-phy" }, + { .compatible = "qcom,sc8180x-edp-phy" }, + { .compatible = "qcom,sc8280xp-dp-phy", .data = &dp_phy_cfg }, + { .compatible = "qcom,sc8280xp-edp-phy", .data = &edp_phy_cfg }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_edp_phy_match_table); + +static struct platform_driver qcom_edp_phy_driver = { + .probe = qcom_edp_phy_probe, + .driver = { + .name = "qcom-edp-phy", + .of_match_table = qcom_edp_phy_match_table, + }, +}; + +module_platform_driver(qcom_edp_phy_driver); + +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); +MODULE_DESCRIPTION("Qualcomm eDP QMP PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c new file mode 100644 index 000000000000..d3e7d5e1d1b6 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 John Crispin <john@phrozen.org> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +struct ipq4019_usb_phy { + struct device *dev; + struct phy *phy; + void __iomem *base; + struct reset_control *por_rst; + struct reset_control *srif_rst; +}; + +static int ipq4019_ss_phy_power_off(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + reset_control_assert(phy->por_rst); + msleep(10); + + return 0; +} + +static int ipq4019_ss_phy_power_on(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + ipq4019_ss_phy_power_off(_phy); + + reset_control_deassert(phy->por_rst); + + return 0; +} + +static const struct phy_ops ipq4019_usb_ss_phy_ops = { + .power_on = ipq4019_ss_phy_power_on, + .power_off = ipq4019_ss_phy_power_off, +}; + +static int ipq4019_hs_phy_power_off(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + reset_control_assert(phy->por_rst); + msleep(10); + + reset_control_assert(phy->srif_rst); + msleep(10); + + return 0; +} + +static int ipq4019_hs_phy_power_on(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + ipq4019_hs_phy_power_off(_phy); + + reset_control_deassert(phy->srif_rst); + msleep(10); + + reset_control_deassert(phy->por_rst); + + return 0; +} + +static const struct phy_ops ipq4019_usb_hs_phy_ops = { + .power_on = ipq4019_hs_phy_power_on, + .power_off = ipq4019_hs_phy_power_off, +}; + +static const struct of_device_id ipq4019_usb_phy_of_match[] = { + { .compatible = "qcom,usb-hs-ipq4019-phy", .data = &ipq4019_usb_hs_phy_ops}, + { .compatible = "qcom,usb-ss-ipq4019-phy", .data = &ipq4019_usb_ss_phy_ops}, + { }, +}; +MODULE_DEVICE_TABLE(of, ipq4019_usb_phy_of_match); + +static int ipq4019_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct ipq4019_usb_phy *phy; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = &pdev->dev; + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) { + dev_err(dev, "failed to remap register memory\n"); + return PTR_ERR(phy->base); + } + + phy->por_rst = devm_reset_control_get(phy->dev, "por_rst"); + if (IS_ERR(phy->por_rst)) { + if (PTR_ERR(phy->por_rst) != -EPROBE_DEFER) + dev_err(dev, "POR reset is missing\n"); + return PTR_ERR(phy->por_rst); + } + + phy->srif_rst = devm_reset_control_get_optional(phy->dev, "srif_rst"); + if (IS_ERR(phy->srif_rst)) + return PTR_ERR(phy->srif_rst); + + phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev)); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy->phy); + } + phy_set_drvdata(phy->phy, phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver ipq4019_usb_phy_driver = { + .probe = ipq4019_usb_phy_probe, + .driver = { + .of_match_table = ipq4019_usb_phy_of_match, + .name = "ipq4019-usb-phy", + } +}; +module_platform_driver(ipq4019_usb_phy_driver); + +MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver"); +MODULE_AUTHOR("John Crispin <john@phrozen.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c index 41a69f56b346..0fc2a1ed39b3 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c @@ -128,7 +128,6 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) { struct qcom_ipq806x_sata_phy *phy; struct device *dev = &pdev->dev; - struct resource *res; struct phy_provider *phy_provider; struct phy *generic_phy; int ret; @@ -137,8 +136,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) if (!phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->mmio = devm_ioremap_resource(dev, res); + phy->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c new file mode 100644 index 000000000000..7bacc527fbad --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/bitfield.h> + +/* USB QSCRATCH Hardware registers */ +#define QSCRATCH_GENERAL_CFG (0x08) +#define HSUSB_PHY_CTRL_REG (0x10) + +/* PHY_CTRL_REG */ +#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) +#define HSUSB_CTRL_USB2_SUSPEND BIT(23) +#define HSUSB_CTRL_UTMI_CLK_EN BIT(21) +#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) +#define HSUSB_CTRL_USE_CLKCORE BIT(18) +#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) +#define HSUSB_CTRL_COMMONONN BIT(11) +#define HSUSB_CTRL_ID_HV_CLAMP BIT(9) +#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) +#define HSUSB_CTRL_CLAMP_EN BIT(7) +#define HSUSB_CTRL_RETENABLEN BIT(1) +#define HSUSB_CTRL_POR BIT(0) + +/* QSCRATCH_GENERAL_CFG */ +#define HSUSB_GCFG_XHCI_REV BIT(2) + +/* USB QSCRATCH Hardware registers */ +#define SSUSB_PHY_CTRL_REG (0x00) +#define SSUSB_PHY_PARAM_CTRL_1 (0x04) +#define SSUSB_PHY_PARAM_CTRL_2 (0x08) +#define CR_PROTOCOL_DATA_IN_REG (0x0c) +#define CR_PROTOCOL_DATA_OUT_REG (0x10) +#define CR_PROTOCOL_CAP_ADDR_REG (0x14) +#define CR_PROTOCOL_CAP_DATA_REG (0x18) +#define CR_PROTOCOL_READ_REG (0x1c) +#define CR_PROTOCOL_WRITE_REG (0x20) + +/* PHY_CTRL_REG */ +#define SSUSB_CTRL_REF_USE_PAD BIT(28) +#define SSUSB_CTRL_TEST_POWERDOWN BIT(27) +#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) +#define SSUSB_CTRL_SS_PHY_EN BIT(8) +#define SSUSB_CTRL_SS_PHY_RESET BIT(7) + +/* SSPHY control registers - Does this need 0x30? */ +#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * (lane)) +#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * (lane)) + +/* SSPHY SoC version specific values */ +#define SSPHY_RX_EQ_VALUE 4 /* Override value for rx_eq */ +/* Override value for transmit preemphasis */ +#define SSPHY_TX_DEEMPH_3_5DB 23 +/* Override value for mpll */ +#define SSPHY_MPLL_VALUE 0 + +/* QSCRATCH PHY_PARAM_CTRL1 fields */ +#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK GENMASK(26, 19) +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK GENMASK(19, 13) +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK GENMASK(13, 7) +#define PHY_PARAM_CTRL1_LOS_BIAS_MASK GENMASK(7, 2) + +#define PHY_PARAM_CTRL1_MASK \ + (PHY_PARAM_CTRL1_TX_FULL_SWING_MASK | \ + PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK | \ + PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK | \ + PHY_PARAM_CTRL1_LOS_BIAS_MASK) + +#define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \ + FIELD_PREP(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK, (x)) +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \ + FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK, (x)) +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \ + FIELD_PREP(PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK, x) +#define PHY_PARAM_CTRL1_LOS_BIAS(x) \ + FIELD_PREP(PHY_PARAM_CTRL1_LOS_BIAS_MASK, (x)) + +/* RX OVRD IN HI bits */ +#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) +#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) +#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) +#define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7) +#define RX_OVRD_IN_HI_RX_EQ(x) FIELD_PREP(RX_OVRD_IN_HI_RX_EQ_MASK, (x)) +#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) +#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) +#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) +#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK GENMASK(4, 2) +#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) +#define RX_OVRD_IN_HI_RX_RATE_MASK GENMASK(2, 0) + +/* TX OVRD DRV LO register bits */ +#define TX_OVRD_DRV_LO_AMPLITUDE_MASK GENMASK(6, 0) +#define TX_OVRD_DRV_LO_PREEMPH_MASK GENMASK(13, 6) +#define TX_OVRD_DRV_LO_PREEMPH(x) ((x) << 7) +#define TX_OVRD_DRV_LO_EN BIT(14) + +/* MPLL bits */ +#define SSPHY_MPLL_MASK GENMASK(8, 5) +#define SSPHY_MPLL(x) ((x) << 5) + +/* SS CAP register bits */ +#define SS_CR_CAP_ADDR_REG BIT(0) +#define SS_CR_CAP_DATA_REG BIT(0) +#define SS_CR_READ_REG BIT(0) +#define SS_CR_WRITE_REG BIT(0) + +#define LATCH_SLEEP 40 +#define LATCH_TIMEOUT 100 + +struct usb_phy { + void __iomem *base; + struct device *dev; + struct clk *xo_clk; + struct clk *ref_clk; + u32 rx_eq; + u32 tx_deamp_3_5db; + u32 mpll; +}; + +struct phy_drvdata { + struct phy_ops ops; + u32 clk_rate; +}; + +/** + * usb_phy_write_readback() - Write register and read back masked value to + * confirm it is written + * + * @phy_dwc3: QCOM DWC3 phy context + * @offset: register offset. + * @mask: register bitmask specifying what should be updated + * @val: value to write. + */ +static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3, + u32 offset, + const u32 mask, u32 val) +{ + u32 write_val, tmp = readl(phy_dwc3->base + offset); + + tmp &= ~mask; /* retain other bits */ + write_val = tmp | val; + + writel(write_val, phy_dwc3->base + offset); + + /* Read back to see if val was written */ + tmp = readl(phy_dwc3->base + offset); + tmp &= mask; /* clear other bits */ + + if (tmp != val) + dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", val, offset); +} + +static int wait_for_latch(void __iomem *addr) +{ + u32 val; + + return readl_poll_timeout(addr, val, !val, LATCH_SLEEP, LATCH_TIMEOUT); +} + +/** + * usb_ss_write_phycreg() - Write SSPHY register + * + * @phy_dwc3: QCOM DWC3 phy context + * @addr: SSPHY address to write. + * @val: value to write. + */ +static int usb_ss_write_phycreg(struct usb_phy *phy_dwc3, + u32 addr, u32 val) +{ + int ret; + + writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_ADDR_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + if (ret) + goto err_wait; + + writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_DATA_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + if (ret) + goto err_wait; + + writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + +err_wait: + if (ret) + dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); + return ret; +} + +/** + * usb_ss_read_phycreg() - Read SSPHY register. + * + * @phy_dwc3: QCOM DWC3 phy context + * @addr: SSPHY address to read. + * @val: pointer in which read is store. + */ +static int usb_ss_read_phycreg(struct usb_phy *phy_dwc3, + u32 addr, u32 *val) +{ + int ret; + + writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_ADDR_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + if (ret) + goto err_wait; + + /* + * Due to hardware bug, first read of SSPHY register might be + * incorrect. Hence as workaround, SW should perform SSPHY register + * read twice, but use only second read and ignore first read. + */ + writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); + if (ret) + goto err_wait; + + /* throwaway read */ + readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); + + writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); + if (ret) + goto err_wait; + + *val = readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); + +err_wait: + return ret; +} + +static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + int ret; + u32 val; + + ret = clk_prepare_enable(phy_dwc3->xo_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy_dwc3->ref_clk); + if (ret) { + clk_disable_unprepare(phy_dwc3->xo_clk); + return ret; + } + + /* + * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel + * enable clamping, and disable RETENTION (power-on default is ENABLED) + */ + val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | + HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | + HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | + HSUSB_CTRL_UTMI_OTG_VBUS_VALID | HSUSB_CTRL_UTMI_CLK_EN | + HSUSB_CTRL_CLAMP_EN | 0x70; + + /* use core clock if external reference is not present */ + if (!phy_dwc3->xo_clk) + val |= HSUSB_CTRL_USE_CLKCORE; + + writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); + usleep_range(2000, 2200); + + /* Disable (bypass) VBUS and ID filters */ + writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); + + return 0; +} + +static int qcom_ipq806x_usb_hs_phy_exit(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + + clk_disable_unprepare(phy_dwc3->ref_clk); + clk_disable_unprepare(phy_dwc3->xo_clk); + + return 0; +} + +static int qcom_ipq806x_usb_ss_phy_init(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + int ret; + u32 data; + + ret = clk_prepare_enable(phy_dwc3->xo_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy_dwc3->ref_clk); + if (ret) { + clk_disable_unprepare(phy_dwc3->xo_clk); + return ret; + } + + /* reset phy */ + data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); + writel(data | SSUSB_CTRL_SS_PHY_RESET, + phy_dwc3->base + SSUSB_PHY_CTRL_REG); + usleep_range(2000, 2200); + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* clear REF_PAD if we don't have XO clk */ + if (!phy_dwc3->xo_clk) + data &= ~SSUSB_CTRL_REF_USE_PAD; + else + data |= SSUSB_CTRL_REF_USE_PAD; + + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* wait for ref clk to become stable, this can take up to 30ms */ + msleep(30); + + data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* + * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates + * in HS mode instead of SS mode. Workaround it by asserting + * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode + */ + ret = usb_ss_read_phycreg(phy_dwc3, 0x102D, &data); + if (ret) + goto err_phy_trans; + + data |= (1 << 7); + ret = usb_ss_write_phycreg(phy_dwc3, 0x102D, data); + if (ret) + goto err_phy_trans; + + ret = usb_ss_read_phycreg(phy_dwc3, 0x1010, &data); + if (ret) + goto err_phy_trans; + + data &= ~0xff0; + data |= 0x20; + ret = usb_ss_write_phycreg(phy_dwc3, 0x1010, data); + if (ret) + goto err_phy_trans; + + /* + * Fix RX Equalization setting as follows + * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 + * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 + * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version + * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 + */ + ret = usb_ss_read_phycreg(phy_dwc3, SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); + if (ret) + goto err_phy_trans; + + data &= ~RX_OVRD_IN_HI_RX_EQ_EN; + data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; + data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; + data |= RX_OVRD_IN_HI_RX_EQ(phy_dwc3->rx_eq); + data |= RX_OVRD_IN_HI_RX_EQ_OVRD; + ret = usb_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_RX_OVRD_IN_HI(0), data); + if (ret) + goto err_phy_trans; + + /* + * Set EQ and TX launch amplitudes as follows + * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version + * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110 + * LANE0.TX_OVRD_DRV_LO.EN set to 1. + */ + ret = usb_ss_read_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); + if (ret) + goto err_phy_trans; + + data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; + data |= TX_OVRD_DRV_LO_PREEMPH(phy_dwc3->tx_deamp_3_5db); + data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; + data |= 0x6E; + data |= TX_OVRD_DRV_LO_EN; + ret = usb_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); + if (ret) + goto err_phy_trans; + + data = 0; + data &= ~SSPHY_MPLL_MASK; + data |= SSPHY_MPLL(phy_dwc3->mpll); + usb_ss_write_phycreg(phy_dwc3, 0x30, data); + + /* + * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows + * TX_FULL_SWING [26:20] amplitude to 110 + * TX_DEEMPH_6DB [19:14] to 32 + * TX_DEEMPH_3_5DB [13:8] set based on SoC version + * LOS_BIAS [7:3] to 9 + */ + data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1); + + data &= ~PHY_PARAM_CTRL1_MASK; + + data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) | + PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) | + PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) | + PHY_PARAM_CTRL1_LOS_BIAS(0x9); + + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, + PHY_PARAM_CTRL1_MASK, data); + +err_phy_trans: + return ret; +} + +static int qcom_ipq806x_usb_ss_phy_exit(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + + /* Sequence to put SSPHY in low power state: + * 1. Clear REF_PHY_EN in PHY_CTRL_REG + * 2. Clear REF_USE_PAD in PHY_CTRL_REG + * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention + */ + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_SS_PHY_EN, 0x0); + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_REF_USE_PAD, 0x0); + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_TEST_POWERDOWN, 0x0); + + clk_disable_unprepare(phy_dwc3->ref_clk); + clk_disable_unprepare(phy_dwc3->xo_clk); + + return 0; +} + +static const struct phy_drvdata qcom_ipq806x_usb_hs_drvdata = { + .ops = { + .init = qcom_ipq806x_usb_hs_phy_init, + .exit = qcom_ipq806x_usb_hs_phy_exit, + .owner = THIS_MODULE, + }, + .clk_rate = 60000000, +}; + +static const struct phy_drvdata qcom_ipq806x_usb_ss_drvdata = { + .ops = { + .init = qcom_ipq806x_usb_ss_phy_init, + .exit = qcom_ipq806x_usb_ss_phy_exit, + .owner = THIS_MODULE, + }, + .clk_rate = 125000000, +}; + +static const struct of_device_id qcom_ipq806x_usb_phy_table[] = { + { .compatible = "qcom,ipq806x-usb-phy-hs", + .data = &qcom_ipq806x_usb_hs_drvdata }, + { .compatible = "qcom,ipq806x-usb-phy-ss", + .data = &qcom_ipq806x_usb_ss_drvdata }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_ipq806x_usb_phy_table); + +static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + resource_size_t size; + struct phy *generic_phy; + struct usb_phy *phy_dwc3; + const struct phy_drvdata *data; + struct phy_provider *phy_provider; + + phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); + if (!phy_dwc3) + return -ENOMEM; + + data = of_device_get_match_data(&pdev->dev); + + phy_dwc3->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + size = resource_size(res); + phy_dwc3->base = devm_ioremap(phy_dwc3->dev, res->start, size); + + if (!phy_dwc3->base) { + dev_err(phy_dwc3->dev, "failed to map reg\n"); + return -ENOMEM; + } + + phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); + if (IS_ERR(phy_dwc3->ref_clk)) { + dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); + return PTR_ERR(phy_dwc3->ref_clk); + } + + clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); + + phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); + if (IS_ERR(phy_dwc3->xo_clk)) { + dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); + phy_dwc3->xo_clk = NULL; + } + + /* Parse device node to probe HSIO settings */ + if (device_property_read_u32(&pdev->dev, "qcom,rx-eq", + &phy_dwc3->rx_eq)) + phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE; + + if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db", + &phy_dwc3->tx_deamp_3_5db)) + phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB; + + if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll)) + phy_dwc3->mpll = SSPHY_MPLL_VALUE; + + generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, &data->ops); + + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, phy_dwc3); + platform_set_drvdata(pdev, phy_dwc3); + + phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, + of_phy_simple_xlate); + + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static struct platform_driver qcom_ipq806x_usb_phy_driver = { + .probe = qcom_ipq806x_usb_phy_probe, + .driver = { + .name = "qcom-ipq806x-usb-phy", + .of_match_table = qcom_ipq806x_usb_phy_table, + }, +}; + +module_platform_driver(qcom_ipq806x_usb_phy_driver); + +MODULE_ALIAS("platform:phy-qcom-ipq806x-usb"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); +MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); +MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); diff --git a/drivers/phy/qualcomm/phy-qcom-pcie2.c b/drivers/phy/qualcomm/phy-qcom-pcie2.c index 9dba3594e6d9..5407e59bb185 100644 --- a/drivers/phy/qualcomm/phy-qcom-pcie2.c +++ b/drivers/phy/qualcomm/phy-qcom-pcie2.c @@ -250,7 +250,6 @@ static int qcom_pcie2_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct qcom_phy *qphy; - struct resource *res; struct device *dev = &pdev->dev; struct phy *phy; int ret; @@ -260,9 +259,7 @@ static int qcom_pcie2_phy_probe(struct platform_device *pdev) return -ENOMEM; qphy->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - qphy->base = devm_ioremap_resource(dev, res); + qphy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(qphy->base)) return PTR_ERR(qphy->base); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c new file mode 100644 index 000000000000..ba9d761ec49a --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -0,0 +1,2928 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +#include "phy-qcom-qmp.h" + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) + +/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ +/* DP PHY soft reset */ +#define SW_DPPHY_RESET BIT(0) +/* mux to select DP PHY reset control, 0:HW control, 1: software reset */ +#define SW_DPPHY_RESET_MUX BIT(1) +/* USB3 PHY soft reset */ +#define SW_USB3PHY_RESET BIT(2) +/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */ +#define SW_USB3PHY_RESET_MUX BIT(3) + +/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */ +#define USB3_MODE BIT(0) /* enables USB3 mode */ +#define DP_MODE BIT(1) /* enables DP mode */ + +/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */ +#define ARCVR_DTCT_EN BIT(0) +#define ALFPS_DTCT_EN BIT(1) +#define ARCVR_DTCT_EVENT_SEL BIT(4) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */ +#define IRQ_CLEAR BIT(0) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */ +#define RCVR_DETECT BIT(0) + +/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ +#define CLAMP_EN BIT(0) /* enables i/o clamp_n */ + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 +#define POWER_DOWN_DELAY_US_MIN 10 +#define POWER_DOWN_DELAY_US_MAX 11 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * register part of layout ? + * if yes, then offset gives index in the reg-layout + */ + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_L(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_STATUS, + QPHY_PCS_AUTONOMOUS_MODE_CTRL, + QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, + QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE +}; + +static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x174, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc, + [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, +}; + +static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x008, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x014, +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), +}; + +static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), +}; + +static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_rbr[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x6f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00), +}; + +static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00), +}; + +static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr2[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x8c), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00), +}; + +static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr3[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x2a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v3_dp_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRANSCEIVER_BIAS_EN, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_VMODE_CTRL1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_INTERFACE_SELECT, 0x3d), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_CLKBUF_ENABLE, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RESET_TSYNC_EN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRAN_DRVR_EMP_EN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_INTERFACE_MODE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_BAND, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_POL_INV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_DRV_LVL, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_EMP_POST1_LVL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = { + /* FLL settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x1f), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_rbr[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x6f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr2[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x8c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr3[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x2a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_VMODE_CTRL1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_INTERFACE_SELECT, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_CLKBUF_ENABLE, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RESET_TSYNC_EN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_INTERFACE_MODE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_BAND, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_POL_INV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_DRV_LVL, 0x2a), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_EMP_POST1_LVL, 0x20), +}; + +static const struct qmp_phy_init_tbl qmp_v5_dp_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x1f), +}; + +static const struct qmp_phy_init_tbl qmp_v5_5nm_dp_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_3, 0x51), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_VMODE_CTRL1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_PRE_STALL_LDO_BOOST_EN, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_INTERFACE_SELECT, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_CLKBUF_ENABLE, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RESET_TSYNC_EN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TRAN_DRVR_EMP_EN, 0xf), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TX_BAND, 0x01), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb43dp_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xfd), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0xfd), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MSB_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MSB_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE0, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORE_CLK_EN, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x76), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE0, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE1, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAXVAL2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SVS_MODE_CLK_SEL, 0x0a), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb43dp_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_2, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_LANE_MODE_3, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX, 0x0a), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb43dp_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B0, 0xd2), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B1, 0xd2), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B2, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B3, 0x21), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B5, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B6, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B7, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B0, 0x6b), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B1, 0x63), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B2, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B3, 0x23), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B4, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B5, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B6, 0x8e), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_MODE_RATE2_B7, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_IVCM_CAL_CODE_OVERRIDE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_IVCM_CAL_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_SUMMER_CAL_SPD_MODE, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_PI_CONTROLS, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_RX_IVCM_POSTCAL_OFFSET, 0x7c), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_VGA_CAL_CNTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_VGA_CAL_MAN_VAL, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_DFE_DAC_ENABLE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_DFE_3, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_GM_CAL, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE2, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_5NM_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x3f), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb43dp_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_CONFIG, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +/* list of regulators */ +struct qmp_regulator_data { + const char *name; + unsigned int enable_load; +}; + +static struct qmp_regulator_data qmp_phy_vreg_l[] = { + { .name = "vdda-phy", .enable_load = 21800 }, + { .name = "vdda-pll", .enable_load = 36000 }, +}; + +static const u8 qmp_dp_v3_pre_emphasis_hbr3_hbr2[4][4] = { + { 0x00, 0x0c, 0x15, 0x1a }, + { 0x02, 0x0e, 0x16, 0xff }, + { 0x02, 0x11, 0xff, 0xff }, + { 0x04, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v3_voltage_swing_hbr3_hbr2[4][4] = { + { 0x02, 0x12, 0x16, 0x1a }, + { 0x09, 0x19, 0x1f, 0xff }, + { 0x10, 0x1f, 0xff, 0xff }, + { 0x1f, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v3_pre_emphasis_hbr_rbr[4][4] = { + { 0x00, 0x0c, 0x14, 0x19 }, + { 0x00, 0x0b, 0x12, 0xff }, + { 0x00, 0x0b, 0xff, 0xff }, + { 0x04, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v3_voltage_swing_hbr_rbr[4][4] = { + { 0x08, 0x0f, 0x16, 0x1f }, + { 0x11, 0x1e, 0x1f, 0xff }, + { 0x19, 0x1f, 0xff, 0xff }, + { 0x1f, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v5_pre_emphasis_hbr3_hbr2[4][4] = { + { 0x20, 0x2c, 0x35, 0x3b }, + { 0x22, 0x2e, 0x36, 0xff }, + { 0x22, 0x31, 0xff, 0xff }, + { 0x24, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v5_voltage_swing_hbr3_hbr2[4][4] = { + { 0x22, 0x32, 0x36, 0x3a }, + { 0x29, 0x39, 0x3f, 0xff }, + { 0x30, 0x3f, 0xff, 0xff }, + { 0x3f, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v5_pre_emphasis_hbr_rbr[4][4] = { + { 0x20, 0x2d, 0x34, 0x3a }, + { 0x20, 0x2e, 0x35, 0xff }, + { 0x20, 0x2e, 0xff, 0xff }, + { 0x24, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v5_voltage_swing_hbr_rbr[4][4] = { + { 0x28, 0x2f, 0x36, 0x3f }, + { 0x31, 0x3e, 0x3f, 0xff }, + { 0x36, 0x3f, 0xff, 0xff }, + { 0x3f, 0xff, 0xff, 0xff } +}; + +struct qmp_phy; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + /* phy-type - PCIE/UFS/USB */ + unsigned int type; + int lanes; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_usb_tbl; + int pcs_usb_tbl_num; + + /* Init sequence for DP PHY block link rates */ + const struct qmp_phy_init_tbl *serdes_tbl_rbr; + int serdes_tbl_rbr_num; + const struct qmp_phy_init_tbl *serdes_tbl_hbr; + int serdes_tbl_hbr_num; + const struct qmp_phy_init_tbl *serdes_tbl_hbr2; + int serdes_tbl_hbr2_num; + const struct qmp_phy_init_tbl *serdes_tbl_hbr3; + int serdes_tbl_hbr3_num; + + /* DP PHY swing and pre_emphasis tables */ + const u8 (*swing_hbr_rbr)[4][4]; + const u8 (*swing_hbr3_hbr2)[4][4]; + const u8 (*pre_emphasis_hbr_rbr)[4][4]; + const u8 (*pre_emphasis_hbr3_hbr2)[4][4]; + + /* DP PHY callbacks */ + int (*configure_dp_phy)(struct qmp_phy *qphy); + void (*configure_dp_tx)(struct qmp_phy *qphy); + int (*calibrate_dp_phy)(struct qmp_phy *qphy); + void (*dp_aux_init)(struct qmp_phy *qphy); + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* resets to be requested */ + const char * const *reset_list; + int num_resets; + /* regulators to be requested */ + const struct qmp_regulator_data *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + unsigned int start_ctrl; + unsigned int pwrdn_ctrl; + /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */ + unsigned int phy_status; + + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; + /* power_down delay in usec */ + int pwrdn_delay_min; + int pwrdn_delay_max; + + /* Offset from PCS to PCS_USB region */ + unsigned int pcs_usb_offset; + +}; + +struct qmp_phy_combo_cfg { + const struct qmp_phy_cfg *usb_cfg; + const struct qmp_phy_cfg *dp_cfg; +}; + +/** + * struct qmp_phy - per-lane phy descriptor + * + * @phy: generic phy + * @cfg: phy specific configuration + * @serdes: iomapped memory space for phy's serdes (i.e. PLL) + * @tx: iomapped memory space for lane's tx + * @rx: iomapped memory space for lane's rx + * @pcs: iomapped memory space for lane's pcs + * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs) + * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs) + * @pcs_misc: iomapped memory space for lane's pcs_misc + * @pcs_usb: iomapped memory space for lane's pcs_usb + * @pipe_clk: pipe clock + * @qmp: QMP phy to which this lane belongs + * @mode: current PHY mode + * @dp_aux_cfg: Display port aux config + * @dp_opts: Display port optional config + * @dp_clks: Display port clocks + */ +struct qmp_phy { + struct phy *phy; + const struct qmp_phy_cfg *cfg; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *pcs; + void __iomem *tx2; + void __iomem *rx2; + void __iomem *pcs_misc; + void __iomem *pcs_usb; + struct clk *pipe_clk; + struct qcom_qmp *qmp; + enum phy_mode mode; + unsigned int dp_aux_cfg; + struct phy_configure_opts_dp dp_opts; + struct qmp_phy_dp_clks *dp_clks; +}; + +struct qmp_phy_dp_clks { + struct qmp_phy *qphy; + struct clk_hw dp_link_hw; + struct clk_hw dp_pixel_hw; +}; + +/** + * struct qcom_qmp - structure holding QMP phy block attributes + * + * @dev: device + * @dp_com: iomapped memory space for phy's dp_com control block + * + * @clks: array of clocks required by phy + * @resets: array of resets required by phy + * @vregs: regulator supplies bulk data + * + * @phys: array of per-lane phy descriptors + * @phy_mutex: mutex lock for PHY common block initialization + * @init_count: phy common block initialization count + * @ufs_reset: optional UFS PHY reset handle + */ +struct qcom_qmp { + struct device *dev; + void __iomem *dp_com; + + struct clk_bulk_data *clks; + struct reset_control_bulk_data *resets; + struct regulator_bulk_data *vregs; + + struct qmp_phy **phys; + + struct mutex phy_mutex; + int init_count; + + struct reset_control *ufs_reset; +}; + +static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy); +static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy); +static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy); +static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy); + +static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy); +static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy); +static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy); +static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy); + +static int qcom_qmp_v5_phy_configure_dp_phy(struct qmp_phy *qphy); + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const qmp_v3_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", "com_aux", +}; + +static const char * const qmp_v4_phy_clk_l[] = { + "aux", "ref_clk_src", "ref", "com_aux", +}; + +/* the primary usb3 phy on sm8250 doesn't have a ref clock */ +static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { + "aux", "ref_clk_src", "com_aux" +}; + +/* list of resets */ +static const char * const msm8996_usb3phy_reset_l[] = { + "phy", "common", +}; + +static const char * const sc7180_usb3phy_reset_l[] = { + "phy", +}; + +static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .lanes = 2, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = sc7180_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sc7180_dpphy_cfg = { + .type = PHY_TYPE_DP, + .lanes = 2, + + .serdes_tbl = qmp_v3_dp_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl), + .tx_tbl = qmp_v3_dp_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v3_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v3_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v3_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v3_dp_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v3_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v3_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v3_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v3_pre_emphasis_hbr3_hbr2, + + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = sc7180_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .dp_aux_init = qcom_qmp_v3_phy_dp_aux_init, + .configure_dp_tx = qcom_qmp_v3_phy_configure_dp_tx, + .configure_dp_phy = qcom_qmp_v3_phy_configure_dp_phy, + .calibrate_dp_phy = qcom_qmp_v3_dp_phy_calibrate, +}; + +static const struct qmp_phy_combo_cfg sc7180_usb3dpphy_cfg = { + .usb_cfg = &sc7180_usb3phy_cfg, + .dp_cfg = &sc7180_dpphy_cfg, +}; + +static const struct qmp_phy_cfg sdm845_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .lanes = 2, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_combo_cfg sdm845_usb3dpphy_cfg = { + .usb_cfg = &sdm845_usb3phy_cfg, + .dp_cfg = &sc7180_dpphy_cfg, +}; + +static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8150_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl), + .rx_tbl = sm8150_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl), + .pcs_tbl = sm8150_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl), + .pcs_usb_tbl = sm8150_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sc8180x_dpphy_cfg = { + .type = PHY_TYPE_DP, + .lanes = 2, + + .serdes_tbl = qmp_v4_dp_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl), + .tx_tbl = qmp_v4_dp_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v4_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v3_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v3_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v3_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v3_pre_emphasis_hbr3_hbr2, + + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = sc7180_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .dp_aux_init = qcom_qmp_v4_phy_dp_aux_init, + .configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx, + .configure_dp_phy = qcom_qmp_v4_phy_configure_dp_phy, + .calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate, +}; + +static const struct qmp_phy_combo_cfg sc8180x_usb3dpphy_cfg = { + .usb_cfg = &sm8150_usb3phy_cfg, + .dp_cfg = &sc8180x_dpphy_cfg, +}; + +static const struct qmp_phy_cfg sc8280xp_usb43dp_usb_cfg = { + .type = PHY_TYPE_USB3, + .lanes = 2, + + .serdes_tbl = sc8280xp_usb43dp_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_serdes_tbl), + .tx_tbl = sc8280xp_usb43dp_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_tx_tbl), + .rx_tbl = sc8280xp_usb43dp_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_rx_tbl), + .pcs_tbl = sc8280xp_usb43dp_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sc8280xp_usb43dp_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sc8280xp_usb43dp_dp_cfg = { + .type = PHY_TYPE_DP, + .lanes = 2, + + .serdes_tbl = qmp_v5_dp_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v5_dp_serdes_tbl), + .tx_tbl = qmp_v5_5nm_dp_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v5_5nm_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v5_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v5_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2, + + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + + .dp_aux_init = qcom_qmp_v4_phy_dp_aux_init, + .configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx, + .configure_dp_phy = qcom_qmp_v5_phy_configure_dp_phy, + .calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate, +}; + +static const struct qmp_phy_combo_cfg sc8280xp_usb43dpphy_combo_cfg = { + .usb_cfg = &sc8280xp_usb43dp_usb_cfg, + .dp_cfg = &sc8280xp_usb43dp_dp_cfg, +}; + +static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sm8250_dpphy_cfg = { + .type = PHY_TYPE_DP, + .lanes = 2, + + .serdes_tbl = qmp_v4_dp_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl), + .tx_tbl = qmp_v4_dp_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v4_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v4_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v4_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v4_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v4_dp_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v3_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v3_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v3_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v3_pre_emphasis_hbr3_hbr2, + + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + + .dp_aux_init = qcom_qmp_v4_phy_dp_aux_init, + .configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx, + .configure_dp_phy = qcom_qmp_v4_phy_configure_dp_phy, + .calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate, +}; + +static const struct qmp_phy_combo_cfg sm8250_usb3dpphy_cfg = { + .usb_cfg = &sm8250_usb3phy_cfg, + .dp_cfg = &sm8250_dpphy_cfg, +}; + +static void qmp_combo_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + if (t->in_layout) + writel(t->val, base + regs[t->offset]); + else + writel(t->val, base + t->offset); + } +} + +static void qmp_combo_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qmp_combo_configure_lane(base, regs, tbl, num, 0xff); +} + +static int qmp_combo_serdes_init(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; + int serdes_tbl_num = cfg->serdes_tbl_num; + + qmp_combo_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num); + + if (cfg->type == PHY_TYPE_DP) { + switch (dp_opts->link_rate) { + case 1620: + qmp_combo_configure(serdes, cfg->regs, + cfg->serdes_tbl_rbr, + cfg->serdes_tbl_rbr_num); + break; + case 2700: + qmp_combo_configure(serdes, cfg->regs, + cfg->serdes_tbl_hbr, + cfg->serdes_tbl_hbr_num); + break; + case 5400: + qmp_combo_configure(serdes, cfg->regs, + cfg->serdes_tbl_hbr2, + cfg->serdes_tbl_hbr2_num); + break; + case 8100: + qmp_combo_configure(serdes, cfg->regs, + cfg->serdes_tbl_hbr3, + cfg->serdes_tbl_hbr3_num); + break; + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + } + + return 0; +} + +static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy) +{ + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + qphy->pcs + QSERDES_DP_PHY_PD_CTL); + + /* Turn on BIAS current for PHY/PLL */ + writel(QSERDES_V3_COM_BIAS_EN | QSERDES_V3_COM_BIAS_EN_MUX | + QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL, + qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); + + writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL); + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | + DP_PHY_PD_CTL_LANE_2_3_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN | + DP_PHY_PD_CTL_DP_CLAMP_EN, + qphy->pcs + QSERDES_DP_PHY_PD_CTL); + + writel(QSERDES_V3_COM_BIAS_EN | + QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_R_EN | + QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL | + QSERDES_V3_COM_CLKBUF_RX_DRIVE_L, + qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); + + writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0); + writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + writel(0x24, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); + writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3); + writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4); + writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5); + writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6); + writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7); + writel(0xbb, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8); + writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9); + qphy->dp_aux_cfg = 0; + + writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | + PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | + PHY_AUX_REQ_ERR_MASK, + qphy->pcs + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK); +} + +static int qmp_combo_configure_dp_swing(struct qmp_phy *qphy, + unsigned int drv_lvl_reg, unsigned int emp_post_reg) +{ + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + const struct qmp_phy_cfg *cfg = qphy->cfg; + unsigned int v_level = 0, p_level = 0; + u8 voltage_swing_cfg, pre_emphasis_cfg; + int i; + + for (i = 0; i < dp_opts->lanes; i++) { + v_level = max(v_level, dp_opts->voltage[i]); + p_level = max(p_level, dp_opts->pre[i]); + } + + if (dp_opts->link_rate <= 2700) { + voltage_swing_cfg = (*cfg->swing_hbr_rbr)[v_level][p_level]; + pre_emphasis_cfg = (*cfg->pre_emphasis_hbr_rbr)[v_level][p_level]; + } else { + voltage_swing_cfg = (*cfg->swing_hbr3_hbr2)[v_level][p_level]; + pre_emphasis_cfg = (*cfg->pre_emphasis_hbr3_hbr2)[v_level][p_level]; + } + + /* TODO: Move check to config check */ + if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF) + return -EINVAL; + + /* Enable MUX to use Cursor values from these registers */ + voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN; + pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN; + + writel(voltage_swing_cfg, qphy->tx + drv_lvl_reg); + writel(pre_emphasis_cfg, qphy->tx + emp_post_reg); + writel(voltage_swing_cfg, qphy->tx2 + drv_lvl_reg); + writel(pre_emphasis_cfg, qphy->tx2 + emp_post_reg); + + return 0; +} + +static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy) +{ + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + u32 bias_en, drvr_en; + + if (qmp_combo_configure_dp_swing(qphy, QSERDES_V3_TX_TX_DRV_LVL, + QSERDES_V3_TX_TX_EMP_POST1_LVL) < 0) + return; + + if (dp_opts->lanes == 1) { + bias_en = 0x3e; + drvr_en = 0x13; + } else { + bias_en = 0x3f; + drvr_en = 0x10; + } + + writel(drvr_en, qphy->tx + QSERDES_V3_TX_HIGHZ_DRVR_EN); + writel(bias_en, qphy->tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); + writel(drvr_en, qphy->tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN); + writel(bias_en, qphy->tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); +} + +static bool qmp_combo_configure_dp_mode(struct qmp_phy *qphy) +{ + u32 val; + bool reverse = false; + + val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN; + + /* + * TODO: Assume orientation is CC1 for now and two lanes, need to + * use type-c connector to understand orientation and lanes. + * + * Otherwise val changes to be like below if this code understood + * the orientation of the type-c cable. + * + * if (lane_cnt == 4 || orientation == ORIENTATION_CC2) + * val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN; + * if (lane_cnt == 4 || orientation == ORIENTATION_CC1) + * val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; + * if (orientation == ORIENTATION_CC2) + * writel(0x4c, qphy->pcs + QSERDES_V3_DP_PHY_MODE); + */ + val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; + writel(val, qphy->pcs + QSERDES_DP_PHY_PD_CTL); + + writel(0x5c, qphy->pcs + QSERDES_DP_PHY_MODE); + + return reverse; +} + +static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy) +{ + const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks; + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + u32 phy_vco_div, status; + unsigned long pixel_freq; + + qmp_combo_configure_dp_mode(qphy); + + writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL); + writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL); + + switch (dp_opts->link_rate) { + case 1620: + phy_vco_div = 0x1; + pixel_freq = 1620000000UL / 2; + break; + case 2700: + phy_vco_div = 0x1; + pixel_freq = 2700000000UL / 2; + break; + case 5400: + phy_vco_div = 0x2; + pixel_freq = 5400000000UL / 4; + break; + case 8100: + phy_vco_div = 0x0; + pixel_freq = 8100000000UL / 6; + break; + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + writel(phy_vco_div, qphy->pcs + QSERDES_V3_DP_PHY_VCO_DIV); + + clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000); + clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq); + + writel(0x04, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); + writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); + writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG); + writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); + writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG); + + writel(0x20, qphy->serdes + QSERDES_V3_COM_RESETSM_CNTRL); + + if (readl_poll_timeout(qphy->serdes + QSERDES_V3_COM_C_READY_STATUS, + status, + ((status & BIT(0)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + + if (readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS, + status, + ((status & BIT(1)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG); + udelay(2000); + writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + + return readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS, + status, + ((status & BIT(1)) > 0), + 500, + 10000); +} + +/* + * We need to calibrate the aux setting here as many times + * as the caller tries + */ +static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy) +{ + static const u8 cfg1_settings[] = { 0x13, 0x23, 0x1d }; + u8 val; + + qphy->dp_aux_cfg++; + qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings); + val = cfg1_settings[qphy->dp_aux_cfg]; + + writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + + return 0; +} + +static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy) +{ + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + qphy->pcs + QSERDES_DP_PHY_PD_CTL); + + /* Turn on BIAS current for PHY/PLL */ + writel(0x17, qphy->serdes + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + + writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0); + writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); + writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3); + writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4); + writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5); + writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6); + writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7); + writel(0xb7, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8); + writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9); + qphy->dp_aux_cfg = 0; + + writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | + PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | + PHY_AUX_REQ_ERR_MASK, + qphy->pcs + QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK); +} + +static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy) +{ + /* Program default values before writing proper values */ + writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL); + writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL); + + writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); + writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); + + qmp_combo_configure_dp_swing(qphy, QSERDES_V4_TX_TX_DRV_LVL, + QSERDES_V4_TX_TX_EMP_POST1_LVL); +} + +static int qcom_qmp_v45_phy_configure_dp_phy(struct qmp_phy *qphy) +{ + const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks; + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + u32 phy_vco_div, status; + unsigned long pixel_freq; + + writel(0x0f, qphy->pcs + QSERDES_V4_DP_PHY_CFG_1); + + qmp_combo_configure_dp_mode(qphy); + + writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); + + writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL); + writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL); + + switch (dp_opts->link_rate) { + case 1620: + phy_vco_div = 0x1; + pixel_freq = 1620000000UL / 2; + break; + case 2700: + phy_vco_div = 0x1; + pixel_freq = 2700000000UL / 2; + break; + case 5400: + phy_vco_div = 0x2; + pixel_freq = 5400000000UL / 4; + break; + case 8100: + phy_vco_div = 0x0; + pixel_freq = 8100000000UL / 6; + break; + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + writel(phy_vco_div, qphy->pcs + QSERDES_V4_DP_PHY_VCO_DIV); + + clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000); + clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq); + + writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); + writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG); + writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); + writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG); + + writel(0x20, qphy->serdes + QSERDES_V4_COM_RESETSM_CNTRL); + + if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_C_READY_STATUS, + status, + ((status & BIT(0)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS, + status, + ((status & BIT(0)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS, + status, + ((status & BIT(1)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + + if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, + status, + ((status & BIT(0)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, + status, + ((status & BIT(1)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + return 0; +} + +static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy) +{ + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + u32 bias0_en, drvr0_en, bias1_en, drvr1_en; + bool reverse = false; + u32 status; + int ret; + + ret = qcom_qmp_v45_phy_configure_dp_phy(qphy); + if (ret < 0) + return ret; + + /* + * At least for 7nm DP PHY this has to be done after enabling link + * clock. + */ + + if (dp_opts->lanes == 1) { + bias0_en = reverse ? 0x3e : 0x15; + bias1_en = reverse ? 0x15 : 0x3e; + drvr0_en = reverse ? 0x13 : 0x10; + drvr1_en = reverse ? 0x10 : 0x13; + } else if (dp_opts->lanes == 2) { + bias0_en = reverse ? 0x3f : 0x15; + bias1_en = reverse ? 0x15 : 0x3f; + drvr0_en = 0x10; + drvr1_en = 0x10; + } else { + bias0_en = 0x3f; + bias1_en = 0x3f; + drvr0_en = 0x10; + drvr1_en = 0x10; + } + + writel(drvr0_en, qphy->tx + QSERDES_V4_TX_HIGHZ_DRVR_EN); + writel(bias0_en, qphy->tx + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); + writel(drvr1_en, qphy->tx2 + QSERDES_V4_TX_HIGHZ_DRVR_EN); + writel(bias1_en, qphy->tx2 + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); + + writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG); + udelay(2000); + writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + + if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, + status, + ((status & BIT(1)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + writel(0x0a, qphy->tx + QSERDES_V4_TX_TX_POL_INV); + writel(0x0a, qphy->tx2 + QSERDES_V4_TX_TX_POL_INV); + + writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL); + writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL); + + writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); + writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); + + return 0; +} + +static int qcom_qmp_v5_phy_configure_dp_phy(struct qmp_phy *qphy) +{ + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + u32 bias0_en, drvr0_en, bias1_en, drvr1_en; + bool reverse = false; + u32 status; + int ret; + + ret = qcom_qmp_v45_phy_configure_dp_phy(qphy); + if (ret < 0) + return ret; + + if (dp_opts->lanes == 1) { + bias0_en = reverse ? 0x3e : 0x1a; + drvr0_en = reverse ? 0x13 : 0x10; + bias1_en = reverse ? 0x15 : 0x3e; + drvr1_en = reverse ? 0x10 : 0x13; + } else if (dp_opts->lanes == 2) { + bias0_en = reverse ? 0x3f : 0x15; + drvr0_en = 0x10; + bias1_en = reverse ? 0x15 : 0x3f; + drvr1_en = 0x10; + } else { + bias0_en = 0x3f; + bias1_en = 0x3f; + drvr0_en = 0x10; + drvr1_en = 0x10; + } + + writel(drvr0_en, qphy->tx + QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN); + writel(bias0_en, qphy->tx + QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN); + writel(drvr1_en, qphy->tx2 + QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN); + writel(bias1_en, qphy->tx2 + QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN); + + writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG); + udelay(2000); + writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + + if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, + status, + ((status & BIT(1)) > 0), + 500, + 10000)) + return -ETIMEDOUT; + + writel(0x0a, qphy->tx + QSERDES_V5_5NM_TX_TX_POL_INV); + writel(0x0a, qphy->tx2 + QSERDES_V5_5NM_TX_TX_POL_INV); + + writel(0x27, qphy->tx + QSERDES_V5_5NM_TX_TX_DRV_LVL); + writel(0x27, qphy->tx2 + QSERDES_V5_5NM_TX_TX_DRV_LVL); + + writel(0x20, qphy->tx + QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL); + writel(0x20, qphy->tx2 + QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL); + + return 0; +} + +/* + * We need to calibrate the aux setting here as many times + * as the caller tries + */ +static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy) +{ + static const u8 cfg1_settings[] = { 0x20, 0x13, 0x23, 0x1d }; + u8 val; + + qphy->dp_aux_cfg++; + qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings); + val = cfg1_settings[qphy->dp_aux_cfg]; + + writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + + return 0; +} + +static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + const struct phy_configure_opts_dp *dp_opts = &opts->dp; + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts)); + if (qphy->dp_opts.set_voltages) { + cfg->configure_dp_tx(qphy); + qphy->dp_opts.set_voltages = 0; + } + + return 0; +} + +static int qcom_qmp_dp_phy_calibrate(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + if (cfg->calibrate_dp_phy) + return cfg->calibrate_dp_phy(qphy); + + return 0; +} + +static int qmp_combo_com_init(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs = qphy->pcs; + void __iomem *dp_com = qmp->dp_com; + int ret; + + mutex_lock(&qmp->phy_mutex); + if (qmp->init_count++) { + mutex_unlock(&qmp->phy_mutex); + return 0; + } + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + goto err_unlock; + } + + ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset assert failed\n"); + goto err_disable_regulators; + } + + ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset deassert failed\n"); + goto err_disable_regulators; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + goto err_assert_reset; + + qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN); + + /* override hardware control for reset of qmp phy */ + qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + /* Default type-c orientation, i.e CC1 */ + qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02); + + qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE); + + /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ + qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); + qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); + + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) + qphy_setbits(pcs, + cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + else + qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + + mutex_unlock(&qmp->phy_mutex); + + return 0; + +err_assert_reset: + reset_control_bulk_assert(cfg->num_resets, qmp->resets); +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); +err_unlock: + mutex_unlock(&qmp->phy_mutex); + + return ret; +} + +static int qmp_combo_com_exit(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + + mutex_lock(&qmp->phy_mutex); + if (--qmp->init_count) { + mutex_unlock(&qmp->phy_mutex); + return 0; + } + + reset_control_assert(qmp->ufs_reset); + + reset_control_bulk_assert(cfg->num_resets, qmp->resets); + + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + mutex_unlock(&qmp->phy_mutex); + + return 0; +} + +static int qmp_combo_init(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + int ret; + dev_vdbg(qmp->dev, "Initializing QMP phy\n"); + + ret = qmp_combo_com_init(qphy); + if (ret) + return ret; + + if (cfg->type == PHY_TYPE_DP) + cfg->dp_aux_init(qphy); + + return 0; +} + +static int qmp_combo_power_on(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *tx = qphy->tx; + void __iomem *rx = qphy->rx; + void __iomem *pcs = qphy->pcs; + void __iomem *status; + unsigned int mask, val, ready; + int ret; + + qmp_combo_serdes_init(qphy); + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + return ret; + } + + /* Tx, Rx, and PCS configurations */ + qmp_combo_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_combo_configure_lane(qphy->tx2, cfg->regs, cfg->tx_tbl, + cfg->tx_tbl_num, 2); + } + + /* Configure special DP tx tunings */ + if (cfg->type == PHY_TYPE_DP) + cfg->configure_dp_tx(qphy); + + qmp_combo_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_combo_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl, + cfg->rx_tbl_num, 2); + } + + /* Configure link rate, swing, etc. */ + if (cfg->type == PHY_TYPE_DP) + cfg->configure_dp_phy(qphy); + else + qmp_combo_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + ret = reset_control_deassert(qmp->ufs_reset); + if (ret) + goto err_disable_pipe_clk; + + if (cfg->has_pwrdn_delay) + usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); + + if (cfg->type != PHY_TYPE_DP) { + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + status = pcs + cfg->regs[QPHY_PCS_STATUS]; + mask = cfg->phy_status; + ready = 0; + + ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + goto err_disable_pipe_clk; + } + } + return 0; + +err_disable_pipe_clk: + clk_disable_unprepare(qphy->pipe_clk); + + return ret; +} + +static int qmp_combo_power_off(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + clk_disable_unprepare(qphy->pipe_clk); + + if (cfg->type == PHY_TYPE_DP) { + /* Assert DP PHY power down */ + writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL); + } else { + /* PHY reset */ + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Put PHY into POWER DOWN state: active low */ + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) { + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } else { + qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } + } + + return 0; +} + +static int qmp_combo_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + + qmp_combo_com_exit(qphy); + + return 0; +} + +static int qmp_combo_enable(struct phy *phy) +{ + int ret; + + ret = qmp_combo_init(phy); + if (ret) + return ret; + + ret = qmp_combo_power_on(phy); + if (ret) + qmp_combo_exit(phy); + + return ret; +} + +static int qmp_combo_disable(struct phy *phy) +{ + int ret; + + ret = qmp_combo_power_off(phy); + if (ret) + return ret; + return qmp_combo_exit(phy); +} + +static int qmp_combo_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + + qphy->mode = mode; + + return 0; +} + +static void qmp_combo_enable_autonomous_mode(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + u32 intr_mask; + + if (qphy->mode == PHY_MODE_USB_HOST_SS || + qphy->mode == PHY_MODE_USB_DEVICE_SS) + intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; + else + intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; + + /* Clear any pending interrupts status */ + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL); + + /* Enable required PHY autonomous mode interrupts */ + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask); + + /* Enable i/o clamp_n for autonomous mode */ + if (pcs_misc) + qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); +} + +static void qmp_combo_disable_autonomous_mode(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + + /* Disable i/o clamp_n on resume for normal mode */ + if (pcs_misc) + qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); + + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN); + + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); +} + +static int __maybe_unused qmp_combo_runtime_suspend(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct qmp_phy *qphy = qmp->phys[0]; + const struct qmp_phy_cfg *cfg = qphy->cfg; + + dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qphy->mode); + + /* Supported only for USB3 PHY and luckily USB3 is the first phy */ + if (cfg->type != PHY_TYPE_USB3) + return 0; + + if (!qmp->init_count) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + qmp_combo_enable_autonomous_mode(qphy); + + clk_disable_unprepare(qphy->pipe_clk); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + return 0; +} + +static int __maybe_unused qmp_combo_runtime_resume(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct qmp_phy *qphy = qmp->phys[0]; + const struct qmp_phy_cfg *cfg = qphy->cfg; + int ret = 0; + + dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qphy->mode); + + /* Supported only for USB3 PHY and luckily USB3 is the first phy */ + if (cfg->type != PHY_TYPE_USB3) + return 0; + + if (!qmp->init_count) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + return ret; + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + return ret; + } + + qmp_combo_disable_autonomous_mode(qphy); + + return 0; +} + +static int qmp_combo_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_vregs; + int ret, i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = cfg->vreg_list[i].name; + + ret = devm_regulator_bulk_get(dev, num, qmp->vregs); + if (ret) { + dev_err(dev, "failed at devm_regulator_bulk_get\n"); + return ret; + } + + for (i = 0; i < num; i++) { + ret = regulator_set_load(qmp->vregs[i].consumer, + cfg->vreg_list[i].enable_load); + if (ret) { + dev_err(dev, "failed to set load at %s\n", + qmp->vregs[i].supply); + return ret; + } + } + + return 0; +} + +static int qmp_combo_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int i; + int ret; + + qmp->resets = devm_kcalloc(dev, cfg->num_resets, + sizeof(*qmp->resets), GFP_KERNEL); + if (!qmp->resets) + return -ENOMEM; + + for (i = 0; i < cfg->num_resets; i++) + qmp->resets[i].id = cfg->reset_list[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get resets\n"); + + return 0; +} + +static int qmp_combo_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_clks; + int i; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + return devm_clk_bulk_get(dev, num, qmp->clks); +} + +static void phy_clk_release_provider(void *res) +{ + of_clk_del_provider(res); +} + +/* + * Register a fixed rate pipe clock. + * + * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate + * controls it. The <s>_pipe_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the <s>_pipe_clksrc here. The gcc driver takes care + * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) +{ + struct clk_fixed_rate *fixed; + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string(np, "clock-output-names", &init.name); + if (ret) { + dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); + return ret; + } + + fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return -ENOMEM; + + init.ops = &clk_fixed_rate_ops; + + /* controllers using QMP phys use 125MHz pipe clock interface */ + fixed->fixed_rate = 125000000; + fixed->hw.init = &init; + + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); +} + +/* + * Display Port PLL driver block diagram for branch clocks + * + * +------------------------------+ + * | DP_VCO_CLK | + * | | + * | +-------------------+ | + * | | (DP PLL/VCO) | | + * | +---------+---------+ | + * | v | + * | +----------+-----------+ | + * | | hsclk_divsel_clk_src | | + * | +----------+-----------+ | + * +------------------------------+ + * | + * +---------<---------v------------>----------+ + * | | + * +--------v----------------+ | + * | dp_phy_pll_link_clk | | + * | link_clk | | + * +--------+----------------+ | + * | | + * | | + * v v + * Input to DISPCC block | + * for link clk, crypto clk | + * and interface clock | + * | + * | + * +--------<------------+-----------------+---<---+ + * | | | + * +----v---------+ +--------v-----+ +--------v------+ + * | vco_divided | | vco_divided | | vco_divided | + * | _clk_src | | _clk_src | | _clk_src | + * | | | | | | + * |divsel_six | | divsel_two | | divsel_four | + * +-------+------+ +-----+--------+ +--------+------+ + * | | | + * v---->----------v-------------<------v + * | + * +----------+-----------------+ + * | dp_phy_pll_vco_div_clk | + * +---------+------------------+ + * | + * v + * Input to DISPCC block + * for DP pixel clock + * + */ +static int qcom_qmp_dp_pixel_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + switch (req->rate) { + case 1620000000UL / 2: + case 2700000000UL / 2: + /* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */ + return 0; + default: + return -EINVAL; + } +} + +static unsigned long +qcom_qmp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + const struct qmp_phy_dp_clks *dp_clks; + const struct qmp_phy *qphy; + const struct phy_configure_opts_dp *dp_opts; + + dp_clks = container_of(hw, struct qmp_phy_dp_clks, dp_pixel_hw); + qphy = dp_clks->qphy; + dp_opts = &qphy->dp_opts; + + switch (dp_opts->link_rate) { + case 1620: + return 1620000000UL / 2; + case 2700: + return 2700000000UL / 2; + case 5400: + return 5400000000UL / 4; + case 8100: + return 8100000000UL / 6; + default: + return 0; + } +} + +static const struct clk_ops qcom_qmp_dp_pixel_clk_ops = { + .determine_rate = qcom_qmp_dp_pixel_clk_determine_rate, + .recalc_rate = qcom_qmp_dp_pixel_clk_recalc_rate, +}; + +static int qcom_qmp_dp_link_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + switch (req->rate) { + case 162000000: + case 270000000: + case 540000000: + case 810000000: + return 0; + default: + return -EINVAL; + } +} + +static unsigned long +qcom_qmp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + const struct qmp_phy_dp_clks *dp_clks; + const struct qmp_phy *qphy; + const struct phy_configure_opts_dp *dp_opts; + + dp_clks = container_of(hw, struct qmp_phy_dp_clks, dp_link_hw); + qphy = dp_clks->qphy; + dp_opts = &qphy->dp_opts; + + switch (dp_opts->link_rate) { + case 1620: + case 2700: + case 5400: + case 8100: + return dp_opts->link_rate * 100000; + default: + return 0; + } +} + +static const struct clk_ops qcom_qmp_dp_link_clk_ops = { + .determine_rate = qcom_qmp_dp_link_clk_determine_rate, + .recalc_rate = qcom_qmp_dp_link_clk_recalc_rate, +}; + +static struct clk_hw * +qcom_qmp_dp_clks_hw_get(struct of_phandle_args *clkspec, void *data) +{ + struct qmp_phy_dp_clks *dp_clks = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= 2) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + if (idx == 0) + return &dp_clks->dp_link_hw; + + return &dp_clks->dp_pixel_hw; +} + +static int phy_dp_clks_register(struct qcom_qmp *qmp, struct qmp_phy *qphy, + struct device_node *np) +{ + struct clk_init_data init = { }; + struct qmp_phy_dp_clks *dp_clks; + char name[64]; + int ret; + + dp_clks = devm_kzalloc(qmp->dev, sizeof(*dp_clks), GFP_KERNEL); + if (!dp_clks) + return -ENOMEM; + + dp_clks->qphy = qphy; + qphy->dp_clks = dp_clks; + + snprintf(name, sizeof(name), "%s::link_clk", dev_name(qmp->dev)); + init.ops = &qcom_qmp_dp_link_clk_ops; + init.name = name; + dp_clks->dp_link_hw.init = &init; + ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_link_hw); + if (ret) + return ret; + + snprintf(name, sizeof(name), "%s::vco_div_clk", dev_name(qmp->dev)); + init.ops = &qcom_qmp_dp_pixel_clk_ops; + init.name = name; + dp_clks->dp_pixel_hw.init = &init; + ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_pixel_hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, qcom_qmp_dp_clks_hw_get, dp_clks); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); +} + +static const struct phy_ops qmp_combo_usb_ops = { + .init = qmp_combo_enable, + .exit = qmp_combo_disable, + .set_mode = qmp_combo_set_mode, + .owner = THIS_MODULE, +}; + +static const struct phy_ops qmp_combo_dp_ops = { + .init = qmp_combo_init, + .configure = qcom_qmp_dp_phy_configure, + .power_on = qmp_combo_power_on, + .calibrate = qcom_qmp_dp_phy_calibrate, + .power_off = qmp_combo_power_off, + .exit = qmp_combo_exit, + .set_mode = qmp_combo_set_mode, + .owner = THIS_MODULE, +}; + +static int qmp_combo_create(struct device *dev, struct device_node *np, int id, + void __iomem *serdes, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct phy *generic_phy; + struct qmp_phy *qphy; + const struct phy_ops *ops; + int ret; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->cfg = cfg; + qphy->serdes = serdes; + /* + * Get memory resources for each phy lane: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 + * For single lane PHYs: pcs_misc (optional) -> 3. + */ + qphy->tx = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qphy->tx)) + return PTR_ERR(qphy->tx); + + qphy->rx = devm_of_iomap(dev, np, 1, NULL); + if (IS_ERR(qphy->rx)) + return PTR_ERR(qphy->rx); + + qphy->pcs = devm_of_iomap(dev, np, 2, NULL); + if (IS_ERR(qphy->pcs)) + return PTR_ERR(qphy->pcs); + + if (cfg->pcs_usb_offset) + qphy->pcs_usb = qphy->pcs + cfg->pcs_usb_offset; + + if (cfg->lanes >= 2) { + qphy->tx2 = devm_of_iomap(dev, np, 3, NULL); + if (IS_ERR(qphy->tx2)) + return PTR_ERR(qphy->tx2); + + qphy->rx2 = devm_of_iomap(dev, np, 4, NULL); + if (IS_ERR(qphy->rx2)) + return PTR_ERR(qphy->rx2); + + qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL); + } else { + qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL); + } + + if (IS_ERR(qphy->pcs_misc)) { + dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); + qphy->pcs_misc = NULL; + } + + /* + * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 + * based phys, so they essentially have pipe clock. So, + * we return error in case phy is USB3 or PIPE type. + * Otherwise, we initialize pipe clock to NULL for + * all phys that don't need this. + */ + qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL); + if (IS_ERR(qphy->pipe_clk)) { + if (cfg->type == PHY_TYPE_USB3) + return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk), + "failed to get lane%d pipe_clk\n", + id); + qphy->pipe_clk = NULL; + } + + if (cfg->type == PHY_TYPE_DP) + ops = &qmp_combo_dp_ops; + else + ops = &qmp_combo_usb_ops; + + generic_phy = devm_phy_create(dev, np, ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create qphy %d\n", ret); + return ret; + } + + qphy->phy = generic_phy; + qphy->qmp = qmp; + qmp->phys[id] = qphy; + phy_set_drvdata(generic_phy, qphy); + + return 0; +} + +static const struct of_device_id qmp_combo_of_match_table[] = { + { + .compatible = "qcom,sc7180-qmp-usb3-dp-phy", + .data = &sc7180_usb3dpphy_cfg, + }, + { + .compatible = "qcom,sdm845-qmp-usb3-dp-phy", + .data = &sdm845_usb3dpphy_cfg, + }, + { + .compatible = "qcom,sm8250-qmp-usb3-dp-phy", + .data = &sm8250_usb3dpphy_cfg, + }, + { + .compatible = "qcom,sc8180x-qmp-usb3-dp-phy", + .data = &sc8180x_usb3dpphy_cfg, + }, + { + .compatible = "qcom,sc8280xp-qmp-usb43dp-phy", + .data = &sc8280xp_usb43dpphy_combo_cfg, + }, + { } +}; +MODULE_DEVICE_TABLE(of, qmp_combo_of_match_table); + +static const struct dev_pm_ops qmp_combo_pm_ops = { + SET_RUNTIME_PM_OPS(qmp_combo_runtime_suspend, + qmp_combo_runtime_resume, NULL) +}; + +static int qmp_combo_probe(struct platform_device *pdev) +{ + struct qcom_qmp *qmp; + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy_provider *phy_provider; + void __iomem *serdes; + void __iomem *usb_serdes; + void __iomem *dp_serdes = NULL; + const struct qmp_phy_combo_cfg *combo_cfg = NULL; + const struct qmp_phy_cfg *cfg = NULL; + const struct qmp_phy_cfg *usb_cfg = NULL; + const struct qmp_phy_cfg *dp_cfg = NULL; + int num, id, expected_phys; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + dev_set_drvdata(dev, qmp); + + /* Get the specific init parameters of QMP phy */ + combo_cfg = of_device_get_match_data(dev); + if (!combo_cfg) + return -EINVAL; + + usb_cfg = combo_cfg->usb_cfg; + cfg = usb_cfg; /* Setup clks and regulators */ + + /* per PHY serdes; usually located at base address */ + usb_serdes = serdes = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + qmp->dp_com = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(qmp->dp_com)) + return PTR_ERR(qmp->dp_com); + + /* Only two serdes for combo PHY */ + dp_serdes = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(dp_serdes)) + return PTR_ERR(dp_serdes); + + dp_cfg = combo_cfg->dp_cfg; + expected_phys = 2; + + mutex_init(&qmp->phy_mutex); + + ret = qmp_combo_clk_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_combo_reset_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_combo_vreg_init(dev, cfg); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + num = of_get_available_child_count(dev->of_node); + /* do we have a rogue child node ? */ + if (num > expected_phys) + return -EINVAL; + + qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); + if (!qmp->phys) + return -ENOMEM; + + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + /* + * Prevent runtime pm from being ON by default. Users can enable + * it using power/control in sysfs. + */ + pm_runtime_forbid(dev); + + id = 0; + for_each_available_child_of_node(dev->of_node, child) { + if (of_node_name_eq(child, "dp-phy")) { + cfg = dp_cfg; + serdes = dp_serdes; + + /* Create per-lane phy */ + ret = qmp_combo_create(dev, child, id, serdes, cfg); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + goto err_node_put; + } + + ret = phy_dp_clks_register(qmp, qmp->phys[id], child); + if (ret) { + dev_err(qmp->dev, + "failed to register DP clock source\n"); + goto err_node_put; + } + } else if (of_node_name_eq(child, "usb3-phy")) { + cfg = usb_cfg; + serdes = usb_serdes; + + /* Create per-lane phy */ + ret = qmp_combo_create(dev, child, id, serdes, cfg); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + goto err_node_put; + } + + /* + * Register the pipe clock provided by phy. + * See function description to see details of this pipe clock. + */ + ret = phy_pipe_clk_register(qmp, child); + if (ret) { + dev_err(qmp->dev, + "failed to register pipe clock source\n"); + goto err_node_put; + } + } + + id++; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + of_node_put(child); + return ret; +} + +static struct platform_driver qmp_combo_driver = { + .probe = qmp_combo_probe, + .driver = { + .name = "qcom-qmp-combo-phy", + .pm = &qmp_combo_pm_ops, + .of_match_table = qmp_combo_of_match_table, + }, +}; + +module_platform_driver(qmp_combo_driver); + +MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); +MODULE_DESCRIPTION("Qualcomm QMP USB+DP combo PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c new file mode 100644 index 000000000000..461f0b5d464a --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +#include "phy-qcom-qmp.h" + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +#define REFCLK_DRV_DSBL BIT(1) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +#define PLL_READY_GATE_EN BIT(3) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) +#define PHYSTATUS_4_20 BIT(7) +/* QPHY_COM_PCS_READY_STATUS bit */ +#define PCS_READY BIT(0) + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 +#define POWER_DOWN_DELAY_US_MIN 10 +#define POWER_DOWN_DELAY_US_MAX 11 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * register part of layout ? + * if yes, then offset gives index in the reg-layout + */ + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_L(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* Common block control registers */ + QPHY_COM_SW_RESET, + QPHY_COM_POWER_DOWN_CONTROL, + QPHY_COM_START_CONTROL, + QPHY_COM_PCS_READY_STATUS, + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE +}; + +static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_COM_SW_RESET] = 0x400, + [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, + [QPHY_COM_START_CONTROL] = 0x408, + [QPHY_COM_PCS_READY_STATUS] = 0x448, + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x174, +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), + QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_IDLE_DTCT_CNTRL, 0x4c), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), + + QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME, 0x05), + + QMP_PHY_INIT_CFG(QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE, 0x05), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_DOWN_CONTROL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG4, 0x00), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG1, 0xa3), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0x0e), +}; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + /* number of PHYs provided by this block */ + int num_phys; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* resets to be requested */ + const char * const *reset_list; + int num_resets; + /* regulators to be requested */ + const char * const *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + unsigned int start_ctrl; + unsigned int pwrdn_ctrl; + unsigned int mask_com_pcs_ready; + /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */ + unsigned int phy_status; + + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; + /* power_down delay in usec */ + int pwrdn_delay_min; + int pwrdn_delay_max; +}; + +/** + * struct qmp_phy - per-lane phy descriptor + * + * @phy: generic phy + * @cfg: phy specific configuration + * @serdes: iomapped memory space for phy's serdes (i.e. PLL) + * @tx: iomapped memory space for lane's tx + * @rx: iomapped memory space for lane's rx + * @pcs: iomapped memory space for lane's pcs + * @pipe_clk: pipe clock + * @index: lane index + * @qmp: QMP phy to which this lane belongs + * @lane_rst: lane's reset controller + */ +struct qmp_phy { + struct phy *phy; + const struct qmp_phy_cfg *cfg; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *pcs; + struct clk *pipe_clk; + unsigned int index; + struct qcom_qmp *qmp; + struct reset_control *lane_rst; +}; + +/** + * struct qcom_qmp - structure holding QMP phy block attributes + * + * @dev: device + * + * @clks: array of clocks required by phy + * @resets: array of resets required by phy + * @vregs: regulator supplies bulk data + * + * @phys: array of per-lane phy descriptors + * @phy_mutex: mutex lock for PHY common block initialization + * @init_count: phy common block initialization count + */ +struct qcom_qmp { + struct device *dev; + + struct clk_bulk_data *clks; + struct reset_control_bulk_data *resets; + struct regulator_bulk_data *vregs; + + struct qmp_phy **phys; + + struct mutex phy_mutex; + int init_count; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const msm8996_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", +}; + +/* list of resets */ +static const char * const msm8996_pciephy_reset_l[] = { + "phy", "common", "cfg", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy", "vdda-pll", +}; + +static const struct qmp_phy_cfg msm8996_pciephy_cfg = { + .num_phys = 3, + + .serdes_tbl = msm8996_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), + .tx_tbl = msm8996_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), + .rx_tbl = msm8996_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), + .pcs_tbl = msm8996_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_pciephy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_regs_layout, + + .start_ctrl = PCS_START | PLL_READY_GATE_EN, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .mask_com_pcs_ready = PCS_READY, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static void qmp_pcie_msm8996_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + if (t->in_layout) + writel(t->val, base + regs[t->offset]); + else + writel(t->val, base + t->offset); + } +} + +static void qmp_pcie_msm8996_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qmp_pcie_msm8996_configure_lane(base, regs, tbl, num, 0xff); +} + +static int qmp_pcie_msm8996_serdes_init(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; + int serdes_tbl_num = cfg->serdes_tbl_num; + void __iomem *status; + unsigned int mask, val; + int ret; + + qmp_pcie_msm8996_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num); + + qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); + qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], + SERDES_START | PCS_START); + + status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; + mask = cfg->mask_com_pcs_ready; + + ret = readl_poll_timeout(status, val, (val & mask), 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, + "phy common block init timed-out\n"); + return ret; + } + + return 0; +} + +static int qmp_pcie_msm8996_com_init(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + int ret; + + mutex_lock(&qmp->phy_mutex); + if (qmp->init_count++) { + mutex_unlock(&qmp->phy_mutex); + return 0; + } + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + goto err_unlock; + } + + ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset assert failed\n"); + goto err_disable_regulators; + } + + ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset deassert failed\n"); + goto err_disable_regulators; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + goto err_assert_reset; + + qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], + SW_PWRDN); + + mutex_unlock(&qmp->phy_mutex); + + return 0; + +err_assert_reset: + reset_control_bulk_assert(cfg->num_resets, qmp->resets); +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); +err_unlock: + mutex_unlock(&qmp->phy_mutex); + + return ret; +} + +static int qmp_pcie_msm8996_com_exit(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + + mutex_lock(&qmp->phy_mutex); + if (--qmp->init_count) { + mutex_unlock(&qmp->phy_mutex); + return 0; + } + + qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], + SERDES_START | PCS_START); + qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], + SW_RESET); + qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], + SW_PWRDN); + + reset_control_bulk_assert(cfg->num_resets, qmp->resets); + + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + mutex_unlock(&qmp->phy_mutex); + + return 0; +} + +static int qmp_pcie_msm8996_init(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + int ret; + dev_vdbg(qmp->dev, "Initializing QMP phy\n"); + + ret = qmp_pcie_msm8996_com_init(qphy); + if (ret) + return ret; + + return 0; +} + +static int qmp_pcie_msm8996_power_on(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *tx = qphy->tx; + void __iomem *rx = qphy->rx; + void __iomem *pcs = qphy->pcs; + void __iomem *status; + unsigned int mask, val, ready; + int ret; + + qmp_pcie_msm8996_serdes_init(qphy); + + ret = reset_control_deassert(qphy->lane_rst); + if (ret) { + dev_err(qmp->dev, "lane%d reset deassert failed\n", + qphy->index); + return ret; + } + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + goto err_reset_lane; + } + + /* Tx, Rx, and PCS configurations */ + qmp_pcie_msm8996_configure_lane(tx, cfg->regs, cfg->tx_tbl, + cfg->tx_tbl_num, 1); + + qmp_pcie_msm8996_configure_lane(rx, cfg->regs, cfg->rx_tbl, + cfg->rx_tbl_num, 1); + + qmp_pcie_msm8996_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + /* + * Pull out PHY from POWER DOWN state. + * This is active low enable signal to power-down PHY. + */ + qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); + + if (cfg->has_pwrdn_delay) + usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); + + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + status = pcs + cfg->regs[QPHY_PCS_STATUS]; + mask = cfg->phy_status; + ready = 0; + + ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + goto err_disable_pipe_clk; + } + + return 0; + +err_disable_pipe_clk: + clk_disable_unprepare(qphy->pipe_clk); +err_reset_lane: + reset_control_assert(qphy->lane_rst); + + return ret; +} + +static int qmp_pcie_msm8996_power_off(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + clk_disable_unprepare(qphy->pipe_clk); + + /* PHY reset */ + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Put PHY into POWER DOWN state: active low */ + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) { + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } else { + qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } + + return 0; +} + +static int qmp_pcie_msm8996_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + + reset_control_assert(qphy->lane_rst); + + qmp_pcie_msm8996_com_exit(qphy); + + return 0; +} + +static int qmp_pcie_msm8996_enable(struct phy *phy) +{ + int ret; + + ret = qmp_pcie_msm8996_init(phy); + if (ret) + return ret; + + ret = qmp_pcie_msm8996_power_on(phy); + if (ret) + qmp_pcie_msm8996_exit(phy); + + return ret; +} + +static int qmp_pcie_msm8996_disable(struct phy *phy) +{ + int ret; + + ret = qmp_pcie_msm8996_power_off(phy); + if (ret) + return ret; + return qmp_pcie_msm8996_exit(phy); +} + +static int qmp_pcie_msm8996_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_vregs; + int i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, num, qmp->vregs); +} + +static int qmp_pcie_msm8996_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int i; + int ret; + + qmp->resets = devm_kcalloc(dev, cfg->num_resets, + sizeof(*qmp->resets), GFP_KERNEL); + if (!qmp->resets) + return -ENOMEM; + + for (i = 0; i < cfg->num_resets; i++) + qmp->resets[i].id = cfg->reset_list[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get resets\n"); + + return 0; +} + +static int qmp_pcie_msm8996_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_clks; + int i; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + return devm_clk_bulk_get(dev, num, qmp->clks); +} + +static void phy_clk_release_provider(void *res) +{ + of_clk_del_provider(res); +} + +/* + * Register a fixed rate pipe clock. + * + * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate + * controls it. The <s>_pipe_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the <s>_pipe_clksrc here. The gcc driver takes care + * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) +{ + struct clk_fixed_rate *fixed; + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string(np, "clock-output-names", &init.name); + if (ret) { + dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); + return ret; + } + + fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return -ENOMEM; + + init.ops = &clk_fixed_rate_ops; + + /* controllers using QMP phys use 125MHz pipe clock interface */ + fixed->fixed_rate = 125000000; + fixed->hw.init = &init; + + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); +} + +static const struct phy_ops qmp_pcie_msm8996_ops = { + .power_on = qmp_pcie_msm8996_enable, + .power_off = qmp_pcie_msm8996_disable, + .owner = THIS_MODULE, +}; + +static void qcom_qmp_reset_control_put(void *data) +{ + reset_control_put(data); +} + +static int qmp_pcie_msm8996_create(struct device *dev, struct device_node *np, int id, + void __iomem *serdes, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct phy *generic_phy; + struct qmp_phy *qphy; + int ret; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->cfg = cfg; + qphy->serdes = serdes; + /* + * Get memory resources for each phy lane: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + */ + qphy->tx = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qphy->tx)) + return PTR_ERR(qphy->tx); + + qphy->rx = devm_of_iomap(dev, np, 1, NULL); + if (IS_ERR(qphy->rx)) + return PTR_ERR(qphy->rx); + + qphy->pcs = devm_of_iomap(dev, np, 2, NULL); + if (IS_ERR(qphy->pcs)) + return PTR_ERR(qphy->pcs); + + qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL); + if (IS_ERR(qphy->pipe_clk)) { + return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk), + "failed to get lane%d pipe clock\n", id); + } + + qphy->lane_rst = of_reset_control_get_exclusive_by_index(np, 0); + if (IS_ERR(qphy->lane_rst)) { + dev_err(dev, "failed to get lane%d reset\n", id); + return PTR_ERR(qphy->lane_rst); + } + ret = devm_add_action_or_reset(dev, qcom_qmp_reset_control_put, + qphy->lane_rst); + if (ret) + return ret; + + generic_phy = devm_phy_create(dev, np, &qmp_pcie_msm8996_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create qphy %d\n", ret); + return ret; + } + + qphy->phy = generic_phy; + qphy->index = id; + qphy->qmp = qmp; + qmp->phys[id] = qphy; + phy_set_drvdata(generic_phy, qphy); + + return 0; +} + +static const struct of_device_id qmp_pcie_msm8996_of_match_table[] = { + { + .compatible = "qcom,msm8996-qmp-pcie-phy", + .data = &msm8996_pciephy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qmp_pcie_msm8996_of_match_table); + +static int qmp_pcie_msm8996_probe(struct platform_device *pdev) +{ + struct qcom_qmp *qmp; + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy_provider *phy_provider; + void __iomem *serdes; + const struct qmp_phy_cfg *cfg = NULL; + int num, id, expected_phys; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + dev_set_drvdata(dev, qmp); + + /* Get the specific init parameters of QMP phy */ + cfg = of_device_get_match_data(dev); + if (!cfg) + return -EINVAL; + + /* per PHY serdes; usually located at base address */ + serdes = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + expected_phys = cfg->num_phys; + + mutex_init(&qmp->phy_mutex); + + ret = qmp_pcie_msm8996_clk_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_pcie_msm8996_reset_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_pcie_msm8996_vreg_init(dev, cfg); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + num = of_get_available_child_count(dev->of_node); + /* do we have a rogue child node ? */ + if (num > expected_phys) + return -EINVAL; + + qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); + if (!qmp->phys) + return -ENOMEM; + + id = 0; + for_each_available_child_of_node(dev->of_node, child) { + /* Create per-lane phy */ + ret = qmp_pcie_msm8996_create(dev, child, id, serdes, cfg); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + goto err_node_put; + } + + /* + * Register the pipe clock provided by phy. + * See function description to see details of this pipe clock. + */ + ret = phy_pipe_clk_register(qmp, child); + if (ret) { + dev_err(qmp->dev, + "failed to register pipe clock source\n"); + goto err_node_put; + } + + id++; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + of_node_put(child); + return ret; +} + +static struct platform_driver qmp_pcie_msm8996_driver = { + .probe = qmp_pcie_msm8996_probe, + .driver = { + .name = "qcom-qmp-msm8996-pcie-phy", + .of_match_table = qmp_pcie_msm8996_of_match_table, + }, +}; + +module_platform_driver(qmp_pcie_msm8996_driver); + +MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); +MODULE_DESCRIPTION("Qualcomm QMP MSM8996 PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h new file mode 100644 index 000000000000..e4a4d2cd85eb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-qhp.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCIE_QHP_H_ +#define QCOM_PHY_QMP_PCIE_QHP_H_ + +/* PCIE GEN3 COM registers */ +#define PCIE_GEN3_QHP_COM_SSC_EN_CENTER 0x14 +#define PCIE_GEN3_QHP_COM_SSC_PER1 0x20 +#define PCIE_GEN3_QHP_COM_SSC_PER2 0x24 +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1 0x28 +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2 0x2c +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1 0x34 +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1 0x38 +#define PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN 0x54 +#define PCIE_GEN3_QHP_COM_CLK_ENABLE1 0x58 +#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0 0x6c +#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0 0x70 +#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1 0x78 +#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1 0x7c +#define PCIE_GEN3_QHP_COM_BGV_TRIM 0x98 +#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE0 0xb4 +#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE1 0xb8 +#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0 0xc0 +#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1 0xc4 +#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0 0xcc +#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1 0xd0 +#define PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL 0xdc +#define PCIE_GEN3_QHP_COM_RESTRIM_CTRL2 0xf0 +#define PCIE_GEN3_QHP_COM_LOCK_CMP_EN 0xf8 +#define PCIE_GEN3_QHP_COM_DEC_START_MODE0 0x100 +#define PCIE_GEN3_QHP_COM_DEC_START_MODE1 0x108 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0 0x11c +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0 0x120 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0 0x124 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1 0x128 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1 0x12c +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1 0x130 +#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0 0x150 +#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1 0x158 +#define PCIE_GEN3_QHP_COM_VCO_TUNE_MAP 0x178 +#define PCIE_GEN3_QHP_COM_BG_CTRL 0x1c8 +#define PCIE_GEN3_QHP_COM_CLK_SELECT 0x1cc +#define PCIE_GEN3_QHP_COM_HSCLK_SEL1 0x1d0 +#define PCIE_GEN3_QHP_COM_CORECLK_DIV 0x1e0 +#define PCIE_GEN3_QHP_COM_CORE_CLK_EN 0x1e8 +#define PCIE_GEN3_QHP_COM_CMN_CONFIG 0x1f0 +#define PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL 0x1fc +#define PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1 0x21c +#define PCIE_GEN3_QHP_COM_CMN_MODE 0x224 +#define PCIE_GEN3_QHP_COM_VREGCLK_DIV1 0x228 +#define PCIE_GEN3_QHP_COM_VREGCLK_DIV2 0x22c + +/* PCIE GEN3 QHP Lane registers */ +#define PCIE_GEN3_QHP_L0_DRVR_CTRL0 0xc +#define PCIE_GEN3_QHP_L0_DRVR_CTRL1 0x10 +#define PCIE_GEN3_QHP_L0_DRVR_CTRL2 0x14 +#define PCIE_GEN3_QHP_L0_DRVR_TAP_EN 0x18 +#define PCIE_GEN3_QHP_L0_TX_BAND_MODE 0x60 +#define PCIE_GEN3_QHP_L0_LANE_MODE 0x64 +#define PCIE_GEN3_QHP_L0_PARALLEL_RATE 0x7c +#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE0 0xc0 +#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE1 0xc4 +#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE2 0xc8 +#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1 0xd0 +#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2 0xd4 +#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0 0xd8 +#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1 0xdc +#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2 0xe0 +#define PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE 0xfc +#define PCIE_GEN3_QHP_L0_CGA_THRESH_DFE 0x100 +#define PCIE_GEN3_QHP_L0_RXENGINE_EN0 0x108 +#define PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME 0x114 +#define PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME 0x118 +#define PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME 0x11c +#define PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME 0x120 +#define PCIE_GEN3_QHP_L0_VGA_GAIN 0x124 +#define PCIE_GEN3_QHP_L0_DFE_GAIN 0x128 +#define PCIE_GEN3_QHP_L0_EQ_GAIN 0x130 +#define PCIE_GEN3_QHP_L0_OFFSET_GAIN 0x134 +#define PCIE_GEN3_QHP_L0_PRE_GAIN 0x138 +#define PCIE_GEN3_QHP_L0_VGA_INITVAL 0x13c +#define PCIE_GEN3_QHP_L0_EQ_INTVAL 0x154 +#define PCIE_GEN3_QHP_L0_EDAC_INITVAL 0x160 +#define PCIE_GEN3_QHP_L0_RXEQ_INITB0 0x168 +#define PCIE_GEN3_QHP_L0_RXEQ_INITB1 0x16c +#define PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1 0x178 +#define PCIE_GEN3_QHP_L0_RXEQ_CTRL 0x180 +#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0 0x184 +#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1 0x188 +#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2 0x18c +#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0 0x190 +#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1 0x194 +#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2 0x198 +#define PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG 0x19c +#define PCIE_GEN3_QHP_L0_RX_BAND 0x1a4 +#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0 0x1c0 +#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1 0x1c4 +#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2 0x1c8 +#define PCIE_GEN3_QHP_L0_SIGDET_ENABLES 0x230 +#define PCIE_GEN3_QHP_L0_SIGDET_CNTRL 0x234 +#define PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL 0x238 +#define PCIE_GEN3_QHP_L0_DCC_GAIN 0x2a4 +#define PCIE_GEN3_QHP_L0_RSM_START 0x2a8 +#define PCIE_GEN3_QHP_L0_RX_EN_SIGNAL 0x2ac +#define PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL 0x2b0 +#define PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0 0x2b8 +#define PCIE_GEN3_QHP_L0_TS0_TIMER 0x2c0 +#define PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE 0x2c4 +#define PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET 0x2cc + +/* PCIE GEN3 PCS registers */ +#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB 0x2c +#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB 0x40 +#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB 0x54 +#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB 0x68 +#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG 0x15c +#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5 0x16c +#define PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG 0x174 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c new file mode 100644 index 000000000000..5be5348fbb26 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -0,0 +1,2408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +#include "phy-qcom-qmp.h" + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +#define REFCLK_DRV_DSBL BIT(1) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) +#define PHYSTATUS_4_20 BIT(7) + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * register part of layout ? + * if yes, then offset gives index in the reg-layout + */ + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_L(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* Common block control registers */ + QPHY_COM_SW_RESET, + QPHY_COM_POWER_DOWN_CONTROL, + QPHY_COM_START_CONTROL, + QPHY_COM_PCS_READY_STATUS, + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE +}; + +static const unsigned int ipq_pciephy_gen3_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, +}; + +static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_COM_SW_RESET] = 0x400, + [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, + [QPHY_COM_START_CONTROL] = 0x408, + [QPHY_COM_PCS_READY_STATUS] = 0x448, + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x174, +}; + +static const unsigned int sdm845_qmp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x174, +}; + +static const unsigned int sdm845_qhp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x2ac, +}; + +static const unsigned int sm8250_pcie_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, +}; + +static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15), +}; + +static const struct qmp_phy_init_tbl msm8998_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), +}; + +static const struct qmp_phy_init_tbl msm8998_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40), +}; + +static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x99), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE2_MODE0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE1_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE2_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x29), + QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x09), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x68), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x53), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x32), + QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x61), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL1, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x01), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x11), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0xf), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x1), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x0), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x6), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0xf), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x0), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x1), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0xa), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0xa), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xa), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x3), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0xD), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xD04), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x2), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0xb), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x1), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x1), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x1), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x2), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x0), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x6), + QMP_PHY_INIT_CFG(QSERDES_TX_RES_CODE_LANE_OFFSET, 0x2), + QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_TX_TX_EMP_POST1_LVL, 0x36), + QMP_PHY_INIT_CFG(QSERDES_TX_SLEW_CNTL, 0x0a), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x4), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE, 0x4), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_OSC_DTCT_ACTIONS, 0x0), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x40), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x0), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x40), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB, 0x0), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x40), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SIGDET_LVL, 0x99), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0xe), + QMP_PHY_INIT_CFG_L(QPHY_SW_RESET, 0x0), + QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x31), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x30), + QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x21), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE0, 0x355), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE0, 0x35555), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0x1a0a), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0xb), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0x40), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV, 0xa), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x32), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TIMER, 0xa), + QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x1), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE1, 0x2), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE1, 0x2aa), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE1, 0x2aaab), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0x3414), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE1, 0x0), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x0), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_EP_DIV_MODE0, 0x19), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_EP_DIV_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x06), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0xe), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x02), +}; + +static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNT_VAL_L, 0x9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNT_VAL_H_TOL, 0x42), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL1, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_H, 0x0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x1), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x11), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG2, 0xb), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG2, 0x52), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG2, 0x50), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG4, 0x1a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5, 0x6), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x007), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_01, 0x59), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xbb), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG4, 0x00), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2, 0x52), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2, 0x10), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4, 0x1a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5, 0x06), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1, 0x00), +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL, 0x27), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_ENABLE1, 0xb0), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0, 0x8c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0, 0x20), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_RESTRIM_CTRL2, 0x05), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VCO_TUNE_MAP, 0x10), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_SELECT, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_HSCLK_SEL1, 0x30), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORE_CLK_EN, 0x73), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL, 0x15), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV1, 0x22), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV2, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BGV_TRIM, 0x20), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BG_CTRL, 0x07), +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL0, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_TAP_EN, 0x0d), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TX_BAND_MODE, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_LANE_MODE, 0x1a), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PARALLEL_RATE, 0x2f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE1, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE2, 0x1b), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0, 0x31), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1, 0x31), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2, 0x03), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE, 0x02), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CGA_THRESH_DFE, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXENGINE_EN0, 0x12), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME, 0x25), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME, 0x05), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_GAIN, 0x26), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_GAIN, 0x12), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_GAIN, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_OFFSET_GAIN, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PRE_GAIN, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_INTVAL, 0x15), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EDAC_INITVAL, 0x28), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB0, 0x7f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB1, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_CTRL, 0x70), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0, 0x8b), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1, 0x08), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2, 0x0a), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0, 0x03), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_BAND, 0x02), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0, 0x5c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1, 0x3e), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_ENABLES, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_CNTRL, 0xa0), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL, 0x08), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DCC_GAIN, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_EN_SIGNAL, 0xc3), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0, 0xbc), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TS0_TIMER, 0x7f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE, 0x15), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL2, 0x0f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_INITVAL, 0x20), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RSM_START, 0x01), +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_rx_tbl[] = { +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG, 0x50), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB, 0x19), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB, 0x17), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5, 0x9f), +}; + +static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x90), +}; + +static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x5), +}; + +static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x6e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x39), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x39), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RCLK_AUXDATA_SEL, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x03), +}; + +static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RATE_SLEW_CNTRL1, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x01), +}; + +static const struct qmp_phy_init_tbl sc8180x_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x90), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x36), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RCLK_AUXDATA_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x30), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RATE_SLEW_CNTRL1, 0x0b), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x12), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P6_P7_PRE, 0x33), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x1_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG2, 0x0f), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x05), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG2, 0x0f), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_gen3x2_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x50), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MISC2, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MODE, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_DC_LEVEL_CTRL, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x56), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x22), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_3, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_VMODE_CTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_PI_QEC_CTRL, 0x00), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_AUX_DATA_TCOARSE_TFINE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_3, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_DAC_ENABLE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_DAC_ENABLE2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_VGA_CAL_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x27), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B1, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B2, 0x5a), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B4, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B0, 0xbd), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B1, 0xf9), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B3, 0xce), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B4, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B0, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B3, 0xcf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B4, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_PHPRE_CTRL, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_MARG_COARSE_CTRL2, 0x12), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG2, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG4, 0x16), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG5, 0x02), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_EQ_CONFIG1, 0x17), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G3_RXEQEVAL_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_RXEQEVAL_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG2, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_LANE1_INSIG_SW_CTRL2, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2, 0x00), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x04), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xd8), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa6), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH3, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH4, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_TX_ADAPT_POST_THRESH, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RATE_SLEW_CNTRL1, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_EQ_CONFIG2, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0xd0), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_DC_LEVEL_CTRL, 0x0f), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1, 0xcc), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3, 0xcc), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6, 0x29), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B0, 0xc5), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B1, 0xad), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B2, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B3, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B4, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B5, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B6, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B0, 0xc7), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B1, 0xef), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B3, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B4, 0x81), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B5, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B6, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3, 0x37), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_3, 0x05), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_MAN_VAL, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_IDAC_SAOFFSET, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_DAC_ENABLE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_GM_CAL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2, 0x1f), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG2, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG3, 0x22), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0x99), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_EQ_CONFIG1, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3, 0x28), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN, 0x2e), +}; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + int lanes; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *serdes_tbl_sec; + int serdes_tbl_num_sec; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl_sec; + int tx_tbl_num_sec; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl_sec; + int rx_tbl_num_sec; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl_sec; + int pcs_tbl_num_sec; + const struct qmp_phy_init_tbl *pcs_misc_tbl; + int pcs_misc_tbl_num; + const struct qmp_phy_init_tbl *pcs_misc_tbl_sec; + int pcs_misc_tbl_num_sec; + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* resets to be requested */ + const char * const *reset_list; + int num_resets; + /* regulators to be requested */ + const char * const *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + unsigned int start_ctrl; + unsigned int pwrdn_ctrl; + /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */ + unsigned int phy_status; + + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; + /* power_down delay in usec */ + int pwrdn_delay_min; + int pwrdn_delay_max; + + /* QMP PHY pipe clock interface rate */ + unsigned long pipe_clock_rate; +}; + +/** + * struct qmp_phy - per-lane phy descriptor + * + * @phy: generic phy + * @cfg: phy specific configuration + * @serdes: iomapped memory space for phy's serdes (i.e. PLL) + * @tx: iomapped memory space for lane's tx + * @rx: iomapped memory space for lane's rx + * @pcs: iomapped memory space for lane's pcs + * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs) + * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs) + * @pcs_misc: iomapped memory space for lane's pcs_misc + * @pipe_clk: pipe clock + * @qmp: QMP phy to which this lane belongs + */ +struct qmp_phy { + struct phy *phy; + const struct qmp_phy_cfg *cfg; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *pcs; + void __iomem *tx2; + void __iomem *rx2; + void __iomem *pcs_misc; + struct clk *pipe_clk; + struct qcom_qmp *qmp; +}; + +/** + * struct qcom_qmp - structure holding QMP phy block attributes + * + * @dev: device + * + * @clks: array of clocks required by phy + * @resets: array of resets required by phy + * @vregs: regulator supplies bulk data + * + * @phys: array of per-lane phy descriptors + */ +struct qcom_qmp { + struct device *dev; + + struct clk_bulk_data *clks; + struct reset_control_bulk_data *resets; + struct regulator_bulk_data *vregs; + + struct qmp_phy **phys; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const msm8996_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", +}; + + +static const char * const sdm845_pciephy_clk_l[] = { + "aux", "cfg_ahb", "ref", "refgen", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy", "vdda-pll", +}; + +static const char * const ipq8074_pciephy_clk_l[] = { + "aux", "cfg_ahb", +}; + +/* list of resets */ +static const char * const ipq8074_pciephy_reset_l[] = { + "phy", "common", +}; + +static const char * const sdm845_pciephy_reset_l[] = { + "phy", +}; + +static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = ipq8074_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_serdes_tbl), + .tx_tbl = ipq8074_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_tx_tbl), + .rx_tbl = ipq8074_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_rx_tbl), + .pcs_tbl = ipq8074_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_pcs_tbl), + .clk_list = ipq8074_pciephy_clk_l, + .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = NULL, + .num_vregs = 0, + .regs = pciephy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg ipq8074_pciephy_gen3_cfg = { + .lanes = 1, + + .serdes_tbl = ipq8074_pcie_gen3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_serdes_tbl), + .tx_tbl = ipq8074_pcie_gen3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_tx_tbl), + .rx_tbl = ipq8074_pcie_gen3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_rx_tbl), + .pcs_tbl = ipq8074_pcie_gen3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_gen3_pcs_tbl), + .clk_list = ipq8074_pciephy_clk_l, + .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = NULL, + .num_vregs = 0, + .regs = ipq_pciephy_gen3_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ + + .pipe_clock_rate = 250000000, +}; + +static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = ipq6018_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq6018_pcie_serdes_tbl), + .tx_tbl = ipq6018_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ipq6018_pcie_tx_tbl), + .rx_tbl = ipq6018_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq6018_pcie_rx_tbl), + .pcs_tbl = ipq6018_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq6018_pcie_pcs_tbl), + .pcs_misc_tbl = ipq6018_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(ipq6018_pcie_pcs_misc_tbl), + .clk_list = ipq8074_pciephy_clk_l, + .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = NULL, + .num_vregs = 0, + .regs = ipq_pciephy_gen3_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = sdm845_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_serdes_tbl), + .tx_tbl = sdm845_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_tx_tbl), + .rx_tbl = sdm845_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_rx_tbl), + .pcs_tbl = sdm845_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_pcs_tbl), + .pcs_misc_tbl = sdm845_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sdm845_qmp_pciephy_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = sdm845_qhp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_serdes_tbl), + .tx_tbl = sdm845_qhp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_tx_tbl), + .rx_tbl = sdm845_qhp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_rx_tbl), + .pcs_tbl = sdm845_qhp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_pcs_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sdm845_qhp_pciephy_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8250_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), + .serdes_tbl_sec = sm8250_qmp_gen3x1_pcie_serdes_tbl, + .serdes_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_serdes_tbl), + .tx_tbl = sm8250_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_tx_tbl), + .rx_tbl = sm8250_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_rx_tbl), + .rx_tbl_sec = sm8250_qmp_gen3x1_pcie_rx_tbl, + .rx_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_rx_tbl), + .pcs_tbl = sm8250_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_tbl), + .pcs_tbl_sec = sm8250_qmp_gen3x1_pcie_pcs_tbl, + .pcs_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_pcs_tbl), + .pcs_misc_tbl = sm8250_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_misc_tbl), + .pcs_misc_tbl_sec = sm8250_qmp_gen3x1_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sm8250_qmp_gen3x2_pciephy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8250_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), + .tx_tbl = sm8250_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_tx_tbl), + .tx_tbl_sec = sm8250_qmp_gen3x2_pcie_tx_tbl, + .tx_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_tx_tbl), + .rx_tbl = sm8250_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_rx_tbl), + .rx_tbl_sec = sm8250_qmp_gen3x2_pcie_rx_tbl, + .rx_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_rx_tbl), + .pcs_tbl = sm8250_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_tbl), + .pcs_tbl_sec = sm8250_qmp_gen3x2_pcie_pcs_tbl, + .pcs_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_pcs_tbl), + .pcs_misc_tbl = sm8250_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_misc_tbl), + .pcs_misc_tbl_sec = sm8250_qmp_gen3x2_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num_sec = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg msm8998_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = msm8998_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl), + .tx_tbl = msm8998_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8998_pcie_tx_tbl), + .rx_tbl = msm8998_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8998_pcie_rx_tbl), + .pcs_tbl = msm8998_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg sc8180x_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = sc8180x_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_serdes_tbl), + .tx_tbl = sc8180x_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_tx_tbl), + .rx_tbl = sc8180x_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_rx_tbl), + .pcs_tbl = sc8180x_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_pcs_tbl), + .pcs_misc_tbl = sc8180x_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sdx55_qmp_pciephy_cfg = { + .lanes = 2, + + .serdes_tbl = sdx55_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_serdes_tbl), + .tx_tbl = sdx55_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_tx_tbl), + .rx_tbl = sdx55_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_rx_tbl), + .pcs_tbl = sdx55_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_pcs_tbl), + .pcs_misc_tbl = sdx55_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS_4_20, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8450_qmp_gen3x1_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_serdes_tbl), + .tx_tbl = sm8450_qmp_gen3x1_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_tx_tbl), + .rx_tbl = sm8450_qmp_gen3x1_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_rx_tbl), + .pcs_tbl = sm8450_qmp_gen3x1_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_pcs_tbl), + .pcs_misc_tbl = sm8450_qmp_gen3x1_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8450_qmp_gen4x2_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_serdes_tbl), + .tx_tbl = sm8450_qmp_gen4x2_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_tx_tbl), + .rx_tbl = sm8450_qmp_gen4x2_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_rx_tbl), + .pcs_tbl = sm8450_qmp_gen4x2_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_pcs_tbl), + .pcs_misc_tbl = sm8450_qmp_gen4x2_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static void qmp_pcie_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + if (t->in_layout) + writel(t->val, base + regs[t->offset]); + else + writel(t->val, base + t->offset); + } +} + +static void qmp_pcie_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qmp_pcie_configure_lane(base, regs, tbl, num, 0xff); +} + +static int qmp_pcie_serdes_init(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; + int serdes_tbl_num = cfg->serdes_tbl_num; + + qmp_pcie_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num); + qmp_pcie_configure(serdes, cfg->regs, cfg->serdes_tbl_sec, cfg->serdes_tbl_num_sec); + + return 0; +} + +static int qmp_pcie_init(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs = qphy->pcs; + int ret; + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + return ret; + } + + ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset assert failed\n"); + goto err_disable_regulators; + } + + ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset deassert failed\n"); + goto err_disable_regulators; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + goto err_assert_reset; + + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) + qphy_setbits(pcs, + cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + else + qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + + return 0; + +err_assert_reset: + reset_control_bulk_assert(cfg->num_resets, qmp->resets); +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return ret; +} + +static int qmp_pcie_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + + reset_control_bulk_assert(cfg->num_resets, qmp->resets); + + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return 0; +} + +static int qmp_pcie_power_on(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *tx = qphy->tx; + void __iomem *rx = qphy->rx; + void __iomem *pcs = qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + void __iomem *status; + unsigned int mask, val, ready; + int ret; + + qmp_pcie_serdes_init(qphy); + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + return ret; + } + + /* Tx, Rx, and PCS configurations */ + qmp_pcie_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1); + qmp_pcie_configure_lane(tx, cfg->regs, cfg->tx_tbl_sec, cfg->tx_tbl_num_sec, 1); + + if (cfg->lanes >= 2) { + qmp_pcie_configure_lane(qphy->tx2, cfg->regs, cfg->tx_tbl, + cfg->tx_tbl_num, 2); + qmp_pcie_configure_lane(qphy->tx2, cfg->regs, cfg->tx_tbl_sec, + cfg->tx_tbl_num_sec, 2); + } + + qmp_pcie_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1); + qmp_pcie_configure_lane(rx, cfg->regs, cfg->rx_tbl_sec, cfg->rx_tbl_num_sec, 1); + + if (cfg->lanes >= 2) { + qmp_pcie_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl, + cfg->rx_tbl_num, 2); + qmp_pcie_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl_sec, + cfg->rx_tbl_num_sec, 2); + } + + qmp_pcie_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + qmp_pcie_configure(pcs, cfg->regs, cfg->pcs_tbl_sec, cfg->pcs_tbl_num_sec); + + qmp_pcie_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl, cfg->pcs_misc_tbl_num); + qmp_pcie_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl_sec, cfg->pcs_misc_tbl_num_sec); + + /* + * Pull out PHY from POWER DOWN state. + * This is active low enable signal to power-down PHY. + */ + qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); + + if (cfg->has_pwrdn_delay) + usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); + + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + status = pcs + cfg->regs[QPHY_PCS_STATUS]; + mask = cfg->phy_status; + ready = 0; + + ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + goto err_disable_pipe_clk; + } + + return 0; + +err_disable_pipe_clk: + clk_disable_unprepare(qphy->pipe_clk); + + return ret; +} + +static int qmp_pcie_power_off(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + clk_disable_unprepare(qphy->pipe_clk); + + /* PHY reset */ + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Put PHY into POWER DOWN state: active low */ + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) { + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } else { + qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } + + return 0; +} + +static int qmp_pcie_enable(struct phy *phy) +{ + int ret; + + ret = qmp_pcie_init(phy); + if (ret) + return ret; + + ret = qmp_pcie_power_on(phy); + if (ret) + qmp_pcie_exit(phy); + + return ret; +} + +static int qmp_pcie_disable(struct phy *phy) +{ + int ret; + + ret = qmp_pcie_power_off(phy); + if (ret) + return ret; + + return qmp_pcie_exit(phy); +} + +static int qmp_pcie_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_vregs; + int i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, num, qmp->vregs); +} + +static int qmp_pcie_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int i; + int ret; + + qmp->resets = devm_kcalloc(dev, cfg->num_resets, + sizeof(*qmp->resets), GFP_KERNEL); + if (!qmp->resets) + return -ENOMEM; + + for (i = 0; i < cfg->num_resets; i++) + qmp->resets[i].id = cfg->reset_list[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get resets\n"); + + return 0; +} + +static int qmp_pcie_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_clks; + int i; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + return devm_clk_bulk_get(dev, num, qmp->clks); +} + +static void phy_clk_release_provider(void *res) +{ + of_clk_del_provider(res); +} + +/* + * Register a fixed rate pipe clock. + * + * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate + * controls it. The <s>_pipe_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the <s>_pipe_clksrc here. The gcc driver takes care + * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) +{ + struct clk_fixed_rate *fixed; + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string(np, "clock-output-names", &init.name); + if (ret) { + dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); + return ret; + } + + fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return -ENOMEM; + + init.ops = &clk_fixed_rate_ops; + + /* + * Controllers using QMP PHY-s use 125MHz pipe clock interface + * unless other frequency is specified in the PHY config. + */ + if (qmp->phys[0]->cfg->pipe_clock_rate) + fixed->fixed_rate = qmp->phys[0]->cfg->pipe_clock_rate; + else + fixed->fixed_rate = 125000000; + + fixed->hw.init = &init; + + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); +} + +static const struct phy_ops qmp_pcie_ops = { + .power_on = qmp_pcie_enable, + .power_off = qmp_pcie_disable, + .owner = THIS_MODULE, +}; + +static int qmp_pcie_create(struct device *dev, struct device_node *np, int id, + void __iomem *serdes, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct phy *generic_phy; + struct qmp_phy *qphy; + int ret; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->cfg = cfg; + qphy->serdes = serdes; + /* + * Get memory resources for each phy lane: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 + * For single lane PHYs: pcs_misc (optional) -> 3. + */ + qphy->tx = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qphy->tx)) + return PTR_ERR(qphy->tx); + + if (of_device_is_compatible(dev->of_node, "qcom,sdm845-qhp-pcie-phy")) + qphy->rx = qphy->tx; + else + qphy->rx = devm_of_iomap(dev, np, 1, NULL); + if (IS_ERR(qphy->rx)) + return PTR_ERR(qphy->rx); + + qphy->pcs = devm_of_iomap(dev, np, 2, NULL); + if (IS_ERR(qphy->pcs)) + return PTR_ERR(qphy->pcs); + + if (cfg->lanes >= 2) { + qphy->tx2 = devm_of_iomap(dev, np, 3, NULL); + if (IS_ERR(qphy->tx2)) + return PTR_ERR(qphy->tx2); + + qphy->rx2 = devm_of_iomap(dev, np, 4, NULL); + if (IS_ERR(qphy->rx2)) + return PTR_ERR(qphy->rx2); + + qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL); + } else { + qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL); + } + + if (IS_ERR(qphy->pcs_misc) && + of_device_is_compatible(dev->of_node, "qcom,ipq6018-qmp-pcie-phy")) + qphy->pcs_misc = qphy->pcs + 0x400; + + if (IS_ERR(qphy->pcs_misc)) { + if (cfg->pcs_misc_tbl || cfg->pcs_misc_tbl_sec) + return PTR_ERR(qphy->pcs_misc); + } + + qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL); + if (IS_ERR(qphy->pipe_clk)) { + return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk), + "failed to get lane%d pipe clock\n", id); + } + + generic_phy = devm_phy_create(dev, np, &qmp_pcie_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create qphy %d\n", ret); + return ret; + } + + qphy->phy = generic_phy; + qphy->qmp = qmp; + qmp->phys[id] = qphy; + phy_set_drvdata(generic_phy, qphy); + + return 0; +} + +static const struct of_device_id qmp_pcie_of_match_table[] = { + { + .compatible = "qcom,msm8998-qmp-pcie-phy", + .data = &msm8998_pciephy_cfg, + }, { + .compatible = "qcom,ipq8074-qmp-pcie-phy", + .data = &ipq8074_pciephy_cfg, + }, { + .compatible = "qcom,ipq8074-qmp-gen3-pcie-phy", + .data = &ipq8074_pciephy_gen3_cfg, + }, { + .compatible = "qcom,ipq6018-qmp-pcie-phy", + .data = &ipq6018_pciephy_cfg, + }, { + .compatible = "qcom,sc8180x-qmp-pcie-phy", + .data = &sc8180x_pciephy_cfg, + }, { + .compatible = "qcom,sdm845-qhp-pcie-phy", + .data = &sdm845_qhp_pciephy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-pcie-phy", + .data = &sdm845_qmp_pciephy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-gen3x1-pcie-phy", + .data = &sm8250_qmp_gen3x1_pciephy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-gen3x2-pcie-phy", + .data = &sm8250_qmp_gen3x2_pciephy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-modem-pcie-phy", + .data = &sm8250_qmp_gen3x2_pciephy_cfg, + }, { + .compatible = "qcom,sdx55-qmp-pcie-phy", + .data = &sdx55_qmp_pciephy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-gen3x1-pcie-phy", + .data = &sm8450_qmp_gen3x1_pciephy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-gen4x2-pcie-phy", + .data = &sm8450_qmp_gen4x2_pciephy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qmp_pcie_of_match_table); + +static int qmp_pcie_probe(struct platform_device *pdev) +{ + struct qcom_qmp *qmp; + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy_provider *phy_provider; + void __iomem *serdes; + const struct qmp_phy_cfg *cfg = NULL; + int num, id; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + dev_set_drvdata(dev, qmp); + + /* Get the specific init parameters of QMP phy */ + cfg = of_device_get_match_data(dev); + if (!cfg) + return -EINVAL; + + /* per PHY serdes; usually located at base address */ + serdes = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + ret = qmp_pcie_clk_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_pcie_reset_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_pcie_vreg_init(dev, cfg); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + num = of_get_available_child_count(dev->of_node); + /* do we have a rogue child node ? */ + if (num > 1) + return -EINVAL; + + qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); + if (!qmp->phys) + return -ENOMEM; + + id = 0; + for_each_available_child_of_node(dev->of_node, child) { + /* Create per-lane phy */ + ret = qmp_pcie_create(dev, child, id, serdes, cfg); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + goto err_node_put; + } + + /* + * Register the pipe clock provided by phy. + * See function description to see details of this pipe clock. + */ + ret = phy_pipe_clk_register(qmp, child); + if (ret) { + dev_err(qmp->dev, + "failed to register pipe clock source\n"); + goto err_node_put; + } + + id++; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + of_node_put(child); + return ret; +} + +static struct platform_driver qmp_pcie_driver = { + .probe = qmp_pcie_probe, + .driver = { + .name = "qcom-qmp-pcie-phy", + .of_match_table = qmp_pcie_of_match_table, + }, +}; + +module_platform_driver(qmp_pcie_driver); + +MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); +MODULE_DESCRIPTION("Qualcomm QMP PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h new file mode 100644 index 000000000000..a45bd301bc9e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-misc-v3.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_MISC_V3_H_ +#define QCOM_PHY_QMP_PCS_MISC_V3_H_ + +/* Only for QMP V3 PHY - PCS_MISC registers */ +#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c +#define QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2 0x2c +#define QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1 0x44 +#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2 0x54 +#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c +#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h new file mode 100644 index 000000000000..4cc02288d418 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V4_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V4_H_ + +/* Only for QMP V4 PHY - PCS_PCIE registers (same as PCS_MISC?) */ +#define QPHY_V4_PCS_PCIE_INT_AUX_CLK_STATUS 0x00 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_STATUS 0x04 +#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG1 0x08 +#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2 0x0c +#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG3 0x10 +#define QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4 0x14 +#define QPHY_V4_PCS_PCIE_PCS_TX_RX_CONFIG 0x18 +#define QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x1c +#define QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_CNTRL 0x20 +#define QPHY_V4_PCS_PCIE_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK 0x24 +#define QPHY_V4_PCS_PCIE_EPCLK_DLY_COUNT_VAL_L 0x28 +#define QPHY_V4_PCS_PCIE_EPCLK_DLY_COUNT_VAL_H 0x2c +#define QPHY_V4_PCS_PCIE_RX_IDLE_DTCT_CNTRL1 0x30 +#define QPHY_V4_PCS_PCIE_RX_IDLE_DTCT_CNTRL2 0x34 +#define QPHY_V4_PCS_PCIE_SIGDET_CNTRL 0x38 +#define QPHY_V4_PCS_PCIE_SIGDET_LOW_2_IDLE_TIME 0x3c +#define QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L 0x40 +#define QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H 0x44 +#define QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L 0x48 +#define QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H 0x4c +#define QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x50 +#define QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG2 0x54 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG1 0x58 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG2 0x5c +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG3 0x60 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG4 0x64 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG5 0x68 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG6 0x6c +#define QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG7 0x70 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG1 0x74 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG2 0x78 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG3 0x7c +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG4 0x80 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5 0x84 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG6 0x88 +#define QPHY_V4_PCS_PCIE_OSC_DTCT_MODE2_CONFIG7 0x8c +#define QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS 0x90 +#define QPHY_V4_PCS_PCIE_LOCAL_FS 0x94 +#define QPHY_V4_PCS_PCIE_LOCAL_LF 0x98 +#define QPHY_V4_PCS_PCIE_LOCAL_FS_RS 0x9c +#define QPHY_V4_PCS_PCIE_EQ_CONFIG1 0xa0 +#define QPHY_V4_PCS_PCIE_EQ_CONFIG2 0xa4 +#define QPHY_V4_PCS_PCIE_PRESET_P0_P1_PRE 0xa8 +#define QPHY_V4_PCS_PCIE_PRESET_P2_P3_PRE 0xac +#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_PRE 0xb0 +#define QPHY_V4_PCS_PCIE_PRESET_P6_P7_PRE 0xb4 +#define QPHY_V4_PCS_PCIE_PRESET_P8_P9_PRE 0xb8 +#define QPHY_V4_PCS_PCIE_PRESET_P10_PRE 0xbc +#define QPHY_V4_PCS_PCIE_PRESET_P1_P3_PRE_RS 0xc0 +#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_PRE_RS 0xc4 +#define QPHY_V4_PCS_PCIE_PRESET_P6_P9_PRE_RS 0xc8 +#define QPHY_V4_PCS_PCIE_PRESET_P0_P1_POST 0xcc +#define QPHY_V4_PCS_PCIE_PRESET_P2_P3_POST 0xd0 +#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_POST 0xd4 +#define QPHY_V4_PCS_PCIE_PRESET_P6_P7_POST 0xd8 +#define QPHY_V4_PCS_PCIE_PRESET_P8_P9_POST 0xdc +#define QPHY_V4_PCS_PCIE_PRESET_P10_POST 0xe0 +#define QPHY_V4_PCS_PCIE_PRESET_P1_P3_POST_RS 0xe4 +#define QPHY_V4_PCS_PCIE_PRESET_P4_P5_POST_RS 0xe8 +#define QPHY_V4_PCS_PCIE_PRESET_P6_P9_POST_RS 0xec +#define QPHY_V4_PCS_PCIE_RXEQEVAL_TIME 0xf0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h new file mode 100644 index 000000000000..af273602998e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v4_20.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V4_20_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V4_20_H_ + +#define QPHY_V4_20_PCS_PCIE_EQ_CONFIG1 0x0a0 +#define QPHY_V4_20_PCS_PCIE_G3_RXEQEVAL_TIME 0x0f0 +#define QPHY_V4_20_PCS_PCIE_G4_RXEQEVAL_TIME 0x0f4 +#define QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG2 0x0fc +#define QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG5 0x108 +#define QPHY_V4_20_PCS_LANE1_INSIG_SW_CTRL2 0x824 +#define QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2 0x828 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h new file mode 100644 index 000000000000..2e19fb3f051e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5.h @@ -0,0 +1,16 @@ +/* Only for QMP V5 PHY - PCS_PCIE registers */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V5_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V5_H_ + +/* Only for QMP V5 PHY - PCS_PCIE registers */ +#define QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x20 +#define QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x54 +#define QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS 0x94 +#define QPHY_V5_PCS_PCIE_EQ_CONFIG2 0xa8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h new file mode 100644 index 000000000000..1eedf50cf9cb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V5_20_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V5_20_H_ + +/* Only for QMP V5_20 PHY - PCIe PCS registers */ +#define QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x01c +#define QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS 0x090 +#define QPHY_V5_20_PCS_PCIE_EQ_CONFIG1 0x0a0 +#define QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5 0x108 +#define QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN 0x15c +#define QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3 0x184 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h new file mode 100644 index 000000000000..ba1ea29d2884 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v3.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_UFS_V3_H_ +#define QCOM_PHY_QMP_PCS_UFS_V3_H_ + +#define QPHY_V3_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x02c +#define QPHY_V3_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x034 +#define QPHY_V3_PCS_UFS_RX_SYM_RESYNC_CTRL 0x134 +#define QPHY_V3_PCS_UFS_RX_MIN_HIBERN8_TIME 0x138 +#define QPHY_V3_PCS_UFS_RX_SIGDET_CTRL1 0x13c +#define QPHY_V3_PCS_UFS_RX_SIGDET_CTRL2 0x140 +#define QPHY_V3_PCS_UFS_TX_MID_TERM_CTRL1 0x1bc +#define QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1 0x1c4 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h new file mode 100644 index 000000000000..a1c7d3d17150 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v4.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_UFS_V4_H_ +#define QCOM_PHY_QMP_PCS_UFS_V4_H_ + +/* Only for QMP V4 PHY - UFS PCS registers */ +#define QPHY_V4_PCS_UFS_PHY_START 0x000 +#define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL 0x004 +#define QPHY_V4_PCS_UFS_SW_RESET 0x008 +#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c +#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010 +#define QPHY_V4_PCS_UFS_PLL_CNTL 0x02c +#define QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x030 +#define QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x038 +#define QPHY_V4_PCS_UFS_BIST_FIXED_PAT_CTRL 0x060 +#define QPHY_V4_PCS_UFS_TX_HSGEAR_CAPABILITY 0x074 +#define QPHY_V4_PCS_UFS_RX_HSGEAR_CAPABILITY 0x0b4 +#define QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL 0x124 +#define QPHY_V4_PCS_UFS_LINECFG_DISABLE 0x148 +#define QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME 0x150 +#define QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2 0x158 +#define QPHY_V4_PCS_UFS_TX_PWM_GEAR_BAND 0x160 +#define QPHY_V4_PCS_UFS_TX_HS_GEAR_BAND 0x168 +#define QPHY_V4_PCS_UFS_READY_STATUS 0x180 +#define QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1 0x1d8 +#define QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1 0x1e0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h new file mode 100644 index 000000000000..bcca23493b7e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v5.h @@ -0,0 +1,27 @@ +/* Only for QMP V5 PHY - UFS PCS registers */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_UFS_V5_H_ +#define QCOM_PHY_QMP_PCS_UFS_V5_H_ + +/* Only for QMP V5 PHY - UFS PCS registers */ +#define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c +#define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010 +#define QPHY_V5_PCS_UFS_PLL_CNTL 0x02c +#define QPHY_V5_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x030 +#define QPHY_V5_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x038 +#define QPHY_V5_PCS_UFS_TX_HSGEAR_CAPABILITY 0x074 +#define QPHY_V5_PCS_UFS_RX_HSGEAR_CAPABILITY 0x0b4 +#define QPHY_V5_PCS_UFS_DEBUG_BUS_CLKSEL 0x124 +#define QPHY_V5_PCS_UFS_RX_MIN_HIBERN8_TIME 0x150 +#define QPHY_V5_PCS_UFS_RX_SIGDET_CTRL1 0x154 +#define QPHY_V5_PCS_UFS_RX_SIGDET_CTRL2 0x158 +#define QPHY_V5_PCS_UFS_TX_PWM_GEAR_BAND 0x160 +#define QPHY_V5_PCS_UFS_TX_HS_GEAR_BAND 0x168 +#define QPHY_V5_PCS_UFS_TX_MID_TERM_CTRL1 0x1d8 +#define QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1 0x1e0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h new file mode 100644 index 000000000000..d7fd4ac0fc55 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v4.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_USB_V4_H_ +#define QCOM_PHY_QMP_PCS_USB_V4_H_ + +/* Only for QMP V4 PHY - USB3 PCS registers */ +#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1 0x000 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010 +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014 +#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018 +#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART 0x01c +#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL 0x020 +#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x028 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x02c +#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x030 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x034 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x038 +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x03c +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x040 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x044 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY 0x048 +#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x04c +#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL 0x050 +#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x054 +#define QPHY_V4_PCS_USB3_TEST_CONTROL 0x058 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h new file mode 100644 index 000000000000..73de626223ed --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v5.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_USB_V5_H_ +#define QCOM_PHY_QMP_PCS_USB_V5_H_ + +/* Only for QMP V5 PHY - USB3 have different offsets than V4 */ +#define QPHY_V5_PCS_USB3_POWER_STATE_CONFIG1 0x000 +#define QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x004 +#define QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x008 +#define QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x00c +#define QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x010 +#define QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x014 +#define QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x018 +#define QPHY_V5_PCS_USB3_LFPS_TX_ECSTART 0x01c +#define QPHY_V5_PCS_USB3_LFPS_PER_TIMER_VAL 0x020 +#define QPHY_V5_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x024 +#define QPHY_V5_PCS_USB3_LFPS_CONFIG1 0x028 +#define QPHY_V5_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x02c +#define QPHY_V5_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x030 +#define QPHY_V5_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x034 +#define QPHY_V5_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x038 +#define QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x03c +#define QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x040 +#define QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x044 +#define QPHY_V5_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x048 +#define QPHY_V5_PCS_USB3_ARCVR_DTCT_CM_DLY 0x04c +#define QPHY_V5_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x050 +#define QPHY_V5_PCS_USB3_ALFPS_DEGLITCH_VAL 0x054 +#define QPHY_V5_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x058 +#define QPHY_V5_PCS_USB3_TEST_CONTROL 0x05c +#define QPHY_V5_PCS_USB3_RXTERMINATION_DLY_SEL 0x060 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h new file mode 100644 index 000000000000..c8515f506872 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v2.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_V2_H_ +#define QCOM_PHY_QMP_PCS_V2_H_ + +/* Only for QMP V2 PHY - PCS registers */ +#define QPHY_V2_PCS_POWER_DOWN_CONTROL 0x004 +#define QPHY_V2_PCS_TXDEEMPH_M6DB_V0 0x024 +#define QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0 0x028 +#define QPHY_V2_PCS_TX_LARGE_AMP_DRV_LVL 0x034 +#define QPHY_V2_PCS_TX_LARGE_AMP_POST_EMP_LVL 0x038 +#define QPHY_V2_PCS_TX_SMALL_AMP_DRV_LVL 0x03c +#define QPHY_V2_PCS_TX_SMALL_AMP_POST_EMP_LVL 0x040 +#define QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE 0x054 +#define QPHY_V2_PCS_RX_IDLE_DTCT_CNTRL 0x058 +#define QPHY_V2_PCS_POWER_STATE_CONFIG1 0x060 +#define QPHY_V2_PCS_POWER_STATE_CONFIG2 0x064 +#define QPHY_V2_PCS_POWER_STATE_CONFIG4 0x06c +#define QPHY_V2_PCS_LOCK_DETECT_CONFIG1 0x080 +#define QPHY_V2_PCS_LOCK_DETECT_CONFIG2 0x084 +#define QPHY_V2_PCS_LOCK_DETECT_CONFIG3 0x088 +#define QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0 +#define QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4 +#define QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8 +#define QPHY_V2_PCS_FLL_CNTRL1 0x0c0 +#define QPHY_V2_PCS_FLL_CNTRL2 0x0c4 +#define QPHY_V2_PCS_FLL_CNT_VAL_L 0x0c8 +#define QPHY_V2_PCS_FLL_CNT_VAL_H_TOL 0x0cc +#define QPHY_V2_PCS_FLL_MAN_CODE 0x0d0 + +/* UFS only ? */ +#define QPHY_V2_PCS_RX_MIN_STALL_NOCONFIG_TIME_CAP 0x0cc +#define QPHY_V2_PCS_RX_SYM_RESYNC_CTRL 0x13c +#define QPHY_V2_PCS_RX_MIN_HIBERN8_TIME 0x140 +#define QPHY_V2_PCS_RX_SIGDET_CTRL2 0x148 +#define QPHY_V2_PCS_RX_PWM_GEAR_BAND 0x154 +#define QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1a8 +#define QPHY_V2_PCS_OSC_DTCT_ACTIONS 0x1ac +#define QPHY_V2_PCS_RX_SIGDET_LVL 0x1d8 +#define QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc +#define QPHY_V2_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h new file mode 100644 index 000000000000..10dbbb006201 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v3.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_V3_H_ +#define QCOM_PHY_QMP_PCS_V3_H_ + +/* Only for QMP V3 PHY - PCS registers */ +#define QPHY_V3_PCS_SW_RESET 0x000 +#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004 +#define QPHY_V3_PCS_START_CONTROL 0x008 +#define QPHY_V3_PCS_TXMGN_V0 0x00c +#define QPHY_V3_PCS_TXMGN_V1 0x010 +#define QPHY_V3_PCS_TXMGN_V2 0x014 +#define QPHY_V3_PCS_TXMGN_V3 0x018 +#define QPHY_V3_PCS_TXMGN_V4 0x01c +#define QPHY_V3_PCS_TXMGN_LS 0x020 +#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024 +#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028 +#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c +#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1 0x030 +#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2 0x034 +#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2 0x038 +#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3 0x03c +#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3 0x040 +#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4 0x044 +#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4 0x048 +#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS 0x04c +#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS 0x050 +#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE 0x054 +#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL 0x058 +#define QPHY_V3_PCS_RATE_SLEW_CNTRL 0x05c +#define QPHY_V3_PCS_POWER_STATE_CONFIG1 0x060 +#define QPHY_V3_PCS_POWER_STATE_CONFIG2 0x064 +#define QPHY_V3_PCS_POWER_STATE_CONFIG3 0x068 +#define QPHY_V3_PCS_POWER_STATE_CONFIG4 0x06c +#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L 0x070 +#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H 0x074 +#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L 0x078 +#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H 0x07c +#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1 0x080 +#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2 0x084 +#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3 0x088 +#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c +#define QPHY_V3_PCS_SIGDET_LOW_2_IDLE_TIME 0x090 +#define QPHY_V3_PCS_BEACON_2_IDLE_TIME_L 0x094 +#define QPHY_V3_PCS_BEACON_2_IDLE_TIME_H 0x098 +#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_SYSCLK 0x09c +#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0 +#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4 +#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8 +#define QPHY_V3_PCS_LFPS_DET_HIGH_COUNT_VAL 0x0ac +#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0 +#define QPHY_V3_PCS_LFPS_TX_END_CNT_P2U3_START 0x0b4 +#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8 +#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc +#define QPHY_V3_PCS_TXONESZEROS_RUN_LENGTH 0x0c0 +#define QPHY_V3_PCS_FLL_CNTRL1 0x0c4 +#define QPHY_V3_PCS_FLL_CNTRL2 0x0c8 +#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc +#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0 +#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4 +#define QPHY_V3_PCS_AUTONOMOUS_MODE_CTRL 0x0d8 +#define QPHY_V3_PCS_LFPS_RXTERM_IRQ_CLEAR 0x0dc +#define QPHY_V3_PCS_ARCVR_DTCT_EN_PERIOD 0x0e0 +#define QPHY_V3_PCS_ARCVR_DTCT_CM_DLY 0x0e4 +#define QPHY_V3_PCS_ALFPS_DEGLITCH_VAL 0x0e8 +#define QPHY_V3_PCS_INSIG_SW_CTRL1 0x0ec +#define QPHY_V3_PCS_INSIG_SW_CTRL2 0x0f0 +#define QPHY_V3_PCS_INSIG_SW_CTRL3 0x0f4 +#define QPHY_V3_PCS_INSIG_MX_CTRL1 0x0f8 +#define QPHY_V3_PCS_INSIG_MX_CTRL2 0x0fc +#define QPHY_V3_PCS_INSIG_MX_CTRL3 0x100 +#define QPHY_V3_PCS_OUTSIG_SW_CTRL1 0x104 +#define QPHY_V3_PCS_OUTSIG_MX_CTRL1 0x108 +#define QPHY_V3_PCS_CLK_DEBUG_BYPASS_CTRL 0x10c +#define QPHY_V3_PCS_TEST_CONTROL 0x110 +#define QPHY_V3_PCS_TEST_CONTROL2 0x114 +#define QPHY_V3_PCS_TEST_CONTROL3 0x118 +#define QPHY_V3_PCS_TEST_CONTROL4 0x11c +#define QPHY_V3_PCS_TEST_CONTROL5 0x120 +#define QPHY_V3_PCS_TEST_CONTROL6 0x124 +#define QPHY_V3_PCS_TEST_CONTROL7 0x128 +#define QPHY_V3_PCS_COM_RESET_CONTROL 0x12c +#define QPHY_V3_PCS_BIST_CTRL 0x130 +#define QPHY_V3_PCS_PRBS_POLY0 0x134 +#define QPHY_V3_PCS_PRBS_POLY1 0x138 +#define QPHY_V3_PCS_PRBS_SEED0 0x13c +#define QPHY_V3_PCS_PRBS_SEED1 0x140 +#define QPHY_V3_PCS_FIXED_PAT_CTRL 0x144 +#define QPHY_V3_PCS_FIXED_PAT0 0x148 +#define QPHY_V3_PCS_FIXED_PAT1 0x14c +#define QPHY_V3_PCS_FIXED_PAT2 0x150 +#define QPHY_V3_PCS_FIXED_PAT3 0x154 +#define QPHY_V3_PCS_COM_CLK_SWITCH_CTRL 0x158 +#define QPHY_V3_PCS_ELECIDLE_DLY_SEL 0x15c +#define QPHY_V3_PCS_SPARE1 0x160 +#define QPHY_V3_PCS_BIST_CHK_ERR_CNT_L_STATUS 0x164 +#define QPHY_V3_PCS_BIST_CHK_ERR_CNT_H_STATUS 0x168 +#define QPHY_V3_PCS_BIST_CHK_STATUS 0x16c +#define QPHY_V3_PCS_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x170 +#define QPHY_V3_PCS_PCS_STATUS 0x174 +#define QPHY_V3_PCS_PCS_STATUS2 0x178 +#define QPHY_V3_PCS_PCS_STATUS3 0x17c +#define QPHY_V3_PCS_COM_RESET_STATUS 0x180 +#define QPHY_V3_PCS_OSC_DTCT_STATUS 0x184 +#define QPHY_V3_PCS_REVISION_ID0 0x188 +#define QPHY_V3_PCS_REVISION_ID1 0x18c +#define QPHY_V3_PCS_REVISION_ID2 0x190 +#define QPHY_V3_PCS_REVISION_ID3 0x194 +#define QPHY_V3_PCS_DEBUG_BUS_0_STATUS 0x198 +#define QPHY_V3_PCS_DEBUG_BUS_1_STATUS 0x19c +#define QPHY_V3_PCS_DEBUG_BUS_2_STATUS 0x1a0 +#define QPHY_V3_PCS_DEBUG_BUS_3_STATUS 0x1a4 +#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1a8 +#define QPHY_V3_PCS_OSC_DTCT_ACTIONS 0x1ac +#define QPHY_V3_PCS_SIGDET_CNTRL 0x1b0 +#define QPHY_V3_PCS_IDAC_CAL_CNTRL 0x1b4 +#define QPHY_V3_PCS_CMN_ACK_OUT_SEL 0x1b8 +#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME_SYSCLK 0x1bc +#define QPHY_V3_PCS_AUTONOMOUS_MODE_STATUS 0x1c0 +#define QPHY_V3_PCS_ENDPOINT_REFCLK_CNTRL 0x1c4 +#define QPHY_V3_PCS_EPCLK_PRE_PLL_LOCK_DLY_SYSCLK 0x1c8 +#define QPHY_V3_PCS_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK 0x1cc +#define QPHY_V3_PCS_EPCLK_DLY_COUNT_VAL_L 0x1d0 +#define QPHY_V3_PCS_EPCLK_DLY_COUNT_VAL_H 0x1d4 +#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8 +#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc +#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0 +#define QPHY_V3_PCS_AUTONOMOUS_MODE_CTRL2 0x1e4 +#define QPHY_V3_PCS_RXTERMINATION_DLY_SEL 0x1e8 +#define QPHY_V3_PCS_LFPS_PER_TIMER_VAL 0x1ec +#define QPHY_V3_PCS_SIGDET_STARTUP_TIMER_VAL 0x1f0 +#define QPHY_V3_PCS_LOCK_DETECT_CONFIG4 0x1f4 +#define QPHY_V3_PCS_RX_SIGDET_DTCT_CNTRL 0x1f8 +#define QPHY_V3_PCS_PCS_STATUS4 0x1fc +#define QPHY_V3_PCS_PCS_STATUS4_CLEAR 0x200 +#define QPHY_V3_PCS_DEC_ERROR_COUNT_STATUS 0x204 +#define QPHY_V3_PCS_COMMA_POS_STATUS 0x208 +#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c +#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210 +#define QPHY_V3_PCS_REFGEN_REQ_CONFIG3 0x214 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h new file mode 100644 index 000000000000..a2c1eba2b693 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_V4_H_ +#define QCOM_PHY_QMP_PCS_V4_H_ + +/* Only for QMP V4 PHY - USB/PCIe PCS registers */ +#define QPHY_V4_PCS_SW_RESET 0x000 +#define QPHY_V4_PCS_REVISION_ID0 0x004 +#define QPHY_V4_PCS_REVISION_ID1 0x008 +#define QPHY_V4_PCS_REVISION_ID2 0x00c +#define QPHY_V4_PCS_REVISION_ID3 0x010 +#define QPHY_V4_PCS_PCS_STATUS1 0x014 +#define QPHY_V4_PCS_PCS_STATUS2 0x018 +#define QPHY_V4_PCS_PCS_STATUS3 0x01c +#define QPHY_V4_PCS_PCS_STATUS4 0x020 +#define QPHY_V4_PCS_PCS_STATUS5 0x024 +#define QPHY_V4_PCS_PCS_STATUS6 0x028 +#define QPHY_V4_PCS_PCS_STATUS7 0x02c +#define QPHY_V4_PCS_DEBUG_BUS_0_STATUS 0x030 +#define QPHY_V4_PCS_DEBUG_BUS_1_STATUS 0x034 +#define QPHY_V4_PCS_DEBUG_BUS_2_STATUS 0x038 +#define QPHY_V4_PCS_DEBUG_BUS_3_STATUS 0x03c +#define QPHY_V4_PCS_POWER_DOWN_CONTROL 0x040 +#define QPHY_V4_PCS_START_CONTROL 0x044 +#define QPHY_V4_PCS_INSIG_SW_CTRL1 0x048 +#define QPHY_V4_PCS_INSIG_SW_CTRL2 0x04c +#define QPHY_V4_PCS_INSIG_SW_CTRL3 0x050 +#define QPHY_V4_PCS_INSIG_SW_CTRL4 0x054 +#define QPHY_V4_PCS_INSIG_SW_CTRL5 0x058 +#define QPHY_V4_PCS_INSIG_SW_CTRL6 0x05c +#define QPHY_V4_PCS_INSIG_SW_CTRL7 0x060 +#define QPHY_V4_PCS_INSIG_SW_CTRL8 0x064 +#define QPHY_V4_PCS_INSIG_MX_CTRL1 0x068 +#define QPHY_V4_PCS_INSIG_MX_CTRL2 0x06c +#define QPHY_V4_PCS_INSIG_MX_CTRL3 0x070 +#define QPHY_V4_PCS_INSIG_MX_CTRL4 0x074 +#define QPHY_V4_PCS_INSIG_MX_CTRL5 0x078 +#define QPHY_V4_PCS_INSIG_MX_CTRL7 0x07c +#define QPHY_V4_PCS_INSIG_MX_CTRL8 0x080 +#define QPHY_V4_PCS_OUTSIG_SW_CTRL1 0x084 +#define QPHY_V4_PCS_OUTSIG_MX_CTRL1 0x088 +#define QPHY_V4_PCS_CLAMP_ENABLE 0x08c +#define QPHY_V4_PCS_POWER_STATE_CONFIG1 0x090 +#define QPHY_V4_PCS_POWER_STATE_CONFIG2 0x094 +#define QPHY_V4_PCS_FLL_CNTRL1 0x098 +#define QPHY_V4_PCS_FLL_CNTRL2 0x09c +#define QPHY_V4_PCS_FLL_CNT_VAL_L 0x0a0 +#define QPHY_V4_PCS_FLL_CNT_VAL_H_TOL 0x0a4 +#define QPHY_V4_PCS_FLL_MAN_CODE 0x0a8 +#define QPHY_V4_PCS_TEST_CONTROL1 0x0ac +#define QPHY_V4_PCS_TEST_CONTROL2 0x0b0 +#define QPHY_V4_PCS_TEST_CONTROL3 0x0b4 +#define QPHY_V4_PCS_TEST_CONTROL4 0x0b8 +#define QPHY_V4_PCS_TEST_CONTROL5 0x0bc +#define QPHY_V4_PCS_TEST_CONTROL6 0x0c0 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG1 0x0c4 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG2 0x0c8 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG3 0x0cc +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG4 0x0d0 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG5 0x0d4 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG6 0x0d8 +#define QPHY_V4_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V4_PCS_REFGEN_REQ_CONFIG2 0x0e0 +#define QPHY_V4_PCS_REFGEN_REQ_CONFIG3 0x0e4 +#define QPHY_V4_PCS_BIST_CTRL 0x0e8 +#define QPHY_V4_PCS_PRBS_POLY0 0x0ec +#define QPHY_V4_PCS_PRBS_POLY1 0x0f0 +#define QPHY_V4_PCS_FIXED_PAT0 0x0f4 +#define QPHY_V4_PCS_FIXED_PAT1 0x0f8 +#define QPHY_V4_PCS_FIXED_PAT2 0x0fc +#define QPHY_V4_PCS_FIXED_PAT3 0x100 +#define QPHY_V4_PCS_FIXED_PAT4 0x104 +#define QPHY_V4_PCS_FIXED_PAT5 0x108 +#define QPHY_V4_PCS_FIXED_PAT6 0x10c +#define QPHY_V4_PCS_FIXED_PAT7 0x110 +#define QPHY_V4_PCS_FIXED_PAT8 0x114 +#define QPHY_V4_PCS_FIXED_PAT9 0x118 +#define QPHY_V4_PCS_FIXED_PAT10 0x11c +#define QPHY_V4_PCS_FIXED_PAT11 0x120 +#define QPHY_V4_PCS_FIXED_PAT12 0x124 +#define QPHY_V4_PCS_FIXED_PAT13 0x128 +#define QPHY_V4_PCS_FIXED_PAT14 0x12c +#define QPHY_V4_PCS_FIXED_PAT15 0x130 +#define QPHY_V4_PCS_TXMGN_CONFIG 0x134 +#define QPHY_V4_PCS_G12S1_TXMGN_V0 0x138 +#define QPHY_V4_PCS_G12S1_TXMGN_V1 0x13c +#define QPHY_V4_PCS_G12S1_TXMGN_V2 0x140 +#define QPHY_V4_PCS_G12S1_TXMGN_V3 0x144 +#define QPHY_V4_PCS_G12S1_TXMGN_V4 0x148 +#define QPHY_V4_PCS_G12S1_TXMGN_V0_RS 0x14c +#define QPHY_V4_PCS_G12S1_TXMGN_V1_RS 0x150 +#define QPHY_V4_PCS_G12S1_TXMGN_V2_RS 0x154 +#define QPHY_V4_PCS_G12S1_TXMGN_V3_RS 0x158 +#define QPHY_V4_PCS_G12S1_TXMGN_V4_RS 0x15c +#define QPHY_V4_PCS_G3S2_TXMGN_MAIN 0x160 +#define QPHY_V4_PCS_G3S2_TXMGN_MAIN_RS 0x164 +#define QPHY_V4_PCS_G12S1_TXDEEMPH_M6DB 0x168 +#define QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB 0x16c +#define QPHY_V4_PCS_G3S2_PRE_GAIN 0x170 +#define QPHY_V4_PCS_G3S2_POST_GAIN 0x174 +#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET 0x178 +#define QPHY_V4_PCS_G3S2_PRE_GAIN_RS 0x17c +#define QPHY_V4_PCS_G3S2_POST_GAIN_RS 0x180 +#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET_RS 0x184 +#define QPHY_V4_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V4_PCS_RX_SIGDET_DTCT_CNTRL 0x18c +#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L 0x190 +#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H 0x194 +#define QPHY_V4_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V4_PCS_RATE_SLEW_CNTRL2 0x19c +#define QPHY_V4_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x1a0 +#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L 0x1a4 +#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_H 0x1a8 +#define QPHY_V4_PCS_TSYNC_RSYNC_TIME 0x1ac +#define QPHY_V4_PCS_CDR_RESET_TIME 0x1b0 +#define QPHY_V4_PCS_TSYNC_DLY_TIME 0x1b4 +#define QPHY_V4_PCS_ELECIDLE_DLY_SEL 0x1b8 +#define QPHY_V4_PCS_CMN_ACK_OUT_SEL 0x1bc +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG1 0x1c0 +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG2 0x1c4 +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG3 0x1c8 +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG4 0x1cc +#define QPHY_V4_PCS_PCS_TX_RX_CONFIG 0x1d0 +#define QPHY_V4_PCS_RX_IDLE_DTCT_CNTRL 0x1d4 +#define QPHY_V4_PCS_RX_DCC_CAL_CONFIG 0x1d8 +#define QPHY_V4_PCS_EQ_CONFIG1 0x1dc +#define QPHY_V4_PCS_EQ_CONFIG2 0x1e0 +#define QPHY_V4_PCS_EQ_CONFIG3 0x1e4 +#define QPHY_V4_PCS_EQ_CONFIG4 0x1e8 +#define QPHY_V4_PCS_EQ_CONFIG5 0x1ec + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h new file mode 100644 index 000000000000..08c3dd115488 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v4_20.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_V4_20_H_ +#define QCOM_PHY_QMP_PCS_V4_20_H_ + +/* Only for QMP V4_20 PHY - USB/PCIe PCS registers */ +#define QPHY_V4_20_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V4_20_PCS_EQ_CONFIG2 0x1d8 +#define QPHY_V4_20_PCS_EQ_CONFIG4 0x1e0 +#define QPHY_V4_20_PCS_EQ_CONFIG5 0x1e4 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h new file mode 100644 index 000000000000..04f260711ea1 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v5.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_V5_H_ +#define QCOM_PHY_QMP_PCS_V5_H_ + +/* Only for QMP V5 PHY - USB/PCIe PCS registers */ +#define QPHY_V5_PCS_LOCK_DETECT_CONFIG1 0x0c4 +#define QPHY_V5_PCS_LOCK_DETECT_CONFIG2 0x0c8 +#define QPHY_V5_PCS_LOCK_DETECT_CONFIG3 0x0cc +#define QPHY_V5_PCS_LOCK_DETECT_CONFIG6 0x0d8 +#define QPHY_V5_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V5_PCS_G3S2_PRE_GAIN 0x170 +#define QPHY_V5_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L 0x190 +#define QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H 0x194 +#define QPHY_V5_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V5_PCS_CDR_RESET_TIME 0x1b0 +#define QPHY_V5_PCS_RX_CONFIG 0x1b0 +#define QPHY_V5_PCS_ALIGN_DETECT_CONFIG1 0x1c0 +#define QPHY_V5_PCS_ALIGN_DETECT_CONFIG2 0x1c4 +#define QPHY_V5_PCS_PCS_TX_RX_CONFIG 0x1d0 +#define QPHY_V5_PCS_EQ_CONFIG1 0x1dc +#define QPHY_V5_PCS_EQ_CONFIG2 0x1e0 +#define QPHY_V5_PCS_EQ_CONFIG3 0x1e4 +#define QPHY_V5_PCS_EQ_CONFIG5 0x1ec + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h new file mode 100644 index 000000000000..c0bd54e0e7b6 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v3.h @@ -0,0 +1,111 @@ + +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_V3_H_ +#define QCOM_PHY_QMP_QSERDES_COM_V3_H_ + +/* Only for QMP V3 PHY - QSERDES COM registers */ +#define QSERDES_V3_COM_ATB_SEL1 0x000 +#define QSERDES_V3_COM_ATB_SEL2 0x004 +#define QSERDES_V3_COM_FREQ_UPDATE 0x008 +#define QSERDES_V3_COM_BG_TIMER 0x00c +#define QSERDES_V3_COM_SSC_EN_CENTER 0x010 +#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014 +#define QSERDES_V3_COM_SSC_ADJ_PER2 0x018 +#define QSERDES_V3_COM_SSC_PER1 0x01c +#define QSERDES_V3_COM_SSC_PER2 0x020 +#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024 +#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028 +#define QSERDES_V3_COM_POST_DIV 0x02c +#define QSERDES_V3_COM_POST_DIV_MUX 0x030 +#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034 +#define QSERDES_V3_COM_CLK_ENABLE1 0x038 +#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c +#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040 +#define QSERDES_V3_COM_PLL_EN 0x044 +#define QSERDES_V3_COM_PLL_IVCO 0x048 +#define QSERDES_V3_COM_CMN_IETRIM 0x04c +#define QSERDES_V3_COM_CMN_IPTRIM 0x050 +#define QSERDES_V3_COM_EP_CLOCK_DETECT_CTR 0x054 +#define QSERDES_V3_COM_SYSCLK_DET_COMP_STATUS 0x058 +#define QSERDES_V3_COM_CLK_EP_DIV 0x05c +#define QSERDES_V3_COM_CP_CTRL_MODE0 0x060 +#define QSERDES_V3_COM_CP_CTRL_MODE1 0x064 +#define QSERDES_V3_COM_PLL_RCTRL_MODE0 0x068 +#define QSERDES_V3_COM_PLL_RCTRL_MODE1 0x06c +#define QSERDES_V3_COM_PLL_CCTRL_MODE0 0x070 +#define QSERDES_V3_COM_PLL_CCTRL_MODE1 0x074 +#define QSERDES_V3_COM_PLL_CNTRL 0x078 +#define QSERDES_V3_COM_BIAS_EN_CTRL_BY_PSM 0x07c +#define QSERDES_V3_COM_SYSCLK_EN_SEL 0x080 +#define QSERDES_V3_COM_CML_SYSCLK_SEL 0x084 +#define QSERDES_V3_COM_RESETSM_CNTRL 0x088 +#define QSERDES_V3_COM_RESETSM_CNTRL2 0x08c +#define QSERDES_V3_COM_LOCK_CMP_EN 0x090 +#define QSERDES_V3_COM_LOCK_CMP_CFG 0x094 +#define QSERDES_V3_COM_LOCK_CMP1_MODE0 0x098 +#define QSERDES_V3_COM_LOCK_CMP2_MODE0 0x09c +#define QSERDES_V3_COM_LOCK_CMP3_MODE0 0x0a0 +#define QSERDES_V3_COM_LOCK_CMP1_MODE1 0x0a4 +#define QSERDES_V3_COM_LOCK_CMP2_MODE1 0x0a8 +#define QSERDES_V3_COM_LOCK_CMP3_MODE1 0x0ac +#define QSERDES_V3_COM_DEC_START_MODE0 0x0b0 +#define QSERDES_V3_COM_DEC_START_MODE1 0x0b4 +#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0 0x0b8 +#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0 0x0bc +#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0 0x0c0 +#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4 +#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8 +#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc +#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0 +#define QSERDES_V3_COM_INTEGLOOP_EN 0x0d4 +#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8 +#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc +#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0 +#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1 0x0e4 +#define QSERDES_V3_COM_VCOCAL_DEADMAN_CTRL 0x0e8 +#define QSERDES_V3_COM_VCO_TUNE_CTRL 0x0ec +#define QSERDES_V3_COM_VCO_TUNE_MAP 0x0f0 +#define QSERDES_V3_COM_VCO_TUNE1_MODE0 0x0f4 +#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8 +#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc +#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100 +#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104 +#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108 +#define QSERDES_V3_COM_VCO_TUNE_MINVAL1 0x10c +#define QSERDES_V3_COM_VCO_TUNE_MINVAL2 0x110 +#define QSERDES_V3_COM_VCO_TUNE_MAXVAL1 0x114 +#define QSERDES_V3_COM_VCO_TUNE_MAXVAL2 0x118 +#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c +#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120 +#define QSERDES_V3_COM_CMN_STATUS 0x124 +#define QSERDES_V3_COM_RESET_SM_STATUS 0x128 +#define QSERDES_V3_COM_RESTRIM_CODE_STATUS 0x12c +#define QSERDES_V3_COM_PLLCAL_CODE1_STATUS 0x130 +#define QSERDES_V3_COM_PLLCAL_CODE2_STATUS 0x134 +#define QSERDES_V3_COM_CLK_SELECT 0x138 +#define QSERDES_V3_COM_HSCLK_SEL 0x13c +#define QSERDES_V3_COM_INTEGLOOP_BINCODE_STATUS 0x140 +#define QSERDES_V3_COM_PLL_ANALOG 0x144 +#define QSERDES_V3_COM_CORECLK_DIV_MODE0 0x148 +#define QSERDES_V3_COM_CORECLK_DIV_MODE1 0x14c +#define QSERDES_V3_COM_SW_RESET 0x150 +#define QSERDES_V3_COM_CORE_CLK_EN 0x154 +#define QSERDES_V3_COM_C_READY_STATUS 0x158 +#define QSERDES_V3_COM_CMN_CONFIG 0x15c +#define QSERDES_V3_COM_CMN_RATE_OVERRIDE 0x160 +#define QSERDES_V3_COM_SVS_MODE_CLK_SEL 0x164 +#define QSERDES_V3_COM_DEBUG_BUS0 0x168 +#define QSERDES_V3_COM_DEBUG_BUS1 0x16c +#define QSERDES_V3_COM_DEBUG_BUS2 0x170 +#define QSERDES_V3_COM_DEBUG_BUS3 0x174 +#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178 +#define QSERDES_V3_COM_CMN_MISC1 0x17c +#define QSERDES_V3_COM_CMN_MISC2 0x180 +#define QSERDES_V3_COM_CMN_MODE 0x184 +#define QSERDES_V3_COM_CMN_VREG_SEL 0x188 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h new file mode 100644 index 000000000000..b0e3298d990d --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v4.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_V4_H_ +#define QCOM_PHY_QMP_QSERDES_COM_V4_H_ + +/* Only for QMP V4 PHY - QSERDES COM registers */ +#define QSERDES_V4_COM_ATB_SEL1 0x000 +#define QSERDES_V4_COM_ATB_SEL2 0x004 +#define QSERDES_V4_COM_FREQ_UPDATE 0x008 +#define QSERDES_V4_COM_BG_TIMER 0x00c +#define QSERDES_V4_COM_SSC_EN_CENTER 0x010 +#define QSERDES_V4_COM_SSC_ADJ_PER1 0x014 +#define QSERDES_V4_COM_SSC_ADJ_PER2 0x018 +#define QSERDES_V4_COM_SSC_PER1 0x01c +#define QSERDES_V4_COM_SSC_PER2 0x020 +#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0 0x024 +#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0 0x028 +#define QSERDES_V4_COM_SSC_STEP_SIZE3_MODE0 0x02c +#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1 0x030 +#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1 0x034 +#define QSERDES_V4_COM_SSC_STEP_SIZE3_MODE1 0x038 +#define QSERDES_V4_COM_POST_DIV 0x03c +#define QSERDES_V4_COM_POST_DIV_MUX 0x040 +#define QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN 0x044 +#define QSERDES_V4_COM_CLK_ENABLE1 0x048 +#define QSERDES_V4_COM_SYS_CLK_CTRL 0x04c +#define QSERDES_V4_COM_SYSCLK_BUF_ENABLE 0x050 +#define QSERDES_V4_COM_PLL_EN 0x054 +#define QSERDES_V4_COM_PLL_IVCO 0x058 +#define QSERDES_V4_COM_CMN_IETRIM 0x05c +#define QSERDES_V4_COM_CMN_IPTRIM 0x060 +#define QSERDES_V4_COM_EP_CLOCK_DETECT_CTRL 0x064 +#define QSERDES_V4_COM_SYSCLK_DET_COMP_STATUS 0x068 +#define QSERDES_V4_COM_CLK_EP_DIV_MODE0 0x06c +#define QSERDES_V4_COM_CLK_EP_DIV_MODE1 0x070 +#define QSERDES_V4_COM_CP_CTRL_MODE0 0x074 +#define QSERDES_V4_COM_CP_CTRL_MODE1 0x078 +#define QSERDES_V4_COM_PLL_RCTRL_MODE0 0x07c +#define QSERDES_V4_COM_PLL_RCTRL_MODE1 0x080 +#define QSERDES_V4_COM_PLL_CCTRL_MODE0 0x084 +#define QSERDES_V4_COM_PLL_CCTRL_MODE1 0x088 +#define QSERDES_V4_COM_PLL_CNTRL 0x08c +#define QSERDES_V4_COM_BIAS_EN_CTRL_BY_PSM 0x090 +#define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094 +#define QSERDES_V4_COM_CML_SYSCLK_SEL 0x098 +#define QSERDES_V4_COM_RESETSM_CNTRL 0x09c +#define QSERDES_V4_COM_RESETSM_CNTRL2 0x0a0 +#define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4 +#define QSERDES_V4_COM_LOCK_CMP_CFG 0x0a8 +#define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac +#define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0 +#define QSERDES_V4_COM_LOCK_CMP1_MODE1 0x0b4 +#define QSERDES_V4_COM_LOCK_CMP2_MODE1 0x0b8 +#define QSERDES_V4_COM_DEC_START_MODE0 0x0bc +#define QSERDES_V4_COM_DEC_START_MSB_MODE0 0x0c0 +#define QSERDES_V4_COM_DEC_START_MODE1 0x0c4 +#define QSERDES_V4_COM_DEC_START_MSB_MODE1 0x0c8 +#define QSERDES_V4_COM_DIV_FRAC_START1_MODE0 0x0cc +#define QSERDES_V4_COM_DIV_FRAC_START2_MODE0 0x0d0 +#define QSERDES_V4_COM_DIV_FRAC_START3_MODE0 0x0d4 +#define QSERDES_V4_COM_DIV_FRAC_START1_MODE1 0x0d8 +#define QSERDES_V4_COM_DIV_FRAC_START2_MODE1 0x0dc +#define QSERDES_V4_COM_DIV_FRAC_START3_MODE1 0x0e0 +#define QSERDES_V4_COM_INTEGLOOP_INITVAL 0x0e4 +#define QSERDES_V4_COM_INTEGLOOP_EN 0x0e8 +#define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0 0x0ec +#define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0 0x0f0 +#define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1 0x0f4 +#define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1 0x0f8 +#define QSERDES_V4_COM_INTEGLOOP_P_PATH_GAIN0 0x0fc +#define QSERDES_V4_COM_INTEGLOOP_P_PATH_GAIN1 0x100 +#define QSERDES_V4_COM_VCOCAL_DEADMAN_CTRL 0x104 +#define QSERDES_V4_COM_VCO_TUNE_CTRL 0x108 +#define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c +#define QSERDES_V4_COM_VCO_TUNE1_MODE0 0x110 +#define QSERDES_V4_COM_VCO_TUNE2_MODE0 0x114 +#define QSERDES_V4_COM_VCO_TUNE1_MODE1 0x118 +#define QSERDES_V4_COM_VCO_TUNE2_MODE1 0x11c +#define QSERDES_V4_COM_VCO_TUNE_INITVAL1 0x120 +#define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124 +#define QSERDES_V4_COM_VCO_TUNE_MINVAL1 0x128 +#define QSERDES_V4_COM_VCO_TUNE_MINVAL2 0x12c +#define QSERDES_V4_COM_VCO_TUNE_MAXVAL1 0x130 +#define QSERDES_V4_COM_VCO_TUNE_MAXVAL2 0x134 +#define QSERDES_V4_COM_VCO_TUNE_TIMER1 0x138 +#define QSERDES_V4_COM_VCO_TUNE_TIMER2 0x13c +#define QSERDES_V4_COM_CMN_STATUS 0x140 +#define QSERDES_V4_COM_RESET_SM_STATUS 0x144 +#define QSERDES_V4_COM_RESTRIM_CODE_STATUS 0x148 +#define QSERDES_V4_COM_PLLCAL_CODE1_STATUS 0x14c +#define QSERDES_V4_COM_PLLCAL_CODE2_STATUS 0x150 +#define QSERDES_V4_COM_CLK_SELECT 0x154 +#define QSERDES_V4_COM_HSCLK_SEL 0x158 +#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c +#define QSERDES_V4_COM_INTEGLOOP_BINCODE_STATUS 0x160 +#define QSERDES_V4_COM_PLL_ANALOG 0x164 +#define QSERDES_V4_COM_CORECLK_DIV_MODE0 0x168 +#define QSERDES_V4_COM_CORECLK_DIV_MODE1 0x16c +#define QSERDES_V4_COM_SW_RESET 0x170 +#define QSERDES_V4_COM_CORE_CLK_EN 0x174 +#define QSERDES_V4_COM_C_READY_STATUS 0x178 +#define QSERDES_V4_COM_CMN_CONFIG 0x17c +#define QSERDES_V4_COM_CMN_RATE_OVERRIDE 0x180 +#define QSERDES_V4_COM_SVS_MODE_CLK_SEL 0x184 +#define QSERDES_V4_COM_DEBUG_BUS0 0x188 +#define QSERDES_V4_COM_DEBUG_BUS1 0x18c +#define QSERDES_V4_COM_DEBUG_BUS2 0x190 +#define QSERDES_V4_COM_DEBUG_BUS3 0x194 +#define QSERDES_V4_COM_DEBUG_BUS_SEL 0x198 +#define QSERDES_V4_COM_CMN_MISC1 0x19c +#define QSERDES_V4_COM_CMN_MISC2 0x1a0 +#define QSERDES_V4_COM_CMN_MODE 0x1a4 +#define QSERDES_V4_COM_VCO_DC_LEVEL_CTRL 0x1a8 +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8 +#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h new file mode 100644 index 000000000000..c8afdf7bc1ee --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v5.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_V5_H_ +#define QCOM_PHY_QMP_QSERDES_COM_V5_H_ + +/* Only for QMP V5 PHY - QSERDES COM registers */ +#define QSERDES_V5_COM_ATB_SEL1 0x000 +#define QSERDES_V5_COM_ATB_SEL2 0x004 +#define QSERDES_V5_COM_FREQ_UPDATE 0x008 +#define QSERDES_V5_COM_BG_TIMER 0x00c +#define QSERDES_V5_COM_SSC_EN_CENTER 0x010 +#define QSERDES_V5_COM_SSC_ADJ_PER1 0x014 +#define QSERDES_V5_COM_SSC_ADJ_PER2 0x018 +#define QSERDES_V5_COM_SSC_PER1 0x01c +#define QSERDES_V5_COM_SSC_PER2 0x020 +#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0 0x024 +#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0 0x028 +#define QSERDES_V5_COM_SSC_STEP_SIZE3_MODE0 0x02c +#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1 0x030 +#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1 0x034 +#define QSERDES_V5_COM_SSC_STEP_SIZE3_MODE1 0x038 +#define QSERDES_V5_COM_POST_DIV 0x03c +#define QSERDES_V5_COM_POST_DIV_MUX 0x040 +#define QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN 0x044 +#define QSERDES_V5_COM_CLK_ENABLE1 0x048 +#define QSERDES_V5_COM_SYS_CLK_CTRL 0x04c +#define QSERDES_V5_COM_SYSCLK_BUF_ENABLE 0x050 +#define QSERDES_V5_COM_PLL_EN 0x054 +#define QSERDES_V5_COM_PLL_IVCO 0x058 +#define QSERDES_V5_COM_CMN_IETRIM 0x05c +#define QSERDES_V5_COM_CMN_IPTRIM 0x060 +#define QSERDES_V5_COM_EP_CLOCK_DETECT_CTRL 0x064 +#define QSERDES_V5_COM_SYSCLK_DET_COMP_STATUS 0x068 +#define QSERDES_V5_COM_CLK_EP_DIV_MODE0 0x06c +#define QSERDES_V5_COM_CLK_EP_DIV_MODE1 0x070 +#define QSERDES_V5_COM_CP_CTRL_MODE0 0x074 +#define QSERDES_V5_COM_CP_CTRL_MODE1 0x078 +#define QSERDES_V5_COM_PLL_RCTRL_MODE0 0x07c +#define QSERDES_V5_COM_PLL_RCTRL_MODE1 0x080 +#define QSERDES_V5_COM_PLL_CCTRL_MODE0 0x084 +#define QSERDES_V5_COM_PLL_CCTRL_MODE1 0x088 +#define QSERDES_V5_COM_PLL_CNTRL 0x08c +#define QSERDES_V5_COM_BIAS_EN_CTRL_BY_PSM 0x090 +#define QSERDES_V5_COM_SYSCLK_EN_SEL 0x094 +#define QSERDES_V5_COM_CML_SYSCLK_SEL 0x098 +#define QSERDES_V5_COM_RESETSM_CNTRL 0x09c +#define QSERDES_V5_COM_RESETSM_CNTRL2 0x0a0 +#define QSERDES_V5_COM_LOCK_CMP_EN 0x0a4 +#define QSERDES_V5_COM_LOCK_CMP_CFG 0x0a8 +#define QSERDES_V5_COM_LOCK_CMP1_MODE0 0x0ac +#define QSERDES_V5_COM_LOCK_CMP2_MODE0 0x0b0 +#define QSERDES_V5_COM_LOCK_CMP1_MODE1 0x0b4 +#define QSERDES_V5_COM_LOCK_CMP2_MODE1 0x0b8 +#define QSERDES_V5_COM_DEC_START_MODE0 0x0bc +#define QSERDES_V5_COM_DEC_START_MSB_MODE0 0x0c0 +#define QSERDES_V5_COM_DEC_START_MODE1 0x0c4 +#define QSERDES_V5_COM_DEC_START_MSB_MODE1 0x0c8 +#define QSERDES_V5_COM_DIV_FRAC_START1_MODE0 0x0cc +#define QSERDES_V5_COM_DIV_FRAC_START2_MODE0 0x0d0 +#define QSERDES_V5_COM_DIV_FRAC_START3_MODE0 0x0d4 +#define QSERDES_V5_COM_DIV_FRAC_START1_MODE1 0x0d8 +#define QSERDES_V5_COM_DIV_FRAC_START2_MODE1 0x0dc +#define QSERDES_V5_COM_DIV_FRAC_START3_MODE1 0x0e0 +#define QSERDES_V5_COM_INTEGLOOP_INITVAL 0x0e4 +#define QSERDES_V5_COM_INTEGLOOP_EN 0x0e8 +#define QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE0 0x0ec +#define QSERDES_V5_COM_INTEGLOOP_GAIN1_MODE0 0x0f0 +#define QSERDES_V5_COM_INTEGLOOP_GAIN0_MODE1 0x0f4 +#define QSERDES_V5_COM_INTEGLOOP_GAIN1_MODE1 0x0f8 +#define QSERDES_V5_COM_INTEGLOOP_P_PATH_GAIN0 0x0fc +#define QSERDES_V5_COM_INTEGLOOP_P_PATH_GAIN1 0x100 +#define QSERDES_V5_COM_VCOCAL_DEADMAN_CTRL 0x104 +#define QSERDES_V5_COM_VCO_TUNE_CTRL 0x108 +#define QSERDES_V5_COM_VCO_TUNE_MAP 0x10c +#define QSERDES_V5_COM_VCO_TUNE1_MODE0 0x110 +#define QSERDES_V5_COM_VCO_TUNE2_MODE0 0x114 +#define QSERDES_V5_COM_VCO_TUNE1_MODE1 0x118 +#define QSERDES_V5_COM_VCO_TUNE2_MODE1 0x11c +#define QSERDES_V5_COM_VCO_TUNE_INITVAL1 0x120 +#define QSERDES_V5_COM_VCO_TUNE_INITVAL2 0x124 +#define QSERDES_V5_COM_VCO_TUNE_MINVAL1 0x128 +#define QSERDES_V5_COM_VCO_TUNE_MINVAL2 0x12c +#define QSERDES_V5_COM_VCO_TUNE_MAXVAL1 0x130 +#define QSERDES_V5_COM_VCO_TUNE_MAXVAL2 0x134 +#define QSERDES_V5_COM_VCO_TUNE_TIMER1 0x138 +#define QSERDES_V5_COM_VCO_TUNE_TIMER2 0x13c +#define QSERDES_V5_COM_CMN_STATUS 0x140 +#define QSERDES_V5_COM_RESET_SM_STATUS 0x144 +#define QSERDES_V5_COM_RESTRIM_CODE_STATUS 0x148 +#define QSERDES_V5_COM_PLLCAL_CODE1_STATUS 0x14c +#define QSERDES_V5_COM_PLLCAL_CODE2_STATUS 0x150 +#define QSERDES_V5_COM_CLK_SELECT 0x154 +#define QSERDES_V5_COM_HSCLK_SEL 0x158 +#define QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL 0x15c +#define QSERDES_V5_COM_INTEGLOOP_BINCODE_STATUS 0x160 +#define QSERDES_V5_COM_PLL_ANALOG 0x164 +#define QSERDES_V5_COM_CORECLK_DIV_MODE0 0x168 +#define QSERDES_V5_COM_CORECLK_DIV_MODE1 0x16c +#define QSERDES_V5_COM_SW_RESET 0x170 +#define QSERDES_V5_COM_CORE_CLK_EN 0x174 +#define QSERDES_V5_COM_C_READY_STATUS 0x178 +#define QSERDES_V5_COM_CMN_CONFIG 0x17c +#define QSERDES_V5_COM_CMN_RATE_OVERRIDE 0x180 +#define QSERDES_V5_COM_SVS_MODE_CLK_SEL 0x184 +#define QSERDES_V5_COM_DEBUG_BUS0 0x188 +#define QSERDES_V5_COM_DEBUG_BUS1 0x18c +#define QSERDES_V5_COM_DEBUG_BUS2 0x190 +#define QSERDES_V5_COM_DEBUG_BUS3 0x194 +#define QSERDES_V5_COM_DEBUG_BUS_SEL 0x198 +#define QSERDES_V5_COM_CMN_MISC1 0x19c +#define QSERDES_V5_COM_CMN_MODE 0x1a0 +#define QSERDES_V5_COM_CMN_MODE_CONTD 0x1a4 +#define QSERDES_V5_COM_VCO_DC_LEVEL_CTRL 0x1a8 +#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac +#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 +#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 +#define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8 +#define QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc +#define QSERDES_V5_COM_RESERVED_1 0x1c0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h new file mode 100644 index 000000000000..fbaf6ef467f8 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_H_ +#define QCOM_PHY_QMP_QSERDES_COM_H_ + +/* Only for QMP V2 PHY - QSERDES COM registers */ +#define QSERDES_COM_ATB_SEL1 0x000 +#define QSERDES_COM_ATB_SEL2 0x004 +#define QSERDES_COM_FREQ_UPDATE 0x008 +#define QSERDES_COM_BG_TIMER 0x00c +#define QSERDES_COM_SSC_EN_CENTER 0x010 +#define QSERDES_COM_SSC_ADJ_PER1 0x014 +#define QSERDES_COM_SSC_ADJ_PER2 0x018 +#define QSERDES_COM_SSC_PER1 0x01c +#define QSERDES_COM_SSC_PER2 0x020 +#define QSERDES_COM_SSC_STEP_SIZE1 0x024 +#define QSERDES_COM_SSC_STEP_SIZE2 0x028 +#define QSERDES_COM_POST_DIV 0x02c +#define QSERDES_COM_POST_DIV_MUX 0x030 +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 +#define QSERDES_COM_CLK_ENABLE1 0x038 +#define QSERDES_COM_SYS_CLK_CTRL 0x03c +#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 +#define QSERDES_COM_PLL_EN 0x044 +#define QSERDES_COM_PLL_IVCO 0x048 +#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c +#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 +#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 +#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 +#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c +#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 +#define QSERDES_COM_LOCK_CMP1_MODE2 0x064 +#define QSERDES_COM_CMN_RSVD0 0x064 +#define QSERDES_COM_LOCK_CMP2_MODE2 0x068 +#define QSERDES_COM_EP_CLOCK_DETECT_CTRL 0x068 +#define QSERDES_COM_LOCK_CMP3_MODE2 0x06c +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS 0x06c +#define QSERDES_COM_BG_TRIM 0x070 +#define QSERDES_COM_CLK_EP_DIV 0x074 +#define QSERDES_COM_CP_CTRL_MODE0 0x078 +#define QSERDES_COM_CP_CTRL_MODE1 0x07c +#define QSERDES_COM_CP_CTRL_MODE2 0x080 +#define QSERDES_COM_CMN_RSVD1 0x080 +#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 +#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 +#define QSERDES_COM_PLL_RCTRL_MODE2 0x08c +#define QSERDES_COM_CMN_RSVD2 0x08c +#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 +#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 +#define QSERDES_COM_PLL_CCTRL_MODE2 0x098 +#define QSERDES_COM_CMN_RSVD3 0x098 +#define QSERDES_COM_PLL_CNTRL 0x09c +#define QSERDES_COM_PHASE_SEL_CTRL 0x0a0 +#define QSERDES_COM_PHASE_SEL_DC 0x0a4 +#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x0a8 +#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8 +#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac +#define QSERDES_COM_CML_SYSCLK_SEL 0x0b0 +#define QSERDES_COM_RESETSM_CNTRL 0x0b4 +#define QSERDES_COM_RESETSM_CNTRL2 0x0b8 +#define QSERDES_COM_RESTRIM_CTRL 0x0bc +#define QSERDES_COM_RESTRIM_CTRL2 0x0c0 +#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 +#define QSERDES_COM_LOCK_CMP_EN 0x0c8 +#define QSERDES_COM_LOCK_CMP_CFG 0x0cc +#define QSERDES_COM_DEC_START_MODE0 0x0d0 +#define QSERDES_COM_DEC_START_MODE1 0x0d4 +#define QSERDES_COM_DEC_START_MODE2 0x0d8 +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x0d8 +#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc +#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 +#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 +#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 +#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec +#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 +#define QSERDES_COM_DIV_FRAC_START1_MODE2 0x0f4 +#define QSERDES_COM_VCO_TUNE_MINVAL1 0x0f4 +#define QSERDES_COM_DIV_FRAC_START2_MODE2 0x0f8 +#define QSERDES_COM_VCO_TUNE_MINVAL2 0x0f8 +#define QSERDES_COM_DIV_FRAC_START3_MODE2 0x0fc +#define QSERDES_COM_CMN_RSVD4 0x0fc +#define QSERDES_COM_INTEGLOOP_INITVAL 0x100 +#define QSERDES_COM_INTEGLOOP_EN 0x104 +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 0x118 +#define QSERDES_COM_VCO_TUNE_MAXVAL1 0x118 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 0x11c +#define QSERDES_COM_VCO_TUNE_MAXVAL2 0x11c +#define QSERDES_COM_RES_TRIM_CONTROL2 0x120 +#define QSERDES_COM_VCO_TUNE_CTRL 0x124 +#define QSERDES_COM_VCO_TUNE_MAP 0x128 +#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c +#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 +#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 +#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 +#define QSERDES_COM_VCO_TUNE1_MODE2 0x13c +#define QSERDES_COM_VCO_TUNE_INITVAL1 0x13c +#define QSERDES_COM_VCO_TUNE2_MODE2 0x140 +#define QSERDES_COM_VCO_TUNE_INITVAL2 0x140 +#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 +#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 +#define QSERDES_COM_SAR 0x14c +#define QSERDES_COM_SAR_CLK 0x150 +#define QSERDES_COM_SAR_CODE_OUT_STATUS 0x154 +#define QSERDES_COM_SAR_CODE_READY_STATUS 0x158 +#define QSERDES_COM_CMN_STATUS 0x15c +#define QSERDES_COM_RESET_SM_STATUS 0x160 +#define QSERDES_COM_RESTRIM_CODE_STATUS 0x164 +#define QSERDES_COM_PLLCAL_CODE1_STATUS 0x168 +#define QSERDES_COM_PLLCAL_CODE2_STATUS 0x16c +#define QSERDES_COM_BG_CTRL 0x170 +#define QSERDES_COM_CLK_SELECT 0x174 +#define QSERDES_COM_HSCLK_SEL 0x178 +#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS 0x17c +#define QSERDES_COM_PLL_ANALOG 0x180 +#define QSERDES_COM_CORECLK_DIV 0x184 +#define QSERDES_COM_SW_RESET 0x188 +#define QSERDES_COM_CORE_CLK_EN 0x18c +#define QSERDES_COM_C_READY_STATUS 0x190 +#define QSERDES_COM_CMN_CONFIG 0x194 +#define QSERDES_COM_CMN_RATE_OVERRIDE 0x198 +#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c +#define QSERDES_COM_DEBUG_BUS0 0x1a0 +#define QSERDES_COM_DEBUG_BUS1 0x1a4 +#define QSERDES_COM_DEBUG_BUS2 0x1a8 +#define QSERDES_COM_DEBUG_BUS3 0x1ac +#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 +#define QSERDES_COM_CMN_MISC1 0x1b4 +#define QSERDES_COM_CMN_MISC2 0x1b8 +#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc +#define QSERDES_COM_CORECLK_DIV_MODE2 0x1c0 +#define QSERDES_COM_CMN_RSVD5 0x1c0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h new file mode 100644 index 000000000000..ad326e301a3a --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-pll.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_PLL_H_ +#define QCOM_PHY_QMP_QSERDES_PLL_H_ + +/* QMP V2 PHY for PCIE gen3 ports - QSERDES PLL registers */ +#define QSERDES_PLL_BG_TIMER 0x00c +#define QSERDES_PLL_SSC_PER1 0x01c +#define QSERDES_PLL_SSC_PER2 0x020 +#define QSERDES_PLL_SSC_STEP_SIZE1_MODE0 0x024 +#define QSERDES_PLL_SSC_STEP_SIZE2_MODE0 0x028 +#define QSERDES_PLL_SSC_STEP_SIZE1_MODE1 0x02c +#define QSERDES_PLL_SSC_STEP_SIZE2_MODE1 0x030 +#define QSERDES_PLL_BIAS_EN_CLKBUFLR_EN 0x03c +#define QSERDES_PLL_CLK_ENABLE1 0x040 +#define QSERDES_PLL_SYS_CLK_CTRL 0x044 +#define QSERDES_PLL_SYSCLK_BUF_ENABLE 0x048 +#define QSERDES_PLL_PLL_IVCO 0x050 +#define QSERDES_PLL_LOCK_CMP1_MODE0 0x054 +#define QSERDES_PLL_LOCK_CMP2_MODE0 0x058 +#define QSERDES_PLL_LOCK_CMP1_MODE1 0x060 +#define QSERDES_PLL_LOCK_CMP2_MODE1 0x064 +#define QSERDES_PLL_BG_TRIM 0x074 +#define QSERDES_PLL_CLK_EP_DIV_MODE0 0x078 +#define QSERDES_PLL_CLK_EP_DIV_MODE1 0x07c +#define QSERDES_PLL_CP_CTRL_MODE0 0x080 +#define QSERDES_PLL_CP_CTRL_MODE1 0x084 +#define QSERDES_PLL_PLL_RCTRL_MODE0 0x088 +#define QSERDES_PLL_PLL_RCTRL_MODE1 0x08c +#define QSERDES_PLL_PLL_CCTRL_MODE0 0x090 +#define QSERDES_PLL_PLL_CCTRL_MODE1 0x094 +#define QSERDES_PLL_BIAS_EN_CTRL_BY_PSM 0x0a4 +#define QSERDES_PLL_SYSCLK_EN_SEL 0x0a8 +#define QSERDES_PLL_RESETSM_CNTRL 0x0b0 +#define QSERDES_PLL_LOCK_CMP_EN 0x0c4 +#define QSERDES_PLL_DEC_START_MODE0 0x0cc +#define QSERDES_PLL_DEC_START_MODE1 0x0d0 +#define QSERDES_PLL_DIV_FRAC_START1_MODE0 0x0d8 +#define QSERDES_PLL_DIV_FRAC_START2_MODE0 0x0dc +#define QSERDES_PLL_DIV_FRAC_START3_MODE0 0x0e0 +#define QSERDES_PLL_DIV_FRAC_START1_MODE1 0x0e4 +#define QSERDES_PLL_DIV_FRAC_START2_MODE1 0x0e8 +#define QSERDES_PLL_DIV_FRAC_START3_MODE1 0x0ec +#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE0 0x100 +#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE0 0x104 +#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE1 0x108 +#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE1 0x10c +#define QSERDES_PLL_VCO_TUNE_MAP 0x120 +#define QSERDES_PLL_VCO_TUNE1_MODE0 0x124 +#define QSERDES_PLL_VCO_TUNE2_MODE0 0x128 +#define QSERDES_PLL_VCO_TUNE1_MODE1 0x12c +#define QSERDES_PLL_VCO_TUNE2_MODE1 0x130 +#define QSERDES_PLL_VCO_TUNE_TIMER1 0x13c +#define QSERDES_PLL_VCO_TUNE_TIMER2 0x140 +#define QSERDES_PLL_CLK_SELECT 0x16c +#define QSERDES_PLL_HSCLK_SEL 0x170 +#define QSERDES_PLL_CORECLK_DIV 0x17c +#define QSERDES_PLL_CORE_CLK_EN 0x184 +#define QSERDES_PLL_CMN_CONFIG 0x18c +#define QSERDES_PLL_SVS_MODE_CLK_SEL 0x194 +#define QSERDES_PLL_CORECLK_DIV_MODE1 0x1b4 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h new file mode 100644 index 000000000000..161e6df30ea8 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v3.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V3_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V3_H_ + +/* Only for QMP V3 PHY - TX registers */ +#define QSERDES_V3_TX_BIST_MODE_LANENO 0x000 +#define QSERDES_V3_TX_CLKBUF_ENABLE 0x008 +#define QSERDES_V3_TX_TX_EMP_POST1_LVL 0x00c +#define QSERDES_V3_TX_TX_DRV_LVL 0x01c +#define QSERDES_V3_TX_RESET_TSYNC_EN 0x024 +#define QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN 0x028 +#define QSERDES_V3_TX_TX_BAND 0x02c +#define QSERDES_V3_TX_SLEW_CNTL 0x030 +#define QSERDES_V3_TX_INTERFACE_SELECT 0x034 +#define QSERDES_V3_TX_RES_CODE_LANE_TX 0x03c +#define QSERDES_V3_TX_RES_CODE_LANE_RX 0x040 +#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044 +#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048 +#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058 +#define QSERDES_V3_TX_TRANSCEIVER_BIAS_EN 0x05c +#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060 +#define QSERDES_V3_TX_TX_POL_INV 0x064 +#define QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN 0x068 +#define QSERDES_V3_TX_LANE_MODE_1 0x08c +#define QSERDES_V3_TX_LANE_MODE_2 0x090 +#define QSERDES_V3_TX_LANE_MODE_3 0x094 +#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4 +#define QSERDES_V3_TX_TRAN_DRVR_EMP_EN 0x0c0 +#define QSERDES_V3_TX_TX_INTERFACE_MODE 0x0c4 +#define QSERDES_V3_TX_VMODE_CTRL1 0x0f0 + +/* Only for QMP V3 PHY - RX registers */ +#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008 +#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c +#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014 +#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024 +#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028 +#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c +#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030 +#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 +#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 +#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044 +#define QSERDES_V3_RX_RX_TERM_BW 0x07c +#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc +#define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0 +#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB 0x0c8 +#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB 0x0cc +#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d0 +#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4 +#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8 +#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc +#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8 +#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc +#define QSERDES_V3_RX_SIGDET_ENABLES 0x100 +#define QSERDES_V3_RX_SIGDET_CNTRL 0x104 +#define QSERDES_V3_RX_SIGDET_LVL 0x108 +#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL 0x10c +#define QSERDES_V3_RX_RX_BAND 0x110 +#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c +#define QSERDES_V3_RX_RX_MODE_00 0x164 +#define QSERDES_V3_RX_RX_MODE_01 0x168 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h new file mode 100644 index 000000000000..6ee3bec9ac4a --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V4_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V4_H_ + +/* Only for QMP V4 PHY - TX registers */ +#define QSERDES_V4_TX_BIST_MODE_LANENO 0x000 +#define QSERDES_V4_TX_BIST_INVERT 0x004 +#define QSERDES_V4_TX_CLKBUF_ENABLE 0x008 +#define QSERDES_V4_TX_TX_EMP_POST1_LVL 0x00c +#define QSERDES_V4_TX_TX_IDLE_LVL_LARGE_AMP 0x010 +#define QSERDES_V4_TX_TX_DRV_LVL 0x014 +#define QSERDES_V4_TX_TX_DRV_LVL_OFFSET 0x018 +#define QSERDES_V4_TX_RESET_TSYNC_EN 0x01c +#define QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN 0x020 +#define QSERDES_V4_TX_TX_BAND 0x024 +#define QSERDES_V4_TX_SLEW_CNTL 0x028 +#define QSERDES_V4_TX_INTERFACE_SELECT 0x02c +#define QSERDES_V4_TX_LPB_EN 0x030 +#define QSERDES_V4_TX_RES_CODE_LANE_TX 0x034 +#define QSERDES_V4_TX_RES_CODE_LANE_RX 0x038 +#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x03c +#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX 0x040 +#define QSERDES_V4_TX_PERL_LENGTH1 0x044 +#define QSERDES_V4_TX_PERL_LENGTH2 0x048 +#define QSERDES_V4_TX_SERDES_BYP_EN_OUT 0x04c +#define QSERDES_V4_TX_DEBUG_BUS_SEL 0x050 +#define QSERDES_V4_TX_TRANSCEIVER_BIAS_EN 0x054 +#define QSERDES_V4_TX_HIGHZ_DRVR_EN 0x058 +#define QSERDES_V4_TX_TX_POL_INV 0x05c +#define QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN 0x060 +#define QSERDES_V4_TX_BIST_PATTERN1 0x064 +#define QSERDES_V4_TX_BIST_PATTERN2 0x068 +#define QSERDES_V4_TX_BIST_PATTERN3 0x06c +#define QSERDES_V4_TX_BIST_PATTERN4 0x070 +#define QSERDES_V4_TX_BIST_PATTERN5 0x074 +#define QSERDES_V4_TX_BIST_PATTERN6 0x078 +#define QSERDES_V4_TX_BIST_PATTERN7 0x07c +#define QSERDES_V4_TX_BIST_PATTERN8 0x080 +#define QSERDES_V4_TX_LANE_MODE_1 0x084 +#define QSERDES_V4_TX_LANE_MODE_2 0x088 +#define QSERDES_V4_TX_LANE_MODE_3 0x08c +#define QSERDES_V4_TX_ATB_SEL1 0x090 +#define QSERDES_V4_TX_ATB_SEL2 0x094 +#define QSERDES_V4_TX_RCV_DETECT_LVL 0x098 +#define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x09c +#define QSERDES_V4_TX_PRBS_SEED1 0x0a0 +#define QSERDES_V4_TX_PRBS_SEED2 0x0a4 +#define QSERDES_V4_TX_PRBS_SEED3 0x0a8 +#define QSERDES_V4_TX_PRBS_SEED4 0x0ac +#define QSERDES_V4_TX_RESET_GEN 0x0b0 +#define QSERDES_V4_TX_RESET_GEN_MUXES 0x0b4 +#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0x0b8 +#define QSERDES_V4_TX_TX_INTERFACE_MODE 0x0bc +#define QSERDES_V4_TX_PWM_CTRL 0x0c0 +#define QSERDES_V4_TX_PWM_ENCODED_OR_DATA 0x0c4 +#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND2 0x0c8 +#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND2 0x0cc +#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND2 0x0d0 +#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND2 0x0d4 +#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x0d8 +#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x0dc +#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x0e0 +#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x0e4 +#define QSERDES_V4_TX_VMODE_CTRL1 0x0e8 +#define QSERDES_V4_TX_ALOG_OBSV_BUS_CTRL_1 0x0ec +#define QSERDES_V4_TX_BIST_STATUS 0x0f0 +#define QSERDES_V4_TX_BIST_ERROR_COUNT1 0x0f4 +#define QSERDES_V4_TX_BIST_ERROR_COUNT2 0x0f8 +#define QSERDES_V4_TX_ALOG_OBSV_BUS_STATUS_1 0x0fc +#define QSERDES_V4_TX_LANE_DIG_CONFIG 0x100 +#define QSERDES_V4_TX_PI_QEC_CTRL 0x104 +#define QSERDES_V4_TX_PRE_EMPH 0x108 +#define QSERDES_V4_TX_SW_RESET 0x10c +#define QSERDES_V4_TX_DCC_OFFSET 0x110 +#define QSERDES_V4_TX_DIG_BKUP_CTRL 0x114 +#define QSERDES_V4_TX_DEBUG_BUS0 0x118 +#define QSERDES_V4_TX_DEBUG_BUS1 0x11c +#define QSERDES_V4_TX_DEBUG_BUS2 0x120 +#define QSERDES_V4_TX_DEBUG_BUS3 0x124 +#define QSERDES_V4_TX_READ_EQCODE 0x128 +#define QSERDES_V4_TX_READ_OFFSETCODE 0x12c +#define QSERDES_V4_TX_IA_ERROR_COUNTER_LOW 0x130 +#define QSERDES_V4_TX_IA_ERROR_COUNTER_HIGH 0x134 +#define QSERDES_V4_TX_VGA_READ_CODE 0x138 +#define QSERDES_V4_TX_VTH_READ_CODE 0x13c +#define QSERDES_V4_TX_DFE_TAP1_READ_CODE 0x140 +#define QSERDES_V4_TX_DFE_TAP2_READ_CODE 0x144 +#define QSERDES_V4_TX_IDAC_STATUS_I 0x148 +#define QSERDES_V4_TX_IDAC_STATUS_IBAR 0x14c +#define QSERDES_V4_TX_IDAC_STATUS_Q 0x150 +#define QSERDES_V4_TX_IDAC_STATUS_QBAR 0x154 +#define QSERDES_V4_TX_IDAC_STATUS_A 0x158 +#define QSERDES_V4_TX_IDAC_STATUS_ABAR 0x15c +#define QSERDES_V4_TX_IDAC_STATUS_SM_ON 0x160 +#define QSERDES_V4_TX_IDAC_STATUS_CAL_DONE 0x164 +#define QSERDES_V4_TX_IDAC_STATUS_SIGNERROR 0x168 +#define QSERDES_V4_TX_DCC_CAL_STATUS 0x16c + +/* Only for QMP V4 PHY - RX registers */ +#define QSERDES_V4_RX_UCDR_FO_GAIN_HALF 0x000 +#define QSERDES_V4_RX_UCDR_FO_GAIN_QUARTER 0x004 +#define QSERDES_V4_RX_UCDR_FO_GAIN 0x008 +#define QSERDES_V4_RX_UCDR_SO_GAIN_HALF 0x00c +#define QSERDES_V4_RX_UCDR_SO_GAIN_QUARTER 0x010 +#define QSERDES_V4_RX_UCDR_SO_GAIN 0x014 +#define QSERDES_V4_RX_UCDR_SVS_FO_GAIN_HALF 0x018 +#define QSERDES_V4_RX_UCDR_SVS_FO_GAIN_QUARTER 0x01c +#define QSERDES_V4_RX_UCDR_SVS_FO_GAIN 0x020 +#define QSERDES_V4_RX_UCDR_SVS_SO_GAIN_HALF 0x024 +#define QSERDES_V4_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028 +#define QSERDES_V4_RX_UCDR_SVS_SO_GAIN 0x02c +#define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN 0x030 +#define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 +#define QSERDES_V4_RX_UCDR_FO_TO_SO_DELAY 0x038 +#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 +#define QSERDES_V4_RX_UCDR_PI_CONTROLS 0x044 +#define QSERDES_V4_RX_UCDR_PI_CTRL2 0x048 +#define QSERDES_V4_RX_UCDR_SB2_THRESH1 0x04c +#define QSERDES_V4_RX_UCDR_SB2_THRESH2 0x050 +#define QSERDES_V4_RX_UCDR_SB2_GAIN1 0x054 +#define QSERDES_V4_RX_UCDR_SB2_GAIN2 0x058 +#define QSERDES_V4_RX_AUX_CONTROL 0x05c +#define QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE 0x060 +#define QSERDES_V4_RX_RCLK_AUXDATA_SEL 0x064 +#define QSERDES_V4_RX_AC_JTAG_ENABLE 0x068 +#define QSERDES_V4_RX_AC_JTAG_INITP 0x06c +#define QSERDES_V4_RX_AC_JTAG_INITN 0x070 +#define QSERDES_V4_RX_AC_JTAG_LVL 0x074 +#define QSERDES_V4_RX_AC_JTAG_MODE 0x078 +#define QSERDES_V4_RX_AC_JTAG_RESET 0x07c +#define QSERDES_V4_RX_RX_TERM_BW 0x080 +#define QSERDES_V4_RX_RX_RCVR_IQ_EN 0x084 +#define QSERDES_V4_RX_RX_IDAC_I_DC_OFFSETS 0x088 +#define QSERDES_V4_RX_RX_IDAC_IBAR_DC_OFFSETS 0x08c +#define QSERDES_V4_RX_RX_IDAC_Q_DC_OFFSETS 0x090 +#define QSERDES_V4_RX_RX_IDAC_QBAR_DC_OFFSETS 0x094 +#define QSERDES_V4_RX_RX_IDAC_A_DC_OFFSETS 0x098 +#define QSERDES_V4_RX_RX_IDAC_ABAR_DC_OFFSETS 0x09c +#define QSERDES_V4_RX_RX_IDAC_EN 0x0a0 +#define QSERDES_V4_RX_RX_IDAC_ENABLES 0x0a4 +#define QSERDES_V4_RX_RX_IDAC_SIGN 0x0a8 +#define QSERDES_V4_RX_RX_HIGHZ_HIGHRATE 0x0ac +#define QSERDES_V4_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0b0 +#define QSERDES_V4_RX_DFE_1 0x0b4 +#define QSERDES_V4_RX_DFE_2 0x0b8 +#define QSERDES_V4_RX_DFE_3 0x0bc +#define QSERDES_V4_RX_DFE_4 0x0c0 +#define QSERDES_V4_RX_TX_ADAPT_PRE_THRESH1 0x0c4 +#define QSERDES_V4_RX_TX_ADAPT_PRE_THRESH2 0x0c8 +#define QSERDES_V4_RX_TX_ADAPT_POST_THRESH 0x0cc +#define QSERDES_V4_RX_TX_ADAPT_MAIN_THRESH 0x0d0 +#define QSERDES_V4_RX_VGA_CAL_CNTRL1 0x0d4 +#define QSERDES_V4_RX_VGA_CAL_CNTRL2 0x0d8 +#define QSERDES_V4_RX_GM_CAL 0x0dc +#define QSERDES_V4_RX_RX_VGA_GAIN2_LSB 0x0e0 +#define QSERDES_V4_RX_RX_VGA_GAIN2_MSB 0x0e4 +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1 0x0e8 +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0 +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4 +#define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW 0x0f8 +#define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH 0x0fc +#define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME 0x100 +#define QSERDES_V4_RX_RX_IDAC_ACCUMULATOR 0x104 +#define QSERDES_V4_RX_RX_EQ_OFFSET_LSB 0x108 +#define QSERDES_V4_RX_RX_EQ_OFFSET_MSB 0x10c +#define QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 +#define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114 +#define QSERDES_V4_RX_SIGDET_ENABLES 0x118 +#define QSERDES_V4_RX_SIGDET_CNTRL 0x11c +#define QSERDES_V4_RX_SIGDET_LVL 0x120 +#define QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL 0x124 +#define QSERDES_V4_RX_RX_BAND 0x128 +#define QSERDES_V4_RX_CDR_FREEZE_UP_DN 0x12c +#define QSERDES_V4_RX_CDR_RESET_OVERRIDE 0x130 +#define QSERDES_V4_RX_RX_INTERFACE_MODE 0x134 +#define QSERDES_V4_RX_JITTER_GEN_MODE 0x138 +#define QSERDES_V4_RX_SJ_AMP1 0x13c +#define QSERDES_V4_RX_SJ_AMP2 0x140 +#define QSERDES_V4_RX_SJ_PER1 0x144 +#define QSERDES_V4_RX_SJ_PER2 0x148 +#define QSERDES_V4_RX_PPM_OFFSET1 0x14c +#define QSERDES_V4_RX_PPM_OFFSET2 0x150 +#define QSERDES_V4_RX_SIGN_PPM_PERIOD1 0x154 +#define QSERDES_V4_RX_SIGN_PPM_PERIOD2 0x158 +#define QSERDES_V4_RX_RX_PWM_ENABLE_AND_DATA 0x15c +#define QSERDES_V4_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x160 +#define QSERDES_V4_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x164 +#define QSERDES_V4_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x168 +#define QSERDES_V4_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x16c +#define QSERDES_V4_RX_RX_MODE_00_LOW 0x170 +#define QSERDES_V4_RX_RX_MODE_00_HIGH 0x174 +#define QSERDES_V4_RX_RX_MODE_00_HIGH2 0x178 +#define QSERDES_V4_RX_RX_MODE_00_HIGH3 0x17c +#define QSERDES_V4_RX_RX_MODE_00_HIGH4 0x180 +#define QSERDES_V4_RX_RX_MODE_01_LOW 0x184 +#define QSERDES_V4_RX_RX_MODE_01_HIGH 0x188 +#define QSERDES_V4_RX_RX_MODE_01_HIGH2 0x18c +#define QSERDES_V4_RX_RX_MODE_01_HIGH3 0x190 +#define QSERDES_V4_RX_RX_MODE_01_HIGH4 0x194 +#define QSERDES_V4_RX_RX_MODE_10_LOW 0x198 +#define QSERDES_V4_RX_RX_MODE_10_HIGH 0x19c +#define QSERDES_V4_RX_RX_MODE_10_HIGH2 0x1a0 +#define QSERDES_V4_RX_RX_MODE_10_HIGH3 0x1a4 +#define QSERDES_V4_RX_RX_MODE_10_HIGH4 0x1a8 +#define QSERDES_V4_RX_PHPRE_CTRL 0x1ac +#define QSERDES_V4_RX_PHPRE_INITVAL 0x1b0 +#define QSERDES_V4_RX_DFE_EN_TIMER 0x1b4 +#define QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET 0x1b8 +#define QSERDES_V4_RX_DCC_CTRL1 0x1bc +#define QSERDES_V4_RX_DCC_CTRL2 0x1c0 +#define QSERDES_V4_RX_VTH_CODE 0x1c4 +#define QSERDES_V4_RX_VTH_MIN_THRESH 0x1c8 +#define QSERDES_V4_RX_VTH_MAX_THRESH 0x1cc +#define QSERDES_V4_RX_ALOG_OBSV_BUS_CTRL_1 0x1d0 +#define QSERDES_V4_RX_PI_CTRL1 0x1d4 +#define QSERDES_V4_RX_PI_CTRL2 0x1d8 +#define QSERDES_V4_RX_PI_QUAD 0x1dc +#define QSERDES_V4_RX_IDATA1 0x1e0 +#define QSERDES_V4_RX_IDATA2 0x1e4 +#define QSERDES_V4_RX_AUX_DATA1 0x1e8 +#define QSERDES_V4_RX_AUX_DATA2 0x1ec +#define QSERDES_V4_RX_AC_JTAG_OUTP 0x1f0 +#define QSERDES_V4_RX_AC_JTAG_OUTN 0x1f4 +#define QSERDES_V4_RX_RX_SIGDET 0x1f8 +#define QSERDES_V4_RX_ALOG_OBSV_BUS_STATUS_1 0x1fc + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h new file mode 100644 index 000000000000..114570f3017f --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v4_20.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V4_20_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V4_20_H_ + +/* Only for QMP V4_20 PHY - TX registers */ +#define QSERDES_V4_20_TX_LANE_MODE_1 0x88 +#define QSERDES_V4_20_TX_LANE_MODE_2 0x8c +#define QSERDES_V4_20_TX_LANE_MODE_3 0x90 +#define QSERDES_V4_20_TX_VMODE_CTRL1 0xc4 +#define QSERDES_V4_20_TX_PI_QEC_CTRL 0xe0 + +/* Only for QMP V4_20 PHY - RX registers */ +#define QSERDES_V4_20_RX_FO_GAIN_RATE2 0x008 +#define QSERDES_V4_20_RX_UCDR_PI_CONTROLS 0x058 +#define QSERDES_V4_20_RX_AUX_DATA_TCOARSE_TFINE 0x0ac +#define QSERDES_V4_20_RX_DFE_3 0x110 +#define QSERDES_V4_20_RX_DFE_DAC_ENABLE1 0x134 +#define QSERDES_V4_20_RX_DFE_DAC_ENABLE2 0x138 +#define QSERDES_V4_20_RX_VGA_CAL_CNTRL2 0x150 +#define QSERDES_V4_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x178 +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B1 0x1c8 +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B2 0x1cc +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B3 0x1d0 +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B4 0x1d4 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B0 0x1d8 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B1 0x1dc +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B2 0x1e0 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B3 0x1e4 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B4 0x1e8 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B0 0x1ec +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B1 0x1f0 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B2 0x1f4 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B3 0x1f8 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B4 0x1fc +#define QSERDES_V4_20_RX_PHPRE_CTRL 0x200 +#define QSERDES_V4_20_RX_DFE_CTLE_POST_CAL_OFFSET 0x20c +#define QSERDES_V4_20_RX_MARG_COARSE_CTRL2 0x23c + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h new file mode 100644 index 000000000000..fe8f3e330d09 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5.h @@ -0,0 +1,231 @@ + +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V5_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V5_H_ + +/* Only for QMP V5 PHY - TX registers */ +#define QSERDES_V5_TX_BIST_MODE_LANENO 0x000 +#define QSERDES_V5_TX_BIST_INVERT 0x004 +#define QSERDES_V5_TX_CLKBUF_ENABLE 0x008 +#define QSERDES_V5_TX_TX_EMP_POST1_LVL 0x00c +#define QSERDES_V5_TX_TX_IDLE_LVL_LARGE_AMP 0x010 +#define QSERDES_V5_TX_TX_DRV_LVL 0x014 +#define QSERDES_V5_TX_TX_DRV_LVL_OFFSET 0x018 +#define QSERDES_V5_TX_RESET_TSYNC_EN 0x01c +#define QSERDES_V5_TX_PRE_STALL_LDO_BOOST_EN 0x020 +#define QSERDES_V5_TX_TX_BAND 0x024 +#define QSERDES_V5_TX_SLEW_CNTL 0x028 +#define QSERDES_V5_TX_INTERFACE_SELECT 0x02c +#define QSERDES_V5_TX_LPB_EN 0x030 +#define QSERDES_V5_TX_RES_CODE_LANE_TX 0x034 +#define QSERDES_V5_TX_RES_CODE_LANE_RX 0x038 +#define QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX 0x03c +#define QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX 0x040 +#define QSERDES_V5_TX_PERL_LENGTH1 0x044 +#define QSERDES_V5_TX_PERL_LENGTH2 0x048 +#define QSERDES_V5_TX_SERDES_BYP_EN_OUT 0x04c +#define QSERDES_V5_TX_DEBUG_BUS_SEL 0x050 +#define QSERDES_V5_TX_TRANSCEIVER_BIAS_EN 0x054 +#define QSERDES_V5_TX_HIGHZ_DRVR_EN 0x058 +#define QSERDES_V5_TX_TX_POL_INV 0x05c +#define QSERDES_V5_TX_PARRATE_REC_DETECT_IDLE_EN 0x060 +#define QSERDES_V5_TX_BIST_PATTERN1 0x064 +#define QSERDES_V5_TX_BIST_PATTERN2 0x068 +#define QSERDES_V5_TX_BIST_PATTERN3 0x06c +#define QSERDES_V5_TX_BIST_PATTERN4 0x070 +#define QSERDES_V5_TX_BIST_PATTERN5 0x074 +#define QSERDES_V5_TX_BIST_PATTERN6 0x078 +#define QSERDES_V5_TX_BIST_PATTERN7 0x07c +#define QSERDES_V5_TX_BIST_PATTERN8 0x080 +#define QSERDES_V5_TX_LANE_MODE_1 0x084 +#define QSERDES_V5_TX_LANE_MODE_2 0x088 +#define QSERDES_V5_TX_LANE_MODE_3 0x08c +#define QSERDES_V5_TX_LANE_MODE_4 0x090 +#define QSERDES_V5_TX_LANE_MODE_5 0x094 +#define QSERDES_V5_TX_ATB_SEL1 0x098 +#define QSERDES_V5_TX_ATB_SEL2 0x09c +#define QSERDES_V5_TX_RCV_DETECT_LVL 0x0a0 +#define QSERDES_V5_TX_RCV_DETECT_LVL_2 0x0a4 +#define QSERDES_V5_TX_PRBS_SEED1 0x0a8 +#define QSERDES_V5_TX_PRBS_SEED2 0x0ac +#define QSERDES_V5_TX_PRBS_SEED3 0x0b0 +#define QSERDES_V5_TX_PRBS_SEED4 0x0b4 +#define QSERDES_V5_TX_RESET_GEN 0x0b8 +#define QSERDES_V5_TX_RESET_GEN_MUXES 0x0bc +#define QSERDES_V5_TX_TRAN_DRVR_EMP_EN 0x0c0 +#define QSERDES_V5_TX_TX_INTERFACE_MODE 0x0c4 +#define QSERDES_V5_TX_VMODE_CTRL1 0x0c8 +#define QSERDES_V5_TX_ALOG_OBSV_BUS_CTRL_1 0x0cc +#define QSERDES_V5_TX_BIST_STATUS 0x0d0 +#define QSERDES_V5_TX_BIST_ERROR_COUNT1 0x0d4 +#define QSERDES_V5_TX_BIST_ERROR_COUNT2 0x0d8 +#define QSERDES_V5_TX_ALOG_OBSV_BUS_STATUS_1 0x0dc +#define QSERDES_V5_TX_LANE_DIG_CONFIG 0x0e0 +#define QSERDES_V5_TX_PI_QEC_CTRL 0x0e4 +#define QSERDES_V5_TX_PRE_EMPH 0x0e8 +#define QSERDES_V5_TX_SW_RESET 0x0ec +#define QSERDES_V5_TX_DCC_OFFSET 0x0f0 +#define QSERDES_V5_TX_DCC_CMUX_POSTCAL_OFFSET 0x0f4 +#define QSERDES_V5_TX_DCC_CMUX_CAL_CTRL1 0x0f8 +#define QSERDES_V5_TX_DCC_CMUX_CAL_CTRL2 0x0fc +#define QSERDES_V5_TX_DIG_BKUP_CTRL 0x100 +#define QSERDES_V5_TX_DEBUG_BUS0 0x104 +#define QSERDES_V5_TX_DEBUG_BUS1 0x108 +#define QSERDES_V5_TX_DEBUG_BUS2 0x10c +#define QSERDES_V5_TX_DEBUG_BUS3 0x110 +#define QSERDES_V5_TX_READ_EQCODE 0x114 +#define QSERDES_V5_TX_READ_OFFSETCODE 0x118 +#define QSERDES_V5_TX_IA_ERROR_COUNTER_LOW 0x11c +#define QSERDES_V5_TX_IA_ERROR_COUNTER_HIGH 0x120 +#define QSERDES_V5_TX_VGA_READ_CODE 0x124 +#define QSERDES_V5_TX_VTH_READ_CODE 0x128 +#define QSERDES_V5_TX_DFE_TAP1_READ_CODE 0x12c +#define QSERDES_V5_TX_DFE_TAP2_READ_CODE 0x130 +#define QSERDES_V5_TX_IDAC_STATUS_I 0x134 +#define QSERDES_V5_TX_IDAC_STATUS_IBAR 0x138 +#define QSERDES_V5_TX_IDAC_STATUS_Q 0x13c +#define QSERDES_V5_TX_IDAC_STATUS_QBAR 0x140 +#define QSERDES_V5_TX_IDAC_STATUS_A 0x144 +#define QSERDES_V5_TX_IDAC_STATUS_ABAR 0x148 +#define QSERDES_V5_TX_IDAC_STATUS_SM_ON 0x14c +#define QSERDES_V5_TX_IDAC_STATUS_CAL_DONE 0x150 +#define QSERDES_V5_TX_IDAC_STATUS_SIGNERROR 0x154 +#define QSERDES_V5_TX_DCC_CAL_STATUS 0x158 +#define QSERDES_V5_TX_DCC_READ_CODE_STATUS 0x15c + +/* Only for QMP V5 PHY - RX registers */ +#define QSERDES_V5_RX_UCDR_FO_GAIN_HALF 0x000 +#define QSERDES_V5_RX_UCDR_FO_GAIN_QUARTER 0x004 +#define QSERDES_V5_RX_UCDR_FO_GAIN 0x008 +#define QSERDES_V5_RX_UCDR_SO_GAIN_HALF 0x00c +#define QSERDES_V5_RX_UCDR_SO_GAIN_QUARTER 0x010 +#define QSERDES_V5_RX_UCDR_SO_GAIN 0x014 +#define QSERDES_V5_RX_UCDR_SVS_FO_GAIN_HALF 0x018 +#define QSERDES_V5_RX_UCDR_SVS_FO_GAIN_QUARTER 0x01c +#define QSERDES_V5_RX_UCDR_SVS_FO_GAIN 0x020 +#define QSERDES_V5_RX_UCDR_SVS_SO_GAIN_HALF 0x024 +#define QSERDES_V5_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028 +#define QSERDES_V5_RX_UCDR_SVS_SO_GAIN 0x02c +#define QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN 0x030 +#define QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 +#define QSERDES_V5_RX_UCDR_FO_TO_SO_DELAY 0x038 +#define QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 +#define QSERDES_V5_RX_UCDR_PI_CONTROLS 0x044 +#define QSERDES_V5_RX_UCDR_PI_CTRL2 0x048 +#define QSERDES_V5_RX_UCDR_SB2_THRESH1 0x04c +#define QSERDES_V5_RX_UCDR_SB2_THRESH2 0x050 +#define QSERDES_V5_RX_UCDR_SB2_GAIN1 0x054 +#define QSERDES_V5_RX_UCDR_SB2_GAIN2 0x058 +#define QSERDES_V5_RX_AUX_CONTROL 0x05c +#define QSERDES_V5_RX_AUX_DATA_TCOARSE_TFINE 0x060 +#define QSERDES_V5_RX_RCLK_AUXDATA_SEL 0x064 +#define QSERDES_V5_RX_AC_JTAG_ENABLE 0x068 +#define QSERDES_V5_RX_AC_JTAG_INITP 0x06c +#define QSERDES_V5_RX_AC_JTAG_INITN 0x070 +#define QSERDES_V5_RX_AC_JTAG_LVL 0x074 +#define QSERDES_V5_RX_AC_JTAG_MODE 0x078 +#define QSERDES_V5_RX_AC_JTAG_RESET 0x07c +#define QSERDES_V5_RX_RX_TERM_BW 0x080 +#define QSERDES_V5_RX_RX_RCVR_IQ_EN 0x084 +#define QSERDES_V5_RX_RX_IDAC_I_DC_OFFSETS 0x088 +#define QSERDES_V5_RX_RX_IDAC_IBAR_DC_OFFSETS 0x08c +#define QSERDES_V5_RX_RX_IDAC_Q_DC_OFFSETS 0x090 +#define QSERDES_V5_RX_RX_IDAC_QBAR_DC_OFFSETS 0x094 +#define QSERDES_V5_RX_RX_IDAC_A_DC_OFFSETS 0x098 +#define QSERDES_V5_RX_RX_IDAC_ABAR_DC_OFFSETS 0x09c +#define QSERDES_V5_RX_RX_IDAC_EN 0x0a0 +#define QSERDES_V5_RX_RX_IDAC_ENABLES 0x0a4 +#define QSERDES_V5_RX_RX_IDAC_SIGN 0x0a8 +#define QSERDES_V5_RX_RX_HIGHZ_HIGHRATE 0x0ac +#define QSERDES_V5_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0b0 +#define QSERDES_V5_RX_DFE_1 0x0b4 +#define QSERDES_V5_RX_DFE_2 0x0b8 +#define QSERDES_V5_RX_DFE_3 0x0bc +#define QSERDES_V5_RX_DFE_4 0x0c0 +#define QSERDES_V5_RX_TX_ADAPT_PRE_THRESH1 0x0c4 +#define QSERDES_V5_RX_TX_ADAPT_PRE_THRESH2 0x0c8 +#define QSERDES_V5_RX_TX_ADAPT_POST_THRESH 0x0cc +#define QSERDES_V5_RX_TX_ADAPT_MAIN_THRESH 0x0d0 +#define QSERDES_V5_RX_VGA_CAL_CNTRL1 0x0d4 +#define QSERDES_V5_RX_VGA_CAL_CNTRL2 0x0d8 +#define QSERDES_V5_RX_GM_CAL 0x0dc +#define QSERDES_V5_RX_RX_VGA_GAIN2_LSB 0x0e0 +#define QSERDES_V5_RX_RX_VGA_GAIN2_MSB 0x0e4 +#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL1 0x0e8 +#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec +#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0 +#define QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4 +#define QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW 0x0f8 +#define QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH 0x0fc +#define QSERDES_V5_RX_RX_IDAC_MEASURE_TIME 0x100 +#define QSERDES_V5_RX_RX_IDAC_ACCUMULATOR 0x104 +#define QSERDES_V5_RX_RX_EQ_OFFSET_LSB 0x108 +#define QSERDES_V5_RX_RX_EQ_OFFSET_MSB 0x10c +#define QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 +#define QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114 +#define QSERDES_V5_RX_SIGDET_ENABLES 0x118 +#define QSERDES_V5_RX_SIGDET_CNTRL 0x11c +#define QSERDES_V5_RX_SIGDET_LVL 0x120 +#define QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL 0x124 +#define QSERDES_V5_RX_RX_BAND 0x128 +#define QSERDES_V5_RX_CDR_FREEZE_UP_DN 0x12c +#define QSERDES_V5_RX_CDR_RESET_OVERRIDE 0x130 +#define QSERDES_V5_RX_RX_INTERFACE_MODE 0x134 +#define QSERDES_V5_RX_JITTER_GEN_MODE 0x138 +#define QSERDES_V5_RX_SJ_AMP1 0x13c +#define QSERDES_V5_RX_SJ_AMP2 0x140 +#define QSERDES_V5_RX_SJ_PER1 0x144 +#define QSERDES_V5_RX_SJ_PER2 0x148 +#define QSERDES_V5_RX_PPM_OFFSET1 0x14c +#define QSERDES_V5_RX_PPM_OFFSET2 0x150 +#define QSERDES_V5_RX_SIGN_PPM_PERIOD1 0x154 +#define QSERDES_V5_RX_SIGN_PPM_PERIOD2 0x158 +#define QSERDES_V5_RX_RX_MODE_00_LOW 0x15c +#define QSERDES_V5_RX_RX_MODE_00_HIGH 0x160 +#define QSERDES_V5_RX_RX_MODE_00_HIGH2 0x164 +#define QSERDES_V5_RX_RX_MODE_00_HIGH3 0x168 +#define QSERDES_V5_RX_RX_MODE_00_HIGH4 0x16c +#define QSERDES_V5_RX_RX_MODE_01_LOW 0x170 +#define QSERDES_V5_RX_RX_MODE_01_HIGH 0x174 +#define QSERDES_V5_RX_RX_MODE_01_HIGH2 0x178 +#define QSERDES_V5_RX_RX_MODE_01_HIGH3 0x17c +#define QSERDES_V5_RX_RX_MODE_01_HIGH4 0x180 +#define QSERDES_V5_RX_RX_MODE_10_LOW 0x184 +#define QSERDES_V5_RX_RX_MODE_10_HIGH 0x188 +#define QSERDES_V5_RX_RX_MODE_10_HIGH2 0x18c +#define QSERDES_V5_RX_RX_MODE_10_HIGH3 0x190 +#define QSERDES_V5_RX_RX_MODE_10_HIGH4 0x194 +#define QSERDES_V5_RX_PHPRE_CTRL 0x198 +#define QSERDES_V5_RX_PHPRE_INITVAL 0x19c +#define QSERDES_V5_RX_DFE_EN_TIMER 0x1a0 +#define QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET 0x1a4 +#define QSERDES_V5_RX_DCC_CTRL1 0x1a8 +#define QSERDES_V5_RX_DCC_CTRL2 0x1ac +#define QSERDES_V5_RX_VTH_CODE 0x1b0 +#define QSERDES_V5_RX_VTH_MIN_THRESH 0x1b4 +#define QSERDES_V5_RX_VTH_MAX_THRESH 0x1b8 +#define QSERDES_V5_RX_ALOG_OBSV_BUS_CTRL_1 0x1bc +#define QSERDES_V5_RX_PI_CTRL1 0x1c0 +#define QSERDES_V5_RX_PI_CTRL2 0x1c4 +#define QSERDES_V5_RX_PI_QUAD 0x1c8 +#define QSERDES_V5_RX_IDATA1 0x1cc +#define QSERDES_V5_RX_IDATA2 0x1d0 +#define QSERDES_V5_RX_AUX_DATA1 0x1d4 +#define QSERDES_V5_RX_AUX_DATA2 0x1d8 +#define QSERDES_V5_RX_AC_JTAG_OUTP 0x1dc +#define QSERDES_V5_RX_AC_JTAG_OUTN 0x1e0 +#define QSERDES_V5_RX_RX_SIGDET 0x1e4 +#define QSERDES_V5_RX_ALOG_OBSV_BUS_STATUS_1 0x1e8 + +/* Only for QMP V5 UFS ? */ +#define QSERDES_V5_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x178 +#define QSERDES_V5_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x17c +#define QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x180 +#define QSERDES_V5_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x184 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h new file mode 100644 index 000000000000..86c01104799e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V5_20_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V5_20_H_ + +/* Only for QMP V5_20 PHY - TX registers */ +#define QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX 0x30 +#define QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX 0x34 +#define QSERDES_V5_20_TX_LANE_MODE_1 0x78 +#define QSERDES_V5_20_TX_LANE_MODE_2 0x7c + +/* Only for QMP V5_20 PHY - RX registers */ +#define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2 0x008 +#define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3 0x00c +#define QSERDES_V5_20_RX_UCDR_PI_CONTROLS 0x020 +#define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1 0x02c +#define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3 0x030 +#define QSERDES_V5_20_RX_RX_IDAC_SAOFFSET 0x07c +#define QSERDES_V5_20_RX_DFE_3 0x090 +#define QSERDES_V5_20_RX_DFE_DAC_ENABLE1 0x0b4 +#define QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1 0x0c4 +#define QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2 0x0c8 +#define QSERDES_V5_20_RX_VGA_CAL_MAN_VAL 0x0dc +#define QSERDES_V5_20_RX_GM_CAL 0x0ec +#define QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4 0x108 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1 0x164 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2 0x168 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3 0x16c +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5 0x174 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6 0x178 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B0 0x17c +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B1 0x180 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B2 0x184 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B3 0x188 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B4 0x18c +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B5 0x190 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B6 0x194 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B0 0x198 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B1 0x19c +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B2 0x1a0 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B3 0x1a4 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B4 0x1a8 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B5 0x1ac +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B6 0x1b0 +#define QSERDES_V5_20_RX_PHPRE_CTRL 0x1b4 +#define QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET 0x1c0 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210 0x1f4 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3 0x1f8 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210 0x1fc +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3 0x200 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210 0x204 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3 0x208 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3 0x210 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3 0x218 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3 0x220 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h new file mode 100644 index 000000000000..a1c088bd5158 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_5nm.h @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V5_5NM_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V5_5NM_H_ + +/* Only for QMP V5 5NM PHY - TX registers */ +#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX 0x30 +#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX 0x34 +#define QSERDES_V5_5NM_TX_LANE_MODE_1 0x78 +#define QSERDES_V5_5NM_TX_LANE_MODE_2 0x7c +#define QSERDES_V5_5NM_TX_LANE_MODE_3 0x80 +#define QSERDES_V5_5NM_TX_BIST_MODE_LANENO 0x00 +#define QSERDES_V5_5NM_TX_BIST_INVERT 0x04 +#define QSERDES_V5_5NM_TX_CLKBUF_ENABLE 0x08 +#define QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL 0x0c +#define QSERDES_V5_5NM_TX_TX_IDLE_LVL_LARGE_AMP 0x10 +#define QSERDES_V5_5NM_TX_TX_DRV_LVL 0x14 +#define QSERDES_V5_5NM_TX_TX_DRV_LVL_OFFSET 0x18 +#define QSERDES_V5_5NM_TX_RESET_TSYNC_EN 0x1c +#define QSERDES_V5_5NM_TX_PRE_STALL_LDO_BOOST_EN 0x20 +#define QSERDES_V5_5NM_TX_LPB_EN 0x24 +#define QSERDES_V5_5NM_TX_RES_CODE_LANE_TX 0x28 +#define QSERDES_V5_5NM_TX_RES_CODE_LANE_RX 0x2c +#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_TX 0x30 +#define QSERDES_V5_5NM_TX_RES_CODE_LANE_OFFSET_RX 0x34 +#define QSERDES_V5_5NM_TX_PERL_LENGTH1 0x38 +#define QSERDES_V5_5NM_TX_PERL_LENGTH2 0x3c +#define QSERDES_V5_5NM_TX_SERDES_BYP_EN_OUT 0x40 +#define QSERDES_V5_5NM_TX_DEBUG_BUS_SEL 0x44 +#define QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN 0x48 +#define QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN 0x4c +#define QSERDES_V5_5NM_TX_TX_POL_INV 0x50 +#define QSERDES_V5_5NM_TX_PARRATE_REC_DETECT_IDLE_EN 0x54 +#define QSERDES_V5_5NM_TX_BIST_PATTERN1 0x58 +#define QSERDES_V5_5NM_TX_BIST_PATTERN2 0x5c +#define QSERDES_V5_5NM_TX_BIST_PATTERN3 0x60 +#define QSERDES_V5_5NM_TX_BIST_PATTERN4 0x64 +#define QSERDES_V5_5NM_TX_BIST_PATTERN5 0x68 +#define QSERDES_V5_5NM_TX_BIST_PATTERN6 0x6c +#define QSERDES_V5_5NM_TX_BIST_PATTERN7 0x70 +#define QSERDES_V5_5NM_TX_BIST_PATTERN8 0x74 +#define QSERDES_V5_5NM_TX_LANE_MODE_1 0x78 +#define QSERDES_V5_5NM_TX_LANE_MODE_2 0x7c +#define QSERDES_V5_5NM_TX_LANE_MODE_3 0x80 +#define QSERDES_V5_5NM_TX_ATB_SEL1 0x84 +#define QSERDES_V5_5NM_TX_ATB_SEL2 0x88 +#define QSERDES_V5_5NM_TX_RCV_DETECT_LVL 0x8c +#define QSERDES_V5_5NM_TX_RCV_DETECT_LVL_2 0x90 +#define QSERDES_V5_5NM_TX_PRBS_SEED1 0x94 +#define QSERDES_V5_5NM_TX_PRBS_SEED2 0x98 +#define QSERDES_V5_5NM_TX_PRBS_SEED3 0x9c +#define QSERDES_V5_5NM_TX_PRBS_SEED4 0xa0 +#define QSERDES_V5_5NM_TX_RESET_GEN 0xa4 +#define QSERDES_V5_5NM_TX_RESET_GEN_MUXES 0xa8 +#define QSERDES_V5_5NM_TX_TRAN_DRVR_EMP_EN 0xac +#define QSERDES_V5_5NM_TX_VMODE_CTRL1 0xb0 +#define QSERDES_V5_5NM_TX_ALOG_OBSV_BUS_CTRL_1 0xb4 +#define QSERDES_V5_5NM_TX_BIST_STATUS 0xb8 +#define QSERDES_V5_5NM_TX_BIST_ERROR_COUNT1 0xbc +#define QSERDES_V5_5NM_TX_BIST_ERROR_COUNT2 0xc0 +#define QSERDES_V5_5NM_TX_ALOG_OBSV_BUS_STATUS_1 0xc4 +#define QSERDES_V5_5NM_TX_LANE_DIG_CONFIG 0xc8 +#define QSERDES_V5_5NM_TX_PI_QEC_CTRL 0xcc +#define QSERDES_V5_5NM_TX_PRE_EMPH 0xd0 +#define QSERDES_V5_5NM_TX_SW_RESET 0xd4 +#define QSERDES_V5_5NM_TX_TX_BAND 0xd8 +#define QSERDES_V5_5NM_TX_SLEW_CNTL0 0xdc +#define QSERDES_V5_5NM_TX_SLEW_CNTL1 0xe0 +#define QSERDES_V5_5NM_TX_INTERFACE_SELECT 0xe4 +#define QSERDES_V5_5NM_TX_DIG_BKUP_CTRL 0xe8 +#define QSERDES_V5_5NM_TX_DEBUG_BUS0 0xec +#define QSERDES_V5_5NM_TX_DEBUG_BUS1 0xf0 +#define QSERDES_V5_5NM_TX_DEBUG_BUS2 0xf4 +#define QSERDES_V5_5NM_TX_DEBUG_BUS3 0xf8 +#define QSERDES_V5_5NM_TX_TX_BKUP_RO_BUS 0xfc + +/* Only for QMP V5 5NM PHY - RX registers */ +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE0 0x000 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE1 0x004 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE2 0x008 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_FO_GAIN_RATE3 0x00c +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE0 0x010 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE1 0x014 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE2 0x018 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_SO_GAIN_RATE3 0x01c +#define QSERDES_V5_5NM_RX_UCDR_SO_SATURATION 0x020 +#define QSERDES_V5_5NM_RX_UCDR_FO_TO_SO_DELAY 0x024 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE0 0x028 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE0 0x02c +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE1 0x030 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE1 0x034 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE2 0x038 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE2 0x03c +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_LOW_RATE3 0x040 +#define QSERDES_V5_5NM_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE3 0x044 +#define QSERDES_V5_5NM_RX_UCDR_PI_CTRL1 0x048 +#define QSERDES_V5_5NM_RX_UCDR_PI_CTRL2 0x04c +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE0 0x050 +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE1 0x054 +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE2 0x058 +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH1_RATE3 0x05c +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE0 0x060 +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE1 0x064 +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE2 0x068 +#define QSERDES_V5_5NM_RX_UCDR_SB2_THRESH2_RATE3 0x06c +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE0 0x070 +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE1 0x074 +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE2 0x078 +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN1_RATE3 0x07c +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE0 0x080 +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE1 0x084 +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE2 0x088 +#define QSERDES_V5_5NM_RX_UCDR_SB2_GAIN2_RATE3 0x08c +#define QSERDES_V5_5NM_RX_RXCLK_DIV2_CTRL 0x090 +#define QSERDES_V5_5NM_RX_RX_BAND 0x094 +#define QSERDES_V5_5NM_RX_RX_TERM_BW 0x098 +#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE0 0x09c +#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE1 0x0a0 +#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE2 0x0a4 +#define QSERDES_V5_5NM_RX_UCDR_FO_GAIN_RATE3 0x0a8 +#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE0 0x0ac +#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE1 0x0b0 +#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE2 0x0b4 +#define QSERDES_V5_5NM_RX_UCDR_SO_GAIN_RATE3 0x0b8 +#define QSERDES_V5_5NM_RX_UCDR_PI_CONTROLS 0x0bc +#define QSERDES_V5_5NM_RX_UCDR_PD_DATA_FILTER_ENABLES 0x0c0 +#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE0 0x0c4 +#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE1 0x0c8 +#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE2 0x0cc +#define QSERDES_V5_5NM_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE3 0x0d0 +#define QSERDES_V5_5NM_RX_AUX_CONTROL 0x0d4 +#define QSERDES_V5_5NM_RX_AUXDATA_TB 0x0d8 +#define QSERDES_V5_5NM_RX_RCLK_AUXDATA_SEL 0x0dc +#define QSERDES_V5_5NM_RX_EOM_CTRL 0x0e0 +#define QSERDES_V5_5NM_RX_AC_JTAG_ENABLE 0x0e4 +#define QSERDES_V5_5NM_RX_AC_JTAG_INITP 0x0e8 +#define QSERDES_V5_5NM_RX_AC_JTAG_INITN 0x0ec +#define QSERDES_V5_5NM_RX_AC_JTAG_LVL 0x0f0 +#define QSERDES_V5_5NM_RX_AC_JTAG_MODE 0x0f4 +#define QSERDES_V5_5NM_RX_AC_JTAG_RESET 0x0f8 +#define QSERDES_V5_5NM_RX_RX_RCVR_IQ_EN 0x0fc +#define QSERDES_V5_5NM_RX_RX_Q_EN_RATES 0x100 +#define QSERDES_V5_5NM_RX_RX_IDAC_I0_DC_OFFSETS 0x104 +#define QSERDES_V5_5NM_RX_RX_IDAC_I0BAR_DC_OFFSETS 0x108 +#define QSERDES_V5_5NM_RX_RX_IDAC_I1_DC_OFFSETS 0x10c +#define QSERDES_V5_5NM_RX_RX_IDAC_I1BAR_DC_OFFSETS 0x110 +#define QSERDES_V5_5NM_RX_RX_IDAC_Q_DC_OFFSETS 0x114 +#define QSERDES_V5_5NM_RX_RX_IDAC_QBAR_DC_OFFSETS 0x118 +#define QSERDES_V5_5NM_RX_RX_IDAC_A_DC_OFFSETS 0x11c +#define QSERDES_V5_5NM_RX_RX_IDAC_ABAR_DC_OFFSETS 0x120 +#define QSERDES_V5_5NM_RX_RX_IDAC_EN 0x124 +#define QSERDES_V5_5NM_RX_RX_IDAC_ENABLES 0x128 +#define QSERDES_V5_5NM_RX_RX_IDAC_SIGN 0x12c +#define QSERDES_V5_5NM_RX_RX_IVCM_CAL_CODE_OVERRIDE 0x130 +#define QSERDES_V5_5NM_RX_RX_IVCM_CAL_CTRL1 0x134 +#define QSERDES_V5_5NM_RX_RX_IVCM_CAL_CTRL2 0x138 +#define QSERDES_V5_5NM_RX_RX_IVCM_POSTCAL_OFFSET 0x13c +#define QSERDES_V5_5NM_RX_RX_SUMMER_CAL_SPD_MODE 0x140 +#define QSERDES_V5_5NM_RX_RX_HIGHZ_PARRATE 0x144 +#define QSERDES_V5_5NM_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x148 +#define QSERDES_V5_5NM_RX_DFE_1 0x14c +#define QSERDES_V5_5NM_RX_DFE_2 0x150 +#define QSERDES_V5_5NM_RX_DFE_3 0x154 +#define QSERDES_V5_5NM_RX_DFE_4 0x158 +#define QSERDES_V5_5NM_RX_DFE_TAP3_CTRL 0x15c +#define QSERDES_V5_5NM_RX_DFE_TAP3_MANVAL_KTAP 0x160 +#define QSERDES_V5_5NM_RX_DFE_TAP4_CTRL 0x164 +#define QSERDES_V5_5NM_RX_DFE_TAP4_MANVAL_KTAP 0x168 +#define QSERDES_V5_5NM_RX_DFE_TAP5_CTRL 0x16c +#define QSERDES_V5_5NM_RX_DFE_TAP5_MANVAL_KTAP 0x170 +#define QSERDES_V5_5NM_RX_TX_ADPT_CTRL 0x174 +#define QSERDES_V5_5NM_RX_DFE_DAC_ENABLE1 0x178 +#define QSERDES_V5_5NM_RX_DFE_DAC_ENABLE2 0x17c +#define QSERDES_V5_5NM_RX_TX_ADAPT_PRE_THRESH1 0x180 +#define QSERDES_V5_5NM_RX_TX_ADAPT_PRE_THRESH2 0x184 +#define QSERDES_V5_5NM_RX_TX_ADAPT_POST_THRESH1 0x188 +#define QSERDES_V5_5NM_RX_TX_ADAPT_POST_THRESH2 0x18c +#define QSERDES_V5_5NM_RX_TX_ADAPT_MAIN_THRESH1 0x190 +#define QSERDES_V5_5NM_RX_TX_ADAPT_MAIN_THRESH2 0x194 +#define QSERDES_V5_5NM_RX_VGA_CAL_CNTRL1 0x198 +#define QSERDES_V5_5NM_RX_VGA_CAL_CNTRL2 0x19c +#define QSERDES_V5_5NM_RX_VGA_CAL_MAN_VAL 0x1a0 +#define QSERDES_V5_5NM_RX_VTHRESH_CAL_CNTRL1 0x1a4 +#define QSERDES_V5_5NM_RX_VTHRESH_CAL_CNTRL2 0x1a8 +#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE0 0x1ac +#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE1 0x1b0 +#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE2 0x1b4 +#define QSERDES_V5_5NM_RX_VTHRESH_CAL_MAN_VAL_RATE3 0x1b8 +#define QSERDES_V5_5NM_RX_GM_CAL 0x1bc +#define QSERDES_V5_5NM_RX_RX_VGA_GAIN2_BLK1 0x1c0 +#define QSERDES_V5_5NM_RX_RX_VGA_GAIN2_BLK2 0x1c4 +#define QSERDES_V5_5NM_RX_RX_EQU_ADAPTOR_CNTRL2 0x1c8 +#define QSERDES_V5_5NM_RX_RX_EQU_ADAPTOR_CNTRL3 0x1cc +#define QSERDES_V5_5NM_RX_RX_EQU_ADAPTOR_CNTRL4 0x1d0 +#define QSERDES_V5_5NM_RX_RX_IDAC_TSETTLE_LOW 0x1d4 +#define QSERDES_V5_5NM_RX_RX_EQ_OFFSET_LSB 0x1d8 +#define QSERDES_V5_5NM_RX_RX_EQ_OFFSET_MSB 0x1dc +#define QSERDES_V5_5NM_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x1e0 +#define QSERDES_V5_5NM_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x1e4 +#define QSERDES_V5_5NM_RX_SIGDET_ENABLES 0x1e8 +#define QSERDES_V5_5NM_RX_SIGDET_CNTRL 0x1ec +#define QSERDES_V5_5NM_RX_SIGDET_LVL 0x1f0 +#define QSERDES_V5_5NM_RX_SIGDET_DEGLITCH_CNTRL 0x1f4 +#define QSERDES_V5_5NM_RX_CDR_FREEZE_UP_DN 0x1f8 +#define QSERDES_V5_5NM_RX_CDR_RESET_OVERRIDE 0x1fc +#define QSERDES_V5_5NM_RX_RX_INTERFACE_MODE 0x200 +#define QSERDES_V5_5NM_RX_JITTER_GEN_MODE 0x204 +#define QSERDES_V5_5NM_RX_SJ_AMP1 0x208 +#define QSERDES_V5_5NM_RX_SJ_AMP2 0x20c +#define QSERDES_V5_5NM_RX_SJ_PER1 0x210 +#define QSERDES_V5_5NM_RX_SJ_PER2 0x214 +#define QSERDES_V5_5NM_RX_PPM_OFFSET1 0x218 +#define QSERDES_V5_5NM_RX_PPM_OFFSET2 0x21c +#define QSERDES_V5_5NM_RX_SIGN_PPM_PERIOD1 0x220 +#define QSERDES_V5_5NM_RX_SIGN_PPM_PERIOD2 0x224 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B0 0x228 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B1 0x22c +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B2 0x230 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B3 0x234 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B4 0x238 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B5 0x23c +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B6 0x240 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE_0_1_B7 0x244 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B0 0x248 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B1 0x24c +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B2 0x250 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B3 0x254 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B4 0x258 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B5 0x25c +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B6 0x260 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE2_B7 0x264 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B0 0x268 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B1 0x26c +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B2 0x270 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B3 0x274 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B4 0x278 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B5 0x27c +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B6 0x280 +#define QSERDES_V5_5NM_RX_RX_MODE_RATE3_B7 0x284 +#define QSERDES_V5_5NM_RX_PHPRE_CTRL 0x288 +#define QSERDES_V5_5NM_RX_PHPRE_INITVAL 0x28c +#define QSERDES_V5_5NM_RX_DFE_EN_TIMER 0x290 +#define QSERDES_V5_5NM_RX_DFE_CTLE_POST_CAL_OFFSET 0x294 +#define QSERDES_V5_5NM_RX_DCC_CTRL1 0x298 +#define QSERDES_V5_5NM_RX_DCC_CTRL2 0x29c +#define QSERDES_V5_5NM_RX_DCC_OFFSET 0x2a0 +#define QSERDES_V5_5NM_RX_DCC_CMUX_POSTCAL_OFFSET 0x2a4 +#define QSERDES_V5_5NM_RX_DCC_CMUX_CAL_CTRL1 0x2a8 +#define QSERDES_V5_5NM_RX_DCC_CMUX_CAL_CTRL2 0x2ac +#define QSERDES_V5_5NM_RX_ALOG_OBSV_BUS_CTRL_1 0x2b0 +#define QSERDES_V5_5NM_RX_RX_MARG_CTRL1 0x2b4 +#define QSERDES_V5_5NM_RX_RX_MARG_CTRL2 0x2b8 +#define QSERDES_V5_5NM_RX_RX_MARG_CTRL3 0x2bc +#define QSERDES_V5_5NM_RX_RX_MARG_CTRL_4 0x2c0 +#define QSERDES_V5_5NM_RX_RX_MARG_CFG_RATE_0_1 0x2c4 +#define QSERDES_V5_5NM_RX_RX_MARG_CFG_RATE_2_3 0x2c8 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_CTRL1 0x2cc +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_CTRL2 0x2d0 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH1_RATE210 0x2d4 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH1_RATE3 0x2d8 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH2_RATE210 0x2dc +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH2_RATE3 0x2e0 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH3_RATE210 0x2e4 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH3_RATE3 0x2e8 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH4_RATE210 0x2ec +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH4_RATE3 0x2f0 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH5_RATE210 0x2f4 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH5_RATE3 0x2f8 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH6_RATE210 0x2fc +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH6_RATE3 0x300 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH7_RATE210 0x304 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_THRESH7_RATE3 0x308 +#define QSERDES_V5_5NM_RX_Q_PI_INTRINSIC_BIAS_RATE10 0x30c +#define QSERDES_V5_5NM_RX_Q_PI_INTRINSIC_BIAS_RATE32 0x310 +#define QSERDES_V5_5NM_RX_RX_MARG_VERTICAL_CTRL 0x314 +#define QSERDES_V5_5NM_RX_RX_MARG_VERTICAL_CODE 0x318 +#define QSERDES_V5_5NM_RX_RES_CODE_THRESH_HIGH_AND_BYP 0x31c +#define QSERDES_V5_5NM_RX_RES_CODE_THRESH_LOW 0x320 +#define QSERDES_V5_5NM_RX_RX_BKUP_CTRL1 0x324 +#define QSERDES_V5_5NM_RX_RX_BKUP_CTRL2 0x328 +#define QSERDES_V5_5NM_RX_RX_BKUP_CTRL3 0x32c +#define QSERDES_V5_5NM_RX_PI_CTRL1 0x330 +#define QSERDES_V5_5NM_RX_PI_CTRL2 0x334 +#define QSERDES_V5_5NM_RX_PI_QUAD 0x338 +#define QSERDES_V5_5NM_RX_QPI_CTRL1 0x33c +#define QSERDES_V5_5NM_RX_QPI_CTRL2 0x340 +#define QSERDES_V5_5NM_RX_QPI_QUAD 0x344 +#define QSERDES_V5_5NM_RX_IDATA1 0x348 +#define QSERDES_V5_5NM_RX_IDATA2 0x34c +#define QSERDES_V5_5NM_RX_IDATA3 0x350 +#define QSERDES_V5_5NM_RX_AC_JTAG_OUTP 0x354 +#define QSERDES_V5_5NM_RX_AC_JTAG_OUTN 0x358 +#define QSERDES_V5_5NM_RX_RX_SIGDET 0x35c +#define QSERDES_V5_5NM_RX_ALOG_OBSV_BUS_STATUS_1 0x360 +#define QSERDES_V5_5NM_RX_READ_EQCODE 0x364 +#define QSERDES_V5_5NM_RX_READ_OFFSETCODE 0x368 +#define QSERDES_V5_5NM_RX_IA_ERROR_COUNTER_LOW 0x36c +#define QSERDES_V5_5NM_RX_IA_ERROR_COUNTER_HIGH 0x370 +#define QSERDES_V5_5NM_RX_VGA_READ_CODE 0x374 +#define QSERDES_V5_5NM_RX_VTHRESH_READ_CODE 0x378 +#define QSERDES_V5_5NM_RX_DFE_TAP1_READ_CODE 0x37c +#define QSERDES_V5_5NM_RX_DFE_TAP2_READ_CODE 0x380 +#define QSERDES_V5_5NM_RX_DFE_TAP3_READ_CODE 0x384 +#define QSERDES_V5_5NM_RX_DFE_TAP4_READ_CODE 0x388 +#define QSERDES_V5_5NM_RX_DFE_TAP5_READ_CODE 0x38c +#define QSERDES_V5_5NM_RX_IDAC_STATUS_I0 0x390 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_I0BAR 0x394 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_I1 0x398 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_I1BAR 0x39c +#define QSERDES_V5_5NM_RX_IDAC_STATUS_Q 0x3a0 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_QBAR 0x3a4 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_A 0x3a8 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_ABAR 0x3ac +#define QSERDES_V5_5NM_RX_IDAC_STATUS_SM_ON 0x3b0 +#define QSERDES_V5_5NM_RX_IDAC_STATUS_SIGNERROR 0x3b4 +#define QSERDES_V5_5NM_RX_IVCM_CAL_STATUS 0x3b8 +#define QSERDES_V5_5NM_RX_IVCM_CAL_DEBUG_STATUS 0x3bc +#define QSERDES_V5_5NM_RX_DCC_CAL_STATUS 0x3c0 +#define QSERDES_V5_5NM_RX_DCC_READ_CODE_STATUS 0x3c4 +#define QSERDES_V5_5NM_RX_RX_MARG_DEBUG1_STATUS 0x3c8 +#define QSERDES_V5_5NM_RX_RX_MARG_DEBUG2_STATUS 0x3cc +#define QSERDES_V5_5NM_RX_RX_MARG_READ_CODE_STATUS 0x3d0 +#define QSERDES_V5_5NM_RX_EOM_ERR_CNT_LSB_STATUS 0x3d4 +#define QSERDES_V5_5NM_RX_EOM_ERR_CNT_MSB_STATUS 0x3d8 +#define QSERDES_V5_5NM_RX_RX_MARG_COARSE_TUNE_STATUS 0x3dc +#define QSERDES_V5_5NM_RX_RX_BKUP_READ_BUS1_STATUS 0x3e0 +#define QSERDES_V5_5NM_RX_RX_BKUP_READ_BUS2_STATUS 0x3e4 +#define QSERDES_V5_5NM_RX_RX_BKUP_READ_BUS3_STATUS 0x3e8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h new file mode 100644 index 000000000000..d20694513eb4 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_H_ + +/* Only for QMP V2 PHY - TX registers */ +#define QSERDES_TX_BIST_MODE_LANENO 0x000 +#define QSERDES_TX_BIST_INVERT 0x004 +#define QSERDES_TX_CLKBUF_ENABLE 0x008 +#define QSERDES_TX_CMN_CONTROL_ONE 0x00c +#define QSERDES_TX_CMN_CONTROL_TWO 0x010 +#define QSERDES_TX_CMN_CONTROL_THREE 0x014 +#define QSERDES_TX_TX_EMP_POST1_LVL 0x018 +#define QSERDES_TX_TX_POST2_EMPH 0x01c +#define QSERDES_TX_TX_BOOST_LVL_UP_DN 0x020 +#define QSERDES_TX_HP_PD_ENABLES 0x024 +#define QSERDES_TX_TX_IDLE_LVL_LARGE_AMP 0x028 +#define QSERDES_TX_TX_DRV_LVL 0x02c +#define QSERDES_TX_TX_DRV_LVL_OFFSET 0x030 +#define QSERDES_TX_RESET_TSYNC_EN 0x034 +#define QSERDES_TX_PRE_STALL_LDO_BOOST_EN 0x038 +#define QSERDES_TX_TX_BAND 0x03c +#define QSERDES_TX_SLEW_CNTL 0x040 +#define QSERDES_TX_INTERFACE_SELECT 0x044 +#define QSERDES_TX_LPB_EN 0x048 +#define QSERDES_TX_RES_CODE_LANE_TX 0x04c +#define QSERDES_TX_RES_CODE_LANE_RX 0x050 +#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 +#define QSERDES_TX_PERL_LENGTH1 0x058 +#define QSERDES_TX_PERL_LENGTH2 0x05c +#define QSERDES_TX_SERDES_BYP_EN_OUT 0x060 +#define QSERDES_TX_DEBUG_BUS_SEL 0x064 +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 +#define QSERDES_TX_TX_POL_INV 0x06c +#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x070 +#define QSERDES_TX_BIST_PATTERN1 0x074 +#define QSERDES_TX_BIST_PATTERN2 0x078 +#define QSERDES_TX_BIST_PATTERN3 0x07c +#define QSERDES_TX_BIST_PATTERN4 0x080 +#define QSERDES_TX_BIST_PATTERN5 0x084 +#define QSERDES_TX_BIST_PATTERN6 0x088 +#define QSERDES_TX_BIST_PATTERN7 0x08c +#define QSERDES_TX_BIST_PATTERN8 0x090 +#define QSERDES_TX_LANE_MODE 0x094 +#define QSERDES_TX_IDAC_CAL_LANE_MODE 0x098 +#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x09c +#define QSERDES_TX_ATB_SEL1 0x0a0 +#define QSERDES_TX_ATB_SEL2 0x0a4 +#define QSERDES_TX_RCV_DETECT_LVL 0x0a8 +#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac +#define QSERDES_TX_PRBS_SEED1 0x0b0 +#define QSERDES_TX_PRBS_SEED2 0x0b4 +#define QSERDES_TX_PRBS_SEED3 0x0b8 +#define QSERDES_TX_PRBS_SEED4 0x0bc +#define QSERDES_TX_RESET_GEN 0x0c0 +#define QSERDES_TX_RESET_GEN_MUXES 0x0c4 +#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x0c8 +#define QSERDES_TX_TX_INTERFACE_MODE 0x0cc +#define QSERDES_TX_PWM_CTRL 0x0d0 +#define QSERDES_TX_PWM_ENCODED_OR_DATA 0x0d4 +#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND2 0x0d8 +#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND2 0x0dc +#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND2 0x0e0 +#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND2 0x0e4 +#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x0e8 +#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x0ec +#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x0f0 +#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x0f4 +#define QSERDES_TX_VMODE_CTRL1 0x0f8 +#define QSERDES_TX_VMODE_CTRL2 0x0fc +#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL 0x100 +#define QSERDES_TX_BIST_STATUS 0x104 +#define QSERDES_TX_BIST_ERROR_COUNT1 0x108 +#define QSERDES_TX_BIST_ERROR_COUNT2 0x10c +#define QSERDES_TX_TX_ALOG_INTF_OBSV 0x110 + +/* Only for QMP V2 PHY - RX registers */ +#define QSERDES_RX_UCDR_FO_GAIN_HALF 0x000 +#define QSERDES_RX_UCDR_FO_GAIN_QUARTER 0x004 +#define QSERDES_RX_UCDR_FO_GAIN_EIGHTH 0x008 +#define QSERDES_RX_UCDR_FO_GAIN 0x00c +#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 +#define QSERDES_RX_UCDR_SO_GAIN_QUARTER 0x014 +#define QSERDES_RX_UCDR_SO_GAIN_EIGHTH 0x018 +#define QSERDES_RX_UCDR_SO_GAIN 0x01c +#define QSERDES_RX_UCDR_SVS_FO_GAIN_HALF 0x020 +#define QSERDES_RX_UCDR_SVS_FO_GAIN_QUARTER 0x024 +#define QSERDES_RX_UCDR_SVS_FO_GAIN_EIGHTH 0x028 +#define QSERDES_RX_UCDR_SVS_FO_GAIN 0x02c +#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF 0x030 +#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER 0x034 +#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH 0x038 +#define QSERDES_RX_UCDR_SVS_SO_GAIN 0x03c +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 +#define QSERDES_RX_UCDR_FD_GAIN 0x044 +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 +#define QSERDES_RX_UCDR_FO_TO_SO_DELAY 0x04c +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW 0x050 +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH 0x054 +#define QSERDES_RX_UCDR_MODULATE 0x058 +#define QSERDES_RX_UCDR_PI_CONTROLS 0x05c +#define QSERDES_RX_RBIST_CONTROL 0x060 +#define QSERDES_RX_AUX_CONTROL 0x064 +#define QSERDES_RX_AUX_DATA_TCOARSE 0x068 +#define QSERDES_RX_AUX_DATA_TFINE_LSB 0x06c +#define QSERDES_RX_AUX_DATA_TFINE_MSB 0x070 +#define QSERDES_RX_RCLK_AUXDATA_SEL 0x074 +#define QSERDES_RX_AC_JTAG_ENABLE 0x078 +#define QSERDES_RX_AC_JTAG_INITP 0x07c +#define QSERDES_RX_AC_JTAG_INITN 0x080 +#define QSERDES_RX_AC_JTAG_LVL 0x084 +#define QSERDES_RX_AC_JTAG_MODE 0x088 +#define QSERDES_RX_AC_JTAG_RESET 0x08c +#define QSERDES_RX_RX_TERM_BW 0x090 +#define QSERDES_RX_RX_RCVR_IQ_EN 0x094 +#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS 0x098 +#define QSERDES_RX_RX_IDAC_IBAR_DC_OFFSETS 0x09c +#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS 0x0a0 +#define QSERDES_RX_RX_IDAC_QBAR_DC_OFFSETS 0x0a4 +#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS 0x0a8 +#define QSERDES_RX_RX_IDAC_ABAR_DC_OFFSETS 0x0ac +#define QSERDES_RX_RX_IDAC_EN 0x0b0 +#define QSERDES_RX_RX_IDAC_ENABLES 0x0b4 +#define QSERDES_RX_RX_IDAC_SIGN 0x0b8 +#define QSERDES_RX_RX_HIGHZ_HIGHRATE 0x0bc +#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0c0 +#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 +#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 +#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc +#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d4 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 +#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION 0x0e4 +#define QSERDES_RX_RX_IDAC_TSETTLE_LOW 0x0e8 +#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH 0x0ec +#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW 0x0f0 +#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH 0x0f4 +#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW 0x0f8 +#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH 0x0fc +#define QSERDES_RX_RX_EQ_OFFSET_LSB 0x100 +#define QSERDES_RX_RX_EQ_OFFSET_MSB 0x104 +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c +#define QSERDES_RX_SIGDET_ENABLES 0x110 +#define QSERDES_RX_SIGDET_CNTRL 0x114 +#define QSERDES_RX_SIGDET_LVL 0x118 +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c +#define QSERDES_RX_RX_BAND 0x120 +#define QSERDES_RX_CDR_FREEZE_UP_DN 0x124 +#define QSERDES_RX_CDR_RESET_OVERRIDE 0x128 +#define QSERDES_RX_RX_INTERFACE_MODE 0x12c +#define QSERDES_RX_JITTER_GEN_MODE 0x130 +#define QSERDES_RX_BUJ_AMP 0x134 +#define QSERDES_RX_SJ_AMP1 0x138 +#define QSERDES_RX_SJ_AMP2 0x13c +#define QSERDES_RX_SJ_PER1 0x140 +#define QSERDES_RX_SJ_PER2 0x144 +#define QSERDES_RX_BUJ_STEP_FREQ1 0x148 +#define QSERDES_RX_BUJ_STEP_FREQ2 0x14c +#define QSERDES_RX_PPM_OFFSET1 0x150 +#define QSERDES_RX_PPM_OFFSET2 0x154 +#define QSERDES_RX_SIGN_PPM_PERIOD1 0x158 +#define QSERDES_RX_SIGN_PPM_PERIOD2 0x15c +#define QSERDES_RX_SSC_CTRL 0x160 +#define QSERDES_RX_SSC_COUNT1 0x164 +#define QSERDES_RX_SSC_COUNT2 0x168 +#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL 0x16c +#define QSERDES_RX_RX_PWM_ENABLE_AND_DATA 0x170 +#define QSERDES_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x174 +#define QSERDES_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x178 +#define QSERDES_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x17c +#define QSERDES_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x180 +#define QSERDES_RX_PI_CTRL1 0x184 +#define QSERDES_RX_PI_CTRL2 0x188 +#define QSERDES_RX_PI_QUAD 0x18c +#define QSERDES_RX_IDATA1 0x190 +#define QSERDES_RX_IDATA2 0x194 +#define QSERDES_RX_AUX_DATA1 0x198 +#define QSERDES_RX_AUX_DATA2 0x19c +#define QSERDES_RX_AC_JTAG_OUTP 0x1a0 +#define QSERDES_RX_AC_JTAG_OUTN 0x1a4 +#define QSERDES_RX_RX_SIGDET 0x1a8 +#define QSERDES_RX_RX_VDCOFF 0x1ac +#define QSERDES_RX_IDAC_CAL_ON 0x1b0 +#define QSERDES_RX_IDAC_STATUS_I 0x1b4 +#define QSERDES_RX_IDAC_STATUS_IBAR 0x1b8 +#define QSERDES_RX_IDAC_STATUS_Q 0x1bc +#define QSERDES_RX_IDAC_STATUS_QBAR 0x1c0 +#define QSERDES_RX_IDAC_STATUS_A 0x1c4 +#define QSERDES_RX_IDAC_STATUS_ABAR 0x1c8 +#define QSERDES_RX_CALST_STATUS_I 0x1cc +#define QSERDES_RX_CALST_STATUS_Q 0x1d0 +#define QSERDES_RX_CALST_STATUS_A 0x1d4 +#define QSERDES_RX_RX_ALOG_INTF_OBSV 0x1d8 +#define QSERDES_RX_READ_EQCODE 0x1dc +#define QSERDES_RX_READ_OFFSETCODE 0x1e0 +#define QSERDES_RX_IA_ERROR_COUNTER_LOW 0x1e4 +#define QSERDES_RX_IA_ERROR_COUNTER_HIGH 0x1e8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c new file mode 100644 index 000000000000..c08d34ad1313 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -0,0 +1,1265 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +#include "phy-qcom-qmp.h" + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) +/* QPHY_PCS_READY_STATUS bit */ +#define PCS_READY BIT(0) + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * register part of layout ? + * if yes, then offset gives index in the reg-layout + */ + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_L(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_READY_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE +}; + +static const unsigned int msm8996_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = 0x00, + [QPHY_PCS_READY_STATUS] = 0x168, +}; + +static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = 0x00, + [QPHY_PCS_READY_STATUS] = 0x160, +}; + +static const unsigned int sm6115_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = 0x00, + [QPHY_PCS_READY_STATUS] = 0x168, +}; + +static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = QPHY_V4_PCS_UFS_PHY_START, + [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_UFS_READY_STATUS, + [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET, +}; + +static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x10), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x54), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), +}; + +static const struct qmp_phy_init_tbl msm8996_ufs_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x02), +}; + +static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), +}; + +static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + + /* Rate B */ + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44), +}; + +static const struct qmp_phy_init_tbl sm6115_ufsphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl sm6115_ufsphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x0F), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5B), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5B), +}; + +static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_PWM_GEAR_BAND, 0x15), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_LARGE_AMP_DRV_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SYM_RESYNC_CTRL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_LARGE_AMP_POST_EMP_LVL, 0x12), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_SMALL_AMP_POST_EMP_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_MIN_HIBERN8_TIME, 0x9a), /* 8 us */ +}; + +static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f), + + /* Rate B */ + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44), +}; + +static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07), +}; + +static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59), +}; + +static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SIGDET_CTRL2, 0x6e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SYM_RESYNC_CTRL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SIGDET_CTRL1, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_MIN_HIBERN8_TIME, 0x9a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23), + + /* Rate B */ + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06), +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x65), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23), + + /* Rate B */ + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x06), +}; + +static const struct qmp_phy_init_tbl sm8350_ufsphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xf5), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_TRAN_DRVR_EMP_EN, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8350_ufsphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0xf1), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_TERM_BW, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_MEASURE_TIME, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x6d), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0x3c), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH4, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_PLL_CNTL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB, 0xd8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_PWM_GEAR_BAND, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_HS_GEAR_BAND, 0x06), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_SIGDET_CTRL1, 0x0e), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1, 0x02), +}; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + int lanes; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* regulators to be requested */ + const char * const *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + unsigned int start_ctrl; + unsigned int pwrdn_ctrl; + /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */ + unsigned int phy_status; + + /* true, if PCS block has no separate SW_RESET register */ + bool no_pcs_sw_reset; +}; + +/** + * struct qmp_phy - per-lane phy descriptor + * + * @phy: generic phy + * @cfg: phy specific configuration + * @serdes: iomapped memory space for phy's serdes (i.e. PLL) + * @tx: iomapped memory space for lane's tx + * @rx: iomapped memory space for lane's rx + * @pcs: iomapped memory space for lane's pcs + * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs) + * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs) + * @pcs_misc: iomapped memory space for lane's pcs_misc + * @qmp: QMP phy to which this lane belongs + */ +struct qmp_phy { + struct phy *phy; + const struct qmp_phy_cfg *cfg; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *pcs; + void __iomem *tx2; + void __iomem *rx2; + void __iomem *pcs_misc; + struct qcom_qmp *qmp; +}; + +/** + * struct qcom_qmp - structure holding QMP phy block attributes + * + * @dev: device + * + * @clks: array of clocks required by phy + * @resets: array of resets required by phy + * @vregs: regulator supplies bulk data + * + * @phys: array of per-lane phy descriptors + * @ufs_reset: optional UFS PHY reset handle + */ +struct qcom_qmp { + struct device *dev; + + struct clk_bulk_data *clks; + struct regulator_bulk_data *vregs; + + struct qmp_phy **phys; + + struct reset_control *ufs_reset; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const msm8996_ufs_phy_clk_l[] = { + "ref", +}; + +/* the primary usb3 phy on sm8250 doesn't have a ref clock */ +static const char * const sm8450_ufs_phy_clk_l[] = { + "qref", "ref", "ref_aux", +}; + +static const char * const sdm845_ufs_phy_clk_l[] = { + "ref", "ref_aux", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy", "vdda-pll", +}; + +static const struct qmp_phy_cfg msm8996_ufs_cfg = { + .lanes = 1, + + .serdes_tbl = msm8996_ufs_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8996_ufs_serdes_tbl), + .tx_tbl = msm8996_ufs_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_ufs_tx_tbl), + .rx_tbl = msm8996_ufs_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8996_ufs_rx_tbl), + + .clk_list = msm8996_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_ufs_phy_clk_l), + + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + + .regs = msm8996_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .no_pcs_sw_reset = true, +}; + +static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { + .lanes = 2, + + .serdes_tbl = sdm845_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl), + .tx_tbl = sdm845_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl), + .rx_tbl = sdm845_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl), + .pcs_tbl = sdm845_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl), + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sdm845_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .no_pcs_sw_reset = true, +}; + +static const struct qmp_phy_cfg sm6115_ufsphy_cfg = { + .lanes = 1, + + .serdes_tbl = sm6115_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm6115_ufsphy_serdes_tbl), + .tx_tbl = sm6115_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_tx_tbl), + .rx_tbl = sm6115_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_rx_tbl), + .pcs_tbl = sm6115_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm6115_ufsphy_pcs_tbl), + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm6115_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + + .no_pcs_sw_reset = true, +}; + +static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl), + .tx_tbl = sm8150_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl), + .rx_tbl = sm8150_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl), + .pcs_tbl = sm8150_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl), + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8150_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg sm8350_ufsphy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8350_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl), + .tx_tbl = sm8350_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl), + .rx_tbl = sm8350_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl), + .pcs_tbl = sm8350_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl), + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8150_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg sm8450_ufsphy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8350_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl), + .tx_tbl = sm8350_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl), + .rx_tbl = sm8350_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl), + .pcs_tbl = sm8350_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl), + .clk_list = sm8450_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sm8450_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8150_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static void qmp_ufs_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + if (t->in_layout) + writel(t->val, base + regs[t->offset]); + else + writel(t->val, base + t->offset); + } +} + +static void qmp_ufs_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qmp_ufs_configure_lane(base, regs, tbl, num, 0xff); +} + +static int qmp_ufs_serdes_init(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; + int serdes_tbl_num = cfg->serdes_tbl_num; + + qmp_ufs_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num); + + return 0; +} + +static int qmp_ufs_com_init(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs = qphy->pcs; + int ret; + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + goto err_disable_regulators; + + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) + qphy_setbits(pcs, + cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + else + qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + + return 0; + +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return ret; +} + +static int qmp_ufs_com_exit(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + + reset_control_assert(qmp->ufs_reset); + + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return 0; +} + +static int qmp_ufs_init(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + int ret; + dev_vdbg(qmp->dev, "Initializing QMP phy\n"); + + if (cfg->no_pcs_sw_reset) { + /* + * Get UFS reset, which is delayed until now to avoid a + * circular dependency where UFS needs its PHY, but the PHY + * needs this UFS reset. + */ + if (!qmp->ufs_reset) { + qmp->ufs_reset = + devm_reset_control_get_exclusive(qmp->dev, + "ufsphy"); + + if (IS_ERR(qmp->ufs_reset)) { + ret = PTR_ERR(qmp->ufs_reset); + dev_err(qmp->dev, + "failed to get UFS reset: %d\n", + ret); + + qmp->ufs_reset = NULL; + return ret; + } + } + + ret = reset_control_assert(qmp->ufs_reset); + if (ret) + return ret; + } + + ret = qmp_ufs_com_init(qphy); + if (ret) + return ret; + + return 0; +} + +static int qmp_ufs_power_on(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *tx = qphy->tx; + void __iomem *rx = qphy->rx; + void __iomem *pcs = qphy->pcs; + void __iomem *status; + unsigned int mask, val, ready; + int ret; + + qmp_ufs_serdes_init(qphy); + + /* Tx, Rx, and PCS configurations */ + qmp_ufs_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_ufs_configure_lane(qphy->tx2, cfg->regs, + cfg->tx_tbl, cfg->tx_tbl_num, 2); + } + + qmp_ufs_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_ufs_configure_lane(qphy->rx2, cfg->regs, + cfg->rx_tbl, cfg->rx_tbl_num, 2); + } + + qmp_ufs_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + ret = reset_control_deassert(qmp->ufs_reset); + if (ret) + return ret; + + /* Pull PHY out of reset state */ + if (!cfg->no_pcs_sw_reset) + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; + mask = PCS_READY; + ready = PCS_READY; + + ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + return ret; + } + + return 0; +} + +static int qmp_ufs_power_off(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + /* PHY reset */ + if (!cfg->no_pcs_sw_reset) + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Put PHY into POWER DOWN state: active low */ + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) { + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } else { + qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } + + return 0; +} + +static int qmp_ufs_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + + qmp_ufs_com_exit(qphy); + + return 0; +} + +static int qmp_ufs_enable(struct phy *phy) +{ + int ret; + + ret = qmp_ufs_init(phy); + if (ret) + return ret; + + ret = qmp_ufs_power_on(phy); + if (ret) + qmp_ufs_exit(phy); + + return ret; +} + +static int qmp_ufs_disable(struct phy *phy) +{ + int ret; + + ret = qmp_ufs_power_off(phy); + if (ret) + return ret; + return qmp_ufs_exit(phy); +} + +static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_vregs; + int i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, num, qmp->vregs); +} + +static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_clks; + int i; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + return devm_clk_bulk_get(dev, num, qmp->clks); +} + +static const struct phy_ops qcom_qmp_ufs_ops = { + .power_on = qmp_ufs_enable, + .power_off = qmp_ufs_disable, + .owner = THIS_MODULE, +}; + +static int qmp_ufs_create(struct device *dev, struct device_node *np, int id, + void __iomem *serdes, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct phy *generic_phy; + struct qmp_phy *qphy; + int ret; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->cfg = cfg; + qphy->serdes = serdes; + /* + * Get memory resources for each phy lane: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 + * For single lane PHYs: pcs_misc (optional) -> 3. + */ + qphy->tx = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qphy->tx)) + return PTR_ERR(qphy->tx); + + qphy->rx = devm_of_iomap(dev, np, 1, NULL); + if (IS_ERR(qphy->rx)) + return PTR_ERR(qphy->rx); + + qphy->pcs = devm_of_iomap(dev, np, 2, NULL); + if (IS_ERR(qphy->pcs)) + return PTR_ERR(qphy->pcs); + + if (cfg->lanes >= 2) { + qphy->tx2 = devm_of_iomap(dev, np, 3, NULL); + if (IS_ERR(qphy->tx2)) + return PTR_ERR(qphy->tx2); + + qphy->rx2 = devm_of_iomap(dev, np, 4, NULL); + if (IS_ERR(qphy->rx2)) + return PTR_ERR(qphy->rx2); + + qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL); + } else { + qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL); + } + + if (IS_ERR(qphy->pcs_misc)) + dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); + + generic_phy = devm_phy_create(dev, np, &qcom_qmp_ufs_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create qphy %d\n", ret); + return ret; + } + + qphy->phy = generic_phy; + qphy->qmp = qmp; + qmp->phys[id] = qphy; + phy_set_drvdata(generic_phy, qphy); + + return 0; +} + +static const struct of_device_id qmp_ufs_of_match_table[] = { + { + .compatible = "qcom,msm8996-qmp-ufs-phy", + .data = &msm8996_ufs_cfg, + }, { + .compatible = "qcom,msm8998-qmp-ufs-phy", + .data = &sdm845_ufsphy_cfg, + }, { + .compatible = "qcom,sc8180x-qmp-ufs-phy", + .data = &sm8150_ufsphy_cfg, + }, { + .compatible = "qcom,sc8280xp-qmp-ufs-phy", + .data = &sm8350_ufsphy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-ufs-phy", + .data = &sdm845_ufsphy_cfg, + }, { + .compatible = "qcom,sm6115-qmp-ufs-phy", + .data = &sm6115_ufsphy_cfg, + }, { + .compatible = "qcom,sm6350-qmp-ufs-phy", + .data = &sdm845_ufsphy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-ufs-phy", + .data = &sm8150_ufsphy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-ufs-phy", + .data = &sm8150_ufsphy_cfg, + }, { + .compatible = "qcom,sm8350-qmp-ufs-phy", + .data = &sm8350_ufsphy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-ufs-phy", + .data = &sm8450_ufsphy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qmp_ufs_of_match_table); + +static int qmp_ufs_probe(struct platform_device *pdev) +{ + struct qcom_qmp *qmp; + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy_provider *phy_provider; + void __iomem *serdes; + const struct qmp_phy_cfg *cfg = NULL; + int num, id; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + dev_set_drvdata(dev, qmp); + + /* Get the specific init parameters of QMP phy */ + cfg = of_device_get_match_data(dev); + if (!cfg) + return -EINVAL; + + /* per PHY serdes; usually located at base address */ + serdes = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + ret = qmp_ufs_clk_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_ufs_vreg_init(dev, cfg); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + num = of_get_available_child_count(dev->of_node); + /* do we have a rogue child node ? */ + if (num > 1) + return -EINVAL; + + qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); + if (!qmp->phys) + return -ENOMEM; + + id = 0; + for_each_available_child_of_node(dev->of_node, child) { + /* Create per-lane phy */ + ret = qmp_ufs_create(dev, child, id, serdes, cfg); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + goto err_node_put; + } + + id++; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + of_node_put(child); + return ret; +} + +static struct platform_driver qmp_ufs_driver = { + .probe = qmp_ufs_probe, + .driver = { + .name = "qcom-qmp-ufs-phy", + .of_match_table = qmp_ufs_of_match_table, + }, +}; + +module_platform_driver(qmp_ufs_driver); + +MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); +MODULE_DESCRIPTION("Qualcomm QMP UFS PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c new file mode 100644 index 000000000000..b84c0d4b5754 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -0,0 +1,2825 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +#include "phy-qcom-qmp.h" + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) + +/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ +/* DP PHY soft reset */ +#define SW_DPPHY_RESET BIT(0) +/* mux to select DP PHY reset control, 0:HW control, 1: software reset */ +#define SW_DPPHY_RESET_MUX BIT(1) +/* USB3 PHY soft reset */ +#define SW_USB3PHY_RESET BIT(2) +/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */ +#define SW_USB3PHY_RESET_MUX BIT(3) + +/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */ +#define USB3_MODE BIT(0) /* enables USB3 mode */ +#define DP_MODE BIT(1) /* enables DP mode */ + +/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */ +#define ARCVR_DTCT_EN BIT(0) +#define ALFPS_DTCT_EN BIT(1) +#define ARCVR_DTCT_EVENT_SEL BIT(4) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */ +#define IRQ_CLEAR BIT(0) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */ +#define RCVR_DETECT BIT(0) + +/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ +#define CLAMP_EN BIT(0) /* enables i/o clamp_n */ + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 +#define POWER_DOWN_DELAY_US_MIN 10 +#define POWER_DOWN_DELAY_US_MAX 11 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * register part of layout ? + * if yes, then offset gives index in the reg-layout + */ + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_L(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_STATUS, + QPHY_PCS_AUTONOMOUS_MODE_CTRL, + QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, + QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, + /* PCS_MISC registers */ + QPHY_PCS_MISC_TYPEC_CTRL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE +}; + +static const unsigned int usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x17c, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8, + [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178, +}; + +static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x174, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc, + [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, +}; + +static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x008, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x014, +}; + +static const unsigned int qcm2290_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x04, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0xd8, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0xdc, + [QPHY_PCS_STATUS] = 0x174, + [QPHY_PCS_MISC_TYPEC_CTRL] = 0x00, +}; + +static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + /* PLL and Loop filter settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + /* SSC settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl ipq8074_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0), +}; + +static const struct qmp_phy_init_tbl ipq8074_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), + /* PLL and Loop filter settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + /* SSC settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { + /* FLL settings */ + QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNTRL2, 0x03), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_CNT_VAL_H_TOL, 0x42), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_FLL_MAN_CODE, 0x85), + + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V2_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG2, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = { + /* FLL settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x50), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = { + /* FLL settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb5), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4c), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x64), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x95), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xef), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), +}; + +static const struct qmp_phy_init_tbl sdx55_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x08), +}; + +static const struct qmp_phy_init_tbl sdx55_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x26), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x048), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), +}; + +static const struct qmp_phy_init_tbl sdx65_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0b), +}; + +static const struct qmp_phy_init_tbl sdx65_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_5, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xbb), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbb), + QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3d, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3c, 2), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xd2), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_INITVAL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x01), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x00), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x00), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0xbd), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00), +}; + +static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + int lanes; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_usb_tbl; + int pcs_usb_tbl_num; + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* resets to be requested */ + const char * const *reset_list; + int num_resets; + /* regulators to be requested */ + const char * const *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + unsigned int start_ctrl; + unsigned int pwrdn_ctrl; + /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */ + unsigned int phy_status; + + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; + /* power_down delay in usec */ + int pwrdn_delay_min; + int pwrdn_delay_max; + + /* true, if PHY has a separate DP_COM control block */ + bool has_phy_dp_com_ctrl; + + /* Offset from PCS to PCS_USB region */ + unsigned int pcs_usb_offset; +}; + +/** + * struct qmp_phy - per-lane phy descriptor + * + * @phy: generic phy + * @cfg: phy specific configuration + * @serdes: iomapped memory space for phy's serdes (i.e. PLL) + * @tx: iomapped memory space for lane's tx + * @rx: iomapped memory space for lane's rx + * @pcs: iomapped memory space for lane's pcs + * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs) + * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs) + * @pcs_misc: iomapped memory space for lane's pcs_misc + * @pcs_usb: iomapped memory space for lane's pcs_usb + * @pipe_clk: pipe clock + * @qmp: QMP phy to which this lane belongs + * @mode: current PHY mode + */ +struct qmp_phy { + struct phy *phy; + const struct qmp_phy_cfg *cfg; + void __iomem *serdes; + void __iomem *tx; + void __iomem *rx; + void __iomem *pcs; + void __iomem *tx2; + void __iomem *rx2; + void __iomem *pcs_misc; + void __iomem *pcs_usb; + struct clk *pipe_clk; + struct qcom_qmp *qmp; + enum phy_mode mode; +}; + +/** + * struct qcom_qmp - structure holding QMP phy block attributes + * + * @dev: device + * @dp_com: iomapped memory space for phy's dp_com control block + * + * @clks: array of clocks required by phy + * @resets: array of resets required by phy + * @vregs: regulator supplies bulk data + * + * @phys: array of per-lane phy descriptors + */ +struct qcom_qmp { + struct device *dev; + void __iomem *dp_com; + + struct clk_bulk_data *clks; + struct reset_control_bulk_data *resets; + struct regulator_bulk_data *vregs; + + struct qmp_phy **phys; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const msm8996_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", +}; + +static const char * const qmp_v3_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", "com_aux", +}; + +static const char * const qmp_v4_phy_clk_l[] = { + "aux", "ref_clk_src", "ref", "com_aux", +}; + +/* the primary usb3 phy on sm8250 doesn't have a ref clock */ +static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { + "aux", "ref_clk_src", "com_aux" +}; + +/* usb3 phy on sdx55 doesn't have com_aux clock */ +static const char * const qmp_v4_sdx55_usbphy_clk_l[] = { + "aux", "cfg_ahb", "ref" +}; + +static const char * const qcm2290_usb3phy_clk_l[] = { + "cfg_ahb", "ref", "com_aux", +}; + +/* list of resets */ +static const char * const msm8996_usb3phy_reset_l[] = { + "phy", "common", +}; + +static const char * const sc7180_usb3phy_reset_l[] = { + "phy", +}; + +static const char * const qcm2290_usb3phy_reset_l[] = { + "phy_phy", "phy", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy", "vdda-pll", +}; + +static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { + .lanes = 1, + + .serdes_tbl = ipq8074_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq8074_usb3_serdes_tbl), + .tx_tbl = msm8996_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), + .rx_tbl = ipq8074_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq8074_usb3_rx_tbl), + .pcs_tbl = ipq8074_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq8074_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { + .lanes = 1, + + .serdes_tbl = msm8996_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl), + .tx_tbl = msm8996_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), + .rx_tbl = msm8996_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl), + .pcs_tbl = msm8996_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, +}; + +static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = sc7180_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, +}; + +static const struct qmp_phy_cfg sc8280xp_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl), + .tx_tbl = sc8280xp_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_tx_tbl), + .rx_tbl = sc8280xp_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_rx_tbl), + .pcs_tbl = sc8280xp_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = qmp_v3_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_serdes_tbl), + .tx_tbl = qmp_v3_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_tx_tbl), + .rx_tbl = qmp_v3_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_rx_tbl), + .pcs_tbl = qmp_v3_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = msm8998_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl), + .tx_tbl = msm8998_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl), + .rx_tbl = msm8998_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl), + .pcs_tbl = msm8998_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8150_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl), + .rx_tbl = sm8150_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl), + .pcs_tbl = sm8150_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl), + .pcs_usb_tbl = sm8150_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, +}; + +static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8150_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_tx_tbl), + .rx_tbl = sm8150_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8150_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sm8150_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_pcs_usb_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x600, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, +}; + +static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8250_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_tx_tbl), + .rx_tbl = sm8250_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8250_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_usb_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x600, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sdx55_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sdx55_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdx55_usb3_uniphy_tx_tbl), + .rx_tbl = sdx55_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdx55_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8250_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_usb_tbl), + .clk_list = qmp_v4_sdx55_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sdx55_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x600, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sdx65_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sdx65_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdx65_usb3_uniphy_tx_tbl), + .rx_tbl = sdx65_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdx65_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8350_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sm8350_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_usb_tbl), + .clk_list = qmp_v4_sdx55_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sdx55_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x1000, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg sm8350_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8350_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_tx_tbl), + .rx_tbl = sm8350_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_rx_tbl), + .pcs_tbl = sm8350_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_tbl), + .pcs_usb_tbl = sm8350_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, +}; + +static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { + .lanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8350_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_tx_tbl), + .rx_tbl = sm8350_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8350_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sm8350_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_pcs_usb_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x1000, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = qcm2290_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), + .tx_tbl = qcm2290_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qcm2290_usb3_tx_tbl), + .rx_tbl = qcm2290_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qcm2290_usb3_rx_tbl), + .pcs_tbl = qcm2290_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl), + .clk_list = qcm2290_usb3phy_clk_l, + .num_clks = ARRAY_SIZE(qcm2290_usb3phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qcm2290_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, +}; + +static void qmp_usb_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + if (t->in_layout) + writel(t->val, base + regs[t->offset]); + else + writel(t->val, base + t->offset); + } +} + +static void qmp_usb_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qmp_usb_configure_lane(base, regs, tbl, num, 0xff); +} + +static int qmp_usb_serdes_init(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; + const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; + int serdes_tbl_num = cfg->serdes_tbl_num; + + qmp_usb_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num); + + return 0; +} + +static int qmp_usb_init(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs = qphy->pcs; + void __iomem *dp_com = qmp->dp_com; + int ret; + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + return ret; + } + + ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset assert failed\n"); + goto err_disable_regulators; + } + + ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset deassert failed\n"); + goto err_disable_regulators; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + goto err_assert_reset; + + if (cfg->has_phy_dp_com_ctrl) { + qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, + SW_PWRDN); + /* override hardware control for reset of qmp phy */ + qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + /* Default type-c orientation, i.e CC1 */ + qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02); + + qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, + USB3_MODE | DP_MODE); + + /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ + qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); + qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); + } + + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) + qphy_setbits(pcs, + cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + else + qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + + return 0; + +err_assert_reset: + reset_control_bulk_assert(cfg->num_resets, qmp->resets); +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return ret; +} + +static int qmp_usb_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + + reset_control_bulk_assert(cfg->num_resets, qmp->resets); + + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return 0; +} + +static int qmp_usb_power_on(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *tx = qphy->tx; + void __iomem *rx = qphy->rx; + void __iomem *pcs = qphy->pcs; + void __iomem *status; + unsigned int mask, val, ready; + int ret; + + qmp_usb_serdes_init(qphy); + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + return ret; + } + + /* Tx, Rx, and PCS configurations */ + qmp_usb_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_usb_configure_lane(qphy->tx2, cfg->regs, + cfg->tx_tbl, cfg->tx_tbl_num, 2); + } + + qmp_usb_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_usb_configure_lane(qphy->rx2, cfg->regs, + cfg->rx_tbl, cfg->rx_tbl_num, 2); + } + + /* Configure link rate, swing, etc. */ + qmp_usb_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + if (cfg->has_pwrdn_delay) + usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); + + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + status = pcs + cfg->regs[QPHY_PCS_STATUS]; + mask = cfg->phy_status; + ready = 0; + + ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + goto err_disable_pipe_clk; + } + + return 0; + +err_disable_pipe_clk: + clk_disable_unprepare(qphy->pipe_clk); + + return ret; +} + +static int qmp_usb_power_off(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + + clk_disable_unprepare(qphy->pipe_clk); + + /* PHY reset */ + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Put PHY into POWER DOWN state: active low */ + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) { + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } else { + qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } + + return 0; +} + +static int qmp_usb_enable(struct phy *phy) +{ + int ret; + + ret = qmp_usb_init(phy); + if (ret) + return ret; + + ret = qmp_usb_power_on(phy); + if (ret) + qmp_usb_exit(phy); + + return ret; +} + +static int qmp_usb_disable(struct phy *phy) +{ + int ret; + + ret = qmp_usb_power_off(phy); + if (ret) + return ret; + return qmp_usb_exit(phy); +} + +static int qmp_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + + qphy->mode = mode; + + return 0; +} + +static void qmp_usb_enable_autonomous_mode(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + u32 intr_mask; + + if (qphy->mode == PHY_MODE_USB_HOST_SS || + qphy->mode == PHY_MODE_USB_DEVICE_SS) + intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; + else + intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; + + /* Clear any pending interrupts status */ + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL); + + /* Enable required PHY autonomous mode interrupts */ + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask); + + /* Enable i/o clamp_n for autonomous mode */ + if (pcs_misc) + qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); +} + +static void qmp_usb_disable_autonomous_mode(struct qmp_phy *qphy) +{ + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + + /* Disable i/o clamp_n on resume for normal mode */ + if (pcs_misc) + qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); + + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN); + + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); +} + +static int __maybe_unused qmp_usb_runtime_suspend(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct qmp_phy *qphy = qmp->phys[0]; + const struct qmp_phy_cfg *cfg = qphy->cfg; + + dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qphy->mode); + + if (!qphy->phy->init_count) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + qmp_usb_enable_autonomous_mode(qphy); + + clk_disable_unprepare(qphy->pipe_clk); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + return 0; +} + +static int __maybe_unused qmp_usb_runtime_resume(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct qmp_phy *qphy = qmp->phys[0]; + const struct qmp_phy_cfg *cfg = qphy->cfg; + int ret = 0; + + dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qphy->mode); + + if (!qphy->phy->init_count) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + return ret; + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + return ret; + } + + qmp_usb_disable_autonomous_mode(qphy); + + return 0; +} + +static int qmp_usb_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_vregs; + int i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, num, qmp->vregs); +} + +static int qmp_usb_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int i; + int ret; + + qmp->resets = devm_kcalloc(dev, cfg->num_resets, + sizeof(*qmp->resets), GFP_KERNEL); + if (!qmp->resets) + return -ENOMEM; + + for (i = 0; i < cfg->num_resets; i++) + qmp->resets[i].id = cfg->reset_list[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get resets\n"); + + return 0; +} + +static int qmp_usb_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = cfg->num_clks; + int i; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + return devm_clk_bulk_get(dev, num, qmp->clks); +} + +static void phy_clk_release_provider(void *res) +{ + of_clk_del_provider(res); +} + +/* + * Register a fixed rate pipe clock. + * + * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate + * controls it. The <s>_pipe_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the <s>_pipe_clksrc here. The gcc driver takes care + * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) +{ + struct clk_fixed_rate *fixed; + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string(np, "clock-output-names", &init.name); + if (ret) { + dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); + return ret; + } + + fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return -ENOMEM; + + init.ops = &clk_fixed_rate_ops; + + /* controllers using QMP phys use 125MHz pipe clock interface */ + fixed->fixed_rate = 125000000; + fixed->hw.init = &init; + + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); +} + +static const struct phy_ops qmp_usb_ops = { + .init = qmp_usb_enable, + .exit = qmp_usb_disable, + .set_mode = qmp_usb_set_mode, + .owner = THIS_MODULE, +}; + +static void __iomem *qmp_usb_iomap(struct device *dev, struct device_node *np, + int index, bool exclusive) +{ + struct resource res; + + if (!exclusive) { + if (of_address_to_resource(np, index, &res)) + return IOMEM_ERR_PTR(-EINVAL); + + return devm_ioremap(dev, res.start, resource_size(&res)); + } + + return devm_of_iomap(dev, np, index, NULL); +} + +static +int qmp_usb_create(struct device *dev, struct device_node *np, int id, + void __iomem *serdes, const struct qmp_phy_cfg *cfg) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct phy *generic_phy; + struct qmp_phy *qphy; + bool exclusive = true; + int ret; + + /* + * FIXME: These bindings should be fixed to not rely on overlapping + * mappings for PCS. + */ + if (of_device_is_compatible(dev->of_node, "qcom,sdx65-qmp-usb3-uni-phy")) + exclusive = false; + if (of_device_is_compatible(dev->of_node, "qcom,sm8350-qmp-usb3-uni-phy")) + exclusive = false; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->cfg = cfg; + qphy->serdes = serdes; + /* + * Get memory resources for each phy lane: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 + * For single lane PHYs: pcs_misc (optional) -> 3. + */ + qphy->tx = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qphy->tx)) + return PTR_ERR(qphy->tx); + + qphy->rx = devm_of_iomap(dev, np, 1, NULL); + if (IS_ERR(qphy->rx)) + return PTR_ERR(qphy->rx); + + qphy->pcs = qmp_usb_iomap(dev, np, 2, exclusive); + if (IS_ERR(qphy->pcs)) + return PTR_ERR(qphy->pcs); + + if (cfg->pcs_usb_offset) + qphy->pcs_usb = qphy->pcs + cfg->pcs_usb_offset; + + if (cfg->lanes >= 2) { + qphy->tx2 = devm_of_iomap(dev, np, 3, NULL); + if (IS_ERR(qphy->tx2)) + return PTR_ERR(qphy->tx2); + + qphy->rx2 = devm_of_iomap(dev, np, 4, NULL); + if (IS_ERR(qphy->rx2)) + return PTR_ERR(qphy->rx2); + + qphy->pcs_misc = devm_of_iomap(dev, np, 5, NULL); + } else { + qphy->pcs_misc = devm_of_iomap(dev, np, 3, NULL); + } + + if (IS_ERR(qphy->pcs_misc)) { + dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); + qphy->pcs_misc = NULL; + } + + qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL); + if (IS_ERR(qphy->pipe_clk)) { + return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk), + "failed to get lane%d pipe clock\n", id); + } + + generic_phy = devm_phy_create(dev, np, &qmp_usb_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create qphy %d\n", ret); + return ret; + } + + qphy->phy = generic_phy; + qphy->qmp = qmp; + qmp->phys[id] = qphy; + phy_set_drvdata(generic_phy, qphy); + + return 0; +} + +static const struct of_device_id qmp_usb_of_match_table[] = { + { + .compatible = "qcom,ipq8074-qmp-usb3-phy", + .data = &ipq8074_usb3phy_cfg, + }, { + .compatible = "qcom,msm8996-qmp-usb3-phy", + .data = &msm8996_usb3phy_cfg, + }, { + .compatible = "qcom,ipq6018-qmp-usb3-phy", + .data = &ipq8074_usb3phy_cfg, + }, { + .compatible = "qcom,sc7180-qmp-usb3-phy", + .data = &sc7180_usb3phy_cfg, + }, { + .compatible = "qcom,sc8180x-qmp-usb3-phy", + .data = &sm8150_usb3phy_cfg, + }, { + .compatible = "qcom,sc8280xp-qmp-usb3-uni-phy", + .data = &sc8280xp_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-usb3-phy", + .data = &qmp_v3_usb3phy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-usb3-uni-phy", + .data = &qmp_v3_usb3_uniphy_cfg, + }, { + .compatible = "qcom,msm8998-qmp-usb3-phy", + .data = &msm8998_usb3phy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-usb3-phy", + .data = &sm8150_usb3phy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-usb3-uni-phy", + .data = &sm8150_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-phy", + .data = &sm8250_usb3phy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-uni-phy", + .data = &sm8250_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sdx55-qmp-usb3-uni-phy", + .data = &sdx55_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sdx65-qmp-usb3-uni-phy", + .data = &sdx65_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8350-qmp-usb3-phy", + .data = &sm8350_usb3phy_cfg, + }, { + .compatible = "qcom,sm8350-qmp-usb3-uni-phy", + .data = &sm8350_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-usb3-phy", + .data = &sm8350_usb3phy_cfg, + }, { + .compatible = "qcom,qcm2290-qmp-usb3-phy", + .data = &qcm2290_usb3phy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qmp_usb_of_match_table); + +static const struct dev_pm_ops qmp_usb_pm_ops = { + SET_RUNTIME_PM_OPS(qmp_usb_runtime_suspend, + qmp_usb_runtime_resume, NULL) +}; + +static int qmp_usb_probe(struct platform_device *pdev) +{ + struct qcom_qmp *qmp; + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy_provider *phy_provider; + void __iomem *serdes; + const struct qmp_phy_cfg *cfg = NULL; + int num, id; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + dev_set_drvdata(dev, qmp); + + /* Get the specific init parameters of QMP phy */ + cfg = of_device_get_match_data(dev); + if (!cfg) + return -EINVAL; + + /* per PHY serdes; usually located at base address */ + serdes = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(serdes)) + return PTR_ERR(serdes); + + /* per PHY dp_com; if PHY has dp_com control block */ + if (cfg->has_phy_dp_com_ctrl) { + qmp->dp_com = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(qmp->dp_com)) + return PTR_ERR(qmp->dp_com); + } + + ret = qmp_usb_clk_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_usb_reset_init(dev, cfg); + if (ret) + return ret; + + ret = qmp_usb_vreg_init(dev, cfg); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + num = of_get_available_child_count(dev->of_node); + /* do we have a rogue child node ? */ + if (num > 1) + return -EINVAL; + + qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); + if (!qmp->phys) + return -ENOMEM; + + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + /* + * Prevent runtime pm from being ON by default. Users can enable + * it using power/control in sysfs. + */ + pm_runtime_forbid(dev); + + id = 0; + for_each_available_child_of_node(dev->of_node, child) { + /* Create per-lane phy */ + ret = qmp_usb_create(dev, child, id, serdes, cfg); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + goto err_node_put; + } + + /* + * Register the pipe clock provided by phy. + * See function description to see details of this pipe clock. + */ + ret = phy_pipe_clk_register(qmp, child); + if (ret) { + dev_err(qmp->dev, + "failed to register pipe clock source\n"); + goto err_node_put; + } + + id++; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + of_node_put(child); + return ret; +} + +static struct platform_driver qmp_usb_driver = { + .probe = qmp_usb_probe, + .driver = { + .name = "qcom-qmp-usb-phy", + .pm = &qmp_usb_pm_ops, + .of_match_table = qmp_usb_of_match_table, + }, +}; + +module_platform_driver(qmp_usb_driver); + +MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); +MODULE_DESCRIPTION("Qualcomm QMP USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c deleted file mode 100644 index 7db2a94f7a99..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ /dev/null @@ -1,2257 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. - */ - -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_address.h> -#include <linux/phy/phy.h> -#include <linux/platform_device.h> -#include <linux/regulator/consumer.h> -#include <linux/reset.h> -#include <linux/slab.h> - -#include <dt-bindings/phy/phy.h> - -#include "phy-qcom-qmp.h" - -/* QPHY_SW_RESET bit */ -#define SW_RESET BIT(0) -/* QPHY_POWER_DOWN_CONTROL */ -#define SW_PWRDN BIT(0) -#define REFCLK_DRV_DSBL BIT(1) -/* QPHY_START_CONTROL bits */ -#define SERDES_START BIT(0) -#define PCS_START BIT(1) -#define PLL_READY_GATE_EN BIT(3) -/* QPHY_PCS_STATUS bit */ -#define PHYSTATUS BIT(6) -/* QPHY_PCS_READY_STATUS & QPHY_COM_PCS_READY_STATUS bit */ -#define PCS_READY BIT(0) - -/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ -/* DP PHY soft reset */ -#define SW_DPPHY_RESET BIT(0) -/* mux to select DP PHY reset control, 0:HW control, 1: software reset */ -#define SW_DPPHY_RESET_MUX BIT(1) -/* USB3 PHY soft reset */ -#define SW_USB3PHY_RESET BIT(2) -/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */ -#define SW_USB3PHY_RESET_MUX BIT(3) - -/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */ -#define USB3_MODE BIT(0) /* enables USB3 mode */ -#define DP_MODE BIT(1) /* enables DP mode */ - -/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */ -#define ARCVR_DTCT_EN BIT(0) -#define ALFPS_DTCT_EN BIT(1) -#define ARCVR_DTCT_EVENT_SEL BIT(4) - -/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */ -#define IRQ_CLEAR BIT(0) - -/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */ -#define RCVR_DETECT BIT(0) - -/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ -#define CLAMP_EN BIT(0) /* enables i/o clamp_n */ - -#define PHY_INIT_COMPLETE_TIMEOUT 10000 -#define POWER_DOWN_DELAY_US_MIN 10 -#define POWER_DOWN_DELAY_US_MAX 11 - -#define MAX_PROP_NAME 32 - -/* Define the assumed distance between lanes for underspecified device trees. */ -#define QMP_PHY_LEGACY_LANE_STRIDE 0x400 - -struct qmp_phy_init_tbl { - unsigned int offset; - unsigned int val; - /* - * register part of layout ? - * if yes, then offset gives index in the reg-layout - */ - int in_layout; -}; - -#define QMP_PHY_INIT_CFG(o, v) \ - { \ - .offset = o, \ - .val = v, \ - } - -#define QMP_PHY_INIT_CFG_L(o, v) \ - { \ - .offset = o, \ - .val = v, \ - .in_layout = 1, \ - } - -/* set of registers with offsets different per-PHY */ -enum qphy_reg_layout { - /* Common block control registers */ - QPHY_COM_SW_RESET, - QPHY_COM_POWER_DOWN_CONTROL, - QPHY_COM_START_CONTROL, - QPHY_COM_PCS_READY_STATUS, - /* PCS registers */ - QPHY_PLL_LOCK_CHK_DLY_TIME, - QPHY_FLL_CNTRL1, - QPHY_FLL_CNTRL2, - QPHY_FLL_CNT_VAL_L, - QPHY_FLL_CNT_VAL_H_TOL, - QPHY_FLL_MAN_CODE, - QPHY_SW_RESET, - QPHY_START_CTRL, - QPHY_PCS_READY_STATUS, - QPHY_PCS_STATUS, - QPHY_PCS_AUTONOMOUS_MODE_CTRL, - QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, - QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, -}; - -static const unsigned int pciephy_regs_layout[] = { - [QPHY_COM_SW_RESET] = 0x400, - [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, - [QPHY_COM_START_CONTROL] = 0x408, - [QPHY_COM_PCS_READY_STATUS] = 0x448, - [QPHY_PLL_LOCK_CHK_DLY_TIME] = 0xa8, - [QPHY_FLL_CNTRL1] = 0xc4, - [QPHY_FLL_CNTRL2] = 0xc8, - [QPHY_FLL_CNT_VAL_L] = 0xcc, - [QPHY_FLL_CNT_VAL_H_TOL] = 0xd0, - [QPHY_FLL_MAN_CODE] = 0xd4, - [QPHY_SW_RESET] = 0x00, - [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_STATUS] = 0x174, -}; - -static const unsigned int usb3phy_regs_layout[] = { - [QPHY_FLL_CNTRL1] = 0xc0, - [QPHY_FLL_CNTRL2] = 0xc4, - [QPHY_FLL_CNT_VAL_L] = 0xc8, - [QPHY_FLL_CNT_VAL_H_TOL] = 0xcc, - [QPHY_FLL_MAN_CODE] = 0xd0, - [QPHY_SW_RESET] = 0x00, - [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_STATUS] = 0x17c, - [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4, - [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8, - [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178, -}; - -static const unsigned int qmp_v3_usb3phy_regs_layout[] = { - [QPHY_SW_RESET] = 0x00, - [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_STATUS] = 0x174, - [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8, - [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc, - [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, -}; - -static const unsigned int sdm845_ufsphy_regs_layout[] = { - [QPHY_START_CTRL] = 0x00, - [QPHY_PCS_READY_STATUS] = 0x160, -}; - -static const unsigned int sm8150_ufsphy_regs_layout[] = { - [QPHY_START_CTRL] = QPHY_V4_PHY_START, - [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_READY_STATUS, - [QPHY_SW_RESET] = QPHY_V4_SW_RESET, -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c), - QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), - QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), - - QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05), - - QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05), - QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02), - QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00), - QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3), - QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e), -}; - -static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15), -}; - -static const struct qmp_phy_init_tbl msm8998_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), -}; - -static const struct qmp_phy_init_tbl msm8998_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40), -}; - -static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x99), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), - /* PLL and Loop filter settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), - /* SSC settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { - /* FLL settings */ - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03), - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02), - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09), - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42), - QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85), - - /* Lock Det settings */ - QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1), - QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f), - QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47), - QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08), -}; - -static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0xf), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x6), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0xf), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x3), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0xD), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xD04), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x2), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0xb), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x2), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x7), -}; - -static const struct qmp_phy_init_tbl ipq8074_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x6), - QMP_PHY_INIT_CFG(QSERDES_TX_RES_CODE_LANE_OFFSET, 0x2), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), -}; - -static const struct qmp_phy_init_tbl ipq8074_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x4), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x4), -}; - -static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x4), - QMP_PHY_INIT_CFG(QPHY_OSC_DTCT_ACTIONS, 0x0), - QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x40), - QMP_PHY_INIT_CFG(QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x0), - QMP_PHY_INIT_CFG(QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x40), - QMP_PHY_INIT_CFG(QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB, 0x0), - QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x40), - QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x73), - QMP_PHY_INIT_CFG(QPHY_RX_SIGDET_LVL, 0x99), - QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M6DB_V0, 0x15), - QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0xe), - QMP_PHY_INIT_CFG_L(QPHY_SW_RESET, 0x0), - QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = { - /* FLL settings */ - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), - - /* Lock Det settings */ - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x50), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = { - /* FLL settings */ - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), - - /* Lock Det settings */ - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb5), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4c), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x64), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6a), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x21), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60), -}; - -static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f), - - /* Rate B */ - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44), -}; - -static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07), -}; - -static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59), -}; - -static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL2, 0x6e), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x0a), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SYM_RESYNC_CTRL, 0x03), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_MID_TERM_CTRL1, 0x43), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL1, 0x0f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_MIN_HIBERN8_TIME, 0x9a), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02), -}; - -static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), -}; - -static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00), -}; - -static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05), -}; - -static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), -}; - -static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_INITVAL2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x98), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x32), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23), - - /* Rate B */ - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06), -}; - -static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c), -}; - -static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x1b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xf6), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3d), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), - -}; - -static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V4_RX_SIGDET_CTRL2, 0x6d), - QMP_PHY_INIT_CFG(QPHY_V4_TX_LARGE_AMP_DRV_LVL, 0x0a), - QMP_PHY_INIT_CFG(QPHY_V4_TX_SMALL_AMP_DRV_LVL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V4_TX_MID_TERM_CTRL1, 0x43), - QMP_PHY_INIT_CFG(QPHY_V4_DEBUG_BUS_CLKSEL, 0x1f), - QMP_PHY_INIT_CFG(QPHY_V4_RX_MIN_HIBERN8_TIME, 0xff), - QMP_PHY_INIT_CFG(QPHY_V4_MULTI_LANE_CTRL1, 0x02), -}; - -/* struct qmp_phy_cfg - per-PHY initialization config */ -struct qmp_phy_cfg { - /* phy-type - PCIE/UFS/USB */ - unsigned int type; - /* number of lanes provided by phy */ - int nlanes; - - /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ - const struct qmp_phy_init_tbl *serdes_tbl; - int serdes_tbl_num; - const struct qmp_phy_init_tbl *tx_tbl; - int tx_tbl_num; - const struct qmp_phy_init_tbl *rx_tbl; - int rx_tbl_num; - const struct qmp_phy_init_tbl *pcs_tbl; - int pcs_tbl_num; - - /* clock ids to be requested */ - const char * const *clk_list; - int num_clks; - /* resets to be requested */ - const char * const *reset_list; - int num_resets; - /* regulators to be requested */ - const char * const *vreg_list; - int num_vregs; - - /* array of registers with different offsets */ - const unsigned int *regs; - - unsigned int start_ctrl; - unsigned int pwrdn_ctrl; - unsigned int mask_com_pcs_ready; - - /* true, if PHY has a separate PHY_COM control block */ - bool has_phy_com_ctrl; - /* true, if PHY has a reset for individual lanes */ - bool has_lane_rst; - /* true, if PHY needs delay after POWER_DOWN */ - bool has_pwrdn_delay; - /* power_down delay in usec */ - int pwrdn_delay_min; - int pwrdn_delay_max; - - /* true, if PHY has a separate DP_COM control block */ - bool has_phy_dp_com_ctrl; - /* true, if PHY has secondary tx/rx lanes to be configured */ - bool is_dual_lane_phy; - - /* true, if PCS block has no separate SW_RESET register */ - bool no_pcs_sw_reset; -}; - -/** - * struct qmp_phy - per-lane phy descriptor - * - * @phy: generic phy - * @tx: iomapped memory space for lane's tx - * @rx: iomapped memory space for lane's rx - * @pcs: iomapped memory space for lane's pcs - * @tx2: iomapped memory space for second lane's tx (in dual lane PHYs) - * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs) - * @pcs_misc: iomapped memory space for lane's pcs_misc - * @pipe_clk: pipe lock - * @index: lane index - * @qmp: QMP phy to which this lane belongs - * @lane_rst: lane's reset controller - */ -struct qmp_phy { - struct phy *phy; - void __iomem *tx; - void __iomem *rx; - void __iomem *pcs; - void __iomem *tx2; - void __iomem *rx2; - void __iomem *pcs_misc; - struct clk *pipe_clk; - unsigned int index; - struct qcom_qmp *qmp; - struct reset_control *lane_rst; -}; - -/** - * struct qcom_qmp - structure holding QMP phy block attributes - * - * @dev: device - * @serdes: iomapped memory space for phy's serdes - * @dp_com: iomapped memory space for phy's dp_com control block - * - * @clks: array of clocks required by phy - * @resets: array of resets required by phy - * @vregs: regulator supplies bulk data - * - * @cfg: phy specific configuration - * @phys: array of per-lane phy descriptors - * @phy_mutex: mutex lock for PHY common block initialization - * @init_count: phy common block initialization count - * @phy_initialized: indicate if PHY has been initialized - * @mode: current PHY mode - * @ufs_reset: optional UFS PHY reset handle - */ -struct qcom_qmp { - struct device *dev; - void __iomem *serdes; - void __iomem *dp_com; - - struct clk_bulk_data *clks; - struct reset_control **resets; - struct regulator_bulk_data *vregs; - - const struct qmp_phy_cfg *cfg; - struct qmp_phy **phys; - - struct mutex phy_mutex; - int init_count; - bool phy_initialized; - enum phy_mode mode; - - struct reset_control *ufs_reset; -}; - -static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) -{ - u32 reg; - - reg = readl(base + offset); - reg |= val; - writel(reg, base + offset); - - /* ensure that above write is through */ - readl(base + offset); -} - -static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) -{ - u32 reg; - - reg = readl(base + offset); - reg &= ~val; - writel(reg, base + offset); - - /* ensure that above write is through */ - readl(base + offset); -} - -/* list of clocks required by phy */ -static const char * const msm8996_phy_clk_l[] = { - "aux", "cfg_ahb", "ref", -}; - -static const char * const qmp_v3_phy_clk_l[] = { - "aux", "cfg_ahb", "ref", "com_aux", -}; - -static const char * const sdm845_ufs_phy_clk_l[] = { - "ref", "ref_aux", -}; - -/* list of resets */ -static const char * const msm8996_pciephy_reset_l[] = { - "phy", "common", "cfg", -}; - -static const char * const msm8996_usb3phy_reset_l[] = { - "phy", "common", -}; - -/* list of regulators */ -static const char * const qmp_phy_vreg_l[] = { - "vdda-phy", "vdda-pll", -}; - -static const struct qmp_phy_cfg msm8996_pciephy_cfg = { - .type = PHY_TYPE_PCIE, - .nlanes = 3, - - .serdes_tbl = msm8996_pcie_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), - .tx_tbl = msm8996_pcie_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), - .rx_tbl = msm8996_pcie_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), - .pcs_tbl = msm8996_pcie_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), - .reset_list = msm8996_pciephy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = pciephy_regs_layout, - - .start_ctrl = PCS_START | PLL_READY_GATE_EN, - .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, - .mask_com_pcs_ready = PCS_READY, - - .has_phy_com_ctrl = true, - .has_lane_rst = true, - .has_pwrdn_delay = true, - .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, - .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, -}; - -static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { - .type = PHY_TYPE_USB3, - .nlanes = 1, - - .serdes_tbl = msm8996_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl), - .tx_tbl = msm8996_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), - .rx_tbl = msm8996_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl), - .pcs_tbl = msm8996_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl), - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = usb3phy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN, -}; - -/* list of resets */ -static const char * const ipq8074_pciephy_reset_l[] = { - "phy", "common", -}; - -static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { - .type = PHY_TYPE_PCIE, - .nlanes = 1, - - .serdes_tbl = ipq8074_pcie_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(ipq8074_pcie_serdes_tbl), - .tx_tbl = ipq8074_pcie_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(ipq8074_pcie_tx_tbl), - .rx_tbl = ipq8074_pcie_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(ipq8074_pcie_rx_tbl), - .pcs_tbl = ipq8074_pcie_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(ipq8074_pcie_pcs_tbl), - .clk_list = NULL, - .num_clks = 0, - .reset_list = ipq8074_pciephy_reset_l, - .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), - .vreg_list = NULL, - .num_vregs = 0, - .regs = pciephy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, - - .has_phy_com_ctrl = false, - .has_lane_rst = false, - .has_pwrdn_delay = true, - .pwrdn_delay_min = 995, /* us */ - .pwrdn_delay_max = 1005, /* us */ -}; - -static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { - .type = PHY_TYPE_USB3, - .nlanes = 1, - - .serdes_tbl = qmp_v3_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), - .tx_tbl = qmp_v3_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), - .rx_tbl = qmp_v3_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), - .pcs_tbl = qmp_v3_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), - .clk_list = qmp_v3_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v3_usb3phy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN, - - .has_pwrdn_delay = true, - .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, - .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, - - .has_phy_dp_com_ctrl = true, - .is_dual_lane_phy = true, -}; - -static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { - .type = PHY_TYPE_USB3, - .nlanes = 1, - - .serdes_tbl = qmp_v3_usb3_uniphy_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_serdes_tbl), - .tx_tbl = qmp_v3_usb3_uniphy_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_tx_tbl), - .rx_tbl = qmp_v3_usb3_uniphy_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_rx_tbl), - .pcs_tbl = qmp_v3_usb3_uniphy_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_uniphy_pcs_tbl), - .clk_list = qmp_v3_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v3_usb3phy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN, - - .has_pwrdn_delay = true, - .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, - .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, -}; - -static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { - .type = PHY_TYPE_UFS, - .nlanes = 2, - - .serdes_tbl = sdm845_ufsphy_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl), - .tx_tbl = sdm845_ufsphy_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl), - .rx_tbl = sdm845_ufsphy_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl), - .pcs_tbl = sdm845_ufsphy_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl), - .clk_list = sdm845_ufs_phy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = sdm845_ufsphy_regs_layout, - - .start_ctrl = SERDES_START, - .pwrdn_ctrl = SW_PWRDN, - - .is_dual_lane_phy = true, - .no_pcs_sw_reset = true, -}; - -static const struct qmp_phy_cfg msm8998_pciephy_cfg = { - .type = PHY_TYPE_PCIE, - .nlanes = 1, - - .serdes_tbl = msm8998_pcie_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl), - .tx_tbl = msm8998_pcie_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(msm8998_pcie_tx_tbl), - .rx_tbl = msm8998_pcie_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(msm8998_pcie_rx_tbl), - .pcs_tbl = msm8998_pcie_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl), - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), - .reset_list = ipq8074_pciephy_reset_l, - .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = pciephy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, -}; - -static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { - .type = PHY_TYPE_USB3, - .nlanes = 1, - - .serdes_tbl = msm8998_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl), - .tx_tbl = msm8998_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl), - .rx_tbl = msm8998_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl), - .pcs_tbl = msm8998_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl), - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v3_usb3phy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN, - - .is_dual_lane_phy = true, -}; - -static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { - .type = PHY_TYPE_UFS, - .nlanes = 2, - - .serdes_tbl = sm8150_ufsphy_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl), - .tx_tbl = sm8150_ufsphy_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl), - .rx_tbl = sm8150_ufsphy_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl), - .pcs_tbl = sm8150_ufsphy_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl), - .clk_list = sdm845_ufs_phy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = sm8150_ufsphy_regs_layout, - - .start_ctrl = SERDES_START, - .pwrdn_ctrl = SW_PWRDN, - - .is_dual_lane_phy = true, -}; - -static void qcom_qmp_phy_configure(void __iomem *base, - const unsigned int *regs, - const struct qmp_phy_init_tbl tbl[], - int num) -{ - int i; - const struct qmp_phy_init_tbl *t = tbl; - - if (!t) - return; - - for (i = 0; i < num; i++, t++) { - if (t->in_layout) - writel(t->val, base + regs[t->offset]); - else - writel(t->val, base + t->offset); - } -} - -static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) -{ - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *serdes = qmp->serdes; - void __iomem *pcs = qphy->pcs; - void __iomem *dp_com = qmp->dp_com; - int ret, i; - - mutex_lock(&qmp->phy_mutex); - if (qmp->init_count++) { - mutex_unlock(&qmp->phy_mutex); - return 0; - } - - /* turn on regulator supplies */ - ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); - if (ret) { - dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); - goto err_reg_enable; - } - - for (i = 0; i < cfg->num_resets; i++) { - ret = reset_control_assert(qmp->resets[i]); - if (ret) { - dev_err(qmp->dev, "%s reset assert failed\n", - cfg->reset_list[i]); - goto err_rst_assert; - } - } - - for (i = cfg->num_resets - 1; i >= 0; i--) { - ret = reset_control_deassert(qmp->resets[i]); - if (ret) { - dev_err(qmp->dev, "%s reset deassert failed\n", - qmp->cfg->reset_list[i]); - goto err_rst; - } - } - - ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); - if (ret) { - dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret); - goto err_rst; - } - - if (cfg->has_phy_dp_com_ctrl) { - qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, - SW_PWRDN); - /* override hardware control for reset of qmp phy */ - qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, - SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); - - qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, - USB3_MODE | DP_MODE); - - /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ - qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, - SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); - } - - if (cfg->has_phy_com_ctrl) - qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], - SW_PWRDN); - else - qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); - - /* Serdes configuration */ - qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, - cfg->serdes_tbl_num); - - if (cfg->has_phy_com_ctrl) { - void __iomem *status; - unsigned int mask, val; - - qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); - qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], - SERDES_START | PCS_START); - - status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; - mask = cfg->mask_com_pcs_ready; - - ret = readl_poll_timeout(status, val, (val & mask), 10, - PHY_INIT_COMPLETE_TIMEOUT); - if (ret) { - dev_err(qmp->dev, - "phy common block init timed-out\n"); - goto err_com_init; - } - } - - mutex_unlock(&qmp->phy_mutex); - - return 0; - -err_com_init: - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); -err_rst: - while (++i < cfg->num_resets) - reset_control_assert(qmp->resets[i]); -err_rst_assert: - regulator_bulk_disable(cfg->num_vregs, qmp->vregs); -err_reg_enable: - mutex_unlock(&qmp->phy_mutex); - - return ret; -} - -static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) -{ - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *serdes = qmp->serdes; - int i = cfg->num_resets; - - mutex_lock(&qmp->phy_mutex); - if (--qmp->init_count) { - mutex_unlock(&qmp->phy_mutex); - return 0; - } - - reset_control_assert(qmp->ufs_reset); - if (cfg->has_phy_com_ctrl) { - qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], - SERDES_START | PCS_START); - qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], - SW_RESET); - qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], - SW_PWRDN); - } - - while (--i >= 0) - reset_control_assert(qmp->resets[i]); - - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); - - regulator_bulk_disable(cfg->num_vregs, qmp->vregs); - - mutex_unlock(&qmp->phy_mutex); - - return 0; -} - -static int qcom_qmp_phy_enable(struct phy *phy) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *tx = qphy->tx; - void __iomem *rx = qphy->rx; - void __iomem *pcs = qphy->pcs; - void __iomem *dp_com = qmp->dp_com; - void __iomem *status; - unsigned int mask, val, ready; - int ret; - - dev_vdbg(qmp->dev, "Initializing QMP phy\n"); - - if (cfg->no_pcs_sw_reset) { - /* - * Get UFS reset, which is delayed until now to avoid a - * circular dependency where UFS needs its PHY, but the PHY - * needs this UFS reset. - */ - if (!qmp->ufs_reset) { - qmp->ufs_reset = - devm_reset_control_get_exclusive(qmp->dev, - "ufsphy"); - - if (IS_ERR(qmp->ufs_reset)) { - ret = PTR_ERR(qmp->ufs_reset); - dev_err(qmp->dev, - "failed to get UFS reset: %d\n", - ret); - - qmp->ufs_reset = NULL; - return ret; - } - } - - ret = reset_control_assert(qmp->ufs_reset); - if (ret) - goto err_lane_rst; - } - - ret = qcom_qmp_phy_com_init(qphy); - if (ret) - return ret; - - if (cfg->has_lane_rst) { - ret = reset_control_deassert(qphy->lane_rst); - if (ret) { - dev_err(qmp->dev, "lane%d reset deassert failed\n", - qphy->index); - goto err_lane_rst; - } - } - - ret = clk_prepare_enable(qphy->pipe_clk); - if (ret) { - dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); - goto err_clk_enable; - } - - /* Tx, Rx, and PCS configurations */ - qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); - /* Configuration for other LANE for USB-DP combo PHY */ - if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(qphy->tx2, cfg->regs, - cfg->tx_tbl, cfg->tx_tbl_num); - - qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); - if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(qphy->rx2, cfg->regs, - cfg->rx_tbl, cfg->rx_tbl_num); - - qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); - ret = reset_control_deassert(qmp->ufs_reset); - if (ret) - goto err_lane_rst; - - /* - * Pull out PHY from POWER DOWN state. - * This is active low enable signal to power-down PHY. - */ - if(cfg->type == PHY_TYPE_PCIE) - qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); - - if (cfg->has_pwrdn_delay) - usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); - - /* Pull PHY out of reset state */ - if (!cfg->no_pcs_sw_reset) - qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - - if (cfg->has_phy_dp_com_ctrl) - qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); - - /* start SerDes and Phy-Coding-Sublayer */ - qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); - - if (cfg->type == PHY_TYPE_UFS) { - status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; - mask = PCS_READY; - ready = PCS_READY; - } else { - status = pcs + cfg->regs[QPHY_PCS_STATUS]; - mask = PHYSTATUS; - ready = 0; - } - - ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, - PHY_INIT_COMPLETE_TIMEOUT); - if (ret) { - dev_err(qmp->dev, "phy initialization timed-out\n"); - goto err_pcs_ready; - } - qmp->phy_initialized = true; - return 0; - -err_pcs_ready: - reset_control_assert(qmp->ufs_reset); - clk_disable_unprepare(qphy->pipe_clk); -err_clk_enable: - if (cfg->has_lane_rst) - reset_control_assert(qphy->lane_rst); -err_lane_rst: - qcom_qmp_phy_com_exit(qmp); - - return ret; -} - -static int qcom_qmp_phy_disable(struct phy *phy) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - - clk_disable_unprepare(qphy->pipe_clk); - - /* PHY reset */ - if (!cfg->no_pcs_sw_reset) - qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - - /* stop SerDes and Phy-Coding-Sublayer */ - qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); - - /* Put PHY into POWER DOWN state: active low */ - qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); - - if (cfg->has_lane_rst) - reset_control_assert(qphy->lane_rst); - - qcom_qmp_phy_com_exit(qmp); - - qmp->phy_initialized = false; - - return 0; -} - -static int qcom_qmp_phy_set_mode(struct phy *phy, - enum phy_mode mode, int submode) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - - qmp->mode = mode; - - return 0; -} - -static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy) -{ - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *pcs = qphy->pcs; - void __iomem *pcs_misc = qphy->pcs_misc; - u32 intr_mask; - - if (qmp->mode == PHY_MODE_USB_HOST_SS || - qmp->mode == PHY_MODE_USB_DEVICE_SS) - intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; - else - intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; - - /* Clear any pending interrupts status */ - qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); - /* Writing 1 followed by 0 clears the interrupt */ - qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); - - qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], - ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL); - - /* Enable required PHY autonomous mode interrupts */ - qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask); - - /* Enable i/o clamp_n for autonomous mode */ - if (pcs_misc) - qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); -} - -static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy) -{ - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *pcs = qphy->pcs; - void __iomem *pcs_misc = qphy->pcs_misc; - - /* Disable i/o clamp_n on resume for normal mode */ - if (pcs_misc) - qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); - - qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], - ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN); - - qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); - /* Writing 1 followed by 0 clears the interrupt */ - qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); -} - -static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - struct qmp_phy *qphy = qmp->phys[0]; - const struct qmp_phy_cfg *cfg = qmp->cfg; - - dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); - - /* Supported only for USB3 PHY */ - if (cfg->type != PHY_TYPE_USB3) - return 0; - - if (!qmp->phy_initialized) { - dev_vdbg(dev, "PHY not initialized, bailing out\n"); - return 0; - } - - qcom_qmp_phy_enable_autonomous_mode(qphy); - - clk_disable_unprepare(qphy->pipe_clk); - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); - - return 0; -} - -static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - struct qmp_phy *qphy = qmp->phys[0]; - const struct qmp_phy_cfg *cfg = qmp->cfg; - int ret = 0; - - dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); - - /* Supported only for USB3 PHY */ - if (cfg->type != PHY_TYPE_USB3) - return 0; - - if (!qmp->phy_initialized) { - dev_vdbg(dev, "PHY not initialized, bailing out\n"); - return 0; - } - - ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); - if (ret) { - dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret); - return ret; - } - - ret = clk_prepare_enable(qphy->pipe_clk); - if (ret) { - dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); - return ret; - } - - qcom_qmp_phy_disable_autonomous_mode(qphy); - - return 0; -} - -static int qcom_qmp_phy_vreg_init(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - int num = qmp->cfg->num_vregs; - int i; - - qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); - if (!qmp->vregs) - return -ENOMEM; - - for (i = 0; i < num; i++) - qmp->vregs[i].supply = qmp->cfg->vreg_list[i]; - - return devm_regulator_bulk_get(dev, num, qmp->vregs); -} - -static int qcom_qmp_phy_reset_init(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - int i; - - qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets, - sizeof(*qmp->resets), GFP_KERNEL); - if (!qmp->resets) - return -ENOMEM; - - for (i = 0; i < qmp->cfg->num_resets; i++) { - struct reset_control *rst; - const char *name = qmp->cfg->reset_list[i]; - - rst = devm_reset_control_get(dev, name); - if (IS_ERR(rst)) { - dev_err(dev, "failed to get %s reset\n", name); - return PTR_ERR(rst); - } - qmp->resets[i] = rst; - } - - return 0; -} - -static int qcom_qmp_phy_clk_init(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - int num = qmp->cfg->num_clks; - int i; - - qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); - if (!qmp->clks) - return -ENOMEM; - - for (i = 0; i < num; i++) - qmp->clks[i].id = qmp->cfg->clk_list[i]; - - return devm_clk_bulk_get(dev, num, qmp->clks); -} - -static void phy_pipe_clk_release_provider(void *res) -{ - of_clk_del_provider(res); -} - -/* - * Register a fixed rate pipe clock. - * - * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate - * controls it. The <s>_pipe_clk coming out of the GCC is requested - * by the PHY driver for its operations. - * We register the <s>_pipe_clksrc here. The gcc driver takes care - * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. - * Below picture shows this relationship. - * - * +---------------+ - * | PHY block |<<---------------------------------------+ - * | | | - * | +-------+ | +-----+ | - * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ - * clk | +-------+ | +-----+ - * +---------------+ - */ -static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) -{ - struct clk_fixed_rate *fixed; - struct clk_init_data init = { }; - int ret; - - if ((qmp->cfg->type != PHY_TYPE_USB3) && - (qmp->cfg->type != PHY_TYPE_PCIE)) { - /* not all phys register pipe clocks, so return success */ - return 0; - } - - ret = of_property_read_string(np, "clock-output-names", &init.name); - if (ret) { - dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); - return ret; - } - - fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); - if (!fixed) - return -ENOMEM; - - init.ops = &clk_fixed_rate_ops; - - /* controllers using QMP phys use 125MHz pipe clock interface */ - fixed->fixed_rate = 125000000; - fixed->hw.init = &init; - - ret = devm_clk_hw_register(qmp->dev, &fixed->hw); - if (ret) - return ret; - - ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); - if (ret) - return ret; - - /* - * Roll a devm action because the clock provider is the child node, but - * the child node is not actually a device. - */ - ret = devm_add_action(qmp->dev, phy_pipe_clk_release_provider, np); - if (ret) - phy_pipe_clk_release_provider(np); - - return ret; -} - -static const struct phy_ops qcom_qmp_phy_gen_ops = { - .init = qcom_qmp_phy_enable, - .exit = qcom_qmp_phy_disable, - .set_mode = qcom_qmp_phy_set_mode, - .owner = THIS_MODULE, -}; - -static const struct phy_ops qcom_qmp_ufs_ops = { - .power_on = qcom_qmp_phy_enable, - .power_off = qcom_qmp_phy_disable, - .set_mode = qcom_qmp_phy_set_mode, - .owner = THIS_MODULE, -}; - -static -int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - struct phy *generic_phy; - struct qmp_phy *qphy; - const struct phy_ops *ops = &qcom_qmp_phy_gen_ops; - char prop_name[MAX_PROP_NAME]; - int ret; - - qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); - if (!qphy) - return -ENOMEM; - - /* - * Get memory resources for each phy lane: - * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. - * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 - * For single lane PHYs: pcs_misc (optional) -> 3. - */ - qphy->tx = of_iomap(np, 0); - if (!qphy->tx) - return -ENOMEM; - - qphy->rx = of_iomap(np, 1); - if (!qphy->rx) - return -ENOMEM; - - qphy->pcs = of_iomap(np, 2); - if (!qphy->pcs) - return -ENOMEM; - - /* - * If this is a dual-lane PHY, then there should be registers for the - * second lane. Some old device trees did not specify this, so fall - * back to old legacy behavior of assuming they can be reached at an - * offset from the first lane. - */ - if (qmp->cfg->is_dual_lane_phy) { - qphy->tx2 = of_iomap(np, 3); - qphy->rx2 = of_iomap(np, 4); - if (!qphy->tx2 || !qphy->rx2) { - dev_warn(dev, - "Underspecified device tree, falling back to legacy register regions\n"); - - /* In the old version, pcs_misc is at index 3. */ - qphy->pcs_misc = qphy->tx2; - qphy->tx2 = qphy->tx + QMP_PHY_LEGACY_LANE_STRIDE; - qphy->rx2 = qphy->rx + QMP_PHY_LEGACY_LANE_STRIDE; - - } else { - qphy->pcs_misc = of_iomap(np, 5); - } - - } else { - qphy->pcs_misc = of_iomap(np, 3); - } - - if (!qphy->pcs_misc) - dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); - - /* - * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 - * based phys, so they essentially have pipe clock. So, - * we return error in case phy is USB3 or PIPE type. - * Otherwise, we initialize pipe clock to NULL for - * all phys that don't need this. - */ - snprintf(prop_name, sizeof(prop_name), "pipe%d", id); - qphy->pipe_clk = of_clk_get_by_name(np, prop_name); - if (IS_ERR(qphy->pipe_clk)) { - if (qmp->cfg->type == PHY_TYPE_PCIE || - qmp->cfg->type == PHY_TYPE_USB3) { - ret = PTR_ERR(qphy->pipe_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, - "failed to get lane%d pipe_clk, %d\n", - id, ret); - return ret; - } - qphy->pipe_clk = NULL; - } - - /* Get lane reset, if any */ - if (qmp->cfg->has_lane_rst) { - snprintf(prop_name, sizeof(prop_name), "lane%d", id); - qphy->lane_rst = of_reset_control_get(np, prop_name); - if (IS_ERR(qphy->lane_rst)) { - dev_err(dev, "failed to get lane%d reset\n", id); - return PTR_ERR(qphy->lane_rst); - } - } - - if (qmp->cfg->type == PHY_TYPE_UFS) - ops = &qcom_qmp_ufs_ops; - - generic_phy = devm_phy_create(dev, np, ops); - if (IS_ERR(generic_phy)) { - ret = PTR_ERR(generic_phy); - dev_err(dev, "failed to create qphy %d\n", ret); - return ret; - } - - qphy->phy = generic_phy; - qphy->index = id; - qphy->qmp = qmp; - qmp->phys[id] = qphy; - phy_set_drvdata(generic_phy, qphy); - - return 0; -} - -static const struct of_device_id qcom_qmp_phy_of_match_table[] = { - { - .compatible = "qcom,msm8996-qmp-pcie-phy", - .data = &msm8996_pciephy_cfg, - }, { - .compatible = "qcom,msm8996-qmp-usb3-phy", - .data = &msm8996_usb3phy_cfg, - }, { - .compatible = "qcom,msm8998-qmp-pcie-phy", - .data = &msm8998_pciephy_cfg, - }, { - .compatible = "qcom,msm8998-qmp-ufs-phy", - .data = &sdm845_ufsphy_cfg, - }, { - .compatible = "qcom,ipq8074-qmp-pcie-phy", - .data = &ipq8074_pciephy_cfg, - }, { - .compatible = "qcom,sdm845-qmp-usb3-phy", - .data = &qmp_v3_usb3phy_cfg, - }, { - .compatible = "qcom,sdm845-qmp-usb3-uni-phy", - .data = &qmp_v3_usb3_uniphy_cfg, - }, { - .compatible = "qcom,sdm845-qmp-ufs-phy", - .data = &sdm845_ufsphy_cfg, - }, { - .compatible = "qcom,msm8998-qmp-usb3-phy", - .data = &msm8998_usb3phy_cfg, - }, { - .compatible = "qcom,sm8150-qmp-ufs-phy", - .data = &sm8150_ufsphy_cfg, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); - -static const struct dev_pm_ops qcom_qmp_phy_pm_ops = { - SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend, - qcom_qmp_phy_runtime_resume, NULL) -}; - -static int qcom_qmp_phy_probe(struct platform_device *pdev) -{ - struct qcom_qmp *qmp; - struct device *dev = &pdev->dev; - struct resource *res; - struct device_node *child; - struct phy_provider *phy_provider; - void __iomem *base; - int num, id; - int ret; - - qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); - if (!qmp) - return -ENOMEM; - - qmp->dev = dev; - dev_set_drvdata(dev, qmp); - - /* Get the specific init parameters of QMP phy */ - qmp->cfg = of_device_get_match_data(dev); - if (!qmp->cfg) - return -EINVAL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - /* per PHY serdes; usually located at base address */ - qmp->serdes = base; - - /* per PHY dp_com; if PHY has dp_com control block */ - if (qmp->cfg->has_phy_dp_com_ctrl) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dp_com"); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - qmp->dp_com = base; - } - - mutex_init(&qmp->phy_mutex); - - ret = qcom_qmp_phy_clk_init(dev); - if (ret) - return ret; - - ret = qcom_qmp_phy_reset_init(dev); - if (ret) - return ret; - - ret = qcom_qmp_phy_vreg_init(dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get regulator supplies: %d\n", - ret); - return ret; - } - - num = of_get_available_child_count(dev->of_node); - /* do we have a rogue child node ? */ - if (num > qmp->cfg->nlanes) - return -EINVAL; - - qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); - if (!qmp->phys) - return -ENOMEM; - - id = 0; - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - /* - * Prevent runtime pm from being ON by default. Users can enable - * it using power/control in sysfs. - */ - pm_runtime_forbid(dev); - - for_each_available_child_of_node(dev->of_node, child) { - /* Create per-lane phy */ - ret = qcom_qmp_phy_create(dev, child, id); - if (ret) { - dev_err(dev, "failed to create lane%d phy, %d\n", - id, ret); - goto err_node_put; - } - - /* - * Register the pipe clock provided by phy. - * See function description to see details of this pipe clock. - */ - ret = phy_pipe_clk_register(qmp, child); - if (ret) { - dev_err(qmp->dev, - "failed to register pipe clock source\n"); - goto err_node_put; - } - id++; - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (!IS_ERR(phy_provider)) - dev_info(dev, "Registered Qcom-QMP phy\n"); - else - pm_runtime_disable(dev); - - return PTR_ERR_OR_ZERO(phy_provider); - -err_node_put: - pm_runtime_disable(dev); - of_node_put(child); - return ret; -} - -static struct platform_driver qcom_qmp_phy_driver = { - .probe = qcom_qmp_phy_probe, - .driver = { - .name = "qcom-qmp-phy", - .pm = &qcom_qmp_phy_pm_ops, - .of_match_table = qcom_qmp_phy_of_match_table, - }, -}; - -module_platform_driver(qcom_qmp_phy_driver); - -MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); -MODULE_DESCRIPTION("Qualcomm QMP PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 90f793c2293d..26274e3c0cf9 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -6,407 +6,134 @@ #ifndef QCOM_PHY_QMP_H_ #define QCOM_PHY_QMP_H_ -/* Only for QMP V2 PHY - QSERDES COM registers */ -#define QSERDES_COM_BG_TIMER 0x00c -#define QSERDES_COM_SSC_EN_CENTER 0x010 -#define QSERDES_COM_SSC_ADJ_PER1 0x014 -#define QSERDES_COM_SSC_ADJ_PER2 0x018 -#define QSERDES_COM_SSC_PER1 0x01c -#define QSERDES_COM_SSC_PER2 0x020 -#define QSERDES_COM_SSC_STEP_SIZE1 0x024 -#define QSERDES_COM_SSC_STEP_SIZE2 0x028 -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 -#define QSERDES_COM_CLK_ENABLE1 0x038 -#define QSERDES_COM_SYS_CLK_CTRL 0x03c -#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 -#define QSERDES_COM_PLL_IVCO 0x048 -#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c -#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 -#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 -#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 -#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c -#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 -#define QSERDES_COM_BG_TRIM 0x070 -#define QSERDES_COM_CLK_EP_DIV 0x074 -#define QSERDES_COM_CP_CTRL_MODE0 0x078 -#define QSERDES_COM_CP_CTRL_MODE1 0x07c -#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 -#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 -#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 -#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 -#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8 -#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac -#define QSERDES_COM_RESETSM_CNTRL 0x0b4 -#define QSERDES_COM_RESTRIM_CTRL 0x0bc -#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 -#define QSERDES_COM_LOCK_CMP_EN 0x0c8 -#define QSERDES_COM_LOCK_CMP_CFG 0x0cc -#define QSERDES_COM_DEC_START_MODE0 0x0d0 -#define QSERDES_COM_DEC_START_MODE1 0x0d4 -#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc -#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 -#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 -#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 -#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec -#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 -#define QSERDES_COM_VCO_TUNE_CTRL 0x124 -#define QSERDES_COM_VCO_TUNE_MAP 0x128 -#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c -#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 -#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 -#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 -#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 -#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 -#define QSERDES_COM_BG_CTRL 0x170 -#define QSERDES_COM_CLK_SELECT 0x174 -#define QSERDES_COM_HSCLK_SEL 0x178 -#define QSERDES_COM_CORECLK_DIV 0x184 -#define QSERDES_COM_CORE_CLK_EN 0x18c -#define QSERDES_COM_C_READY_STATUS 0x190 -#define QSERDES_COM_CMN_CONFIG 0x194 -#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c -#define QSERDES_COM_DEBUG_BUS0 0x1a0 -#define QSERDES_COM_DEBUG_BUS1 0x1a4 -#define QSERDES_COM_DEBUG_BUS2 0x1a8 -#define QSERDES_COM_DEBUG_BUS3 0x1ac -#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 -#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc +#include "phy-qcom-qmp-qserdes-com.h" +#include "phy-qcom-qmp-qserdes-txrx.h" -/* Only for QMP V2 PHY - TX registers */ -#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 -#define QSERDES_TX_DEBUG_BUS_SEL 0x064 -#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 -#define QSERDES_TX_LANE_MODE 0x094 -#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac +#include "phy-qcom-qmp-qserdes-com-v3.h" +#include "phy-qcom-qmp-qserdes-txrx-v3.h" -/* Only for QMP V2 PHY - RX registers */ -#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 -#define QSERDES_RX_UCDR_SO_GAIN 0x01c -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 -#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 -#define QSERDES_RX_RX_TERM_BW 0x090 -#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 -#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 -#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc -#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 -#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 -#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c -#define QSERDES_RX_SIGDET_ENABLES 0x110 -#define QSERDES_RX_SIGDET_CNTRL 0x114 -#define QSERDES_RX_SIGDET_LVL 0x118 -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c -#define QSERDES_RX_RX_BAND 0x120 -#define QSERDES_RX_RX_INTERFACE_MODE 0x12c +#include "phy-qcom-qmp-qserdes-com-v4.h" +#include "phy-qcom-qmp-qserdes-txrx-v4.h" +#include "phy-qcom-qmp-qserdes-txrx-v4_20.h" -/* Only for QMP V2 PHY - PCS registers */ -#define QPHY_POWER_DOWN_CONTROL 0x04 -#define QPHY_TXDEEMPH_M6DB_V0 0x24 -#define QPHY_TXDEEMPH_M3P5DB_V0 0x28 -#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54 -#define QPHY_RX_IDLE_DTCT_CNTRL 0x58 -#define QPHY_POWER_STATE_CONFIG1 0x60 -#define QPHY_POWER_STATE_CONFIG2 0x64 -#define QPHY_POWER_STATE_CONFIG4 0x6c -#define QPHY_LOCK_DETECT_CONFIG1 0x80 -#define QPHY_LOCK_DETECT_CONFIG2 0x84 -#define QPHY_LOCK_DETECT_CONFIG3 0x88 -#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0 -#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4 -#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1A8 -#define QPHY_OSC_DTCT_ACTIONS 0x1AC -#define QPHY_RX_SIGDET_LVL 0x1D8 -#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC -#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0 +#include "phy-qcom-qmp-qserdes-com-v5.h" +#include "phy-qcom-qmp-qserdes-txrx-v5.h" +#include "phy-qcom-qmp-qserdes-txrx-v5_20.h" +#include "phy-qcom-qmp-qserdes-txrx-v5_5nm.h" -/* Only for QMP V3 PHY - DP COM registers */ -#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 -#define QPHY_V3_DP_COM_SW_RESET 0x04 -#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08 -#define QPHY_V3_DP_COM_SWI_CTRL 0x0c -#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10 -#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14 -#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c +#include "phy-qcom-qmp-qserdes-pll.h" -/* Only for QMP V3 PHY - QSERDES COM registers */ -#define QSERDES_V3_COM_BG_TIMER 0x00c -#define QSERDES_V3_COM_SSC_EN_CENTER 0x010 -#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014 -#define QSERDES_V3_COM_SSC_ADJ_PER2 0x018 -#define QSERDES_V3_COM_SSC_PER1 0x01c -#define QSERDES_V3_COM_SSC_PER2 0x020 -#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024 -#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028 -#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034 -#define QSERDES_V3_COM_CLK_ENABLE1 0x038 -#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c -#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040 -#define QSERDES_V3_COM_PLL_IVCO 0x048 -#define QSERDES_V3_COM_LOCK_CMP1_MODE0 0x098 -#define QSERDES_V3_COM_LOCK_CMP2_MODE0 0x09c -#define QSERDES_V3_COM_LOCK_CMP3_MODE0 0x0a0 -#define QSERDES_V3_COM_LOCK_CMP1_MODE1 0x0a4 -#define QSERDES_V3_COM_LOCK_CMP2_MODE1 0x0a8 -#define QSERDES_V3_COM_LOCK_CMP3_MODE1 0x0ac -#define QSERDES_V3_COM_CLK_EP_DIV 0x05c -#define QSERDES_V3_COM_CP_CTRL_MODE0 0x060 -#define QSERDES_V3_COM_CP_CTRL_MODE1 0x064 -#define QSERDES_V3_COM_PLL_RCTRL_MODE0 0x068 -#define QSERDES_V3_COM_PLL_RCTRL_MODE1 0x06c -#define QSERDES_V3_COM_PLL_CCTRL_MODE0 0x070 -#define QSERDES_V3_COM_PLL_CCTRL_MODE1 0x074 -#define QSERDES_V3_COM_SYSCLK_EN_SEL 0x080 -#define QSERDES_V3_COM_RESETSM_CNTRL 0x088 -#define QSERDES_V3_COM_RESETSM_CNTRL2 0x08c -#define QSERDES_V3_COM_LOCK_CMP_EN 0x090 -#define QSERDES_V3_COM_LOCK_CMP_CFG 0x094 -#define QSERDES_V3_COM_DEC_START_MODE0 0x0b0 -#define QSERDES_V3_COM_DEC_START_MODE1 0x0b4 -#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0 0x0b8 -#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0 0x0bc -#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0 0x0c0 -#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4 -#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8 -#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc -#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0 -#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8 -#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc -#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0 -#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1 0x0e4 -#define QSERDES_V3_COM_VCO_TUNE_CTRL 0x0ec -#define QSERDES_V3_COM_VCO_TUNE_MAP 0x0f0 -#define QSERDES_V3_COM_VCO_TUNE1_MODE0 0x0f4 -#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8 -#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc -#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100 -#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104 -#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108 -#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c -#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120 -#define QSERDES_V3_COM_CLK_SELECT 0x138 -#define QSERDES_V3_COM_HSCLK_SEL 0x13c -#define QSERDES_V3_COM_CORECLK_DIV_MODE0 0x148 -#define QSERDES_V3_COM_CORECLK_DIV_MODE1 0x14c -#define QSERDES_V3_COM_CORE_CLK_EN 0x154 -#define QSERDES_V3_COM_C_READY_STATUS 0x158 -#define QSERDES_V3_COM_CMN_CONFIG 0x15c -#define QSERDES_V3_COM_SVS_MODE_CLK_SEL 0x164 -#define QSERDES_V3_COM_DEBUG_BUS0 0x168 -#define QSERDES_V3_COM_DEBUG_BUS1 0x16c -#define QSERDES_V3_COM_DEBUG_BUS2 0x170 -#define QSERDES_V3_COM_DEBUG_BUS3 0x174 -#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178 -#define QSERDES_V3_COM_CMN_MODE 0x184 +#include "phy-qcom-qmp-pcs-v2.h" -/* Only for QMP V3 PHY - TX registers */ -#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044 -#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048 -#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058 -#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060 -#define QSERDES_V3_TX_LANE_MODE_1 0x08c -#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4 +#include "phy-qcom-qmp-pcs-v3.h" +#include "phy-qcom-qmp-pcs-misc-v3.h" +#include "phy-qcom-qmp-pcs-ufs-v3.h" -/* Only for QMP V3 PHY - RX registers */ -#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008 -#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c -#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014 -#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024 -#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028 -#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c -#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030 -#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 -#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c -#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 -#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044 -#define QSERDES_V3_RX_RX_TERM_BW 0x07c -#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc -#define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0 -#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB 0x0c8 -#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB 0x0cc -#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4 -#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8 -#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc -#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8 -#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc -#define QSERDES_V3_RX_SIGDET_ENABLES 0x100 -#define QSERDES_V3_RX_SIGDET_CNTRL 0x104 -#define QSERDES_V3_RX_SIGDET_LVL 0x108 -#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL 0x10c -#define QSERDES_V3_RX_RX_BAND 0x110 -#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c -#define QSERDES_V3_RX_RX_MODE_00 0x164 -#define QSERDES_V3_RX_RX_MODE_01 0x168 +#include "phy-qcom-qmp-pcs-v4.h" +#include "phy-qcom-qmp-pcs-pcie-v4.h" +#include "phy-qcom-qmp-pcs-usb-v4.h" +#include "phy-qcom-qmp-pcs-ufs-v4.h" -/* Only for QMP V3 PHY - PCS registers */ -#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004 -#define QPHY_V3_PCS_TXMGN_V0 0x00c -#define QPHY_V3_PCS_TXMGN_V1 0x010 -#define QPHY_V3_PCS_TXMGN_V2 0x014 -#define QPHY_V3_PCS_TXMGN_V3 0x018 -#define QPHY_V3_PCS_TXMGN_V4 0x01c -#define QPHY_V3_PCS_TXMGN_LS 0x020 -#define QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL 0x02c -#define QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL 0x034 -#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024 -#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028 -#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c -#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1 0x030 -#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2 0x034 -#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2 0x038 -#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3 0x03c -#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3 0x040 -#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4 0x044 -#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4 0x048 -#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS 0x04c -#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS 0x050 -#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE 0x054 -#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL 0x058 -#define QPHY_V3_PCS_RATE_SLEW_CNTRL 0x05c -#define QPHY_V3_PCS_POWER_STATE_CONFIG1 0x060 -#define QPHY_V3_PCS_POWER_STATE_CONFIG2 0x064 -#define QPHY_V3_PCS_POWER_STATE_CONFIG4 0x06c -#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L 0x070 -#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H 0x074 -#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L 0x078 -#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H 0x07c -#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1 0x080 -#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2 0x084 -#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3 0x088 -#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c -#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0 -#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4 -#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8 -#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0 -#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8 -#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc -#define QPHY_V3_PCS_FLL_CNTRL1 0x0c4 -#define QPHY_V3_PCS_FLL_CNTRL2 0x0c8 -#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc -#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0 -#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4 -#define QPHY_V3_PCS_RX_SYM_RESYNC_CTRL 0x134 -#define QPHY_V3_PCS_RX_MIN_HIBERN8_TIME 0x138 -#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c -#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140 -#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1a8 -#define QPHY_V3_PCS_OSC_DTCT_ACTIONS 0x1ac -#define QPHY_V3_PCS_SIGDET_CNTRL 0x1b0 -#define QPHY_V3_PCS_TX_MID_TERM_CTRL1 0x1bc -#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4 -#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8 -#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc -#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0 -#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c -#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210 +#include "phy-qcom-qmp-pcs-v4_20.h" +#include "phy-qcom-qmp-pcs-pcie-v4_20.h" -/* Only for QMP V3 PHY - PCS_MISC registers */ -#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c -#define QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2 0x2c -#define QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1 0x44 -#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2 0x54 -#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c -#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60 +#include "phy-qcom-qmp-pcs-v5.h" +#include "phy-qcom-qmp-pcs-pcie-v5.h" +#include "phy-qcom-qmp-pcs-usb-v5.h" +#include "phy-qcom-qmp-pcs-ufs-v5.h" -/* Only for QMP V4 PHY - QSERDES COM registers */ -#define QSERDES_V4_COM_PLL_IVCO 0x058 -#define QSERDES_V4_COM_CMN_IPTRIM 0x060 -#define QSERDES_V4_COM_CP_CTRL_MODE0 0x074 -#define QSERDES_V4_COM_CP_CTRL_MODE1 0x078 -#define QSERDES_V4_COM_PLL_RCTRL_MODE0 0x07c -#define QSERDES_V4_COM_PLL_RCTRL_MODE1 0x080 -#define QSERDES_V4_COM_PLL_CCTRL_MODE0 0x084 -#define QSERDES_V4_COM_PLL_CCTRL_MODE1 0x088 -#define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094 -#define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4 -#define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac -#define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0 -#define QSERDES_V4_COM_LOCK_CMP1_MODE1 0x0b4 -#define QSERDES_V4_COM_DEC_START_MODE0 0x0bc -#define QSERDES_V4_COM_LOCK_CMP2_MODE1 0x0b8 -#define QSERDES_V4_COM_DEC_START_MODE1 0x0c4 -#define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c -#define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124 -#define QSERDES_V4_COM_HSCLK_SEL 0x158 -#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c -#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac -#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 -#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 -#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc -#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8 +#include "phy-qcom-qmp-pcs-pcie-v5_20.h" -/* Only for QMP V4 PHY - TX registers */ -#define QSERDES_V4_TX_LANE_MODE_1 0x84 -#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 -#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC -#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0xe0 -#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0xe4 -#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0xb8 +#include "phy-qcom-qmp-pcie-qhp.h" -/* Only for QMP V4 PHY - RX registers */ -#define QSERDES_V4_RX_UCDR_FO_GAIN 0x008 -#define QSERDES_V4_RX_UCDR_SO_GAIN 0x014 -#define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN 0x030 -#define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 -#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c -#define QSERDES_V4_RX_UCDR_PI_CONTROLS 0x044 -#define QSERDES_V4_RX_UCDR_PI_CTRL2 0x048 -#define QSERDES_V4_RX_AC_JTAG_ENABLE 0x068 -#define QSERDES_V4_RX_AC_JTAG_MODE 0x078 -#define QSERDES_V4_RX_RX_TERM_BW 0x080 -#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec -#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0 -#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4 -#define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW 0x0f8 -#define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH 0x0fc -#define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME 0x100 -#define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114 -#define QSERDES_V4_RX_SIGDET_CNTRL 0x11c -#define QSERDES_V4_RX_SIGDET_LVL 0x120 -#define QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL 0x124 -#define QSERDES_V4_RX_RX_BAND 0x128 -#define QSERDES_V4_RX_RX_MODE_00_LOW 0x170 -#define QSERDES_V4_RX_RX_MODE_00_HIGH 0x174 -#define QSERDES_V4_RX_RX_MODE_00_HIGH2 0x178 -#define QSERDES_V4_RX_RX_MODE_00_HIGH3 0x17c -#define QSERDES_V4_RX_RX_MODE_00_HIGH4 0x180 -#define QSERDES_V4_RX_RX_MODE_01_LOW 0x184 -#define QSERDES_V4_RX_RX_MODE_01_HIGH 0x188 -#define QSERDES_V4_RX_RX_MODE_01_HIGH2 0x18c -#define QSERDES_V4_RX_RX_MODE_01_HIGH3 0x190 -#define QSERDES_V4_RX_RX_MODE_01_HIGH4 0x194 -#define QSERDES_V4_RX_RX_MODE_10_LOW 0x198 -#define QSERDES_V4_RX_RX_MODE_10_HIGH 0x19c -#define QSERDES_V4_RX_RX_MODE_10_HIGH2 0x1a0 -#define QSERDES_V4_RX_RX_MODE_10_HIGH3 0x1a4 -#define QSERDES_V4_RX_RX_MODE_10_HIGH4 0x1a8 -#define QSERDES_V4_RX_DCC_CTRL1 0x1bc +/* Only for QMP V3 & V4 PHY - DP COM registers */ +#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 +#define QPHY_V3_DP_COM_SW_RESET 0x04 +#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08 +#define QPHY_V3_DP_COM_SWI_CTRL 0x0c +#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10 +#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14 +#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c -/* Only for QMP V4 PHY - PCS registers */ -#define QPHY_V4_PHY_START 0x000 -#define QPHY_V4_POWER_DOWN_CONTROL 0x004 -#define QPHY_V4_SW_RESET 0x008 -#define QPHY_V4_TIMER_20US_CORECLK_STEPS_MSB 0x00c -#define QPHY_V4_TIMER_20US_CORECLK_STEPS_LSB 0x010 -#define QPHY_V4_PLL_CNTL 0x02c -#define QPHY_V4_TX_LARGE_AMP_DRV_LVL 0x030 -#define QPHY_V4_TX_SMALL_AMP_DRV_LVL 0x038 -#define QPHY_V4_BIST_FIXED_PAT_CTRL 0x060 -#define QPHY_V4_TX_HSGEAR_CAPABILITY 0x074 -#define QPHY_V4_RX_HSGEAR_CAPABILITY 0x0b4 -#define QPHY_V4_DEBUG_BUS_CLKSEL 0x124 -#define QPHY_V4_LINECFG_DISABLE 0x148 -#define QPHY_V4_RX_MIN_HIBERN8_TIME 0x150 -#define QPHY_V4_RX_SIGDET_CTRL2 0x158 -#define QPHY_V4_TX_PWM_GEAR_BAND 0x160 -#define QPHY_V4_TX_HS_GEAR_BAND 0x168 -#define QPHY_V4_PCS_READY_STATUS 0x180 -#define QPHY_V4_TX_MID_TERM_CTRL1 0x1d8 -#define QPHY_V4_MULTI_LANE_CTRL1 0x1e0 +/* QSERDES V3 COM bits */ +# define QSERDES_V3_COM_BIAS_EN 0x0001 +# define QSERDES_V3_COM_BIAS_EN_MUX 0x0002 +# define QSERDES_V3_COM_CLKBUF_R_EN 0x0004 +# define QSERDES_V3_COM_CLKBUF_L_EN 0x0008 +# define QSERDES_V3_COM_EN_SYSCLK_TX_SEL 0x0010 +# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_L 0x0020 +# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_R 0x0040 + +/* QSERDES V3 TX bits */ +# define DP_PHY_TXn_TX_EMP_POST1_LVL_MASK 0x001f +# define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN 0x0020 +# define DP_PHY_TXn_TX_DRV_LVL_MASK 0x001f +# define DP_PHY_TXn_TX_DRV_LVL_MUX_EN 0x0020 + +/* QMP PHY - DP PHY registers */ +#define QSERDES_DP_PHY_REVISION_ID0 0x000 +#define QSERDES_DP_PHY_REVISION_ID1 0x004 +#define QSERDES_DP_PHY_REVISION_ID2 0x008 +#define QSERDES_DP_PHY_REVISION_ID3 0x00c +#define QSERDES_DP_PHY_CFG 0x010 +#define QSERDES_DP_PHY_PD_CTL 0x018 +# define DP_PHY_PD_CTL_PWRDN 0x001 +# define DP_PHY_PD_CTL_PSR_PWRDN 0x002 +# define DP_PHY_PD_CTL_AUX_PWRDN 0x004 +# define DP_PHY_PD_CTL_LANE_0_1_PWRDN 0x008 +# define DP_PHY_PD_CTL_LANE_2_3_PWRDN 0x010 +# define DP_PHY_PD_CTL_PLL_PWRDN 0x020 +# define DP_PHY_PD_CTL_DP_CLAMP_EN 0x040 +#define QSERDES_DP_PHY_MODE 0x01c +#define QSERDES_DP_PHY_AUX_CFG0 0x020 +#define QSERDES_DP_PHY_AUX_CFG1 0x024 +#define QSERDES_DP_PHY_AUX_CFG2 0x028 +#define QSERDES_DP_PHY_AUX_CFG3 0x02c +#define QSERDES_DP_PHY_AUX_CFG4 0x030 +#define QSERDES_DP_PHY_AUX_CFG5 0x034 +#define QSERDES_DP_PHY_AUX_CFG6 0x038 +#define QSERDES_DP_PHY_AUX_CFG7 0x03c +#define QSERDES_DP_PHY_AUX_CFG8 0x040 +#define QSERDES_DP_PHY_AUX_CFG9 0x044 + +/* Only for QMP V3 PHY - DP PHY registers */ +#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK 0x048 +# define PHY_AUX_STOP_ERR_MASK 0x01 +# define PHY_AUX_DEC_ERR_MASK 0x02 +# define PHY_AUX_SYNC_ERR_MASK 0x04 +# define PHY_AUX_ALIGN_ERR_MASK 0x08 +# define PHY_AUX_REQ_ERR_MASK 0x10 + +#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_CLEAR 0x04c +#define QSERDES_V3_DP_PHY_AUX_BIST_CFG 0x050 + +#define QSERDES_V3_DP_PHY_VCO_DIV 0x064 +#define QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL 0x06c +#define QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL 0x088 + +#define QSERDES_V3_DP_PHY_SPARE0 0x0ac +#define DP_PHY_SPARE0_MASK 0x0f +#define DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT 0x04(0x0004) + +#define QSERDES_V3_DP_PHY_STATUS 0x0c0 + +/* Only for QMP V4 PHY - DP PHY registers */ +#define QSERDES_V4_DP_PHY_CFG_1 0x014 +#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK 0x054 +#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_CLEAR 0x058 +#define QSERDES_V4_DP_PHY_VCO_DIV 0x070 +#define QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL 0x078 +#define QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL 0x09c +#define QSERDES_V4_DP_PHY_SPARE0 0x0c8 +#define QSERDES_V4_DP_PHY_AUX_INTERRUPT_STATUS 0x0d8 +#define QSERDES_V4_DP_PHY_STATUS 0x0dc + +/* Only for QMP V4 PHY - PCS_MISC registers */ +#define QPHY_V4_PCS_MISC_TYPEC_CTRL 0x00 +#define QPHY_V4_PCS_MISC_TYPEC_PWRDN_CTRL 0x04 +#define QPHY_V4_PCS_MISC_PCS_MISC_CONFIG1 0x08 +#define QPHY_V4_PCS_MISC_CLAMP_ENABLE 0x0c +#define QPHY_V4_PCS_MISC_TYPEC_STATUS 0x10 +#define QPHY_V4_PCS_MISC_PLACEHOLDER_STATUS 0x14 #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index bf94a52d3087..2ef638b32e8f 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved. */ #include <linux/clk.h> @@ -22,6 +22,7 @@ #include <dt-bindings/phy/phy-qcom-qusb2.h> +#define QUSB2PHY_PLL 0x0 #define QUSB2PHY_PLL_TEST 0x04 #define CLK_REF_SEL BIT(7) @@ -66,6 +67,14 @@ #define IMP_RES_OFFSET_MASK GENMASK(5, 0) #define IMP_RES_OFFSET_SHIFT 0x0 +/* QUSB2PHY_PLL_BIAS_CONTROL_2 register bits */ +#define BIAS_CTRL2_RES_OFFSET_MASK GENMASK(5, 0) +#define BIAS_CTRL2_RES_OFFSET_SHIFT 0x0 + +/* QUSB2PHY_CHG_CONTROL_2 register bits */ +#define CHG_CTRL2_OFFSET_MASK GENMASK(5, 4) +#define CHG_CTRL2_OFFSET_SHIFT 0x4 + /* QUSB2PHY_PORT_TUNE1 register bits */ #define HSTX_TRIM_MASK GENMASK(7, 4) #define HSTX_TRIM_SHIFT 0x4 @@ -73,6 +82,10 @@ #define PREEMPHASIS_EN_MASK GENMASK(1, 0) #define PREEMPHASIS_EN_SHIFT 0x0 +/* QUSB2PHY_PORT_TUNE2 register bits */ +#define HSDISC_TRIM_MASK GENMASK(1, 0) +#define HSDISC_TRIM_SHIFT 0x0 + #define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x04 #define QUSB2PHY_PLL_CLOCK_INVERTERS 0x18c #define QUSB2PHY_PLL_CMODE 0x2c @@ -123,6 +136,35 @@ enum qusb2phy_reg_layout { QUSB2PHY_INTR_CTRL, }; +static const struct qusb2_phy_init_tbl ipq6018_init_tbl[] = { + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL, 0x14), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xF8), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xB3), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xC0), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x00), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TEST, 0x80), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F), +}; + +static const unsigned int ipq6018_regs_layout[] = { + [QUSB2PHY_PLL_STATUS] = 0x38, + [QUSB2PHY_PORT_TUNE1] = 0x80, + [QUSB2PHY_PORT_TUNE2] = 0x84, + [QUSB2PHY_PORT_TUNE3] = 0x88, + [QUSB2PHY_PORT_TUNE4] = 0x8C, + [QUSB2PHY_PORT_TUNE5] = 0x90, + [QUSB2PHY_PORT_TEST1] = 0x98, + [QUSB2PHY_PORT_TEST2] = 0x9C, + [QUSB2PHY_PORT_POWERDOWN] = 0xB4, + [QUSB2PHY_INTR_CTRL] = 0xBC, +}; + static const unsigned int msm8996_regs_layout[] = { [QUSB2PHY_PLL_STATUS] = 0x38, [QUSB2PHY_PORT_TUNE1] = 0x80, @@ -177,7 +219,23 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), }; -static const unsigned int sdm845_regs_layout[] = { +static const struct qusb2_phy_init_tbl sm6115_init_tbl[] = { + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x53), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x81), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x17), + + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), + + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), + + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), +}; + +static const unsigned int qusb2_v2_regs_layout[] = { [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, [QUSB2PHY_PLL_STATUS] = 0x1a0, [QUSB2PHY_PORT_TUNE1] = 0x240, @@ -191,7 +249,7 @@ static const unsigned int sdm845_regs_layout[] = { [QUSB2PHY_INTR_CTRL] = 0x230, }; -static const struct qusb2_phy_init_tbl sdm845_init_tbl[] = { +static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80), @@ -233,6 +291,9 @@ struct qusb2_phy_cfg { /* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */ bool has_pll_override; + + /* true if PHY default clk scheme is single-ended */ + bool se_clk_scheme_default; }; static const struct qusb2_phy_cfg msm8996_phy_cfg = { @@ -241,6 +302,7 @@ static const struct qusb2_phy_cfg msm8996_phy_cfg = { .regs = msm8996_regs_layout, .has_pll_test = true, + .se_clk_scheme_default = true, .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), .mask_core_ready = PLL_LOCKED, .autoresume_en = BIT(3), @@ -254,29 +316,94 @@ static const struct qusb2_phy_cfg msm8998_phy_cfg = { .disable_ctrl = POWER_DOWN, .mask_core_ready = CORE_READY_STATUS, .has_pll_override = true, + .se_clk_scheme_default = true, .autoresume_en = BIT(0), .update_tune1_with_efuse = true, }; -static const struct qusb2_phy_cfg sdm845_phy_cfg = { - .tbl = sdm845_init_tbl, - .tbl_num = ARRAY_SIZE(sdm845_init_tbl), - .regs = sdm845_regs_layout, +static const struct qusb2_phy_cfg ipq6018_phy_cfg = { + .tbl = ipq6018_init_tbl, + .tbl_num = ARRAY_SIZE(ipq6018_init_tbl), + .regs = ipq6018_regs_layout, + + .disable_ctrl = POWER_DOWN, + .mask_core_ready = PLL_LOCKED, + /* autoresume not used */ + .autoresume_en = BIT(0), +}; + +static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = { + .tbl = qusb2_v2_init_tbl, + .tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl), + .regs = qusb2_v2_regs_layout, .disable_ctrl = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN | POWER_DOWN), .mask_core_ready = CORE_READY_STATUS, .has_pll_override = true, + .se_clk_scheme_default = true, .autoresume_en = BIT(0), .update_tune1_with_efuse = true, }; +static const struct qusb2_phy_cfg sdm660_phy_cfg = { + .tbl = msm8996_init_tbl, + .tbl_num = ARRAY_SIZE(msm8996_init_tbl), + .regs = msm8996_regs_layout, + + .has_pll_test = true, + .se_clk_scheme_default = false, + .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), + .mask_core_ready = PLL_LOCKED, + .autoresume_en = BIT(3), +}; + +static const struct qusb2_phy_cfg sm6115_phy_cfg = { + .tbl = sm6115_init_tbl, + .tbl_num = ARRAY_SIZE(sm6115_init_tbl), + .regs = msm8996_regs_layout, + + .has_pll_test = true, + .se_clk_scheme_default = true, + .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), + .mask_core_ready = PLL_LOCKED, + .autoresume_en = BIT(3), +}; + static const char * const qusb2_phy_vreg_names[] = { - "vdda-pll", "vdda-phy-dpdm", + "vdd", "vdda-pll", "vdda-phy-dpdm", }; #define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) +/* struct override_param - structure holding qusb2 v2 phy overriding param + * set override true if the device tree property exists and read and assign + * to value + */ +struct override_param { + bool override; + u8 value; +}; + +/*struct override_params - structure holding qusb2 v2 phy overriding params + * @imp_res_offset: rescode offset to be updated in IMP_CTRL1 register + * @hstx_trim: HSTX_TRIM to be updated in TUNE1 register + * @preemphasis: Amplitude Pre-Emphasis to be updated in TUNE1 register + * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1 + * @bias_ctrl: bias ctrl to be updated in BIAS_CONTROL_2 register + * @charge_ctrl: charge ctrl to be updated in CHG_CTRL2 register + * @hsdisc_trim: disconnect threshold to be updated in TUNE2 register + */ +struct override_params { + struct override_param imp_res_offset; + struct override_param hstx_trim; + struct override_param preemphasis; + struct override_param preemphasis_width; + struct override_param bias_ctrl; + struct override_param charge_ctrl; + struct override_param hsdisc_trim; +}; + /** * struct qusb2_phy - structure holding qusb2 phy attributes * @@ -292,14 +419,7 @@ static const char * const qusb2_phy_vreg_names[] = { * @tcsr: TCSR syscon register map * @cell: nvmem cell containing phy tuning value * - * @override_imp_res_offset: PHY should use different rescode offset - * @imp_res_offset_value: rescode offset to be updated in IMP_CTRL1 register - * @override_hstx_trim: PHY should use different HSTX o/p current value - * @hstx_trim_value: HSTX_TRIM value to be updated in TUNE1 register - * @override_preemphasis: PHY should use different pre-amphasis amplitude - * @preemphasis_level: Amplitude Pre-Emphasis to be updated in TUNE1 register - * @override_preemphasis_width: PHY should use different pre-emphasis duration - * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1 + * @overrides: pointer to structure for all overriding tuning params * * @cfg: phy config data * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme @@ -319,14 +439,7 @@ struct qusb2_phy { struct regmap *tcsr; struct nvmem_cell *cell; - bool override_imp_res_offset; - u8 imp_res_offset_value; - bool override_hstx_trim; - u8 hstx_trim_value; - bool override_preemphasis; - u8 preemphasis_level; - bool override_preemphasis_width; - u8 preemphasis_width; + struct override_params overrides; const struct qusb2_phy_cfg *cfg; bool has_se_clk_scheme; @@ -394,24 +507,35 @@ void qcom_qusb2_phy_configure(void __iomem *base, static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy) { const struct qusb2_phy_cfg *cfg = qphy->cfg; + struct override_params *or = &qphy->overrides; - if (qphy->override_imp_res_offset) + if (or->imp_res_offset.override) qusb2_write_mask(qphy->base, QUSB2PHY_IMP_CTRL1, - qphy->imp_res_offset_value << IMP_RES_OFFSET_SHIFT, + or->imp_res_offset.value << IMP_RES_OFFSET_SHIFT, IMP_RES_OFFSET_MASK); - if (qphy->override_hstx_trim) + if (or->bias_ctrl.override) + qusb2_write_mask(qphy->base, QUSB2PHY_PLL_BIAS_CONTROL_2, + or->bias_ctrl.value << BIAS_CTRL2_RES_OFFSET_SHIFT, + BIAS_CTRL2_RES_OFFSET_MASK); + + if (or->charge_ctrl.override) + qusb2_write_mask(qphy->base, QUSB2PHY_CHG_CTRL2, + or->charge_ctrl.value << CHG_CTRL2_OFFSET_SHIFT, + CHG_CTRL2_OFFSET_MASK); + + if (or->hstx_trim.override) qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], - qphy->hstx_trim_value << HSTX_TRIM_SHIFT, + or->hstx_trim.value << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); - if (qphy->override_preemphasis) + if (or->preemphasis.override) qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], - qphy->preemphasis_level << PREEMPHASIS_EN_SHIFT, + or->preemphasis.value << PREEMPHASIS_EN_SHIFT, PREEMPHASIS_EN_MASK); - if (qphy->override_preemphasis_width) { - if (qphy->preemphasis_width == + if (or->preemphasis_width.override) { + if (or->preemphasis_width.value == QUSB2_V2_PREEMPHASIS_WIDTH_HALF_BIT) qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], @@ -421,6 +545,11 @@ static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy) cfg->regs[QUSB2PHY_PORT_TUNE1], PREEMPH_WIDTH_HALF_BIT); } + + if (or->hsdisc_trim.override) + qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2], + or->hsdisc_trim.value << HSDISC_TRIM_SHIFT, + HSDISC_TRIM_MASK); } /* @@ -432,7 +561,7 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) { struct device *dev = &qphy->phy->dev; const struct qusb2_phy_cfg *cfg = qphy->cfg; - u8 *val; + u8 *val, hstx_trim; /* efuse register is optional */ if (!qphy->cell) @@ -446,7 +575,13 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) * set while configuring the phy. */ val = nvmem_cell_read(qphy->cell, NULL); - if (IS_ERR(val) || !val[0]) { + if (IS_ERR(val)) { + dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); + return; + } + hstx_trim = val[0]; + kfree(val); + if (!hstx_trim) { dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); return; } @@ -454,12 +589,10 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) /* Fused TUNE1/2 value is the higher nibble only */ if (cfg->update_tune1_with_efuse) qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], - val[0] << HSTX_TRIM_SHIFT, - HSTX_TRIM_MASK); + hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); else qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2], - val[0] << HSTX_TRIM_SHIFT, - HSTX_TRIM_MASK); + hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); } static int qusb2_phy_set_mode(struct phy *phy, @@ -659,8 +792,13 @@ static int qusb2_phy_init(struct phy *phy) /* Required to get phy pll lock successfully */ usleep_range(150, 160); - /* Default is single-ended clock on msm8996 */ - qphy->has_se_clk_scheme = true; + /* + * Not all the SoCs have got a readable TCSR_PHY_CLK_SCHEME + * register in the TCSR so, if there's none, use the default + * value hardcoded in the configuration. + */ + qphy->has_se_clk_scheme = cfg->se_clk_scheme_default; + /* * read TCSR_PHY_CLK_SCHEME register to check if single-ended * clock scheme is selected. If yes, then disable differential @@ -768,14 +906,42 @@ static const struct phy_ops qusb2_phy_gen_ops = { static const struct of_device_id qusb2_phy_of_match_table[] = { { + .compatible = "qcom,ipq6018-qusb2-phy", + .data = &ipq6018_phy_cfg, + }, { + .compatible = "qcom,ipq8074-qusb2-phy", + .data = &msm8996_phy_cfg, + }, { + .compatible = "qcom,msm8953-qusb2-phy", + .data = &msm8996_phy_cfg, + }, { .compatible = "qcom,msm8996-qusb2-phy", .data = &msm8996_phy_cfg, }, { .compatible = "qcom,msm8998-qusb2-phy", .data = &msm8998_phy_cfg, }, { + .compatible = "qcom,qcm2290-qusb2-phy", + .data = &sm6115_phy_cfg, + }, { + .compatible = "qcom,sdm660-qusb2-phy", + .data = &sdm660_phy_cfg, + }, { + .compatible = "qcom,sm4250-qusb2-phy", + .data = &sm6115_phy_cfg, + }, { + .compatible = "qcom,sm6115-qusb2-phy", + .data = &sm6115_phy_cfg, + }, { + /* + * Deprecated. Only here to support legacy device + * trees that didn't include "qcom,qusb2-v2-phy" + */ .compatible = "qcom,sdm845-qusb2-phy", - .data = &sdm845_phy_cfg, + .data = &qusb2_v2_phy_cfg, + }, { + .compatible = "qcom,qusb2-v2-phy", + .data = &qusb2_v2_phy_cfg, }, { }, }; @@ -792,35 +958,29 @@ static int qusb2_phy_probe(struct platform_device *pdev) struct qusb2_phy *qphy; struct phy_provider *phy_provider; struct phy *generic_phy; - struct resource *res; int ret, i; int num; u32 value; + struct override_params *or; qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); if (!qphy) return -ENOMEM; + or = &qphy->overrides; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - qphy->base = devm_ioremap_resource(dev, res); + qphy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(qphy->base)) return PTR_ERR(qphy->base); qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb"); - if (IS_ERR(qphy->cfg_ahb_clk)) { - ret = PTR_ERR(qphy->cfg_ahb_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get cfg ahb clk, %d\n", ret); - return ret; - } + if (IS_ERR(qphy->cfg_ahb_clk)) + return dev_err_probe(dev, PTR_ERR(qphy->cfg_ahb_clk), + "failed to get cfg ahb clk\n"); qphy->ref_clk = devm_clk_get(dev, "ref"); - if (IS_ERR(qphy->ref_clk)) { - ret = PTR_ERR(qphy->ref_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get ref clk, %d\n", ret); - return ret; - } + if (IS_ERR(qphy->ref_clk)) + return dev_err_probe(dev, PTR_ERR(qphy->ref_clk), + "failed to get ref clk\n"); qphy->iface_clk = devm_clk_get_optional(dev, "iface"); if (IS_ERR(qphy->iface_clk)) @@ -837,12 +997,9 @@ static int qusb2_phy_probe(struct platform_device *pdev) qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; ret = devm_regulator_bulk_get(dev, num, qphy->vregs); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get regulator supplies: %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); /* Get the specific init parameters of QMP phy */ qphy->cfg = of_device_get_match_data(dev); @@ -864,26 +1021,44 @@ static int qusb2_phy_probe(struct platform_device *pdev) if (!of_property_read_u32(dev->of_node, "qcom,imp-res-offset-value", &value)) { - qphy->imp_res_offset_value = (u8)value; - qphy->override_imp_res_offset = true; + or->imp_res_offset.value = (u8)value; + or->imp_res_offset.override = true; + } + + if (!of_property_read_u32(dev->of_node, "qcom,bias-ctrl-value", + &value)) { + or->bias_ctrl.value = (u8)value; + or->bias_ctrl.override = true; + } + + if (!of_property_read_u32(dev->of_node, "qcom,charge-ctrl-value", + &value)) { + or->charge_ctrl.value = (u8)value; + or->charge_ctrl.override = true; } if (!of_property_read_u32(dev->of_node, "qcom,hstx-trim-value", &value)) { - qphy->hstx_trim_value = (u8)value; - qphy->override_hstx_trim = true; + or->hstx_trim.value = (u8)value; + or->hstx_trim.override = true; } if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-level", &value)) { - qphy->preemphasis_level = (u8)value; - qphy->override_preemphasis = true; + or->preemphasis.value = (u8)value; + or->preemphasis.override = true; } if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-width", &value)) { - qphy->preemphasis_width = (u8)value; - qphy->override_preemphasis_width = true; + or->preemphasis_width.value = (u8)value; + or->preemphasis_width.override = true; + } + + if (!of_property_read_u32(dev->of_node, "qcom,hsdisc-trim-value", + &value)) { + or->hsdisc_trim.value = (u8)value; + or->hsdisc_trim.override = true; } pm_runtime_set_active(dev); diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c new file mode 100644 index 000000000000..a59063596214 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c) +#define SLEEPM BIT(0) +#define OPMODE_MASK GENMASK(4, 3) +#define OPMODE_NORMAL (0x00) +#define OPMODE_NONDRIVING BIT(3) +#define TERMSEL BIT(5) + +#define USB2_PHY_USB_PHY_UTMI_CTRL1 (0x40) +#define XCVRSEL BIT(0) + +#define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50) +#define POR BIT(1) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) +#define SIDDQ BIT(2) +#define RETENABLEN BIT(3) +#define FSEL_MASK GENMASK(6, 4) +#define FSEL_DEFAULT (0x3 << 4) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58) +#define VBUSVLDEXTSEL0 BIT(4) +#define PLLBTUNE BIT(5) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c) +#define VREGBYPASS BIT(0) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60) +#define VBUSVLDEXT0 BIT(0) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64) +#define USB2_AUTO_RESUME BIT(0) +#define USB2_SUSPEND_N BIT(2) +#define USB2_SUSPEND_N_SEL BIT(3) + +#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0 (0x6c) +#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1 (0x70) +#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2 (0x74) +#define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3 (0x78) +#define PARAM_OVRD_MASK 0xFF + +#define USB2_PHY_USB_PHY_CFG0 (0x94) +#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0) +#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1) + +#define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0) +#define REFCLK_SEL_MASK GENMASK(1, 0) +#define REFCLK_SEL_DEFAULT (0x2 << 0) + +#define HS_DISCONNECT_MASK GENMASK(2, 0) +#define SQUELCH_DETECTOR_MASK GENMASK(7, 5) + +#define HS_AMPLITUDE_MASK GENMASK(3, 0) +#define PREEMPHASIS_DURATION_MASK BIT(5) +#define PREEMPHASIS_AMPLITUDE_MASK GENMASK(7, 6) + +#define HS_RISE_FALL_MASK GENMASK(1, 0) +#define HS_CROSSOVER_VOLTAGE_MASK GENMASK(3, 2) +#define HS_OUTPUT_IMPEDANCE_MASK GENMASK(5, 4) + +#define LS_FS_OUTPUT_IMPEDANCE_MASK GENMASK(3, 0) + +static const char * const qcom_snps_hsphy_vreg_names[] = { + "vdda-pll", "vdda33", "vdda18", +}; + +#define SNPS_HS_NUM_VREGS ARRAY_SIZE(qcom_snps_hsphy_vreg_names) + +struct override_param { + s32 value; + u8 reg_val; +}; + +struct override_param_map { + const char *prop_name; + const struct override_param *param_table; + u8 table_size; + u8 reg_offset; + u8 param_mask; +}; + +struct phy_override_seq { + bool need_update; + u8 offset; + u8 value; + u8 mask; +}; + +#define NUM_HSPHY_TUNING_PARAMS (9) + +/** + * struct qcom_snps_hsphy - snps hs phy attributes + * + * @phy: generic phy + * @base: iomapped memory space for snps hs phy + * + * @cfg_ahb_clk: AHB2PHY interface clock + * @ref_clk: phy reference clock + * @iface_clk: phy interface clock + * @phy_reset: phy reset control + * @vregs: regulator supplies bulk data + * @phy_initialized: if PHY has been initialized correctly + * @mode: contains the current mode the PHY is in + */ +struct qcom_snps_hsphy { + struct phy *phy; + void __iomem *base; + + struct clk *cfg_ahb_clk; + struct clk *ref_clk; + struct reset_control *phy_reset; + struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS]; + + bool phy_initialized; + enum phy_mode mode; + struct phy_override_seq update_seq_cfg[NUM_HSPHY_TUNING_PARAMS]; +}; + +static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset, + u32 mask, u32 val) +{ + u32 reg; + + reg = readl_relaxed(base + offset); + reg &= ~mask; + reg |= val & mask; + writel_relaxed(reg, base + offset); + + /* Ensure above write is completed */ + readl_relaxed(base + offset); +} + +static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy) +{ + dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n"); + + if (hsphy->mode == PHY_MODE_USB_HOST) { + /* Enable auto-resume to meet remote wakeup timing */ + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_AUTO_RESUME, + USB2_AUTO_RESUME); + usleep_range(500, 1000); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL2, + 0, USB2_AUTO_RESUME); + } + + clk_disable_unprepare(hsphy->cfg_ahb_clk); + return 0; +} + +static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy) +{ + int ret; + + dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n"); + + ret = clk_prepare_enable(hsphy->cfg_ahb_clk); + if (ret) { + dev_err(&hsphy->phy->dev, "failed to enable cfg ahb clock\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev) +{ + struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev); + + if (!hsphy->phy_initialized) + return 0; + + qcom_snps_hsphy_suspend(hsphy); + return 0; +} + +static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev) +{ + struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev); + + if (!hsphy->phy_initialized) + return 0; + + qcom_snps_hsphy_resume(hsphy); + return 0; +} + +static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + + hsphy->mode = mode; + return 0; +} + +static const struct override_param hs_disconnect_sc7280[] = { + { -272, 0 }, + { 0, 1 }, + { 317, 2 }, + { 630, 3 }, + { 973, 4 }, + { 1332, 5 }, + { 1743, 6 }, + { 2156, 7 }, +}; + +static const struct override_param squelch_det_threshold_sc7280[] = { + { -2090, 7 }, + { -1560, 6 }, + { -1030, 5 }, + { -530, 4 }, + { 0, 3 }, + { 530, 2 }, + { 1060, 1 }, + { 1590, 0 }, +}; + +static const struct override_param hs_amplitude_sc7280[] = { + { -660, 0 }, + { -440, 1 }, + { -220, 2 }, + { 0, 3 }, + { 230, 4 }, + { 440, 5 }, + { 650, 6 }, + { 890, 7 }, + { 1110, 8 }, + { 1330, 9 }, + { 1560, 10 }, + { 1780, 11 }, + { 2000, 12 }, + { 2220, 13 }, + { 2430, 14 }, + { 2670, 15 }, +}; + +static const struct override_param preemphasis_duration_sc7280[] = { + { 10000, 1 }, + { 20000, 0 }, +}; + +static const struct override_param preemphasis_amplitude_sc7280[] = { + { 10000, 1 }, + { 20000, 2 }, + { 30000, 3 }, + { 40000, 0 }, +}; + +static const struct override_param hs_rise_fall_time_sc7280[] = { + { -4100, 3 }, + { 0, 2 }, + { 2810, 1 }, + { 5430, 0 }, +}; + +static const struct override_param hs_crossover_voltage_sc7280[] = { + { -31000, 1 }, + { 0, 3 }, + { 28000, 2 }, +}; + +static const struct override_param hs_output_impedance_sc7280[] = { + { -2300000, 3 }, + { 0, 2 }, + { 2600000, 1 }, + { 6100000, 0 }, +}; + +static const struct override_param ls_fs_output_impedance_sc7280[] = { + { -1053, 15 }, + { -557, 7 }, + { 0, 3 }, + { 612, 1 }, + { 1310, 0 }, +}; + +static const struct override_param_map sc7280_snps_7nm_phy[] = { + { + "qcom,hs-disconnect-bp", + hs_disconnect_sc7280, + ARRAY_SIZE(hs_disconnect_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0, + HS_DISCONNECT_MASK + }, + { + "qcom,squelch-detector-bp", + squelch_det_threshold_sc7280, + ARRAY_SIZE(squelch_det_threshold_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0, + SQUELCH_DETECTOR_MASK + }, + { + "qcom,hs-amplitude-bp", + hs_amplitude_sc7280, + ARRAY_SIZE(hs_amplitude_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1, + HS_AMPLITUDE_MASK + }, + { + "qcom,pre-emphasis-duration-bp", + preemphasis_duration_sc7280, + ARRAY_SIZE(preemphasis_duration_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1, + PREEMPHASIS_DURATION_MASK, + }, + { + "qcom,pre-emphasis-amplitude-bp", + preemphasis_amplitude_sc7280, + ARRAY_SIZE(preemphasis_amplitude_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1, + PREEMPHASIS_AMPLITUDE_MASK, + }, + { + "qcom,hs-rise-fall-time-bp", + hs_rise_fall_time_sc7280, + ARRAY_SIZE(hs_rise_fall_time_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2, + HS_RISE_FALL_MASK + }, + { + "qcom,hs-crossover-voltage-microvolt", + hs_crossover_voltage_sc7280, + ARRAY_SIZE(hs_crossover_voltage_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2, + HS_CROSSOVER_VOLTAGE_MASK + }, + { + "qcom,hs-output-impedance-micro-ohms", + hs_output_impedance_sc7280, + ARRAY_SIZE(hs_output_impedance_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2, + HS_OUTPUT_IMPEDANCE_MASK, + }, + { + "qcom,ls-fs-output-impedance-bp", + ls_fs_output_impedance_sc7280, + ARRAY_SIZE(ls_fs_output_impedance_sc7280), + USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3, + LS_FS_OUTPUT_IMPEDANCE_MASK, + }, + {}, +}; + +static int qcom_snps_hsphy_init(struct phy *phy) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + int ret, i; + + dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__); + + ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs); + if (ret) + return ret; + + ret = clk_prepare_enable(hsphy->cfg_ahb_clk); + if (ret) { + dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); + goto poweroff_phy; + } + + ret = reset_control_assert(hsphy->phy_reset); + if (ret) { + dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); + goto disable_ahb_clk; + } + + usleep_range(100, 150); + + ret = reset_control_deassert(hsphy->phy_reset); + if (ret) { + dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); + goto disable_ahb_clk; + } + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0, + UTMI_PHY_CMN_CTRL_OVERRIDE_EN, + UTMI_PHY_CMN_CTRL_OVERRIDE_EN); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5, + POR, POR); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0, + FSEL_MASK, 0); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1, + PLLBTUNE, PLLBTUNE); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL, + REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1, + VBUSVLDEXTSEL0, VBUSVLDEXTSEL0); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1, + VBUSVLDEXT0, VBUSVLDEXT0); + + for (i = 0; i < ARRAY_SIZE(hsphy->update_seq_cfg); i++) { + if (hsphy->update_seq_cfg[i].need_update) + qcom_snps_hsphy_write_mask(hsphy->base, + hsphy->update_seq_cfg[i].offset, + hsphy->update_seq_cfg[i].mask, + hsphy->update_seq_cfg[i].value); + } + + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2, + VREGBYPASS, VREGBYPASS); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_SUSPEND_N_SEL | USB2_SUSPEND_N, + USB2_SUSPEND_N_SEL | USB2_SUSPEND_N); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0, + SLEEPM, SLEEPM); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0, + SIDDQ, 0); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5, + POR, 0); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_SUSPEND_N_SEL, 0); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0, + UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0); + + hsphy->phy_initialized = true; + + return 0; + +disable_ahb_clk: + clk_disable_unprepare(hsphy->cfg_ahb_clk); +poweroff_phy: + regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs); + + return ret; +} + +static int qcom_snps_hsphy_exit(struct phy *phy) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + + reset_control_assert(hsphy->phy_reset); + clk_disable_unprepare(hsphy->cfg_ahb_clk); + regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs); + hsphy->phy_initialized = false; + + return 0; +} + +static const struct phy_ops qcom_snps_hsphy_gen_ops = { + .init = qcom_snps_hsphy_init, + .exit = qcom_snps_hsphy_exit, + .set_mode = qcom_snps_hsphy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id qcom_snps_hsphy_of_match_table[] = { + { .compatible = "qcom,sm8150-usb-hs-phy", }, + { .compatible = "qcom,usb-snps-hs-5nm-phy", }, + { + .compatible = "qcom,usb-snps-hs-7nm-phy", + .data = &sc7280_snps_7nm_phy, + }, + { .compatible = "qcom,usb-snps-femto-v2-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table); + +static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend, + qcom_snps_hsphy_runtime_resume, NULL) +}; + +static void qcom_snps_hsphy_override_param_update_val( + const struct override_param_map map, + s32 dt_val, struct phy_override_seq *seq_entry) +{ + int i; + + /* + * Param table for each param is in increasing order + * of dt values. We need to iterate over the list to + * select the entry that matches the dt value and pick + * up the corresponding register value. + */ + for (i = 0; i < map.table_size - 1; i++) { + if (map.param_table[i].value == dt_val) + break; + } + + seq_entry->need_update = true; + seq_entry->offset = map.reg_offset; + seq_entry->mask = map.param_mask; + seq_entry->value = map.param_table[i].reg_val << __ffs(map.param_mask); +} + +static void qcom_snps_hsphy_read_override_param_seq(struct device *dev) +{ + struct device_node *node = dev->of_node; + s32 val; + int ret, i; + struct qcom_snps_hsphy *hsphy; + const struct override_param_map *cfg = of_device_get_match_data(dev); + + if (!cfg) + return; + + hsphy = dev_get_drvdata(dev); + + for (i = 0; cfg[i].prop_name != NULL; i++) { + ret = of_property_read_s32(node, cfg[i].prop_name, &val); + if (ret) + continue; + + qcom_snps_hsphy_override_param_update_val(cfg[i], val, + &hsphy->update_seq_cfg[i]); + dev_dbg(&hsphy->phy->dev, "Read param: %s dt_val: %d reg_val: 0x%x\n", + cfg[i].prop_name, val, hsphy->update_seq_cfg[i].value); + + } +} + +static int qcom_snps_hsphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_snps_hsphy *hsphy; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret, i; + int num; + + hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL); + if (!hsphy) + return -ENOMEM; + + hsphy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hsphy->base)) + return PTR_ERR(hsphy->base); + + hsphy->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(hsphy->ref_clk)) + return dev_err_probe(dev, PTR_ERR(hsphy->ref_clk), + "failed to get ref clk\n"); + + hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(hsphy->phy_reset)) { + dev_err(dev, "failed to get phy core reset\n"); + return PTR_ERR(hsphy->phy_reset); + } + + num = ARRAY_SIZE(hsphy->vregs); + for (i = 0; i < num; i++) + hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i]; + + ret = devm_regulator_bulk_get(dev, num, hsphy->vregs); + if (ret) + return dev_err_probe(dev, ret, + "failed to get regulator supplies\n"); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + /* + * Prevent runtime pm from being ON by default. Users can enable + * it using power/control in sysfs. + */ + pm_runtime_forbid(dev); + + generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create phy, %d\n", ret); + return ret; + } + hsphy->phy = generic_phy; + + dev_set_drvdata(dev, hsphy); + phy_set_drvdata(generic_phy, hsphy); + qcom_snps_hsphy_read_override_param_seq(dev); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_dbg(dev, "Registered Qcom-SNPS HS phy\n"); + else + pm_runtime_disable(dev); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver qcom_snps_hsphy_driver = { + .probe = qcom_snps_hsphy_probe, + .driver = { + .name = "qcom-snps-hs-femto-v2-phy", + .pm = &qcom_snps_hsphy_pm_ops, + .of_match_table = qcom_snps_hsphy_of_match_table, + }, +}; + +module_platform_driver(qcom_snps_hsphy_driver); + +MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h deleted file mode 100644 index 9bf973a0d8c3..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_I_H_ -#define UFS_QCOM_PHY_I_H_ - -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/phy/phy.h> -#include <linux/regulator/consumer.h> -#include <linux/reset.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/iopoll.h> - -#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ - { \ - .reg_offset = reg, \ - .cfg_value = val, \ - } - -#define UFS_QCOM_PHY_NAME_LEN 30 - -enum { - MASK_SERDES_START = 0x1, - MASK_PCS_READY = 0x1, -}; - -enum { - OFFSET_SERDES_START = 0x0, -}; - -struct ufs_qcom_phy_stored_attributes { - u32 att; - u32 value; -}; - - -struct ufs_qcom_phy_calibration { - u32 reg_offset; - u32 cfg_value; -}; - -struct ufs_qcom_phy_vreg { - const char *name; - struct regulator *reg; - int max_uA; - int min_uV; - int max_uV; - bool enabled; -}; - -struct ufs_qcom_phy { - struct list_head list; - struct device *dev; - void __iomem *mmio; - void __iomem *dev_ref_clk_ctrl_mmio; - struct clk *tx_iface_clk; - struct clk *rx_iface_clk; - bool is_iface_clk_enabled; - struct clk *ref_clk_src; - struct clk *ref_clk_parent; - struct clk *ref_clk; - bool is_ref_clk_enabled; - bool is_dev_ref_clk_enabled; - struct ufs_qcom_phy_vreg vdda_pll; - struct ufs_qcom_phy_vreg vdda_phy; - struct ufs_qcom_phy_vreg vddp_ref_clk; - unsigned int quirks; - - /* - * If UFS link is put into Hibern8 and if UFS PHY analog hardware is - * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 - * exit might fail even after powering on UFS PHY analog hardware. - * Enabling this quirk will help to solve above issue by doing - * custom PHY settings just before PHY analog power collapse. - */ - #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) - - u8 host_ctrl_rev_major; - u16 host_ctrl_rev_minor; - u16 host_ctrl_rev_step; - - char name[UFS_QCOM_PHY_NAME_LEN]; - struct ufs_qcom_phy_calibration *cached_regs; - int cached_regs_table_size; - struct ufs_qcom_phy_specific_ops *phy_spec_ops; - - enum phy_mode mode; - struct reset_control *ufs_reset; -}; - -/** - * 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 - * @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. - * @set_tx_lane_enable: pointer to a function that enable tx lanes - * @power_control: pointer to a function that controls analog rail of phy - * and writes to QSERDES_RX_SIGDET_CNTRL attribute - */ -struct ufs_qcom_phy_specific_ops { - int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_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); - void (*power_control)(struct ufs_qcom_phy *phy, bool val); -}; - -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); -int ufs_qcom_phy_power_on(struct phy *generic_phy); -int ufs_qcom_phy_power_off(struct phy *generic_phy); -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_remove(struct phy *generic_phy, - struct ufs_qcom_phy *ufs_qcom_phy); -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops); -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, - bool is_rate_B); -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c deleted file mode 100644 index 54b355bfc24c..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-qmp-14nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_14nm" -#define UFS_PHY_VDDA_PHY_UV (925000) - -static -int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); - int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - int err; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, - tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, - "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - return err; -} - -static -void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static -int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, - enum phy_mode mode, int submode) -{ - 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) -{ - writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON (or OFF). - */ - mb(); -} - -static inline -void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - /* - * 14nm PHY does not have TX_LANE_ENABLE register. - * Implement this function so as not to propagate error to caller. - */ -} - -static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - /* Ensure register value is committed */ - mb(); -} - -static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { - .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 = 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, - .power_control = ufs_qcom_phy_qmp_14nm_power_control, -}; - -static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_14nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; - phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; - - ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-14nm"}, - {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { - .probe = ufs_qcom_phy_qmp_14nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, - .name = "ufs_qcom_phy_qmp_14nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h deleted file mode 100644 index ceca654b9338..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_QMP_14NM_H_ -#define UFS_QCOM_PHY_QMP_14NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY QSERDES COM registers */ -#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) -#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) -#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) -#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) -#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) -#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) -#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) -#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) -#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) -#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) -#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) -#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) -#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) -#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) -#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) -#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) -#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) -#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) -#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) -#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) -#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) -#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) -#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) -#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) -#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) -#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) -#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) -#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) -#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) -#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) -#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) -#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) -#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) -#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) -#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) -#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) -#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) -#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) - -/* UFS PHY TX registers */ -#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) -#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) - -/* UFS PHY RX registers */ -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) -#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) -#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) -#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) -#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) -#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) -#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) -#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) -#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) - -/* - * This structure represents the 14nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_14nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), -}; - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c deleted file mode 100644 index 3e9d8b71e995..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-qmp-20nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_20nm" - -static -int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; - int tbl_size_A, tbl_size_B; - u8 major = ufs_qcom_phy->host_ctrl_rev_major; - u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; - u16 step = ufs_qcom_phy->host_ctrl_rev_step; - int err; - - if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); - tbl_A = phy_cal_table_rate_A_1_2_0; - } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); - tbl_A = phy_cal_table_rate_A_1_3_0; - } else { - dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", - __func__); - err = -ENODEV; - goto out; - } - - tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - tbl_B = phy_cal_table_rate_B; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, - tbl_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - -out: - return err; -} - -static -void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static -int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, - enum phy_mode mode, int submode) -{ - 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) -{ - bool hibern8_exit_after_pwr_collapse = phy->quirks & - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; - - if (val) { - writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON. - */ - mb(); - - if (hibern8_exit_after_pwr_collapse) { - /* - * Give atleast 1us delay after restoring PHY analog - * power. - */ - usleep_range(1, 2); - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x08, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure workaround is deactivated before proceeding - * with normal PHY operations. - */ - mb(); - } - } else { - if (hibern8_exit_after_pwr_collapse) { - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x02, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure that above workaround is activated before - * PHY analog power collapse. - */ - mb(); - } - - writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * ensure that PHY knows its PHY analog rail is going - * to be powered down - */ - mb(); - } -} - -static -void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, - phy->mmio + UFS_PHY_TX_LANE_ENABLE); - mb(); -} - -static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - mb(); -} - -static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { - .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 = 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, - .power_control = ufs_qcom_phy_qmp_20nm_power_control, -}; - -static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_20nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-20nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { - .probe = ufs_qcom_phy_qmp_20nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, - .name = "ufs_qcom_phy_qmp_20nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h deleted file mode 100644 index 8ce79f524eed..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h +++ /dev/null @@ -1,226 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_QMP_20NM_H_ -#define UFS_QCOM_PHY_QMP_20NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ - -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY PLL block registers */ -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) -#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) -#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) -#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) -#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) -#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) -#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) -#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) -#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) -#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) -#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) -#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) -#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) -#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) -#define QSERDES_COM_BGTC COM_OFF(0xA0) -#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) -#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) -#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) -#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) -#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) -#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) -#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) -#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) -#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) -#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) - -/* TX LANE n (0, 1) registers */ -#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) -#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) -#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) - -/* RX LANE n (0, 1) registers */ -#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) -#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) -#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) -#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) -#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) -#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) -#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) -#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) -#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) -#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) -#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) -#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) -#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) -#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) -#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) -#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) -#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) -#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) -#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) -#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) -#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) -#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) -#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) -#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) -#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) -#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) -#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) -#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) -#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) -#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) -#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) -#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) -#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) -#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) -#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) -#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) -#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) -#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) -#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) -#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) -#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) -#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) -#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) -#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) -#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) -#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) -#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) -#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) -#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) -#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) - -#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 - -/* - * This structure represents the 20nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_20nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), -}; - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c deleted file mode 100644 index 763c8d396af1..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ /dev/null @@ -1,648 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-i.h" - -#define MAX_PROP_NAME 32 -#define VDDA_PHY_MIN_UV 1000000 -#define VDDA_PHY_MAX_UV 1000000 -#define VDDA_PLL_MIN_UV 1800000 -#define VDDA_PLL_MAX_UV 1800000 -#define VDDP_REF_CLK_MIN_UV 1200000 -#define VDDP_REF_CLK_MAX_UV 1200000 - -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, - int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, - int tbl_size_B, bool is_rate_B) -{ - int i; - int ret = 0; - - if (!tbl_A) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_A; i++) - writel_relaxed(tbl_A[i].cfg_value, - ufs_qcom_phy->mmio + tbl_A[i].reg_offset); - - /* - * In case we would like to work in rate B, we need - * to override a registers that were configured in rate A table - * with registers of rate B table. - * table. - */ - if (is_rate_B) { - if (!tbl_B) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", - __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_B; i++) - writel_relaxed(tbl_B[i].cfg_value, - ufs_qcom_phy->mmio + tbl_B[i].reg_offset); - } - - /* flush buffered writes */ - mb(); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); - -/* - * This assumes the embedded phy structure inside generic_phy is of type - * struct ufs_qcom_phy. In order to function properly it's crucial - * to keep the embedded struct "struct ufs_qcom_phy common_cfg" - * as the first inside generic_phy. - */ -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) -{ - return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); -} -EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); - -static -int ufs_qcom_phy_base_init(struct platform_device *pdev, - struct ufs_qcom_phy *phy_common) -{ - struct device *dev = &pdev->dev; - struct resource *res; - int err = 0; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); - phy_common->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->mmio)) { - err = PTR_ERR((void const *)phy_common->mmio); - phy_common->mmio = NULL; - dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", - __func__, err); - return err; - } - - /* "dev_ref_clk_ctrl_mem" is optional resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dev_ref_clk_ctrl_mem"); - phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) - phy_common->dev_ref_clk_ctrl_mmio = NULL; - - return 0; -} - -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops) -{ - int err; - struct device *dev = &pdev->dev; - struct phy *generic_phy = NULL; - struct phy_provider *phy_provider; - - err = ufs_qcom_phy_base_init(pdev, common_cfg); - if (err) { - dev_err(dev, "%s: phy base init failed %d\n", __func__, err); - goto out; - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - err = PTR_ERR(phy_provider); - dev_err(dev, "%s: failed to register phy %d\n", __func__, err); - goto out; - } - - generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); - if (IS_ERR(generic_phy)) { - err = PTR_ERR(generic_phy); - dev_err(dev, "%s: failed to create phy %d\n", __func__, err); - generic_phy = NULL; - goto out; - } - - common_cfg->phy_spec_ops = phy_spec_ops; - common_cfg->dev = dev; - -out: - return generic_phy; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); - -static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) -{ - struct reset_control *reset; - - if (phy_common->ufs_reset) - return 0; - - reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0); - if (IS_ERR(reset)) - return PTR_ERR(reset); - - phy_common->ufs_reset = reset; - return 0; -} - -static int __ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out, bool err_print) -{ - struct clk *clk; - int err = 0; - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - if (err_print) - dev_err(dev, "failed to get %s err %d", name, err); - } else { - *clk_out = clk; - } - - return err; -} - -static int ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out) -{ - return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); -} - -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) -{ - int err; - - if (of_device_is_compatible(phy_common->dev->of_node, - "qcom,msm8996-ufs-phy-qmp-14nm")) - goto skip_txrx_clk; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", - &phy_common->tx_iface_clk); - if (err) - goto out; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", - &phy_common->rx_iface_clk); - if (err) - goto out; - -skip_txrx_clk: - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", - &phy_common->ref_clk_src); - if (err) - goto out; - - /* - * "ref_clk_parent" is optional hence don't abort init if it's not - * found. - */ - __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", - &phy_common->ref_clk_parent, false); - - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", - &phy_common->ref_clk); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); - -static int ufs_qcom_phy_init_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, - const char *name) -{ - int err = 0; - - char prop_name[MAX_PROP_NAME]; - - vreg->name = name; - vreg->reg = devm_regulator_get(dev, name); - if (IS_ERR(vreg->reg)) { - err = PTR_ERR(vreg->reg); - dev_err(dev, "failed to get %s, %d\n", name, err); - goto out; - } - - if (dev->of_node) { - snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); - err = of_property_read_u32(dev->of_node, - prop_name, &vreg->max_uA); - if (err && err != -EINVAL) { - dev_err(dev, "%s: failed to read %s\n", - __func__, prop_name); - goto out; - } else if (err == -EINVAL || !vreg->max_uA) { - if (regulator_count_voltages(vreg->reg) > 0) { - dev_err(dev, "%s: %s is mandatory\n", - __func__, prop_name); - goto out; - } - err = 0; - } - } - - if (!strcmp(name, "vdda-pll")) { - vreg->max_uV = VDDA_PLL_MAX_UV; - vreg->min_uV = VDDA_PLL_MIN_UV; - } else if (!strcmp(name, "vdda-phy")) { - vreg->max_uV = VDDA_PHY_MAX_UV; - vreg->min_uV = VDDA_PHY_MIN_UV; - } else if (!strcmp(name, "vddp-ref-clk")) { - vreg->max_uV = VDDP_REF_CLK_MAX_UV; - vreg->min_uV = VDDP_REF_CLK_MIN_UV; - } - -out: - return err; -} - -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) -{ - int err; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, - "vdda-pll"); - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, - "vdda-phy"); - - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, - "vddp-ref-clk"); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); - -static int ufs_qcom_phy_cfg_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, bool on) -{ - int ret = 0; - struct regulator *reg = vreg->reg; - const char *name = vreg->name; - int min_uV; - int uA_load; - - if (regulator_count_voltages(reg) > 0) { - min_uV = on ? vreg->min_uV : 0; - ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); - if (ret) { - dev_err(dev, "%s: %s set voltage failed, err=%d\n", - __func__, name, ret); - goto out; - } - uA_load = on ? vreg->max_uA : 0; - ret = regulator_set_load(reg, uA_load); - if (ret >= 0) { - /* - * regulator_set_load() returns new regulator - * mode upon success. - */ - ret = 0; - } else { - dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", - __func__, name, uA_load, ret); - goto out; - } - } -out: - return ret; -} - -static int ufs_qcom_phy_enable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || vreg->enabled) - goto out; - - ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); - if (ret) { - dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", - __func__, ret); - goto out; - } - - ret = regulator_enable(vreg->reg); - if (ret) { - dev_err(dev, "%s: enable failed, err=%d\n", - __func__, ret); - goto out; - } - - vreg->enabled = true; -out: - return ret; -} - -static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_ref_clk_enabled) - goto out; - - /* - * reference clock is propagated in a daisy-chained manner from - * source to phy, so ungate them at each stage. - */ - ret = clk_prepare_enable(phy->ref_clk_src); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", - __func__, ret); - goto out; - } - - /* - * "ref_clk_parent" is optional clock hence make sure that clk reference - * is available before trying to enable the clock. - */ - if (phy->ref_clk_parent) { - ret = clk_prepare_enable(phy->ref_clk_parent); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", - __func__, ret); - goto out_disable_src; - } - } - - ret = clk_prepare_enable(phy->ref_clk); - if (ret) { - dev_err(phy->dev, "%s: ref_clk enable failed %d\n", - __func__, ret); - goto out_disable_parent; - } - - phy->is_ref_clk_enabled = true; - goto out; - -out_disable_parent: - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); -out_disable_src: - clk_disable_unprepare(phy->ref_clk_src); -out: - return ret; -} - -static int ufs_qcom_phy_disable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || !vreg->enabled) - goto out; - - ret = regulator_disable(vreg->reg); - - if (!ret) { - /* ignore errors on applying disable config */ - ufs_qcom_phy_cfg_vreg(dev, vreg, false); - vreg->enabled = false; - } else { - dev_err(dev, "%s: %s disable failed, err=%d\n", - __func__, vreg->name, ret); - } -out: - return ret; -} - -static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_ref_clk_enabled) { - clk_disable_unprepare(phy->ref_clk); - /* - * "ref_clk_parent" is optional clock hence make sure that clk - * reference is available before trying to disable the clock. - */ - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); - clk_disable_unprepare(phy->ref_clk_src); - phy->is_ref_clk_enabled = false; - } -} - -/* Turn ON M-PHY RMMI interface clocks */ -static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_iface_clk_enabled) - goto out; - - ret = clk_prepare_enable(phy->tx_iface_clk); - if (ret) { - dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", - __func__, ret); - goto out; - } - ret = clk_prepare_enable(phy->rx_iface_clk); - if (ret) { - clk_disable_unprepare(phy->tx_iface_clk); - dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", - __func__, ret); - goto out; - } - phy->is_iface_clk_enabled = true; - -out: - return ret; -} - -/* Turn OFF M-PHY RMMI interface clocks */ -static void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_iface_clk_enabled) { - clk_disable_unprepare(phy->tx_iface_clk); - clk_disable_unprepare(phy->rx_iface_clk); - phy->is_iface_clk_enabled = false; - } -} - -static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy) -{ - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { - dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); - } - - return ret; -} - -int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { - dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, - tx_lanes); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); - -void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, - u8 major, u16 minor, u16 step) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - - ufs_qcom_phy->host_ctrl_rev_major = major; - ufs_qcom_phy->host_ctrl_rev_minor = minor; - ufs_qcom_phy->host_ctrl_rev_step = step; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); - -static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_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__); - return -ENOTSUPP; - } - - return ufs_qcom_phy->phy_spec_ops-> - is_physical_coding_sublayer_ready(ufs_qcom_phy); -} - -int ufs_qcom_phy_power_on(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - struct device *dev = phy_common->dev; - bool is_rate_B = false; - int err; - - err = ufs_qcom_phy_get_reset(phy_common); - if (err) - return err; - - err = reset_control_assert(phy_common->ufs_reset); - if (err) - return err; - - if (phy_common->mode == PHY_MODE_UFS_HS_B) - is_rate_B = true; - - err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); - if (err) - return err; - - err = reset_control_deassert(phy_common->ufs_reset); - if (err) { - dev_err(dev, "Failed to assert UFS PHY reset"); - return err; - } - - 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; - - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); - if (err) { - dev_err(dev, "%s enable vdda_phy failed, err=%d\n", - __func__, err); - goto out; - } - - phy_common->phy_spec_ops->power_control(phy_common, true); - - /* vdda_pll also enables ref clock LDOs so enable it first */ - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); - if (err) { - dev_err(dev, "%s enable vdda_pll failed, err=%d\n", - __func__, err); - goto out_disable_phy; - } - - err = ufs_qcom_phy_enable_iface_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy iface clock failed, err=%d\n", - __func__, err); - goto out_disable_pll; - } - - err = ufs_qcom_phy_enable_ref_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy ref clock failed, err=%d\n", - __func__, err); - goto out_disable_iface_clk; - } - - /* enable device PHY ref_clk pad rail */ - if (phy_common->vddp_ref_clk.reg) { - err = ufs_qcom_phy_enable_vreg(dev, - &phy_common->vddp_ref_clk); - if (err) { - dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", - __func__, err); - goto out_disable_ref_clk; - } - } - - goto out; - -out_disable_ref_clk: - ufs_qcom_phy_disable_ref_clk(phy_common); -out_disable_iface_clk: - ufs_qcom_phy_disable_iface_clk(phy_common); -out_disable_pll: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); -out_disable_phy: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); - -int ufs_qcom_phy_power_off(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->phy_spec_ops->power_control(phy_common, false); - - if (phy_common->vddp_ref_clk.reg) - ufs_qcom_phy_disable_vreg(phy_common->dev, - &phy_common->vddp_ref_clk); - ufs_qcom_phy_disable_ref_clk(phy_common); - ufs_qcom_phy_disable_iface_clk(phy_common); - - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); - reset_control_assert(phy_common->ufs_reset); - return 0; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); - -MODULE_AUTHOR("Yaniv Gardi <ygardi@codeaurora.org>"); -MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c new file mode 100644 index 000000000000..8807e59a1162 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2009-2018, Linux Foundation. All rights reserved. + * Copyright (c) 2018-2020, Linaro Limited + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +/* PHY register and bit definitions */ +#define PHY_CTRL_COMMON0 0x078 +#define SIDDQ BIT(2) +#define PHY_IRQ_CMD 0x0d0 +#define PHY_INTR_MASK0 0x0d4 +#define PHY_INTR_CLEAR0 0x0dc +#define DPDM_MASK 0x1e +#define DP_1_0 BIT(4) +#define DP_0_1 BIT(3) +#define DM_1_0 BIT(2) +#define DM_0_1 BIT(1) + +enum hsphy_voltage { + VOL_NONE, + VOL_MIN, + VOL_MAX, + VOL_NUM, +}; + +enum hsphy_vreg { + VDD, + VDDA_1P8, + VDDA_3P3, + VREG_NUM, +}; + +struct hsphy_init_seq { + int offset; + int val; + int delay; +}; + +struct hsphy_data { + const struct hsphy_init_seq *init_seq; + unsigned int init_seq_num; +}; + +struct hsphy_priv { + void __iomem *base; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *phy_reset; + struct reset_control *por_reset; + struct regulator_bulk_data vregs[VREG_NUM]; + const struct hsphy_data *data; + enum phy_mode mode; +}; + +static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct hsphy_priv *priv = phy_get_drvdata(phy); + + priv->mode = PHY_MODE_INVALID; + + if (mode > 0) + priv->mode = mode; + + return 0; +} + +static void qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv *priv) +{ + u32 val; + + /* Clear any existing interrupts before enabling the interrupts */ + val = readb(priv->base + PHY_INTR_CLEAR0); + val |= DPDM_MASK; + writeb(val, priv->base + PHY_INTR_CLEAR0); + + writeb(0x0, priv->base + PHY_IRQ_CMD); + usleep_range(200, 220); + writeb(0x1, priv->base + PHY_IRQ_CMD); + + /* Make sure the interrupts are cleared */ + usleep_range(200, 220); + + val = readb(priv->base + PHY_INTR_MASK0); + switch (priv->mode) { + case PHY_MODE_USB_HOST_HS: + case PHY_MODE_USB_HOST_FS: + case PHY_MODE_USB_DEVICE_HS: + case PHY_MODE_USB_DEVICE_FS: + val |= DP_1_0 | DM_0_1; + break; + case PHY_MODE_USB_HOST_LS: + case PHY_MODE_USB_DEVICE_LS: + val |= DP_0_1 | DM_1_0; + break; + default: + /* No device connected */ + val |= DP_0_1 | DM_0_1; + break; + } + writeb(val, priv->base + PHY_INTR_MASK0); +} + +static void qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv *priv) +{ + u32 val; + + val = readb(priv->base + PHY_INTR_MASK0); + val &= ~DPDM_MASK; + writeb(val, priv->base + PHY_INTR_MASK0); + + /* Clear any pending interrupts */ + val = readb(priv->base + PHY_INTR_CLEAR0); + val |= DPDM_MASK; + writeb(val, priv->base + PHY_INTR_CLEAR0); + + writeb(0x0, priv->base + PHY_IRQ_CMD); + usleep_range(200, 220); + + writeb(0x1, priv->base + PHY_IRQ_CMD); + usleep_range(200, 220); +} + +static void qcom_snps_hsphy_enter_retention(struct hsphy_priv *priv) +{ + u32 val; + + val = readb(priv->base + PHY_CTRL_COMMON0); + val |= SIDDQ; + writeb(val, priv->base + PHY_CTRL_COMMON0); +} + +static void qcom_snps_hsphy_exit_retention(struct hsphy_priv *priv) +{ + u32 val; + + val = readb(priv->base + PHY_CTRL_COMMON0); + val &= ~SIDDQ; + writeb(val, priv->base + PHY_CTRL_COMMON0); +} + +static int qcom_snps_hsphy_power_on(struct phy *phy) +{ + struct hsphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = regulator_bulk_enable(VREG_NUM, priv->vregs); + if (ret) + return ret; + + qcom_snps_hsphy_disable_hv_interrupts(priv); + qcom_snps_hsphy_exit_retention(priv); + + return 0; +} + +static int qcom_snps_hsphy_power_off(struct phy *phy) +{ + struct hsphy_priv *priv = phy_get_drvdata(phy); + + qcom_snps_hsphy_enter_retention(priv); + qcom_snps_hsphy_enable_hv_interrupts(priv); + regulator_bulk_disable(VREG_NUM, priv->vregs); + + return 0; +} + +static int qcom_snps_hsphy_reset(struct hsphy_priv *priv) +{ + int ret; + + ret = reset_control_assert(priv->phy_reset); + if (ret) + return ret; + + usleep_range(10, 15); + + ret = reset_control_deassert(priv->phy_reset); + if (ret) + return ret; + + usleep_range(80, 100); + + return 0; +} + +static void qcom_snps_hsphy_init_sequence(struct hsphy_priv *priv) +{ + const struct hsphy_data *data = priv->data; + const struct hsphy_init_seq *seq; + int i; + + /* Device match data is optional. */ + if (!data) + return; + + seq = data->init_seq; + + for (i = 0; i < data->init_seq_num; i++, seq++) { + writeb(seq->val, priv->base + seq->offset); + if (seq->delay) + usleep_range(seq->delay, seq->delay + 10); + } +} + +static int qcom_snps_hsphy_por_reset(struct hsphy_priv *priv) +{ + int ret; + + ret = reset_control_assert(priv->por_reset); + if (ret) + return ret; + + /* + * The Femto PHY is POR reset in the following scenarios. + * + * 1. After overriding the parameter registers. + * 2. Low power mode exit from PHY retention. + * + * Ensure that SIDDQ is cleared before bringing the PHY + * out of reset. + */ + qcom_snps_hsphy_exit_retention(priv); + + /* + * As per databook, 10 usec delay is required between + * PHY POR assert and de-assert. + */ + usleep_range(10, 20); + ret = reset_control_deassert(priv->por_reset); + if (ret) + return ret; + + /* + * As per databook, it takes 75 usec for PHY to stabilize + * after the reset. + */ + usleep_range(80, 100); + + return 0; +} + +static int qcom_snps_hsphy_init(struct phy *phy) +{ + struct hsphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); + if (ret) + return ret; + + ret = qcom_snps_hsphy_reset(priv); + if (ret) + goto disable_clocks; + + qcom_snps_hsphy_init_sequence(priv); + + ret = qcom_snps_hsphy_por_reset(priv); + if (ret) + goto disable_clocks; + + return 0; + +disable_clocks: + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + return ret; +} + +static int qcom_snps_hsphy_exit(struct phy *phy) +{ + struct hsphy_priv *priv = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + + return 0; +} + +static const struct phy_ops qcom_snps_hsphy_ops = { + .init = qcom_snps_hsphy_init, + .exit = qcom_snps_hsphy_exit, + .power_on = qcom_snps_hsphy_power_on, + .power_off = qcom_snps_hsphy_power_off, + .set_mode = qcom_snps_hsphy_set_mode, + .owner = THIS_MODULE, +}; + +static const char * const qcom_snps_hsphy_clks[] = { + "ref", + "ahb", + "sleep", +}; + +static int qcom_snps_hsphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct hsphy_priv *priv; + struct phy *phy; + int ret; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->num_clks = ARRAY_SIZE(qcom_snps_hsphy_clks); + priv->clks = devm_kcalloc(dev, priv->num_clks, sizeof(*priv->clks), + GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + for (i = 0; i < priv->num_clks; i++) + priv->clks[i].id = qcom_snps_hsphy_clks[i]; + + ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks); + if (ret) + return ret; + + priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(priv->phy_reset)) + return PTR_ERR(priv->phy_reset); + + priv->por_reset = devm_reset_control_get_exclusive(dev, "por"); + if (IS_ERR(priv->por_reset)) + return PTR_ERR(priv->por_reset); + + priv->vregs[VDD].supply = "vdd"; + priv->vregs[VDDA_1P8].supply = "vdda1p8"; + priv->vregs[VDDA_3P3].supply = "vdda3p3"; + + ret = devm_regulator_bulk_get(dev, VREG_NUM, priv->vregs); + if (ret) + return ret; + + /* Get device match data */ + priv->data = device_get_match_data(dev); + + phy = devm_phy_create(dev, dev->of_node, &qcom_snps_hsphy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + ret = regulator_set_load(priv->vregs[VDDA_1P8].consumer, 19000); + if (ret < 0) + return ret; + + ret = regulator_set_load(priv->vregs[VDDA_3P3].consumer, 16000); + if (ret < 0) + goto unset_1p8_load; + + return 0; + +unset_1p8_load: + regulator_set_load(priv->vregs[VDDA_1P8].consumer, 0); + + return ret; +} + +/* + * The macro is used to define an initialization sequence. Each tuple + * is meant to program 'value' into phy register at 'offset' with 'delay' + * in us followed. + */ +#define HSPHY_INIT_CFG(o, v, d) { .offset = o, .val = v, .delay = d, } + +static const struct hsphy_init_seq init_seq_femtophy[] = { + HSPHY_INIT_CFG(0xc0, 0x01, 0), + HSPHY_INIT_CFG(0xe8, 0x0d, 0), + HSPHY_INIT_CFG(0x74, 0x12, 0), + HSPHY_INIT_CFG(0x98, 0x63, 0), + HSPHY_INIT_CFG(0x9c, 0x03, 0), + HSPHY_INIT_CFG(0xa0, 0x1d, 0), + HSPHY_INIT_CFG(0xa4, 0x03, 0), + HSPHY_INIT_CFG(0x8c, 0x23, 0), + HSPHY_INIT_CFG(0x78, 0x08, 0), + HSPHY_INIT_CFG(0x7c, 0xdc, 0), + HSPHY_INIT_CFG(0x90, 0xe0, 20), + HSPHY_INIT_CFG(0x74, 0x10, 0), + HSPHY_INIT_CFG(0x90, 0x60, 0), +}; + +static const struct hsphy_init_seq init_seq_mdm9607[] = { + HSPHY_INIT_CFG(0x80, 0x44, 0), + HSPHY_INIT_CFG(0x81, 0x38, 0), + HSPHY_INIT_CFG(0x82, 0x24, 0), + HSPHY_INIT_CFG(0x83, 0x13, 0), +}; + +static const struct hsphy_data hsphy_data_femtophy = { + .init_seq = init_seq_femtophy, + .init_seq_num = ARRAY_SIZE(init_seq_femtophy), +}; + +static const struct hsphy_data hsphy_data_mdm9607 = { + .init_seq = init_seq_mdm9607, + .init_seq_num = ARRAY_SIZE(init_seq_mdm9607), +}; + +static const struct of_device_id qcom_snps_hsphy_match[] = { + { .compatible = "qcom,usb-hs-28nm-femtophy", .data = &hsphy_data_femtophy, }, + { .compatible = "qcom,usb-hs-28nm-mdm9607", .data = &hsphy_data_mdm9607, }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_match); + +static struct platform_driver qcom_snps_hsphy_driver = { + .probe = qcom_snps_hsphy_probe, + .driver = { + .name = "qcom,usb-hs-28nm-phy", + .of_match_table = qcom_snps_hsphy_match, + }, +}; +module_platform_driver(qcom_snps_hsphy_driver); + +MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c index 61054272a7c8..53e46c220a3a 100644 --- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Copyright (C) 2016 Linaro Ltd */ #include <linux/module.h> @@ -53,9 +53,10 @@ static int qcom_usb_hs_phy_set_mode(struct phy *phy, case PHY_MODE_USB_OTG: case PHY_MODE_USB_HOST: val |= ULPI_INT_IDGRD; - /* fall through */ + fallthrough; case PHY_MODE_USB_DEVICE: val |= ULPI_INT_SESS_VALID; + break; default: break; } diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c index 04d18d52f700..20f6dd37c7c1 100644 --- a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c +++ b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Copyright (C) 2016 Linaro Ltd */ #include <linux/module.h> @@ -54,8 +54,10 @@ static int qcom_usb_hsic_phy_power_on(struct phy *phy) /* Configure pins for HSIC functionality */ pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); - if (IS_ERR(pins_default)) - return PTR_ERR(pins_default); + if (IS_ERR(pins_default)) { + ret = PTR_ERR(pins_default); + goto err_ulpi; + } ret = pinctrl_select_state(uphy->pctl, pins_default); if (ret) diff --git a/drivers/phy/qualcomm/phy-qcom-usb-ss.c b/drivers/phy/qualcomm/phy-qcom-usb-ss.c new file mode 100644 index 000000000000..a3a6d3ce7ea1 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-usb-ss.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012-2014,2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2020, Linaro Limited + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#define PHY_CTRL0 0x6C +#define PHY_CTRL1 0x70 +#define PHY_CTRL2 0x74 +#define PHY_CTRL4 0x7C + +/* PHY_CTRL bits */ +#define REF_PHY_EN BIT(0) +#define LANE0_PWR_ON BIT(2) +#define SWI_PCS_CLK_SEL BIT(4) +#define TST_PWR_DOWN BIT(4) +#define PHY_RESET BIT(7) + +#define NUM_BULK_CLKS 3 +#define NUM_BULK_REGS 2 + +struct ssphy_priv { + void __iomem *base; + struct device *dev; + struct reset_control *reset_com; + struct reset_control *reset_phy; + struct regulator_bulk_data regs[NUM_BULK_REGS]; + struct clk_bulk_data clks[NUM_BULK_CLKS]; + enum phy_mode mode; +}; + +static inline void qcom_ssphy_updatel(void __iomem *addr, u32 mask, u32 val) +{ + writel((readl(addr) & ~mask) | val, addr); +} + +static int qcom_ssphy_do_reset(struct ssphy_priv *priv) +{ + int ret; + + if (!priv->reset_com) { + qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET, + PHY_RESET); + usleep_range(10, 20); + qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET, 0); + } else { + ret = reset_control_assert(priv->reset_com); + if (ret) { + dev_err(priv->dev, "Failed to assert reset com\n"); + return ret; + } + + ret = reset_control_assert(priv->reset_phy); + if (ret) { + dev_err(priv->dev, "Failed to assert reset phy\n"); + return ret; + } + + usleep_range(10, 20); + + ret = reset_control_deassert(priv->reset_com); + if (ret) { + dev_err(priv->dev, "Failed to deassert reset com\n"); + return ret; + } + + ret = reset_control_deassert(priv->reset_phy); + if (ret) { + dev_err(priv->dev, "Failed to deassert reset phy\n"); + return ret; + } + } + + return 0; +} + +static int qcom_ssphy_power_on(struct phy *phy) +{ + struct ssphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = regulator_bulk_enable(NUM_BULK_REGS, priv->regs); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(NUM_BULK_CLKS, priv->clks); + if (ret) + goto err_disable_regulator; + + ret = qcom_ssphy_do_reset(priv); + if (ret) + goto err_disable_clock; + + writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0); + qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON); + qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN); + qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0); + + return 0; +err_disable_clock: + clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks); +err_disable_regulator: + regulator_bulk_disable(NUM_BULK_REGS, priv->regs); + + return ret; +} + +static int qcom_ssphy_power_off(struct phy *phy) +{ + struct ssphy_priv *priv = phy_get_drvdata(phy); + + qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0); + qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0); + qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN); + + clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks); + regulator_bulk_disable(NUM_BULK_REGS, priv->regs); + + return 0; +} + +static int qcom_ssphy_init_clock(struct ssphy_priv *priv) +{ + priv->clks[0].id = "ref"; + priv->clks[1].id = "ahb"; + priv->clks[2].id = "pipe"; + + return devm_clk_bulk_get(priv->dev, NUM_BULK_CLKS, priv->clks); +} + +static int qcom_ssphy_init_regulator(struct ssphy_priv *priv) +{ + int ret; + + priv->regs[0].supply = "vdd"; + priv->regs[1].supply = "vdda1p8"; + ret = devm_regulator_bulk_get(priv->dev, NUM_BULK_REGS, priv->regs); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(priv->dev, "Failed to get regulators\n"); + return ret; + } + + return ret; +} + +static int qcom_ssphy_init_reset(struct ssphy_priv *priv) +{ + priv->reset_com = devm_reset_control_get_optional_exclusive(priv->dev, "com"); + if (IS_ERR(priv->reset_com)) { + dev_err(priv->dev, "Failed to get reset control com\n"); + return PTR_ERR(priv->reset_com); + } + + if (priv->reset_com) { + /* if reset_com is present, reset_phy is no longer optional */ + priv->reset_phy = devm_reset_control_get_exclusive(priv->dev, "phy"); + if (IS_ERR(priv->reset_phy)) { + dev_err(priv->dev, "Failed to get reset control phy\n"); + return PTR_ERR(priv->reset_phy); + } + } + + return 0; +} + +static const struct phy_ops qcom_ssphy_ops = { + .power_off = qcom_ssphy_power_off, + .power_on = qcom_ssphy_power_on, + .owner = THIS_MODULE, +}; + +static int qcom_ssphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct ssphy_priv *priv; + struct phy *phy; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct ssphy_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->mode = PHY_MODE_INVALID; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = qcom_ssphy_init_clock(priv); + if (ret) + return ret; + + ret = qcom_ssphy_init_reset(priv); + if (ret) + return ret; + + ret = qcom_ssphy_init_regulator(priv); + if (ret) + return ret; + + phy = devm_phy_create(dev, dev->of_node, &qcom_ssphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "Failed to create the SS phy\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id qcom_ssphy_match[] = { + { .compatible = "qcom,usb-ss-28nm-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_ssphy_match); + +static struct platform_driver qcom_ssphy_driver = { + .probe = qcom_ssphy_probe, + .driver = { + .name = "qcom-usb-ssphy", + .of_match_table = qcom_ssphy_match, + }, +}; +module_platform_driver(qcom_ssphy_driver); + +MODULE_DESCRIPTION("Qualcomm SuperSpeed USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ralink/Kconfig b/drivers/phy/ralink/Kconfig index da982c9cffb3..c2373b30b8a6 100644 --- a/drivers/phy/ralink/Kconfig +++ b/drivers/phy/ralink/Kconfig @@ -2,6 +2,14 @@ # # PHY drivers for Ralink platforms. # +config PHY_MT7621_PCI + tristate "MediaTek MT7621 PCI PHY Driver" + depends on (RALINK && OF) || COMPILE_TEST + select GENERIC_PHY + select REGMAP_MMIO + help + Say 'Y' here to add support for MediaTek MT7621 PCI PHY driver, + config PHY_RALINK_USB tristate "Ralink USB PHY driver" depends on RALINK || COMPILE_TEST diff --git a/drivers/phy/ralink/Makefile b/drivers/phy/ralink/Makefile index d8d3ffcf0a15..cda2a4a7ca5e 100644 --- a/drivers/phy/ralink/Makefile +++ b/drivers/phy/ralink/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_PHY_MT7621_PCI) += phy-mt7621-pci.o obj-$(CONFIG_PHY_RALINK_USB) += phy-ralink-usb.o diff --git a/drivers/phy/ralink/phy-mt7621-pci.c b/drivers/phy/ralink/phy-mt7621-pci.c new file mode 100644 index 000000000000..85888ab2d307 --- /dev/null +++ b/drivers/phy/ralink/phy-mt7621-pci.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Mediatek MT7621 PCI PHY Driver + * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com> + */ + +#include <dt-bindings/phy/phy.h> +#include <linux/clk.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/sys_soc.h> + +#define RG_PE1_PIPE_REG 0x02c +#define RG_PE1_PIPE_RST BIT(12) +#define RG_PE1_PIPE_CMD_FRC BIT(4) + +#define RG_P0_TO_P1_WIDTH 0x100 +#define RG_PE1_H_LCDDS_REG 0x49c +#define RG_PE1_H_LCDDS_PCW GENMASK(30, 0) + +#define RG_PE1_FRC_H_XTAL_REG 0x400 +#define RG_PE1_FRC_H_XTAL_TYPE BIT(8) +#define RG_PE1_H_XTAL_TYPE GENMASK(10, 9) + +#define RG_PE1_FRC_PHY_REG 0x000 +#define RG_PE1_FRC_PHY_EN BIT(4) +#define RG_PE1_PHY_EN BIT(5) + +#define RG_PE1_H_PLL_REG 0x490 +#define RG_PE1_H_PLL_BC GENMASK(23, 22) +#define RG_PE1_H_PLL_BP GENMASK(21, 18) +#define RG_PE1_H_PLL_IR GENMASK(15, 12) +#define RG_PE1_H_PLL_IC GENMASK(11, 8) +#define RG_PE1_H_PLL_PREDIV GENMASK(7, 6) +#define RG_PE1_PLL_DIVEN GENMASK(3, 1) + +#define RG_PE1_H_PLL_FBKSEL_REG 0x4bc +#define RG_PE1_H_PLL_FBKSEL GENMASK(5, 4) + +#define RG_PE1_H_LCDDS_SSC_PRD_REG 0x4a4 +#define RG_PE1_H_LCDDS_SSC_PRD GENMASK(15, 0) + +#define RG_PE1_H_LCDDS_SSC_DELTA_REG 0x4a8 +#define RG_PE1_H_LCDDS_SSC_DELTA GENMASK(11, 0) +#define RG_PE1_H_LCDDS_SSC_DELTA1 GENMASK(27, 16) + +#define RG_PE1_LCDDS_CLK_PH_INV_REG 0x4a0 +#define RG_PE1_LCDDS_CLK_PH_INV BIT(5) + +#define RG_PE1_H_PLL_BR_REG 0x4ac +#define RG_PE1_H_PLL_BR GENMASK(18, 16) + +#define RG_PE1_MSTCKDIV_REG 0x414 +#define RG_PE1_MSTCKDIV GENMASK(7, 6) + +#define RG_PE1_FRC_MSTCKDIV BIT(5) + +#define MAX_PHYS 2 + +/** + * struct mt7621_pci_phy - Mt7621 Pcie PHY core + * @dev: pointer to device + * @regmap: kernel regmap pointer + * @phy: pointer to the kernel PHY device + * @sys_clk: pointer to the system XTAL clock + * @port_base: base register + * @has_dual_port: if the phy has dual ports. + * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst' + * needs to be executed. Depends on chip revision. + */ +struct mt7621_pci_phy { + struct device *dev; + struct regmap *regmap; + struct phy *phy; + struct clk *sys_clk; + void __iomem *port_base; + bool has_dual_port; + bool bypass_pipe_rst; +}; + +static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy, + u32 reg, u32 clr, u32 set) +{ + u32 val; + + /* + * We cannot use 'regmap_write_bits' here because internally + * 'set' is masked before is set to the value that will be + * written to the register. That way results in no reliable + * pci setup. Avoid to mask 'set' before set value to 'val' + * completely avoid the problem. + */ + regmap_read(phy->regmap, reg, &val); + val &= ~clr; + val |= set; + regmap_write(phy->regmap, reg, val); +} + +static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy) +{ + mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST); + mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC); + + if (phy->has_dual_port) { + mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, + 0, RG_PE1_PIPE_RST); + mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, + 0, RG_PE1_PIPE_CMD_FRC); + } +} + +static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) +{ + struct device *dev = phy->dev; + unsigned long clk_rate; + + clk_rate = clk_get_rate(phy->sys_clk); + if (!clk_rate) + return -EINVAL; + + /* Set PCIe Port PHY to disable SSC */ + /* Debug Xtal Type */ + mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG, + RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE, + RG_PE1_FRC_H_XTAL_TYPE | + FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00)); + + /* disable port */ + mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN, + RG_PE1_FRC_PHY_EN); + + if (phy->has_dual_port) { + mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, + RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); + } + + if (clk_rate == 40000000) { /* 40MHz Xtal */ + /* Set Pre-divider ratio (for host mode) */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, + FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01)); + + dev_dbg(dev, "Xtal is 40MHz\n"); + } else if (clk_rate == 25000000) { /* 25MHz Xal */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, + FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); + + /* Select feedback clock */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG, + RG_PE1_H_PLL_FBKSEL, + FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01)); + + /* DDS NCPO PCW (for host mode) */ + mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, + RG_PE1_H_LCDDS_SSC_PRD, + FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00)); + + /* DDS SSC dither period control */ + mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, + RG_PE1_H_LCDDS_SSC_PRD, + FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d)); + + /* DDS SSC dither amplitude control */ + mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG, + RG_PE1_H_LCDDS_SSC_DELTA | + RG_PE1_H_LCDDS_SSC_DELTA1, + FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA, 0x4a) | + FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA1, 0x4a)); + + dev_dbg(dev, "Xtal is 25MHz\n"); + } else { /* 20MHz Xtal */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, + FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); + + dev_dbg(dev, "Xtal is 20MHz\n"); + } + + /* DDS clock inversion */ + mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG, + RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV); + + /* Set PLL bits */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, + RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR | + RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN, + FIELD_PREP(RG_PE1_H_PLL_BC, 0x02) | + FIELD_PREP(RG_PE1_H_PLL_BP, 0x06) | + FIELD_PREP(RG_PE1_H_PLL_IR, 0x02) | + FIELD_PREP(RG_PE1_H_PLL_IC, 0x01) | + FIELD_PREP(RG_PE1_PLL_DIVEN, 0x02)); + + mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR, + FIELD_PREP(RG_PE1_H_PLL_BR, 0x00)); + + if (clk_rate == 40000000) { /* 40MHz Xtal */ + /* set force mode enable of da_pe1_mstckdiv */ + mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, + RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, + FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) | + RG_PE1_FRC_MSTCKDIV); + } + + return 0; +} + +static int mt7621_pci_phy_init(struct phy *phy) +{ + struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); + + if (mphy->bypass_pipe_rst) + mt7621_bypass_pipe_rst(mphy); + + return mt7621_set_phy_for_ssc(mphy); +} + +static int mt7621_pci_phy_power_on(struct phy *phy) +{ + struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); + + /* Enable PHY and disable force mode */ + mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, + RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); + + if (mphy->has_dual_port) { + mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, + RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); + } + + return 0; +} + +static int mt7621_pci_phy_power_off(struct phy *phy) +{ + struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); + + /* Disable PHY */ + mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, + RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); + + if (mphy->has_dual_port) { + mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, + RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); + } + + return 0; +} + +static int mt7621_pci_phy_exit(struct phy *phy) +{ + return 0; +} + +static const struct phy_ops mt7621_pci_phy_ops = { + .init = mt7621_pci_phy_init, + .exit = mt7621_pci_phy_exit, + .power_on = mt7621_pci_phy_power_on, + .power_off = mt7621_pci_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev); + + if (WARN_ON(args->args[0] >= MAX_PHYS)) + return ERR_PTR(-ENODEV); + + mt7621_phy->has_dual_port = args->args[0]; + + dev_dbg(dev, "PHY for 0x%px (dual port = %d)\n", + mt7621_phy->port_base, mt7621_phy->has_dual_port); + + return mt7621_phy->phy; +} + +static const struct soc_device_attribute mt7621_pci_quirks_match[] = { + { .soc_id = "mt7621", .revision = "E2" }, + { /* sentinel */ } +}; + +static const struct regmap_config mt7621_pci_phy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x700, +}; + +static int mt7621_pci_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct soc_device_attribute *attr; + struct phy_provider *provider; + struct mt7621_pci_phy *phy; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + attr = soc_device_match(mt7621_pci_quirks_match); + if (attr) + phy->bypass_pipe_rst = true; + + phy->dev = dev; + platform_set_drvdata(pdev, phy); + + phy->port_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->port_base)) { + dev_err(dev, "failed to remap phy regs\n"); + return PTR_ERR(phy->port_base); + } + + phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base, + &mt7621_pci_phy_regmap_config); + if (IS_ERR(phy->regmap)) + return PTR_ERR(phy->regmap); + + phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy->phy); + } + + phy->sys_clk = devm_clk_get(dev, NULL); + if (IS_ERR(phy->sys_clk)) { + dev_err(dev, "failed to get phy clock\n"); + return PTR_ERR(phy->sys_clk); + } + + phy_set_drvdata(phy->phy, phy); + + provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id mt7621_pci_phy_ids[] = { + { .compatible = "mediatek,mt7621-pci-phy" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt7621_pci_phy_ids); + +static struct platform_driver mt7621_pci_phy_driver = { + .probe = mt7621_pci_phy_probe, + .driver = { + .name = "mt7621-pci-phy", + .of_match_table = mt7621_pci_phy_ids, + }, +}; + +builtin_platform_driver(mt7621_pci_phy_driver); + +MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>"); +MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ralink/phy-ralink-usb.c b/drivers/phy/ralink/phy-ralink-usb.c index ba3c197fc5b0..2bd8ad2e76ed 100644 --- a/drivers/phy/ralink/phy-ralink-usb.c +++ b/drivers/phy/ralink/phy-ralink-usb.c @@ -142,7 +142,7 @@ static int ralink_usb_phy_power_off(struct phy *_phy) return 0; } -static struct phy_ops ralink_usb_phy_ops = { +static const struct phy_ops ralink_usb_phy_ops = { .power_on = ralink_usb_phy_power_on, .power_off = ralink_usb_phy_power_off, .owner = THIS_MODULE, @@ -170,7 +170,6 @@ MODULE_DEVICE_TABLE(of, ralink_usb_phy_of_match); static int ralink_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct phy_provider *phy_provider; const struct of_device_id *match; struct ralink_usb_phy *phy; @@ -194,8 +193,7 @@ static int ralink_usb_phy_probe(struct platform_device *pdev) /* The MT7628 and MT7688 require extra setup of PHY registers. */ if (of_device_is_compatible(dev->of_node, "mediatek,mt7628-usbphy")) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->base = devm_ioremap_resource(&pdev->dev, res); + phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy->base)) { dev_err(dev, "failed to remap register memory\n"); return PTR_ERR(phy->base); diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c index 2e279ac0fa4d..c375a4676a3d 100644 --- a/drivers/phy/renesas/phy-rcar-gen2.c +++ b/drivers/phy/renesas/phy-rcar-gen2.c @@ -339,7 +339,6 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev) struct rcar_gen2_phy_driver *drv; struct phy_provider *provider; struct device_node *np; - struct resource *res; void __iomem *base; struct clk *clk; const struct rcar_gen2_phy_data *data; @@ -357,8 +356,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev) return PTR_ERR(clk); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/phy/renesas/phy-rcar-gen3-pcie.c b/drivers/phy/renesas/phy-rcar-gen3-pcie.c index c4e4aa216936..4dc721eb9577 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-pcie.c +++ b/drivers/phy/renesas/phy-rcar-gen3-pcie.c @@ -76,7 +76,6 @@ static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct phy_provider *provider; struct rcar_gen3_phy *phy; - struct resource *res; void __iomem *base; int error; @@ -86,8 +85,7 @@ static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index bfb22f868857..9de617ca9daa 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -64,6 +64,7 @@ /* VBCTRL */ #define USB2_VBCTRL_OCCLREN BIT(16) #define USB2_VBCTRL_DRVVBUSSEL BIT(8) +#define USB2_VBCTRL_VBOUT BIT(0) /* LINECTRL1 */ #define USB2_LINECTRL1_DPRPD_EN BIT(19) @@ -78,6 +79,10 @@ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) +/* RZ/G2L specific */ +#define USB2_OBINT_IDCHG_EN BIT(0) +#define USB2_LINECTRL1_USB2_IDMON BIT(0) + #define NUM_OF_PHYS 4 enum rcar_gen3_phy_index { PHY_INDEX_BOTH_HC, @@ -111,9 +116,17 @@ struct rcar_gen3_chan { struct work_struct work; struct mutex lock; /* protects rphys[...].powered */ enum usb_dr_mode dr_mode; + int irq; + u32 obint_enable_bits; bool extcon_host; bool is_otg_channel; bool uses_otg_pins; + bool soc_no_adp_ctrl; +}; + +struct rcar_gen3_phy_drv_data { + const struct phy_ops *phy_usb2_ops; + bool no_adp_ctrl; }; /* @@ -171,14 +184,22 @@ static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) { void __iomem *usb2_base = ch->base; - u32 val = readl(usb2_base + USB2_ADPCTRL); + u32 vbus_ctrl_reg = USB2_ADPCTRL; + u32 vbus_ctrl_val = USB2_ADPCTRL_DRVVBUS; + u32 val; dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus); + if (ch->soc_no_adp_ctrl) { + vbus_ctrl_reg = USB2_VBCTRL; + vbus_ctrl_val = USB2_VBCTRL_VBOUT; + } + + val = readl(usb2_base + vbus_ctrl_reg); if (vbus) - val |= USB2_ADPCTRL_DRVVBUS; + val |= vbus_ctrl_val; else - val &= ~USB2_ADPCTRL_DRVVBUS; - writel(val, usb2_base + USB2_ADPCTRL); + val &= ~vbus_ctrl_val; + writel(val, usb2_base + vbus_ctrl_reg); } static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable) @@ -187,9 +208,9 @@ static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable) u32 val = readl(usb2_base + USB2_OBINTEN); if (ch->uses_otg_pins && enable) - val |= USB2_OBINT_BITS; + val |= ch->obint_enable_bits; else - val &= ~USB2_OBINT_BITS; + val &= ~ch->obint_enable_bits; writel(val, usb2_base + USB2_OBINTEN); } @@ -251,6 +272,9 @@ static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) if (!ch->uses_otg_pins) return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true; + if (ch->soc_no_adp_ctrl) + return !!(readl(ch->base + USB2_LINECTRL1) & USB2_LINECTRL1_USB2_IDMON); + return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); } @@ -375,26 +399,55 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD; writel(val, usb2_base + USB2_LINECTRL1); - val = readl(usb2_base + USB2_VBCTRL); - val &= ~USB2_VBCTRL_OCCLREN; - writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); - val = readl(usb2_base + USB2_ADPCTRL); - writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); - + if (!ch->soc_no_adp_ctrl) { + val = readl(usb2_base + USB2_VBCTRL); + val &= ~USB2_VBCTRL_OCCLREN; + writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); + val = readl(usb2_base + USB2_ADPCTRL); + writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); + } msleep(20); writel(0xffffffff, usb2_base + USB2_OBINTSTA); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); + writel(ch->obint_enable_bits, usb2_base + USB2_OBINTEN); rcar_gen3_device_recognition(ch); } +static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) +{ + struct rcar_gen3_chan *ch = _ch; + void __iomem *usb2_base = ch->base; + u32 status = readl(usb2_base + USB2_OBINTSTA); + irqreturn_t ret = IRQ_NONE; + + if (status & ch->obint_enable_bits) { + dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); + writel(ch->obint_enable_bits, usb2_base + USB2_OBINTSTA); + rcar_gen3_device_recognition(ch); + ret = IRQ_HANDLED; + } + + return ret; +} + static int rcar_gen3_phy_usb2_init(struct phy *p) { struct rcar_gen3_phy *rphy = phy_get_drvdata(p); struct rcar_gen3_chan *channel = rphy->ch; void __iomem *usb2_base = channel->base; u32 val; + int ret; + + if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) { + INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); + ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, + IRQF_SHARED, dev_name(channel->dev), channel); + if (ret < 0) { + dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); + return ret; + } + } /* Initialize USB2 part */ val = readl(usb2_base + USB2_INT_ENABLE); @@ -433,6 +486,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p) val &= ~USB2_INT_ENABLE_UCOM_INTEN; writel(val, usb2_base + USB2_INT_ENABLE); + if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel)) + free_irq(channel->irq, channel); + return 0; } @@ -503,43 +559,45 @@ static const struct phy_ops rz_g1c_phy_usb2_ops = { .owner = THIS_MODULE, }; -static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) -{ - struct rcar_gen3_chan *ch = _ch; - void __iomem *usb2_base = ch->base; - u32 status = readl(usb2_base + USB2_OBINTSTA); - irqreturn_t ret = IRQ_NONE; +static const struct rcar_gen3_phy_drv_data rcar_gen3_phy_usb2_data = { + .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, + .no_adp_ctrl = false, +}; - if (status & USB2_OBINT_BITS) { - dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); - rcar_gen3_device_recognition(ch); - ret = IRQ_HANDLED; - } +static const struct rcar_gen3_phy_drv_data rz_g1c_phy_usb2_data = { + .phy_usb2_ops = &rz_g1c_phy_usb2_ops, + .no_adp_ctrl = false, +}; - return ret; -} +static const struct rcar_gen3_phy_drv_data rz_g2l_phy_usb2_data = { + .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, + .no_adp_ctrl = true, +}; static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { { .compatible = "renesas,usb2-phy-r8a77470", - .data = &rz_g1c_phy_usb2_ops, + .data = &rz_g1c_phy_usb2_data, }, { .compatible = "renesas,usb2-phy-r8a7795", - .data = &rcar_gen3_phy_usb2_ops, + .data = &rcar_gen3_phy_usb2_data, }, { .compatible = "renesas,usb2-phy-r8a7796", - .data = &rcar_gen3_phy_usb2_ops, + .data = &rcar_gen3_phy_usb2_data, }, { .compatible = "renesas,usb2-phy-r8a77965", - .data = &rcar_gen3_phy_usb2_ops, + .data = &rcar_gen3_phy_usb2_data, + }, + { + .compatible = "renesas,rzg2l-usb2-phy", + .data = &rz_g2l_phy_usb2_data, }, { .compatible = "renesas,rcar-gen3-usb2-phy", - .data = &rcar_gen3_phy_usb2_ops, + .data = &rcar_gen3_phy_usb2_data, }, { /* sentinel */ }, }; @@ -593,12 +651,11 @@ static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np) static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) { + const struct rcar_gen3_phy_drv_data *phy_data; struct device *dev = &pdev->dev; struct rcar_gen3_chan *channel; struct phy_provider *provider; - struct resource *res; - const struct phy_ops *phy_usb2_ops; - int irq, ret = 0, i; + int ret = 0, i; if (!dev->of_node) { dev_err(dev, "This driver needs device tree\n"); @@ -609,21 +666,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) if (!channel) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - channel->base = devm_ioremap_resource(dev, res); + channel->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(channel->base)) return PTR_ERR(channel->base); - /* call request_irq for OTG */ - irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { - 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->obint_enable_bits = USB2_OBINT_BITS; + /* get irq number here and request_irq for OTG in phy_init */ + channel->irq = platform_get_irq_optional(pdev, 0); channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { int ret; @@ -648,14 +697,21 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) * And then, phy-core will manage runtime pm for this device. */ pm_runtime_enable(dev); - phy_usb2_ops = of_device_get_match_data(dev); - if (!phy_usb2_ops) - return -EINVAL; + + phy_data = of_device_get_match_data(dev); + if (!phy_data) { + ret = -EINVAL; + goto error; + } + + channel->soc_no_adp_ctrl = phy_data->no_adp_ctrl; + if (phy_data->no_adp_ctrl) + channel->obint_enable_bits = USB2_OBINT_IDCHG_EN; mutex_init(&channel->lock); for (i = 0; i < NUM_OF_PHYS; i++) { channel->rphys[i].phy = devm_phy_create(dev, NULL, - phy_usb2_ops); + phy_data->phy_usb2_ops); if (IS_ERR(channel->rphys[i].phy)) { dev_err(dev, "Failed to create USB2 PHY\n"); ret = PTR_ERR(channel->rphys[i].phy); diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb3.c b/drivers/phy/renesas/phy-rcar-gen3-usb3.c index 566b4cf4ff38..f27d6f471629 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb3.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb3.c @@ -133,7 +133,6 @@ static int rcar_gen3_phy_usb3_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rcar_gen3_usb3 *r; struct phy_provider *provider; - struct resource *res; int ret = 0; struct clk *clk; @@ -146,8 +145,7 @@ static int rcar_gen3_phy_usb3_probe(struct platform_device *pdev) if (!r) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - r->base = devm_ioremap_resource(dev, res); + r->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(r->base)) return PTR_ERR(r->base); diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 0824b9dd5683..94360fc96a6f 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -9,6 +9,18 @@ config PHY_ROCKCHIP_DP help Enable this to support the Rockchip Display Port PHY. +config PHY_ROCKCHIP_DPHY_RX0 + tristate "Rockchip MIPI Synopsys DPHY RX0 driver" + depends on ARCH_ROCKCHIP || COMPILE_TEST + select GENERIC_PHY_MIPI_DPHY + select GENERIC_PHY + help + Enable this to support the Rockchip MIPI Synopsys DPHY RX0 + associated to the Rockchip ISP module present in RK3399 SoCs. + + To compile this driver as a module, choose M here: the module + will be called phy-rockchip-dphy-rx0. + config PHY_ROCKCHIP_EMMC tristate "Rockchip EMMC PHY Driver" depends on ARCH_ROCKCHIP && OF @@ -20,6 +32,7 @@ config PHY_ROCKCHIP_INNO_HDMI tristate "Rockchip INNO HDMI PHY Driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF depends on COMMON_CLK + depends on HAS_IOMEM select GENERIC_PHY help Enable this to support the Rockchip Innosilicon HDMI PHY. @@ -35,6 +48,15 @@ config PHY_ROCKCHIP_INNO_USB2 help Support for Rockchip USB2.0 PHY with Innosilicon IP block. +config PHY_ROCKCHIP_INNO_CSIDPHY + tristate "Rockchip Innosilicon MIPI CSI PHY driver" + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Enable this to support the Rockchip MIPI CSI PHY with + Innosilicon IP block. + config PHY_ROCKCHIP_INNO_DSIDPHY tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF @@ -44,6 +66,14 @@ config PHY_ROCKCHIP_INNO_DSIDPHY Enable this to support the Rockchip MIPI/LVDS/TTL PHY with Innosilicon IP block. +config PHY_ROCKCHIP_NANENG_COMBO_PHY + tristate "Rockchip NANENG COMBO PHY Driver" + depends on ARCH_ROCKCHIP && OF + select GENERIC_PHY + help + Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII + combo PHY with NaNeng IP block. + config PHY_ROCKCHIP_PCIE tristate "Rockchip PCIe PHY Driver" depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST @@ -53,6 +83,15 @@ config PHY_ROCKCHIP_PCIE help Enable this to support the Rockchip PCIe PHY. +config PHY_ROCKCHIP_SNPS_PCIE3 + tristate "Rockchip Snps PCIe3 PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST + depends on HAS_IOMEM + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the Rockchip snps PCIe3 PHY. + config PHY_ROCKCHIP_TYPEC tristate "Rockchip TYPEC PHY Driver" depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index 9f59a81e4e0d..7eab129230d1 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -1,9 +1,13 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o +obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o +obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o +obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o +obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o diff --git a/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c new file mode 100644 index 000000000000..639452f47869 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Rockchip MIPI Synopsys DPHY RX0 driver + * + * Copyright (C) 2019 Collabora, Ltd. + * + * Based on: + * + * drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c + * in https://chromium.googlesource.com/chromiumos/third_party/kernel, + * chromeos-4.4 branch. + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * Jacob Chen <jacob2.chen@rock-chips.com> + * Shunqian Zheng <zhengsq@rock-chips.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define RK3399_GRF_SOC_CON9 0x6224 +#define RK3399_GRF_SOC_CON21 0x6254 +#define RK3399_GRF_SOC_CON22 0x6258 +#define RK3399_GRF_SOC_CON23 0x625c +#define RK3399_GRF_SOC_CON24 0x6260 +#define RK3399_GRF_SOC_CON25 0x6264 +#define RK3399_GRF_SOC_STATUS1 0xe2a4 + +#define CLOCK_LANE_HS_RX_CONTROL 0x34 +#define LANE0_HS_RX_CONTROL 0x44 +#define LANE1_HS_RX_CONTROL 0x54 +#define LANE2_HS_RX_CONTROL 0x84 +#define LANE3_HS_RX_CONTROL 0x94 +#define LANES_THS_SETTLE_CONTROL 0x75 +#define THS_SETTLE_COUNTER_THRESHOLD 0x04 + +struct hsfreq_range { + u16 range_h; + u8 cfg_bit; +}; + +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = { + { 89, 0x00 }, { 99, 0x10 }, { 109, 0x20 }, { 129, 0x01 }, + { 139, 0x11 }, { 149, 0x21 }, { 169, 0x02 }, { 179, 0x12 }, + { 199, 0x22 }, { 219, 0x03 }, { 239, 0x13 }, { 249, 0x23 }, + { 269, 0x04 }, { 299, 0x14 }, { 329, 0x05 }, { 359, 0x15 }, + { 399, 0x25 }, { 449, 0x06 }, { 499, 0x16 }, { 549, 0x07 }, + { 599, 0x17 }, { 649, 0x08 }, { 699, 0x18 }, { 749, 0x09 }, + { 799, 0x19 }, { 849, 0x29 }, { 899, 0x39 }, { 949, 0x0a }, + { 999, 0x1a }, { 1049, 0x2a }, { 1099, 0x3a }, { 1149, 0x0b }, + { 1199, 0x1b }, { 1249, 0x2b }, { 1299, 0x3b }, { 1349, 0x0c }, + { 1399, 0x1c }, { 1449, 0x2c }, { 1500, 0x3c } +}; + +static const char * const rk3399_mipidphy_clks[] = { + "dphy-ref", + "dphy-cfg", + "grf", +}; + +enum dphy_reg_id { + GRF_DPHY_RX0_TURNDISABLE = 0, + GRF_DPHY_RX0_FORCERXMODE, + GRF_DPHY_RX0_FORCETXSTOPMODE, + GRF_DPHY_RX0_ENABLE, + GRF_DPHY_RX0_TESTCLR, + GRF_DPHY_RX0_TESTCLK, + GRF_DPHY_RX0_TESTEN, + GRF_DPHY_RX0_TESTDIN, + GRF_DPHY_RX0_TURNREQUEST, + GRF_DPHY_RX0_TESTDOUT, + GRF_DPHY_TX0_TURNDISABLE, + GRF_DPHY_TX0_FORCERXMODE, + GRF_DPHY_TX0_FORCETXSTOPMODE, + GRF_DPHY_TX0_TURNREQUEST, + GRF_DPHY_TX1RX1_TURNDISABLE, + GRF_DPHY_TX1RX1_FORCERXMODE, + GRF_DPHY_TX1RX1_FORCETXSTOPMODE, + GRF_DPHY_TX1RX1_ENABLE, + GRF_DPHY_TX1RX1_MASTERSLAVEZ, + GRF_DPHY_TX1RX1_BASEDIR, + GRF_DPHY_TX1RX1_ENABLECLK, + GRF_DPHY_TX1RX1_TURNREQUEST, + GRF_DPHY_RX1_SRC_SEL, + /* rk3288 only */ + GRF_CON_DISABLE_ISP, + GRF_CON_ISP_DPHY_SEL, + GRF_DSI_CSI_TESTBUS_SEL, + GRF_DVP_V18SEL, + /* below is for rk3399 only */ + GRF_DPHY_RX0_CLK_INV_SEL, + GRF_DPHY_RX1_CLK_INV_SEL, +}; + +struct dphy_reg { + u16 offset; + u8 mask; + u8 shift; +}; + +#define PHY_REG(_offset, _width, _shift) \ + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, } + +static const struct dphy_reg rk3399_grf_dphy_regs[] = { + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0), + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10), + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11), + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0), + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4), + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8), + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12), + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0), + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4), + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8), + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12), + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0), + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4), + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8), + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12), + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0), + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4), + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5), + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6), + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7), + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0), + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8), + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9), + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10), + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0), +}; + +struct rk_dphy_drv_data { + const char * const *clks; + unsigned int num_clks; + const struct hsfreq_range *hsfreq_ranges; + unsigned int num_hsfreq_ranges; + const struct dphy_reg *regs; +}; + +struct rk_dphy { + struct device *dev; + struct regmap *grf; + struct clk_bulk_data *clks; + + const struct rk_dphy_drv_data *drv_data; + struct phy_configure_opts_mipi_dphy config; + + u8 hsfreq; +}; + +static inline void rk_dphy_write_grf(struct rk_dphy *priv, + unsigned int index, u8 value) +{ + const struct dphy_reg *reg = &priv->drv_data->regs[index]; + /* Update high word */ + unsigned int val = (value << reg->shift) | + (reg->mask << (reg->shift + 16)); + + if (WARN_ON(!reg->offset)) + return; + regmap_write(priv->grf, reg->offset, val); +} + +static void rk_dphy_write(struct rk_dphy *priv, u8 test_code, u8 test_data) +{ + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_code); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 1); + /* + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content + * is latched internally as the current test code. Test data is + * programmed internally by rising edge on TESTCLK. + * This code assumes that TESTCLK is already 1. + */ + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 0); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 0); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_data); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1); +} + +static void rk_dphy_enable(struct rk_dphy *priv) +{ + rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCERXMODE, 0); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0); + + /* Disable lane turn around, which is ignored in receive mode */ + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNREQUEST, 0); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf); + + rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE, + GENMASK(priv->config.lanes - 1, 0)); + + /* dphy start */ + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 1); + usleep_range(100, 150); + rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 0); + usleep_range(100, 150); + + /* set clock lane */ + /* HS hsfreq_range & lane 0 settle bypass */ + rk_dphy_write(priv, CLOCK_LANE_HS_RX_CONTROL, 0); + /* HS RX Control of lane0 */ + rk_dphy_write(priv, LANE0_HS_RX_CONTROL, priv->hsfreq << 1); + /* HS RX Control of lane1 */ + rk_dphy_write(priv, LANE1_HS_RX_CONTROL, priv->hsfreq << 1); + /* HS RX Control of lane2 */ + rk_dphy_write(priv, LANE2_HS_RX_CONTROL, priv->hsfreq << 1); + /* HS RX Control of lane3 */ + rk_dphy_write(priv, LANE3_HS_RX_CONTROL, priv->hsfreq << 1); + /* HS RX Data Lanes Settle State Time Control */ + rk_dphy_write(priv, LANES_THS_SETTLE_CONTROL, + THS_SETTLE_COUNTER_THRESHOLD); + + /* Normal operation */ + rk_dphy_write(priv, 0x0, 0); +} + +static int rk_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct rk_dphy *priv = phy_get_drvdata(phy); + const struct rk_dphy_drv_data *drv_data = priv->drv_data; + struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy; + unsigned int hsfreq = 0; + unsigned int i; + u64 data_rate_mbps; + int ret; + + /* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */ + ret = phy_mipi_dphy_config_validate(config); + if (ret) + return ret; + + data_rate_mbps = div_u64(config->hs_clk_rate, 1000 * 1000); + + dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n", + config->lanes, data_rate_mbps); + for (i = 0; i < drv_data->num_hsfreq_ranges; i++) { + if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) { + hsfreq = drv_data->hsfreq_ranges[i].cfg_bit; + break; + } + } + if (!hsfreq) + return -EINVAL; + + priv->hsfreq = hsfreq; + priv->config = *config; + return 0; +} + +static int rk_dphy_power_on(struct phy *phy) +{ + struct rk_dphy *priv = phy_get_drvdata(phy); + int ret; + + ret = clk_bulk_enable(priv->drv_data->num_clks, priv->clks); + if (ret) + return ret; + + rk_dphy_enable(priv); + + return 0; +} + +static int rk_dphy_power_off(struct phy *phy) +{ + struct rk_dphy *priv = phy_get_drvdata(phy); + + rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE, 0); + clk_bulk_disable(priv->drv_data->num_clks, priv->clks); + return 0; +} + +static int rk_dphy_init(struct phy *phy) +{ + struct rk_dphy *priv = phy_get_drvdata(phy); + + return clk_bulk_prepare(priv->drv_data->num_clks, priv->clks); +} + +static int rk_dphy_exit(struct phy *phy) +{ + struct rk_dphy *priv = phy_get_drvdata(phy); + + clk_bulk_unprepare(priv->drv_data->num_clks, priv->clks); + return 0; +} + +static const struct phy_ops rk_dphy_ops = { + .power_on = rk_dphy_power_on, + .power_off = rk_dphy_power_off, + .init = rk_dphy_init, + .exit = rk_dphy_exit, + .configure = rk_dphy_configure, + .owner = THIS_MODULE, +}; + +static const struct rk_dphy_drv_data rk3399_mipidphy_drv_data = { + .clks = rk3399_mipidphy_clks, + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks), + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges), + .regs = rk3399_grf_dphy_regs, +}; + +static const struct of_device_id rk_dphy_dt_ids[] = { + { + .compatible = "rockchip,rk3399-mipi-dphy-rx0", + .data = &rk3399_mipidphy_drv_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, rk_dphy_dt_ids); + +static int rk_dphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct rk_dphy_drv_data *drv_data; + struct phy_provider *phy_provider; + struct rk_dphy *priv; + struct phy *phy; + unsigned int i; + int ret; + + if (!dev->parent || !dev->parent->of_node) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = dev; + + priv->grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->grf)) { + dev_err(dev, "Can't find GRF syscon\n"); + return -ENODEV; + } + + drv_data = of_device_get_match_data(dev); + priv->drv_data = drv_data; + priv->clks = devm_kcalloc(&pdev->dev, drv_data->num_clks, + sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + for (i = 0; i < drv_data->num_clks; i++) + priv->clks[i].id = drv_data->clks[i]; + ret = devm_clk_bulk_get(&pdev->dev, drv_data->num_clks, priv->clks); + if (ret) + return ret; + + phy = devm_phy_create(dev, np, &rk_dphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver rk_dphy_driver = { + .probe = rk_dphy_probe, + .driver = { + .name = "rockchip-mipi-dphy-rx0", + .of_match_table = rk_dphy_dt_ids, + }, +}; +module_platform_driver(rk_dphy_driver); + +MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>"); +MODULE_DESCRIPTION("Rockchip MIPI Synopsys DPHY RX0 driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c index 2dc19ddd120f..20023f6eb994 100644 --- a/drivers/phy/rockchip/phy-rockchip-emmc.c +++ b/drivers/phy/rockchip/phy-rockchip-emmc.c @@ -65,8 +65,14 @@ #define PHYCTRL_OTAPDLYENA 0x1 #define PHYCTRL_OTAPDLYENA_MASK 0x1 #define PHYCTRL_OTAPDLYENA_SHIFT 0xb +#define PHYCTRL_OTAPDLYSEL_DEFAULT 0x4 +#define PHYCTRL_OTAPDLYSEL_MAXVALUE 0xf #define PHYCTRL_OTAPDLYSEL_MASK 0xf #define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 +#define PHYCTRL_REN_STRB_DISABLE 0x0 +#define PHYCTRL_REN_STRB_ENABLE 0x1 +#define PHYCTRL_REN_STRB_MASK 0x1 +#define PHYCTRL_REN_STRB_SHIFT 0x9 #define PHYCTRL_IS_CALDONE(x) \ ((((x) >> PHYCTRL_CALDONE_SHIFT) & \ @@ -80,6 +86,8 @@ struct rockchip_emmc_phy { struct regmap *reg_base; struct clk *emmcclk; unsigned int drive_impedance; + unsigned int enable_strobe_pulldown; + unsigned int output_tapdelay_select; }; static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) @@ -240,15 +248,17 @@ static int rockchip_emmc_phy_init(struct phy *phy) * - SDHCI driver to get the PHY * - SDHCI driver to init the PHY * - * The clock is optional, so upon any error we just set to NULL. + * The clock is optional, using clk_get_optional() to get the clock + * and do error processing if the return value != NULL * * NOTE: we don't do anything special for EPROBE_DEFER here. Given the * above expected use case, EPROBE_DEFER isn't sensible to expect, so * it's just like any other error. */ - rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + rk_phy->emmcclk = clk_get_optional(&phy->dev, "emmcclk"); if (IS_ERR(rk_phy->emmcclk)) { - dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); + ret = PTR_ERR(rk_phy->emmcclk); + dev_err(&phy->dev, "Error getting emmcclk: %d\n", ret); rk_phy->emmcclk = NULL; } @@ -291,10 +301,17 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) /* Output tap delay */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(4, + HIWORD_UPDATE(rk_phy->output_tapdelay_select, PHYCTRL_OTAPDLYSEL_MASK, PHYCTRL_OTAPDLYSEL_SHIFT)); + /* Internal pull-down for strobe line */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON2, + HIWORD_UPDATE(rk_phy->enable_strobe_pulldown, + PHYCTRL_REN_STRB_MASK, + PHYCTRL_REN_STRB_SHIFT)); + /* Power up emmc phy analog blocks */ return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); } @@ -359,10 +376,22 @@ static int rockchip_emmc_phy_probe(struct platform_device *pdev) rk_phy->reg_offset = reg_offset; rk_phy->reg_base = grf; rk_phy->drive_impedance = PHYCTRL_DR_50OHM; + rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_DISABLE; + rk_phy->output_tapdelay_select = PHYCTRL_OTAPDLYSEL_DEFAULT; if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val)) rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val); + if (of_property_read_bool(dev->of_node, "rockchip,enable-strobe-pulldown")) + rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_ENABLE; + + if (!of_property_read_u32(dev->of_node, "rockchip,output-tapdelay-select", &val)) { + if (val <= PHYCTRL_OTAPDLYSEL_MAXVALUE) + rk_phy->output_tapdelay_select = val; + else + dev_err(dev, "output-tapdelay-select exceeds limit, apply default\n"); + } + generic_phy = devm_phy_create(dev, dev->of_node, &ops); if (IS_ERR(generic_phy)) { dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c new file mode 100644 index 000000000000..75f948bdea6a --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip MIPI RX Innosilicon DPHY driver + * + * Copyright (C) 2021 Fuzhou Rockchip Electronics Co., Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +/* GRF */ +#define RK1808_GRF_PD_VI_CON_OFFSET 0x0430 + +#define RK3326_GRF_PD_VI_CON_OFFSET 0x0430 + +#define RK3368_GRF_SOC_CON6_OFFSET 0x0418 + +#define RK3568_GRF_VI_CON0 0x0340 +#define RK3568_GRF_VI_CON1 0x0344 + +/* PHY */ +#define CSIDPHY_CTRL_LANE_ENABLE 0x00 +#define CSIDPHY_CTRL_LANE_ENABLE_CK BIT(6) +#define CSIDPHY_CTRL_LANE_ENABLE_MASK GENMASK(5, 2) +#define CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED BIT(0) + +/* not present on all variants */ +#define CSIDPHY_CTRL_PWRCTL 0x04 +#define CSIDPHY_CTRL_PWRCTL_UNDEFINED GENMASK(7, 5) +#define CSIDPHY_CTRL_PWRCTL_SYNCRST BIT(2) +#define CSIDPHY_CTRL_PWRCTL_LDO_PD BIT(1) +#define CSIDPHY_CTRL_PWRCTL_PLL_PD BIT(0) + +#define CSIDPHY_CTRL_DIG_RST 0x80 +#define CSIDPHY_CTRL_DIG_RST_UNDEFINED 0x1e +#define CSIDPHY_CTRL_DIG_RST_RESET BIT(0) + +/* offset after ths_settle_offset */ +#define CSIDPHY_CLK_THS_SETTLE 0 +#define CSIDPHY_LANE_THS_SETTLE(n) (((n) + 1) * 0x80) +#define CSIDPHY_THS_SETTLE_MASK GENMASK(6, 0) + +/* offset after calib_offset */ +#define CSIDPHY_CLK_CALIB_EN 0 +#define CSIDPHY_LANE_CALIB_EN(n) (((n) + 1) * 0x80) +#define CSIDPHY_CALIB_EN BIT(7) + +/* Configure the count time of the THS-SETTLE by protocol. */ +#define RK1808_CSIDPHY_CLK_WR_THS_SETTLE 0x160 +#define RK3326_CSIDPHY_CLK_WR_THS_SETTLE 0x100 +#define RK3368_CSIDPHY_CLK_WR_THS_SETTLE 0x100 +#define RK3568_CSIDPHY_CLK_WR_THS_SETTLE 0x160 + +/* Calibration reception enable */ +#define RK1808_CSIDPHY_CLK_CALIB_EN 0x168 +#define RK3568_CSIDPHY_CLK_CALIB_EN 0x168 + +/* + * The higher 16-bit of this register is used for write protection + * only if BIT(x + 16) set to 1 the BIT(x) can be written. + */ +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define HZ_TO_MHZ(freq) div_u64(freq, 1000 * 1000) + +enum dphy_reg_id { + /* rk1808 & rk3326 */ + GRF_DPHY_CSIPHY_FORCERXMODE, + GRF_DPHY_CSIPHY_CLKLANE_EN, + GRF_DPHY_CSIPHY_DATALANE_EN, +}; + +struct dphy_reg { + u32 offset; + u32 mask; + u32 shift; +}; + +#define PHY_REG(_offset, _width, _shift) \ + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, } + +static const struct dphy_reg rk1808_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 0), + [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 1, 8), + [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 4), +}; + +static const struct dphy_reg rk3326_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 0), + [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 1, 8), + [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 4), +}; + +static const struct dphy_reg rk3368_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3368_GRF_SOC_CON6_OFFSET, 4, 8), +}; + +static const struct dphy_reg rk3568_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3568_GRF_VI_CON0, 4, 0), + [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 4, 4), + [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 1, 8), +}; + +struct hsfreq_range { + u32 range_h; + u8 cfg_bit; +}; + +struct dphy_drv_data { + int pwrctl_offset; + int ths_settle_offset; + int calib_offset; + const struct hsfreq_range *hsfreq_ranges; + int num_hsfreq_ranges; + const struct dphy_reg *grf_regs; +}; + +struct rockchip_inno_csidphy { + struct device *dev; + void __iomem *phy_base; + struct clk *pclk; + struct regmap *grf; + struct reset_control *rst; + const struct dphy_drv_data *drv_data; + struct phy_configure_opts_mipi_dphy config; + u8 hsfreq; +}; + +static inline void write_grf_reg(struct rockchip_inno_csidphy *priv, + int index, u8 value) +{ + const struct dphy_drv_data *drv_data = priv->drv_data; + const struct dphy_reg *reg = &drv_data->grf_regs[index]; + + if (reg->offset) + regmap_write(priv->grf, reg->offset, + HIWORD_UPDATE(value, reg->mask, reg->shift)); +} + +/* These tables must be sorted by .range_h ascending. */ +static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = { + { 109, 0x02}, { 149, 0x03}, { 199, 0x06}, { 249, 0x06}, + { 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e}, + { 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e}, + {1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37}, + {2199, 0x3c}, {2399, 0x41}, {2499, 0x46} +}; + +static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = { + { 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03}, + { 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07}, + { 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b}, + {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e} +}; + +static const struct hsfreq_range rk3368_mipidphy_hsfreq_ranges[] = { + { 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03}, + { 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07}, + { 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b}, + {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e} +}; + +static void rockchip_inno_csidphy_ths_settle(struct rockchip_inno_csidphy *priv, + int hsfreq, int offset) +{ + const struct dphy_drv_data *drv_data = priv->drv_data; + u32 val; + + val = readl(priv->phy_base + drv_data->ths_settle_offset + offset); + val &= ~CSIDPHY_THS_SETTLE_MASK; + val |= hsfreq; + writel(val, priv->phy_base + drv_data->ths_settle_offset + offset); +} + +static int rockchip_inno_csidphy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + const struct dphy_drv_data *drv_data = priv->drv_data; + struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy; + unsigned int hsfreq = 0; + unsigned int i; + u64 data_rate_mbps; + int ret; + + /* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */ + ret = phy_mipi_dphy_config_validate(config); + if (ret) + return ret; + + data_rate_mbps = HZ_TO_MHZ(config->hs_clk_rate); + + dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n", + config->lanes, data_rate_mbps); + for (i = 0; i < drv_data->num_hsfreq_ranges; i++) { + if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) { + hsfreq = drv_data->hsfreq_ranges[i].cfg_bit; + break; + } + } + if (!hsfreq) + return -EINVAL; + + priv->hsfreq = hsfreq; + priv->config = *config; + return 0; +} + +static int rockchip_inno_csidphy_power_on(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + const struct dphy_drv_data *drv_data = priv->drv_data; + u64 data_rate_mbps = HZ_TO_MHZ(priv->config.hs_clk_rate); + u32 val; + int ret, i; + + ret = clk_enable(priv->pclk); + if (ret < 0) + return ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret < 0) { + clk_disable(priv->pclk); + return ret; + } + + /* phy start */ + if (drv_data->pwrctl_offset >= 0) + writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED | + CSIDPHY_CTRL_PWRCTL_SYNCRST, + priv->phy_base + drv_data->pwrctl_offset); + + /* set data lane num and enable clock lane */ + val = FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_MASK, GENMASK(priv->config.lanes - 1, 0)) | + FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_CK, 1) | + FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED, 1); + writel(val, priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE); + + /* Reset dphy analog part */ + if (drv_data->pwrctl_offset >= 0) + writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED, + priv->phy_base + drv_data->pwrctl_offset); + usleep_range(500, 1000); + + /* Reset dphy digital part */ + writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED, + priv->phy_base + CSIDPHY_CTRL_DIG_RST); + writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED + CSIDPHY_CTRL_DIG_RST_RESET, + priv->phy_base + CSIDPHY_CTRL_DIG_RST); + + /* not into receive mode/wait stopstate */ + write_grf_reg(priv, GRF_DPHY_CSIPHY_FORCERXMODE, 0x0); + + /* enable calibration */ + if (data_rate_mbps > 1500 && drv_data->calib_offset >= 0) { + writel(CSIDPHY_CALIB_EN, + priv->phy_base + drv_data->calib_offset + + CSIDPHY_CLK_CALIB_EN); + for (i = 0; i < priv->config.lanes; i++) + writel(CSIDPHY_CALIB_EN, + priv->phy_base + drv_data->calib_offset + + CSIDPHY_LANE_CALIB_EN(i)); + } + + rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq, + CSIDPHY_CLK_THS_SETTLE); + for (i = 0; i < priv->config.lanes; i++) + rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq, + CSIDPHY_LANE_THS_SETTLE(i)); + + write_grf_reg(priv, GRF_DPHY_CSIPHY_CLKLANE_EN, 0x1); + write_grf_reg(priv, GRF_DPHY_CSIPHY_DATALANE_EN, + GENMASK(priv->config.lanes - 1, 0)); + + return 0; +} + +static int rockchip_inno_csidphy_power_off(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + const struct dphy_drv_data *drv_data = priv->drv_data; + + /* disable all lanes */ + writel(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED, + priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE); + + /* disable pll and ldo */ + if (drv_data->pwrctl_offset >= 0) + writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED | + CSIDPHY_CTRL_PWRCTL_LDO_PD | + CSIDPHY_CTRL_PWRCTL_PLL_PD, + priv->phy_base + drv_data->pwrctl_offset); + usleep_range(500, 1000); + + pm_runtime_put(priv->dev); + clk_disable(priv->pclk); + + return 0; +} + +static int rockchip_inno_csidphy_init(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + + return clk_prepare(priv->pclk); +} + +static int rockchip_inno_csidphy_exit(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + + clk_unprepare(priv->pclk); + + return 0; +} + +static const struct phy_ops rockchip_inno_csidphy_ops = { + .power_on = rockchip_inno_csidphy_power_on, + .power_off = rockchip_inno_csidphy_power_off, + .init = rockchip_inno_csidphy_init, + .exit = rockchip_inno_csidphy_exit, + .configure = rockchip_inno_csidphy_configure, + .owner = THIS_MODULE, +}; + +static const struct dphy_drv_data rk1808_mipidphy_drv_data = { + .pwrctl_offset = -1, + .ths_settle_offset = RK1808_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = RK1808_CSIDPHY_CLK_CALIB_EN, + .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges), + .grf_regs = rk1808_grf_dphy_regs, +}; + +static const struct dphy_drv_data rk3326_mipidphy_drv_data = { + .pwrctl_offset = CSIDPHY_CTRL_PWRCTL, + .ths_settle_offset = RK3326_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = -1, + .hsfreq_ranges = rk3326_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3326_mipidphy_hsfreq_ranges), + .grf_regs = rk3326_grf_dphy_regs, +}; + +static const struct dphy_drv_data rk3368_mipidphy_drv_data = { + .pwrctl_offset = CSIDPHY_CTRL_PWRCTL, + .ths_settle_offset = RK3368_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = -1, + .hsfreq_ranges = rk3368_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3368_mipidphy_hsfreq_ranges), + .grf_regs = rk3368_grf_dphy_regs, +}; + +static const struct dphy_drv_data rk3568_mipidphy_drv_data = { + .pwrctl_offset = -1, + .ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN, + .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges), + .grf_regs = rk3568_grf_dphy_regs, +}; + +static const struct of_device_id rockchip_inno_csidphy_match_id[] = { + { + .compatible = "rockchip,px30-csi-dphy", + .data = &rk3326_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk1808-csi-dphy", + .data = &rk1808_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk3326-csi-dphy", + .data = &rk3326_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk3368-csi-dphy", + .data = &rk3368_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk3568-csi-dphy", + .data = &rk3568_mipidphy_drv_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, rockchip_inno_csidphy_match_id); + +static int rockchip_inno_csidphy_probe(struct platform_device *pdev) +{ + struct rockchip_inno_csidphy *priv; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct phy *phy; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + platform_set_drvdata(pdev, priv); + + priv->drv_data = of_device_get_match_data(dev); + if (!priv->drv_data) { + dev_err(dev, "Can't find device data\n"); + return -ENODEV; + } + + priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(priv->grf)) { + dev_err(dev, "Can't find GRF syscon\n"); + return PTR_ERR(priv->grf); + } + + priv->phy_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->phy_base)) + return PTR_ERR(priv->phy_base); + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + dev_err(dev, "failed to get pclk\n"); + return PTR_ERR(priv->pclk); + } + + priv->rst = devm_reset_control_get(dev, "apb"); + if (IS_ERR(priv->rst)) { + dev_err(dev, "failed to get system reset control\n"); + return PTR_ERR(priv->rst); + } + + phy = devm_phy_create(dev, NULL, &rockchip_inno_csidphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + pm_runtime_enable(dev); + + return 0; +} + +static int rockchip_inno_csidphy_remove(struct platform_device *pdev) +{ + struct rockchip_inno_csidphy *priv = platform_get_drvdata(pdev); + + pm_runtime_disable(priv->dev); + + return 0; +} + +static struct platform_driver rockchip_inno_csidphy_driver = { + .driver = { + .name = "rockchip-inno-csidphy", + .of_match_table = rockchip_inno_csidphy_match_id, + }, + .probe = rockchip_inno_csidphy_probe, + .remove = rockchip_inno_csidphy_remove, +}; + +module_platform_driver(rockchip_inno_csidphy_driver); +MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); +MODULE_DESCRIPTION("Rockchip MIPI Innosilicon CSI-DPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c index a7c6c940a3a8..2c5847faff63 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -5,22 +5,23 @@ * Author: Wyon Bi <bivvy.bi@rock-chips.com> */ +#include <linux/bits.h> #include <linux/kernel.h> #include <linux/clk.h> #include <linux/iopoll.h> #include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> +#include <linux/time64.h> + #include <linux/phy/phy.h> #include <linux/phy/phy-mipi-dphy.h> -#include <linux/pm_runtime.h> -#include <linux/mfd/syscon.h> - -#define PSEC_PER_SEC 1000000000000LL #define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) @@ -83,9 +84,25 @@ #define DATA_LANE_0_SKEW_PHASE_MASK GENMASK(2, 0) #define DATA_LANE_0_SKEW_PHASE(x) UPDATE(x, 2, 0) /* Analog Register Part: reg08 */ +#define PLL_POST_DIV_ENABLE_MASK BIT(5) +#define PLL_POST_DIV_ENABLE BIT(5) #define SAMPLE_CLOCK_DIRECTION_MASK BIT(4) #define SAMPLE_CLOCK_DIRECTION_REVERSE BIT(4) #define SAMPLE_CLOCK_DIRECTION_FORWARD 0 +#define LOWFRE_EN_MASK BIT(5) +#define PLL_OUTPUT_FREQUENCY_DIV_BY_1 0 +#define PLL_OUTPUT_FREQUENCY_DIV_BY_2 1 +/* Analog Register Part: reg0b */ +#define CLOCK_LANE_VOD_RANGE_SET_MASK GENMASK(3, 0) +#define CLOCK_LANE_VOD_RANGE_SET(x) UPDATE(x, 3, 0) +#define VOD_MIN_RANGE 0x1 +#define VOD_MID_RANGE 0x3 +#define VOD_BIG_RANGE 0x7 +#define VOD_MAX_RANGE 0xf +/* Analog Register Part: reg1E */ +#define PLL_MODE_SEL_MASK GENMASK(6, 5) +#define PLL_MODE_SEL_LVDS_MODE 0 +#define PLL_MODE_SEL_MIPI_MODE BIT(5) /* Digital Register Part: reg00 */ #define REG_DIG_RSTN_MASK BIT(0) #define REG_DIG_RSTN_NORMAL BIT(0) @@ -101,20 +118,22 @@ #define T_LPX_CNT_MASK GENMASK(5, 0) #define T_LPX_CNT(x) UPDATE(x, 5, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg06 */ +#define T_HS_ZERO_CNT_HI_MASK BIT(7) +#define T_HS_ZERO_CNT_HI(x) UPDATE(x, 7, 7) #define T_HS_PREPARE_CNT_MASK GENMASK(6, 0) #define T_HS_PREPARE_CNT(x) UPDATE(x, 6, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg07 */ -#define T_HS_ZERO_CNT_MASK GENMASK(5, 0) -#define T_HS_ZERO_CNT(x) UPDATE(x, 5, 0) +#define T_HS_ZERO_CNT_LO_MASK GENMASK(5, 0) +#define T_HS_ZERO_CNT_LO(x) UPDATE(x, 5, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg08 */ #define T_HS_TRAIL_CNT_MASK GENMASK(6, 0) #define T_HS_TRAIL_CNT(x) UPDATE(x, 6, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg09 */ -#define T_HS_EXIT_CNT_MASK GENMASK(4, 0) -#define T_HS_EXIT_CNT(x) UPDATE(x, 4, 0) +#define T_HS_EXIT_CNT_LO_MASK GENMASK(4, 0) +#define T_HS_EXIT_CNT_LO(x) UPDATE(x, 4, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0a */ -#define T_CLK_POST_CNT_MASK GENMASK(3, 0) -#define T_CLK_POST_CNT(x) UPDATE(x, 3, 0) +#define T_CLK_POST_CNT_LO_MASK GENMASK(3, 0) +#define T_CLK_POST_CNT_LO(x) UPDATE(x, 3, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0c */ #define LPDT_TX_PPI_SYNC_MASK BIT(2) #define LPDT_TX_PPI_SYNC_ENABLE BIT(2) @@ -128,9 +147,13 @@ #define T_CLK_PRE_CNT_MASK GENMASK(3, 0) #define T_CLK_PRE_CNT(x) UPDATE(x, 3, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg10 */ +#define T_CLK_POST_CNT_HI_MASK GENMASK(7, 6) +#define T_CLK_POST_CNT_HI(x) UPDATE(x, 7, 6) #define T_TA_GO_CNT_MASK GENMASK(5, 0) #define T_TA_GO_CNT(x) UPDATE(x, 5, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg11 */ +#define T_HS_EXIT_CNT_HI_MASK BIT(6) +#define T_HS_EXIT_CNT_HI(x) UPDATE(x, 6, 6) #define T_TA_SURE_CNT_MASK GENMASK(5, 0) #define T_TA_SURE_CNT(x) UPDATE(x, 5, 0) /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg12 */ @@ -168,11 +191,23 @@ #define DSI_PHY_STATUS 0xb0 #define PHY_LOCK BIT(0) +enum phy_max_rate { + MAX_1GHZ, + MAX_2_5GHZ, +}; + +struct inno_video_phy_plat_data { + const struct inno_mipi_dphy_timing *inno_mipi_dphy_timing_table; + const unsigned int num_timings; + enum phy_max_rate max_rate; +}; + struct inno_dsidphy { struct device *dev; struct clk *ref_clk; struct clk *pclk_phy; struct clk *pclk_host; + const struct inno_video_phy_plat_data *pdata; void __iomem *phy_base; void __iomem *host_base; struct reset_control *rst; @@ -199,6 +234,53 @@ enum { REGISTER_PART_LVDS, }; +struct inno_mipi_dphy_timing { + unsigned long rate; + u8 lpx; + u8 hs_prepare; + u8 clk_lane_hs_zero; + u8 data_lane_hs_zero; + u8 hs_trail; +}; + +static const +struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_1ghz[] = { + { 110000000, 0x0, 0x20, 0x16, 0x02, 0x22}, + { 150000000, 0x0, 0x06, 0x16, 0x03, 0x45}, + { 200000000, 0x0, 0x18, 0x17, 0x04, 0x0b}, + { 250000000, 0x0, 0x05, 0x17, 0x05, 0x16}, + { 300000000, 0x0, 0x51, 0x18, 0x06, 0x2c}, + { 400000000, 0x0, 0x64, 0x19, 0x07, 0x33}, + { 500000000, 0x0, 0x20, 0x1b, 0x07, 0x4e}, + { 600000000, 0x0, 0x6a, 0x1d, 0x08, 0x3a}, + { 700000000, 0x0, 0x3e, 0x1e, 0x08, 0x6a}, + { 800000000, 0x0, 0x21, 0x1f, 0x09, 0x29}, + {1000000000, 0x0, 0x09, 0x20, 0x09, 0x27}, +}; + +static const +struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_2_5ghz[] = { + { 110000000, 0x02, 0x7f, 0x16, 0x02, 0x02}, + { 150000000, 0x02, 0x7f, 0x16, 0x03, 0x02}, + { 200000000, 0x02, 0x7f, 0x17, 0x04, 0x02}, + { 250000000, 0x02, 0x7f, 0x17, 0x05, 0x04}, + { 300000000, 0x02, 0x7f, 0x18, 0x06, 0x04}, + { 400000000, 0x03, 0x7e, 0x19, 0x07, 0x04}, + { 500000000, 0x03, 0x7c, 0x1b, 0x07, 0x08}, + { 600000000, 0x03, 0x70, 0x1d, 0x08, 0x10}, + { 700000000, 0x05, 0x40, 0x1e, 0x08, 0x30}, + { 800000000, 0x05, 0x02, 0x1f, 0x09, 0x30}, + {1000000000, 0x05, 0x08, 0x20, 0x09, 0x30}, + {1200000000, 0x06, 0x03, 0x32, 0x14, 0x0f}, + {1400000000, 0x09, 0x03, 0x32, 0x14, 0x0f}, + {1600000000, 0x0d, 0x42, 0x36, 0x0e, 0x0f}, + {1800000000, 0x0e, 0x47, 0x7a, 0x0e, 0x0f}, + {2000000000, 0x11, 0x64, 0x7a, 0x0e, 0x0b}, + {2200000000, 0x13, 0x64, 0x7e, 0x15, 0x0b}, + {2400000000, 0x13, 0x33, 0x7f, 0x15, 0x6a}, + {2500000000, 0x15, 0x54, 0x7f, 0x15, 0x6a}, +}; + static inline struct inno_dsidphy *hw_to_inno(struct clk_hw *hw) { return container_of(hw, struct inno_dsidphy, pll.hw); @@ -289,31 +371,15 @@ static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno, static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) { struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg; - const struct { - unsigned long rate; - u8 hs_prepare; - u8 clk_lane_hs_zero; - u8 data_lane_hs_zero; - u8 hs_trail; - } timings[] = { - { 110000000, 0x20, 0x16, 0x02, 0x22}, - { 150000000, 0x06, 0x16, 0x03, 0x45}, - { 200000000, 0x18, 0x17, 0x04, 0x0b}, - { 250000000, 0x05, 0x17, 0x05, 0x16}, - { 300000000, 0x51, 0x18, 0x06, 0x2c}, - { 400000000, 0x64, 0x19, 0x07, 0x33}, - { 500000000, 0x20, 0x1b, 0x07, 0x4e}, - { 600000000, 0x6a, 0x1d, 0x08, 0x3a}, - { 700000000, 0x3e, 0x1e, 0x08, 0x6a}, - { 800000000, 0x21, 0x1f, 0x09, 0x29}, - {1000000000, 0x09, 0x20, 0x09, 0x27}, - }; + const struct inno_mipi_dphy_timing *timings; u32 t_txbyteclkhs, t_txclkesc; u32 txbyteclkhs, txclkesc, esc_clk_div; u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait; u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero; unsigned int i; + timings = inno->pdata->inno_mipi_dphy_timing_table; + inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate); /* Select MIPI mode */ @@ -326,6 +392,13 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) REG_FBDIV_HI_MASK, REG_FBDIV_HI(inno->pll.fbdiv)); phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04, REG_FBDIV_LO_MASK, REG_FBDIV_LO(inno->pll.fbdiv)); + if (inno->pdata->max_rate == MAX_2_5GHZ) { + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08, + PLL_POST_DIV_ENABLE_MASK, PLL_POST_DIV_ENABLE); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x0b, + CLOCK_LANE_VOD_RANGE_SET_MASK, + CLOCK_LANE_VOD_RANGE_SET(VOD_MAX_RANGE)); + } /* Enable PLL and LDO */ phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01, REG_LDOPD_MASK | REG_PLLPD_MASK, @@ -364,15 +437,7 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) * The value of counter for HS Tclk-pre * Tclk-pre = Tpin_txbyteclkhs * value */ - clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs); - - /* - * The value of counter for HS Tlpx Time - * Tlpx = Tpin_txbyteclkhs * (2 + value) - */ - lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs); - if (lpx >= 2) - lpx -= 2; + clk_pre = DIV_ROUND_UP(cfg->clk_pre, BITS_PER_BYTE); /* * The value of counter for HS Tta-go @@ -393,13 +458,24 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) */ ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc); - for (i = 0; i < ARRAY_SIZE(timings); i++) + for (i = 0; i < inno->pdata->num_timings; i++) if (inno->pll.rate <= timings[i].rate) break; - if (i == ARRAY_SIZE(timings)) + if (i == inno->pdata->num_timings) --i; + /* + * The value of counter for HS Tlpx Time + * Tlpx = Tpin_txbyteclkhs * (2 + value) + */ + if (inno->pdata->max_rate == MAX_1GHZ) { + lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs); + if (lpx >= 2) + lpx -= 2; + } else + lpx = timings[i].lpx; + hs_prepare = timings[i].hs_prepare; hs_trail = timings[i].hs_trail; clk_lane_hs_zero = timings[i].clk_lane_hs_zero; @@ -416,14 +492,23 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) T_LPX_CNT(lpx)); phy_update_bits(inno, i, 0x06, T_HS_PREPARE_CNT_MASK, T_HS_PREPARE_CNT(hs_prepare)); - phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_MASK, - T_HS_ZERO_CNT(hs_zero)); + if (inno->pdata->max_rate == MAX_2_5GHZ) + phy_update_bits(inno, i, 0x06, T_HS_ZERO_CNT_HI_MASK, + T_HS_ZERO_CNT_HI(hs_zero >> 6)); + phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_LO_MASK, + T_HS_ZERO_CNT_LO(hs_zero)); phy_update_bits(inno, i, 0x08, T_HS_TRAIL_CNT_MASK, T_HS_TRAIL_CNT(hs_trail)); - phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_MASK, - T_HS_EXIT_CNT(hs_exit)); - phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_MASK, - T_CLK_POST_CNT(clk_post)); + if (inno->pdata->max_rate == MAX_2_5GHZ) + phy_update_bits(inno, i, 0x11, T_HS_EXIT_CNT_HI_MASK, + T_HS_EXIT_CNT_HI(hs_exit >> 5)); + phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_LO_MASK, + T_HS_EXIT_CNT_LO(hs_exit)); + if (inno->pdata->max_rate == MAX_2_5GHZ) + phy_update_bits(inno, i, 0x10, T_CLK_POST_CNT_HI_MASK, + T_CLK_POST_CNT_HI(clk_post >> 4)); + phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_LO_MASK, + T_CLK_POST_CNT_LO(clk_post)); phy_update_bits(inno, i, 0x0e, T_CLK_PRE_CNT_MASK, T_CLK_PRE_CNT(clk_pre)); phy_update_bits(inno, i, 0x0c, T_WAKEUP_CNT_HI_MASK, @@ -451,8 +536,9 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno) /* Sample clock reverse direction */ phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08, - SAMPLE_CLOCK_DIRECTION_MASK, - SAMPLE_CLOCK_DIRECTION_REVERSE); + SAMPLE_CLOCK_DIRECTION_MASK | LOWFRE_EN_MASK, + SAMPLE_CLOCK_DIRECTION_REVERSE | + PLL_OUTPUT_FREQUENCY_DIV_BY_1); /* Select LVDS mode */ phy_update_bits(inno, REGISTER_PART_LVDS, 0x03, @@ -472,6 +558,10 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno) msleep(20); + /* Select PLL mode */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x1e, + PLL_MODE_SEL_MASK, PLL_MODE_SEL_LVDS_MODE); + /* Reset LVDS digital logic */ phy_update_bits(inno, REGISTER_PART_LVDS, 0x00, LVDS_DIGITAL_INTERNAL_RESET_MASK, @@ -591,6 +681,18 @@ static const struct phy_ops inno_dsidphy_ops = { .owner = THIS_MODULE, }; +static const struct inno_video_phy_plat_data max_1ghz_video_phy_plat_data = { + .inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_1ghz, + .num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_1ghz), + .max_rate = MAX_1GHZ, +}; + +static const struct inno_video_phy_plat_data max_2_5ghz_video_phy_plat_data = { + .inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_2_5ghz, + .num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_2_5ghz), + .max_rate = MAX_2_5GHZ, +}; + static int inno_dsidphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -604,11 +706,12 @@ static int inno_dsidphy_probe(struct platform_device *pdev) return -ENOMEM; inno->dev = dev; + inno->pdata = of_device_get_match_data(inno->dev); platform_set_drvdata(pdev, inno); inno->phy_base = devm_platform_ioremap_resource(pdev, 0); - if (!inno->phy_base) - return -ENOMEM; + if (IS_ERR(inno->phy_base)) + return PTR_ERR(inno->phy_base); inno->ref_clk = devm_clk_get(dev, "ref"); if (IS_ERR(inno->ref_clk)) { @@ -662,9 +765,19 @@ static int inno_dsidphy_remove(struct platform_device *pdev) } static const struct of_device_id inno_dsidphy_of_match[] = { - { .compatible = "rockchip,px30-dsi-dphy", }, - { .compatible = "rockchip,rk3128-dsi-dphy", }, - { .compatible = "rockchip,rk3368-dsi-dphy", }, + { + .compatible = "rockchip,px30-dsi-dphy", + .data = &max_1ghz_video_phy_plat_data, + }, { + .compatible = "rockchip,rk3128-dsi-dphy", + .data = &max_1ghz_video_phy_plat_data, + }, { + .compatible = "rockchip,rk3368-dsi-dphy", + .data = &max_1ghz_video_phy_plat_data, + }, { + .compatible = "rockchip,rk3568-dsi-dphy", + .data = &max_2_5ghz_video_phy_plat_data, + }, {} }; MODULE_DEVICE_TABLE(of, inno_dsidphy_of_match); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index 9ca20c947283..80acca4e9e14 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -620,7 +620,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw, unsigned long parent_rate) { struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); - const struct pre_pll_config *cfg = pre_pll_cfg_table; + const struct pre_pll_config *cfg; unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); u32 v; int ret; @@ -774,7 +774,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw, unsigned long parent_rate) { struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); - const struct pre_pll_config *cfg = pre_pll_cfg_table; + const struct pre_pll_config *cfg; unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); u32 val; int ret; @@ -1144,7 +1144,6 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev) { struct inno_hdmi_phy *inno; struct phy_provider *phy_provider; - struct resource *res; void __iomem *regs; int ret; @@ -1158,8 +1157,7 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev) if (!inno->plat_data || !inno->plat_data->ops) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(inno->dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 680cc0c8825c..e6ededc51523 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -116,11 +116,15 @@ struct rockchip_chg_det_reg { * @bvalid_det_en: vbus valid rise detection enable register. * @bvalid_det_st: vbus valid rise detection status register. * @bvalid_det_clr: vbus valid rise detection clear register. + * @id_det_en: id detection enable register. + * @id_det_st: id detection state register. + * @id_det_clr: id detection clear register. * @ls_det_en: linestate detection enable register. * @ls_det_st: linestate detection state register. * @ls_det_clr: linestate detection clear register. * @utmi_avalid: utmi vbus avalid status register. * @utmi_bvalid: utmi vbus bvalid status register. + * @utmi_id: utmi id state register. * @utmi_ls: utmi linestate state register. * @utmi_hstdet: utmi host disconnect register. */ @@ -129,11 +133,15 @@ struct rockchip_usb2phy_port_cfg { struct usb2phy_reg bvalid_det_en; struct usb2phy_reg bvalid_det_st; struct usb2phy_reg bvalid_det_clr; + struct usb2phy_reg id_det_en; + struct usb2phy_reg id_det_st; + struct usb2phy_reg id_det_clr; struct usb2phy_reg ls_det_en; struct usb2phy_reg ls_det_st; struct usb2phy_reg ls_det_clr; struct usb2phy_reg utmi_avalid; struct usb2phy_reg utmi_bvalid; + struct usb2phy_reg utmi_id; struct usb2phy_reg utmi_ls; struct usb2phy_reg utmi_hstdet; }; @@ -161,6 +169,7 @@ struct rockchip_usb2phy_cfg { * @suspended: phy suspended flag. * @vbus_attached: otg device vbus status. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. + * @id_irq: IRQ number assigned for ID pin detection. * @ls_irq: IRQ number assigned for linestate detection. * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate * irqs to one irq in otg-port. @@ -179,6 +188,7 @@ struct rockchip_usb2phy_port { bool suspended; bool vbus_attached; int bvalid_irq; + int id_irq; int ls_irq; int otg_mux_irq; struct mutex mutex; @@ -204,6 +214,7 @@ struct rockchip_usb2phy_port { * @dcd_retries: The retry count used to track Data contact * detection process. * @edev: extcon device for notification registration + * @irq: muxed interrupt for single irq configuration * @phy_cfg: phy register configuration, assigned by driver data. * @ports: phy port instance. */ @@ -218,6 +229,7 @@ struct rockchip_usb2phy { enum power_supply_type chg_type; u8 dcd_retries; struct extcon_dev *edev; + int irq; const struct rockchip_usb2phy_cfg *phy_cfg; struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; }; @@ -251,7 +263,7 @@ static inline bool property_enabled(struct regmap *base, return false; tmp = (orig & mask) >> reg->bitstart; - return tmp == reg->enable; + return tmp != reg->disable; } static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) @@ -321,7 +333,7 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) struct device_node *node = rphy->dev->of_node; struct clk_init_data init; const char *clk_name; - int ret; + int ret = 0; init.flags = 0; init.name = "clk_usbphy_480m"; @@ -352,15 +364,8 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) if (ret < 0) goto err_clk_provider; - ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, - rphy); - if (ret < 0) - goto err_unreg_action; - - return 0; + return devm_add_action_or_reset(rphy->dev, rockchip_usb2phy_clk480m_unregister, rphy); -err_unreg_action: - of_clk_del_provider(node); err_clk_provider: clk_unregister(rphy->clk480m); err_ret: @@ -424,6 +429,19 @@ static int rockchip_usb2phy_init(struct phy *phy) if (ret) goto out; + /* clear id status and enable id detect irq */ + ret = property_enable(rphy->grf, + &rport->port_cfg->id_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, + &rport->port_cfg->id_det_en, + true); + if (ret) + goto out; + schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY * 3); } else { @@ -546,7 +564,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) rport->state = OTG_STATE_B_IDLE; if (!vbus_attach) rockchip_usb2phy_power_off(rport->phy); - /* fall through */ + fallthrough; case OTG_STATE_B_IDLE: if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) { dev_dbg(&rport->phy->dev, "usb otg host connect\n"); @@ -754,16 +772,15 @@ static void rockchip_chg_detect_work(struct work_struct *work) rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; else rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; - /* fall through */ + fallthrough; case USB_CHG_STATE_SECONDARY_DONE: rphy->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - /* fall through */ + fallthrough; case USB_CHG_STATE_DETECTED: /* put the controller in normal mode */ property_enable(base, &rphy->phy_cfg->chg_det.opmode, true); rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); - dev_info(&rport->phy->dev, "charger = %s\n", + dev_dbg(&rport->phy->dev, "charger = %s\n", chg_to_string(rphy->chg_type)); return; default: @@ -835,7 +852,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work) dev_dbg(&rport->phy->dev, "FS/LS online\n"); break; } - /* fall through */ + fallthrough; case PHY_STATE_CONNECT: if (rport->suspended) { dev_dbg(&rport->phy->dev, "Connected\n"); @@ -911,27 +928,163 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) if (!property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st)) return IRQ_NONE; - mutex_lock(&rport->mutex); - /* clear bvalid detect irq pending status */ property_enable(rphy->grf, &rport->port_cfg->bvalid_det_clr, true); - mutex_unlock(&rport->mutex); - rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); return IRQ_HANDLED; } -static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) +static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) { struct rockchip_usb2phy_port *rport = data; struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + bool id; - if (property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st)) - return rockchip_usb2phy_bvalid_irq(irq, data); - else + if (!property_enabled(rphy->grf, &rport->port_cfg->id_det_st)) return IRQ_NONE; + + /* clear id detect irq pending status */ + property_enable(rphy->grf, &rport->port_cfg->id_det_clr, true); + + id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id); + extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + + ret |= rockchip_usb2phy_bvalid_irq(irq, data); + ret |= rockchip_usb2phy_id_irq(irq, data); + + return ret; +} + +static irqreturn_t rockchip_usb2phy_irq(int irq, void *data) +{ + struct rockchip_usb2phy *rphy = data; + struct rockchip_usb2phy_port *rport; + irqreturn_t ret = IRQ_NONE; + unsigned int index; + + for (index = 0; index < rphy->phy_cfg->num_ports; index++) { + rport = &rphy->ports[index]; + if (!rport->phy) + continue; + + switch (rport->port_id) { + case USB2PHY_PORT_OTG: + if (rport->mode != USB_DR_MODE_HOST && + rport->mode != USB_DR_MODE_UNKNOWN) + ret |= rockchip_usb2phy_otg_mux_irq(irq, rport); + break; + case USB2PHY_PORT_HOST: + ret |= rockchip_usb2phy_linestate_irq(irq, rport); + break; + } + } + + return ret; +} + +static int rockchip_usb2phy_port_irq_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) +{ + int ret; + + /* + * If the usb2 phy used combined irq for otg and host port, + * don't need to init otg and host port irq separately. + */ + if (rphy->irq > 0) + return 0; + + switch (rport->port_id) { + case USB2PHY_PORT_HOST: + rport->ls_irq = of_irq_get_byname(child_np, "linestate"); + if (rport->ls_irq < 0) { + dev_err(rphy->dev, "no linestate irq provided\n"); + return rport->ls_irq; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, + rockchip_usb2phy_linestate_irq, + IRQF_ONESHOT, + "rockchip_usb2phy", rport); + if (ret) { + dev_err(rphy->dev, "failed to request linestate irq handle\n"); + return ret; + } + break; + case USB2PHY_PORT_OTG: + /* + * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate + * interrupts muxed together, so probe the otg-mux interrupt first, + * if not found, then look for the regular interrupts one by one. + */ + rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux"); + if (rport->otg_mux_irq > 0) { + ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq, + NULL, + rockchip_usb2phy_otg_mux_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_otg", + rport); + if (ret) { + dev_err(rphy->dev, + "failed to request otg-mux irq handle\n"); + return ret; + } + } else { + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); + if (rport->bvalid_irq < 0) { + dev_err(rphy->dev, "no vbus valid irq provided\n"); + ret = rport->bvalid_irq; + return ret; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, + NULL, + rockchip_usb2phy_bvalid_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_bvalid", + rport); + if (ret) { + dev_err(rphy->dev, + "failed to request otg-bvalid irq handle\n"); + return ret; + } + + rport->id_irq = of_irq_get_byname(child_np, "otg-id"); + if (rport->id_irq < 0) { + dev_err(rphy->dev, "no otg-id irq provided\n"); + ret = rport->id_irq; + return ret; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->id_irq, + NULL, + rockchip_usb2phy_id_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_id", + rport); + if (ret) { + dev_err(rphy->dev, + "failed to request otg-id irq handle\n"); + return ret; + } + } + break; + default: + return -EINVAL; + } + + return 0; } static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, @@ -947,18 +1100,9 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, mutex_init(&rport->mutex); INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); - rport->ls_irq = of_irq_get_byname(child_np, "linestate"); - if (rport->ls_irq < 0) { - dev_err(rphy->dev, "no linestate irq provided\n"); - return rport->ls_irq; - } - - ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, - rockchip_usb2phy_linestate_irq, - IRQF_ONESHOT, - "rockchip_usb2phy", rport); + ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np); if (ret) { - dev_err(rphy->dev, "failed to request linestate irq handle\n"); + dev_err(rphy->dev, "failed to setup host irq\n"); return ret; } @@ -980,7 +1124,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) { - int ret; + int ret, id; rport->port_id = USB2PHY_PORT_OTG; rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; @@ -1007,43 +1151,10 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); - /* - * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate - * interrupts muxed together, so probe the otg-mux interrupt first, - * if not found, then look for the regular interrupts one by one. - */ - rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux"); - if (rport->otg_mux_irq > 0) { - ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq, - NULL, - rockchip_usb2phy_otg_mux_irq, - IRQF_ONESHOT, - "rockchip_usb2phy_otg", - rport); - if (ret) { - dev_err(rphy->dev, - "failed to request otg-mux irq handle\n"); - goto out; - } - } else { - rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); - if (rport->bvalid_irq < 0) { - dev_err(rphy->dev, "no vbus valid irq provided\n"); - ret = rport->bvalid_irq; - goto out; - } - - ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, - NULL, - rockchip_usb2phy_bvalid_irq, - IRQF_ONESHOT, - "rockchip_usb2phy_bvalid", - rport); - if (ret) { - dev_err(rphy->dev, - "failed to request otg-bvalid irq handle\n"); - goto out; - } + ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np); + if (ret) { + dev_err(rphy->dev, "failed to init irq for host port\n"); + goto out; } if (!IS_ERR(rphy->edev)) { @@ -1051,8 +1162,16 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, ret = devm_extcon_register_notifier(rphy->dev, rphy->edev, EXTCON_USB_HOST, &rport->event_nb); - if (ret) + if (ret) { dev_err(rphy->dev, "register USB HOST notifier failed\n"); + goto out; + } + + if (!of_property_read_bool(rphy->dev->of_node, "extcon")) { + /* do initial sync of usb state */ + id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id); + extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id); + } } out: @@ -1081,12 +1200,19 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) return -EINVAL; } - if (!dev->parent || !dev->parent->of_node) - return -EINVAL; + if (!dev->parent || !dev->parent->of_node) { + rphy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf"); + if (IS_ERR(rphy->grf)) { + dev_err(dev, "failed to locate usbgrf\n"); + return PTR_ERR(rphy->grf); + } + } - rphy->grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(rphy->grf)) - return PTR_ERR(rphy->grf); + else { + rphy->grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(rphy->grf)) + return PTR_ERR(rphy->grf); + } if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) { rphy->usbgrf = @@ -1098,16 +1224,26 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) rphy->usbgrf = NULL; } - if (of_property_read_u32(np, "reg", ®)) { + if (of_property_read_u32_index(np, "reg", 0, ®)) { dev_err(dev, "the reg property is not assigned in %pOFn node\n", np); return -EINVAL; } + /* support address_cells=2 */ + if (reg == 0) { + if (of_property_read_u32_index(np, "reg", 1, ®)) { + dev_err(dev, "the reg property is not assigned in %pOFn node\n", + np); + return -EINVAL; + } + } + rphy->dev = dev; phy_cfgs = match->data; rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rphy->irq = platform_get_irq_optional(pdev, 0); platform_set_drvdata(pdev, rphy); ret = rockchip_usb2phy_extcon_register(rphy); @@ -1157,7 +1293,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); if (IS_ERR(phy)) { - dev_err(dev, "failed to create phy\n"); + dev_err_probe(dev, PTR_ERR(phy), "failed to create phy\n"); ret = PTR_ERR(phy); goto put_child; } @@ -1180,11 +1316,27 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) next_child: /* to prevent out of boundary */ - if (++index >= rphy->phy_cfg->num_ports) + if (++index >= rphy->phy_cfg->num_ports) { + of_node_put(child_np); break; + } } provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + if (rphy->irq > 0) { + ret = devm_request_threaded_irq(rphy->dev, rphy->irq, NULL, + rockchip_usb2phy_irq, + IRQF_ONESHOT, + "rockchip_usb2phy", + rphy); + if (ret) { + dev_err(rphy->dev, + "failed to request usb2phy irq handle\n"); + goto put_child; + } + } + return PTR_ERR_OR_ZERO(provider); put_child: @@ -1208,10 +1360,14 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, + .id_det_en = { 0x0680, 6, 5, 0, 3 }, + .id_det_st = { 0x0690, 6, 5, 0, 3 }, + .id_det_clr = { 0x06a0, 6, 5, 0, 3 }, .ls_det_en = { 0x0680, 2, 2, 0, 1 }, .ls_det_st = { 0x0690, 2, 2, 0, 1 }, .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, .utmi_bvalid = { 0x0480, 4, 4, 0, 1 }, + .utmi_id = { 0x0480, 1, 1, 0, 1 }, .utmi_ls = { 0x0480, 3, 2, 0, 1 }, }, [USB2PHY_PORT_HOST] = { @@ -1256,6 +1412,53 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = { + { + .reg = 0x100, + .num_ports = 2, + .clkout_ctl = { 0x108, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x3020, 3, 2, 0, 3 }, + .bvalid_det_st = { 0x3024, 3, 2, 0, 3 }, + .bvalid_det_clr = { 0x3028, 3, 2, 0, 3 }, + .id_det_en = { 0x3020, 5, 4, 0, 3 }, + .id_det_st = { 0x3024, 5, 4, 0, 3 }, + .id_det_clr = { 0x3028, 5, 4, 0, 3 }, + .ls_det_en = { 0x3020, 0, 0, 0, 1 }, + .ls_det_st = { 0x3024, 0, 0, 0, 1 }, + .ls_det_clr = { 0x3028, 0, 0, 0, 1 }, + .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, + .utmi_id = { 0x0120, 6, 6, 0, 1 }, + .utmi_ls = { 0x0120, 5, 4, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0104, 8, 0, 0, 0x1d1 }, + .ls_det_en = { 0x3020, 1, 1, 0, 1 }, + .ls_det_st = { 0x3024, 1, 1, 0, 1 }, + .ls_det_clr = { 0x3028, 1, 1, 0, 1 }, + .utmi_ls = { 0x0120, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x0120, 19, 19, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0x0100, 3, 0, 5, 1 }, + .cp_det = { 0x0120, 24, 24, 0, 1 }, + .dcp_det = { 0x0120, 23, 23, 0, 1 }, + .dp_det = { 0x0120, 25, 25, 0, 1 }, + .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, + .idp_src_en = { 0x0108, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { { .reg = 0x100, @@ -1264,14 +1467,18 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { .port_cfgs = { [USB2PHY_PORT_OTG] = { .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, - .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, - .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, - .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, + .bvalid_det_en = { 0x0110, 3, 2, 0, 3 }, + .bvalid_det_st = { 0x0114, 3, 2, 0, 3 }, + .bvalid_det_clr = { 0x0118, 3, 2, 0, 3 }, + .id_det_en = { 0x0110, 5, 4, 0, 3 }, + .id_det_st = { 0x0114, 5, 4, 0, 3 }, + .id_det_clr = { 0x0118, 5, 4, 0, 3 }, .ls_det_en = { 0x0110, 0, 0, 0, 1 }, .ls_det_st = { 0x0114, 0, 0, 0, 1 }, .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, + .utmi_id = { 0x0120, 6, 6, 0, 1 }, .utmi_ls = { 0x0120, 5, 4, 0, 1 }, }, [USB2PHY_PORT_HOST] = { @@ -1329,8 +1536,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, + .id_det_en = { 0xe3c0, 5, 4, 0, 3 }, + .id_det_st = { 0xe3e0, 5, 4, 0, 3 }, + .id_det_clr = { 0xe3d0, 5, 4, 0, 3 }, .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, + .utmi_id = { 0xe2ac, 8, 8, 0, 1 }, }, [USB2PHY_PORT_HOST] = { .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, @@ -1364,8 +1575,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, + .id_det_en = { 0xe3c0, 10, 9, 0, 3 }, + .id_det_st = { 0xe3e0, 10, 9, 0, 3 }, + .id_det_clr = { 0xe3d0, 10, 9, 0, 3 }, .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, + .utmi_id = { 0xe2ac, 11, 11, 0, 1 }, }, [USB2PHY_PORT_HOST] = { .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, @@ -1380,6 +1595,73 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { + { + .reg = 0xfe8a0000, + .num_ports = 2, + .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x0080, 3, 2, 0, 3 }, + .bvalid_det_st = { 0x0084, 3, 2, 0, 3 }, + .bvalid_det_clr = { 0x0088, 3, 2, 0, 3 }, + .id_det_en = { 0x0080, 5, 4, 0, 3 }, + .id_det_st = { 0x0084, 5, 4, 0, 3 }, + .id_det_clr = { 0x0088, 5, 4, 0, 3 }, + .utmi_avalid = { 0x00c0, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 }, + .utmi_id = { 0x00c0, 6, 6, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + /* Select suspend control from controller */ + .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d2 }, + .ls_det_en = { 0x0080, 1, 1, 0, 1 }, + .ls_det_st = { 0x0084, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, + .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0x0000, 3, 0, 5, 1 }, + .cp_det = { 0x00c0, 24, 24, 0, 1 }, + .dcp_det = { 0x00c0, 23, 23, 0, 1 }, + .dp_det = { 0x00c0, 25, 25, 0, 1 }, + .idm_sink_en = { 0x0008, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0008, 7, 7, 0, 1 }, + .idp_src_en = { 0x0008, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0008, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0008, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0008, 11, 11, 0, 1 }, + }, + }, + { + .reg = 0xfe8b0000, + .num_ports = 2, + .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .utmi_ls = { 0x00c0, 5, 4, 0, 1 }, + .utmi_hstdet = { 0x00c0, 7, 7, 0, 1 } + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 1, 1, 0, 1 }, + .ls_det_st = { 0x0084, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, + .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } + } + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { { .reg = 0x100, @@ -1425,9 +1707,11 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, + { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs }, { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, + { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, {} }; diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c new file mode 100644 index 000000000000..7b213825fb5d --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip PIPE USB3.0 PCIE SATA Combo Phy driver + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + */ + +#include <dt-bindings/phy/phy.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/units.h> + +#define BIT_WRITEABLE_SHIFT 16 +#define REF_CLOCK_24MHz (24 * HZ_PER_MHZ) +#define REF_CLOCK_25MHz (25 * HZ_PER_MHZ) +#define REF_CLOCK_100MHz (100 * HZ_PER_MHZ) + +/* COMBO PHY REG */ +#define PHYREG6 0x14 +#define PHYREG6_PLL_DIV_MASK GENMASK(7, 6) +#define PHYREG6_PLL_DIV_SHIFT 6 +#define PHYREG6_PLL_DIV_2 1 + +#define PHYREG7 0x18 +#define PHYREG7_TX_RTERM_MASK GENMASK(7, 4) +#define PHYREG7_TX_RTERM_SHIFT 4 +#define PHYREG7_TX_RTERM_50OHM 8 +#define PHYREG7_RX_RTERM_MASK GENMASK(3, 0) +#define PHYREG7_RX_RTERM_SHIFT 0 +#define PHYREG7_RX_RTERM_44OHM 15 + +#define PHYREG8 0x1C +#define PHYREG8_SSC_EN BIT(4) + +#define PHYREG11 0x28 +#define PHYREG11_SU_TRIM_0_7 0xF0 + +#define PHYREG12 0x2C +#define PHYREG12_PLL_LPF_ADJ_VALUE 4 + +#define PHYREG13 0x30 +#define PHYREG13_RESISTER_MASK GENMASK(5, 4) +#define PHYREG13_RESISTER_SHIFT 0x4 +#define PHYREG13_RESISTER_HIGH_Z 3 +#define PHYREG13_CKRCV_AMP0 BIT(7) + +#define PHYREG14 0x34 +#define PHYREG14_CKRCV_AMP1 BIT(0) + +#define PHYREG15 0x38 +#define PHYREG15_CTLE_EN BIT(0) +#define PHYREG15_SSC_CNT_MASK GENMASK(7, 6) +#define PHYREG15_SSC_CNT_SHIFT 6 +#define PHYREG15_SSC_CNT_VALUE 1 + +#define PHYREG16 0x3C +#define PHYREG16_SSC_CNT_VALUE 0x5f + +#define PHYREG18 0x44 +#define PHYREG18_PLL_LOOP 0x32 + +#define PHYREG32 0x7C +#define PHYREG32_SSC_MASK GENMASK(7, 4) +#define PHYREG32_SSC_DIR_SHIFT 4 +#define PHYREG32_SSC_UPWARD 0 +#define PHYREG32_SSC_DOWNWARD 1 +#define PHYREG32_SSC_OFFSET_SHIFT 6 +#define PHYREG32_SSC_OFFSET_500PPM 1 + +#define PHYREG33 0x80 +#define PHYREG33_PLL_KVCO_MASK GENMASK(4, 2) +#define PHYREG33_PLL_KVCO_SHIFT 2 +#define PHYREG33_PLL_KVCO_VALUE 2 + +struct rockchip_combphy_priv; + +struct combphy_reg { + u16 offset; + u16 bitend; + u16 bitstart; + u16 disable; + u16 enable; +}; + +struct rockchip_combphy_grfcfg { + struct combphy_reg pcie_mode_set; + struct combphy_reg usb_mode_set; + struct combphy_reg sgmii_mode_set; + struct combphy_reg qsgmii_mode_set; + struct combphy_reg pipe_rxterm_set; + struct combphy_reg pipe_txelec_set; + struct combphy_reg pipe_txcomp_set; + struct combphy_reg pipe_clk_25m; + struct combphy_reg pipe_clk_100m; + struct combphy_reg pipe_phymode_sel; + struct combphy_reg pipe_rate_sel; + struct combphy_reg pipe_rxterm_sel; + struct combphy_reg pipe_txelec_sel; + struct combphy_reg pipe_txcomp_sel; + struct combphy_reg pipe_clk_ext; + struct combphy_reg pipe_sel_usb; + struct combphy_reg pipe_sel_qsgmii; + struct combphy_reg pipe_phy_status; + struct combphy_reg con0_for_pcie; + struct combphy_reg con1_for_pcie; + struct combphy_reg con2_for_pcie; + struct combphy_reg con3_for_pcie; + struct combphy_reg con0_for_sata; + struct combphy_reg con1_for_sata; + struct combphy_reg con2_for_sata; + struct combphy_reg con3_for_sata; + struct combphy_reg pipe_con0_for_sata; + struct combphy_reg pipe_xpcs_phy_ready; +}; + +struct rockchip_combphy_cfg { + const struct rockchip_combphy_grfcfg *grfcfg; + int (*combphy_cfg)(struct rockchip_combphy_priv *priv); +}; + +struct rockchip_combphy_priv { + u8 type; + void __iomem *mmio; + int num_clks; + struct clk_bulk_data *clks; + struct device *dev; + struct regmap *pipe_grf; + struct regmap *phy_grf; + struct phy *phy; + struct reset_control *phy_rst; + const struct rockchip_combphy_cfg *cfg; + bool enable_ssc; + bool ext_refclk; + struct clk *refclk; +}; + +static void rockchip_combphy_updatel(struct rockchip_combphy_priv *priv, + int mask, int val, int reg) +{ + unsigned int temp; + + temp = readl(priv->mmio + reg); + temp = (temp & ~(mask)) | val; + writel(temp, priv->mmio + reg); +} + +static int rockchip_combphy_param_write(struct regmap *base, + const struct combphy_reg *reg, bool en) +{ + u32 val, mask, tmp; + + tmp = en ? reg->enable : reg->disable; + mask = GENMASK(reg->bitend, reg->bitstart); + val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); + + return regmap_write(base, reg->offset, val); +} + +static u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv) +{ + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + u32 mask, val; + + mask = GENMASK(cfg->pipe_phy_status.bitend, + cfg->pipe_phy_status.bitstart); + + regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val); + val = (val & mask) >> cfg->pipe_phy_status.bitstart; + + return val; +} + +static int rockchip_combphy_init(struct phy *phy) +{ + struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + u32 val; + int ret; + + ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); + if (ret) { + dev_err(priv->dev, "failed to enable clks\n"); + return ret; + } + + switch (priv->type) { + case PHY_TYPE_PCIE: + case PHY_TYPE_USB3: + case PHY_TYPE_SATA: + case PHY_TYPE_SGMII: + case PHY_TYPE_QSGMII: + if (priv->cfg->combphy_cfg) + ret = priv->cfg->combphy_cfg(priv); + break; + default: + dev_err(priv->dev, "incompatible PHY type\n"); + ret = -EINVAL; + break; + } + + if (ret) { + dev_err(priv->dev, "failed to init phy for phy type %x\n", priv->type); + goto err_clk; + } + + ret = reset_control_deassert(priv->phy_rst); + if (ret) + goto err_clk; + + if (priv->type == PHY_TYPE_USB3) { + ret = readx_poll_timeout_atomic(rockchip_combphy_is_ready, + priv, val, + val == cfg->pipe_phy_status.enable, + 10, 1000); + if (ret) + dev_warn(priv->dev, "wait phy status ready timeout\n"); + } + + return 0; + +err_clk: + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + + return ret; +} + +static int rockchip_combphy_exit(struct phy *phy) +{ + struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + reset_control_assert(priv->phy_rst); + + return 0; +} + +static const struct phy_ops rochchip_combphy_ops = { + .init = rockchip_combphy_init, + .exit = rockchip_combphy_exit, + .owner = THIS_MODULE, +}; + +static struct phy *rockchip_combphy_xlate(struct device *dev, struct of_phandle_args *args) +{ + struct rockchip_combphy_priv *priv = dev_get_drvdata(dev); + + if (args->args_count != 1) { + dev_err(dev, "invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + if (priv->type != PHY_NONE && priv->type != args->args[0]) + dev_warn(dev, "phy type select %d overwriting type %d\n", + args->args[0], priv->type); + + priv->type = args->args[0]; + + return priv->phy; +} + +static int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy_priv *priv) +{ + int i; + + priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); + if (priv->num_clks < 1) + return -EINVAL; + + priv->refclk = NULL; + for (i = 0; i < priv->num_clks; i++) { + if (!strncmp(priv->clks[i].id, "ref", 3)) { + priv->refclk = priv->clks[i].clk; + break; + } + } + + if (!priv->refclk) { + dev_err(dev, "no refclk found\n"); + return -EINVAL; + } + + priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf"); + if (IS_ERR(priv->pipe_grf)) { + dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n"); + return PTR_ERR(priv->pipe_grf); + } + + priv->phy_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-phy-grf"); + if (IS_ERR(priv->phy_grf)) { + dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n"); + return PTR_ERR(priv->phy_grf); + } + + priv->enable_ssc = device_property_present(dev, "rockchip,enable-ssc"); + + priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk"); + + priv->phy_rst = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(priv->phy_rst)) + return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n"); + + return 0; +} + +static int rockchip_combphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct rockchip_combphy_priv *priv; + const struct rockchip_combphy_cfg *phy_cfg; + struct resource *res; + int ret; + + phy_cfg = of_device_get_match_data(dev); + if (!phy_cfg) { + dev_err(dev, "no OF match data provided\n"); + return -EINVAL; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->mmio)) { + ret = PTR_ERR(priv->mmio); + return ret; + } + + priv->dev = dev; + priv->type = PHY_NONE; + priv->cfg = phy_cfg; + + ret = rockchip_combphy_parse_dt(dev, priv); + if (ret) + return ret; + + ret = reset_control_assert(priv->phy_rst); + if (ret) { + dev_err(dev, "failed to reset phy\n"); + return ret; + } + + priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create combphy\n"); + return PTR_ERR(priv->phy); + } + + dev_set_drvdata(dev, priv); + phy_set_drvdata(priv->phy, priv); + + phy_provider = devm_of_phy_provider_register(dev, rockchip_combphy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) +{ + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + unsigned long rate; + u32 val; + + switch (priv->type) { + case PHY_TYPE_PCIE: + /* Set SSC downward spread spectrum. */ + rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, + PHYREG32); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true); + break; + + case PHY_TYPE_USB3: + /* Set SSC downward spread spectrum. */ + rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, + PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, + PHYREG32); + + /* Enable adaptive CTLE for USB3.0 Rx. */ + val = readl(priv->mmio + PHYREG15); + val |= PHYREG15_CTLE_EN; + writel(val, priv->mmio + PHYREG15); + + /* Set PLL KVCO fine tuning signals. */ + rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, + PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, + PHYREG33); + + /* Enable controlling random jitter. */ + writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + + /* Set PLL input clock divider 1/2. */ + rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, + PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, + PHYREG6); + + writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); + rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true); + break; + + case PHY_TYPE_SATA: + /* Enable adaptive CTLE for SATA Rx. */ + val = readl(priv->mmio + PHYREG15); + val |= PHYREG15_CTLE_EN; + writel(val, priv->mmio + PHYREG15); + /* + * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. + * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) + */ + val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; + val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; + writel(val, priv->mmio + PHYREG7); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_sata, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_sata, true); + rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true); + break; + + case PHY_TYPE_SGMII: + rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->sgmii_mode_set, true); + break; + + case PHY_TYPE_QSGMII: + rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_rate_sel, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true); + break; + + default: + dev_err(priv->dev, "incompatible PHY type\n"); + return -EINVAL; + } + + rate = clk_get_rate(priv->refclk); + + switch (rate) { + case REF_CLOCK_24MHz: + if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) { + /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ + val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; + rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, + val, PHYREG15); + + writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + } + break; + + case REF_CLOCK_25MHz: + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true); + break; + + case REF_CLOCK_100MHz: + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); + if (priv->type == PHY_TYPE_PCIE) { + /* PLL KVCO fine tuning. */ + val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, + val, PHYREG33); + + /* Enable controlling random jitter. */ + writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + + val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT; + rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, + val, PHYREG6); + + writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + } else if (priv->type == PHY_TYPE_SATA) { + /* downward spread spectrum +500ppm */ + val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; + val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; + rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + } + break; + + default: + dev_err(priv->dev, "unsupported rate: %lu\n", rate); + return -EINVAL; + } + + if (priv->ext_refclk) { + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); + if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { + val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; + val |= PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13); + + val = readl(priv->mmio + PHYREG14); + val |= PHYREG14_CKRCV_AMP1; + writel(val, priv->mmio + PHYREG14); + } + } + + if (priv->enable_ssc) { + val = readl(priv->mmio + PHYREG8); + val |= PHYREG8_SSC_EN; + writel(val, priv->mmio + PHYREG8); + } + + return 0; +} + +static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = { + /* pipe-phy-grf */ + .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 }, + .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 }, + .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 }, + .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 }, + .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 }, + .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 }, + .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 }, + .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 }, + .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 }, + .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 }, + .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 }, + .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 }, + .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 }, + .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 }, + .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 }, + .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 }, + .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 }, + .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 }, + .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 }, + .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 }, + .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 }, + .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, + .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 }, + .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 }, + .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 }, + .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 }, + /* pipe-grf */ + .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 }, + .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 }, +}; + +static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = { + .grfcfg = &rk3568_combphy_grfcfgs, + .combphy_cfg = rk3568_combphy_cfg, +}; + +static const struct of_device_id rockchip_combphy_of_match[] = { + { + .compatible = "rockchip,rk3568-naneng-combphy", + .data = &rk3568_combphy_cfgs, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match); + +static struct platform_driver rockchip_combphy_driver = { + .probe = rockchip_combphy_probe, + .driver = { + .name = "rockchip-naneng-combphy", + .of_match_table = rockchip_combphy_of_match, + }, +}; +module_platform_driver(rockchip_combphy_driver); + +MODULE_DESCRIPTION("Rockchip NANENG COMBPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c new file mode 100644 index 000000000000..1d355b32ba55 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip PCIE3.0 phy driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/pcie.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +/* Register for RK3568 */ +#define GRF_PCIE30PHY_CON1 0x4 +#define GRF_PCIE30PHY_CON6 0x18 +#define GRF_PCIE30PHY_CON9 0x24 +#define GRF_PCIE30PHY_DA_OCM (BIT(15) | BIT(31)) +#define GRF_PCIE30PHY_STATUS0 0x80 +#define GRF_PCIE30PHY_WR_EN (0xf << 16) +#define SRAM_INIT_DONE(reg) (reg & BIT(14)) + +#define RK3568_BIFURCATION_LANE_0_1 BIT(0) + +/* Register for RK3588 */ +#define PHP_GRF_PCIESEL_CON 0x100 +#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 +#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 +#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 +#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0)) + +#define RK3588_BIFURCATION_LANE_0_1 BIT(0) +#define RK3588_BIFURCATION_LANE_2_3 BIT(1) +#define RK3588_LANE_AGGREGATION BIT(2) + +struct rockchip_p3phy_ops; + +struct rockchip_p3phy_priv { + const struct rockchip_p3phy_ops *ops; + void __iomem *mmio; + /* mode: RC, EP */ + int mode; + /* pcie30_phymode: Aggregation, Bifurcation */ + int pcie30_phymode; + struct regmap *phy_grf; + struct regmap *pipe_grf; + struct reset_control *p30phy; + struct phy *phy; + struct clk_bulk_data *clks; + int num_clks; + int num_lanes; + u32 lanes[4]; +}; + +struct rockchip_p3phy_ops { + int (*phy_init)(struct rockchip_p3phy_priv *priv); +}; + +static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); + + /* Actually We don't care EP/RC mode, but just record it */ + switch (submode) { + case PHY_MODE_PCIE_RC: + priv->mode = PHY_MODE_PCIE_RC; + break; + case PHY_MODE_PCIE_EP: + priv->mode = PHY_MODE_PCIE_EP; + break; + default: + dev_err(&phy->dev, "%s, invalid mode\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv) +{ + struct phy *phy = priv->phy; + bool bifurcation = false; + int ret; + u32 reg; + + /* Deassert PCIe PMA output clamp mode */ + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM); + + for (int i = 0; i < priv->num_lanes; i++) { + dev_info(&phy->dev, "lane number %d, val %d\n", i, priv->lanes[i]); + if (priv->lanes[i] > 1) + bifurcation = true; + } + + /* Set bifurcation if needed, and it doesn't care RC/EP */ + if (bifurcation) { + dev_info(&phy->dev, "bifurcation enabled\n"); + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6, + GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1); + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1, + GRF_PCIE30PHY_DA_OCM); + } else { + dev_dbg(&phy->dev, "bifurcation disabled\n"); + regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6, + GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1); + } + + reset_control_deassert(priv->p30phy); + + ret = regmap_read_poll_timeout(priv->phy_grf, + GRF_PCIE30PHY_STATUS0, + reg, SRAM_INIT_DONE(reg), + 0, 500); + if (ret) + dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n", + __func__, reg); + return ret; +} + +static const struct rockchip_p3phy_ops rk3568_ops = { + .phy_init = rockchip_p3phy_rk3568_init, +}; + +static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv) +{ + u32 reg = 0; + u8 mode = 0; + int ret; + + /* Deassert PCIe PMA output clamp mode */ + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24)); + + /* Set bifurcation if needed */ + for (int i = 0; i < priv->num_lanes; i++) { + if (!priv->lanes[i]) + mode |= (BIT(i) << 3); + + if (priv->lanes[i] > 1) + mode |= (BIT(i) >> 1); + } + + if (!mode) + reg = RK3588_LANE_AGGREGATION; + else { + if (mode & (BIT(0) | BIT(1))) + reg |= RK3588_BIFURCATION_LANE_0_1; + + if (mode & (BIT(2) | BIT(3))) + reg |= RK3588_BIFURCATION_LANE_2_3; + } + + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, (0x7<<16) | reg); + + /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */ + if (!IS_ERR(priv->pipe_grf)) { + reg = (mode & (BIT(6) | BIT(7))) >> 6; + if (reg) + regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON, + (reg << 16) | reg); + } + + reset_control_deassert(priv->p30phy); + + ret = regmap_read_poll_timeout(priv->phy_grf, + RK3588_PCIE3PHY_GRF_PHY0_STATUS1, + reg, RK3588_SRAM_INIT_DONE(reg), + 0, 500); + ret |= regmap_read_poll_timeout(priv->phy_grf, + RK3588_PCIE3PHY_GRF_PHY1_STATUS1, + reg, RK3588_SRAM_INIT_DONE(reg), + 0, 500); + if (ret) + dev_err(&priv->phy->dev, "lock failed 0x%x, check input refclk and power supply\n", + reg); + return ret; +} + +static const struct rockchip_p3phy_ops rk3588_ops = { + .phy_init = rockchip_p3phy_rk3588_init, +}; + +static int rochchip_p3phy_init(struct phy *phy) +{ + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); + if (ret) { + dev_err(&priv->phy->dev, "failed to enable PCIe bulk clks %d\n", ret); + return ret; + } + + reset_control_assert(priv->p30phy); + udelay(1); + + if (priv->ops->phy_init) { + ret = priv->ops->phy_init(priv); + if (ret) + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + } + + return ret; +} + +static int rochchip_p3phy_exit(struct phy *phy) +{ + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); + reset_control_assert(priv->p30phy); + return 0; +} + +static const struct phy_ops rochchip_p3phy_ops = { + .init = rochchip_p3phy_init, + .exit = rochchip_p3phy_exit, + .set_mode = rockchip_p3phy_set_mode, + .owner = THIS_MODULE, +}; + +static int rockchip_p3phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct rockchip_p3phy_priv *priv; + struct device_node *np = dev->of_node; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(priv->mmio)) { + ret = PTR_ERR(priv->mmio); + return ret; + } + + priv->ops = of_device_get_match_data(&pdev->dev); + if (!priv->ops) { + dev_err(dev, "no of match data provided\n"); + return -EINVAL; + } + + priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf"); + if (IS_ERR(priv->phy_grf)) { + dev_err(dev, "failed to find rockchip,phy_grf regmap\n"); + return PTR_ERR(priv->phy_grf); + } + + if (of_device_is_compatible(np, "rockchip,rk3588-pcie3-phy")) { + priv->pipe_grf = + syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,pipe-grf"); + if (IS_ERR(priv->pipe_grf)) + dev_info(dev, "failed to find rockchip,pipe_grf regmap\n"); + } else { + priv->pipe_grf = NULL; + } + + priv->num_lanes = of_property_read_variable_u32_array(dev->of_node, "data-lanes", + priv->lanes, 2, + ARRAY_SIZE(priv->lanes)); + + /* if no data-lanes assume aggregation */ + if (priv->num_lanes == -EINVAL) { + dev_dbg(dev, "no data-lanes property found\n"); + priv->num_lanes = 1; + priv->lanes[0] = 1; + } else if (priv->num_lanes < 0) { + dev_err(dev, "failed to read data-lanes property %d\n", priv->num_lanes); + return priv->num_lanes; + } + + priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create combphy\n"); + return PTR_ERR(priv->phy); + } + + priv->p30phy = devm_reset_control_get_optional_exclusive(dev, "phy"); + if (IS_ERR(priv->p30phy)) { + return dev_err_probe(dev, PTR_ERR(priv->p30phy), + "failed to get phy reset control\n"); + } + if (!priv->p30phy) + dev_info(dev, "no phy reset control specified\n"); + + priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); + if (priv->num_clks < 1) + return -ENODEV; + + dev_set_drvdata(dev, priv); + phy_set_drvdata(priv->phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_p3phy_of_match[] = { + { .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops }, + { .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops }, + { }, +}; +MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match); + +static struct platform_driver rockchip_p3phy_driver = { + .probe = rockchip_p3phy_probe, + .driver = { + .name = "rockchip-snps-pcie3-phy", + .of_match_table = rockchip_p3phy_of_match, + }, +}; +module_platform_driver(rockchip_p3phy_driver); +MODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 24563160197f..d76440ae10ff 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -347,7 +347,7 @@ struct usb3phy_reg { }; /** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. + * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration. * @reg: the base address for usb3-phy config. * @typec_conn_dir: the register of type-c connector direction. * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. @@ -1105,15 +1105,14 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct resource *res; const struct rockchip_usb3phy_port_cfg *phy_cfgs; - const struct of_device_id *match; int index, ret; tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); if (!tcphy) return -ENOMEM; - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { + phy_cfgs = of_device_get_match_data(dev); + if (!phy_cfgs) { dev_err(dev, "phy configs are not assigned!\n"); return -EINVAL; } @@ -1123,7 +1122,6 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) if (IS_ERR(tcphy->base)) return PTR_ERR(tcphy->base); - phy_cfgs = match->data; /* find out a proper config which can be matched with dt. */ index = 0; while (phy_cfgs[index].reg) { @@ -1180,6 +1178,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) dev_err(dev, "failed to create phy: %pOFn\n", child_np); pm_runtime_disable(dev); + of_node_put(child_np); return PTR_ERR(phy); } diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 9e483d1fdaf2..3ccaabf2850a 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -3,23 +3,23 @@ # Phy drivers for Samsung platforms # config PHY_EXYNOS_DP_VIDEO - tristate "EXYNOS SoC series Display Port PHY driver" + tristate "Exynos SoC series Display Port PHY driver" depends on OF depends on ARCH_EXYNOS || COMPILE_TEST default ARCH_EXYNOS select GENERIC_PHY help - Support for Display Port PHY found on Samsung EXYNOS SoCs. + Support for Display Port PHY found on Samsung Exynos SoCs. config PHY_EXYNOS_MIPI_VIDEO - tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" + tristate "S5P/Exynos SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select GENERIC_PHY default y if ARCH_S5PV210 || ARCH_EXYNOS help Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P - and EXYNOS SoCs. + and Exynos SoCs. config PHY_EXYNOS_PCIE bool "Exynos PCIe PHY driver" @@ -29,8 +29,17 @@ config PHY_EXYNOS_PCIE Enable PCIe PHY support for Exynos SoC series. This driver provides PHY interface for Exynos PCIe controller. +config PHY_SAMSUNG_UFS + tristate "Exynos SoC series UFS PHY driver" + depends on OF && (ARCH_EXYNOS || COMPILE_TEST) + select GENERIC_PHY + help + Enable this to support the Samsung Exynos SoC UFS PHY driver for + Samsung Exynos SoCs. This driver provides the interface for UFS host + controller to do PHY related programming. + config PHY_SAMSUNG_USB2 - tristate "Samsung USB 2.0 PHY driver" + tristate "S5P/Exynos SoC series USB 2.0 PHY driver" depends on HAS_IOMEM depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST select GENERIC_PHY @@ -38,9 +47,9 @@ config PHY_SAMSUNG_USB2 default ARCH_EXYNOS help Enable this to support the Samsung USB 2.0 PHY driver for Samsung - SoCs. This driver provides the interface for USB 2.0 PHY. Support - for particular PHYs will be enabled based on the SoC type in addition - to this driver. + S5Pv210 and Exynos SoCs. This driver provides the interface for USB + 2.0 PHY. Support for particular PHYs will be enabled based on the SoC + type in addition to this driver. config PHY_EXYNOS4210_USB2 bool diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile index db9b1aa0de6e..afb34a153e34 100644 --- a/drivers/phy/samsung/Makefile +++ b/drivers/phy/samsung/Makefile @@ -2,6 +2,11 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o +obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-exynos-ufs.o +phy-exynos-ufs-y += phy-samsung-ufs.o +phy-exynos-ufs-y += phy-exynos7-ufs.o +phy-exynos-ufs-y += phy-exynosautov9-ufs.o +phy-exynos-ufs-y += phy-fsd-ufs.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c index 6c607df1dc9a..2b670ef91deb 100644 --- a/drivers/phy/samsung/phy-exynos-dp-video.c +++ b/drivers/phy/samsung/phy-exynos-dp-video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS SoC series Display Port PHY driver + * Samsung Exynos SoC series Display Port PHY driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Author: Jingoo Han <jg1.han@samsung.com> @@ -115,5 +115,5 @@ static struct platform_driver exynos_dp_video_phy_driver = { module_platform_driver(exynos_dp_video_phy_driver); MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); -MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); +MODULE_DESCRIPTION("Samsung Exynos SoC DP PHY driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c index bb51195f189f..c1df1ef3ee3c 100644 --- a/drivers/phy/samsung/phy-exynos-mipi-video.c +++ b/drivers/phy/samsung/phy-exynos-mipi-video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver * * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> @@ -364,6 +364,6 @@ static struct platform_driver exynos_mipi_video_phy_driver = { }; module_platform_driver(exynos_mipi_video_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); +MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c index 659e7ae0a6cf..53c9230c2907 100644 --- a/drivers/phy/samsung/phy-exynos-pcie.c +++ b/drivers/phy/samsung/phy-exynos-pcie.c @@ -1,73 +1,44 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS SoC series PCIe PHY driver + * Samsung Exynos SoC series PCIe PHY driver * * Phy provider for PCIe controller on Exynos SoC series * - * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * Copyright (C) 2017-2020 Samsung Electronics Co., Ltd. * Jaehoon Chung <jh80.chung@samsung.com> */ -#include <linux/delay.h> #include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/init.h> #include <linux/mfd/syscon.h> -#include <linux/of.h> -#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/regmap.h> -/* PCIe Purple registers */ -#define PCIE_PHY_GLOBAL_RESET 0x000 -#define PCIE_PHY_COMMON_RESET 0x004 -#define PCIE_PHY_CMN_REG 0x008 -#define PCIE_PHY_MAC_RESET 0x00c -#define PCIE_PHY_PLL_LOCKED 0x010 -#define PCIE_PHY_TRSVREG_RESET 0x020 -#define PCIE_PHY_TRSV_RESET 0x024 - -/* PCIe PHY registers */ -#define PCIE_PHY_IMPEDANCE 0x004 -#define PCIE_PHY_PLL_DIV_0 0x008 -#define PCIE_PHY_PLL_BIAS 0x00c -#define PCIE_PHY_DCC_FEEDBACK 0x014 -#define PCIE_PHY_PLL_DIV_1 0x05c -#define PCIE_PHY_COMMON_POWER 0x064 -#define PCIE_PHY_COMMON_PD_CMN BIT(3) -#define PCIE_PHY_TRSV0_EMP_LVL 0x084 -#define PCIE_PHY_TRSV0_DRV_LVL 0x088 -#define PCIE_PHY_TRSV0_RXCDR 0x0ac -#define PCIE_PHY_TRSV0_POWER 0x0c4 -#define PCIE_PHY_TRSV0_PD_TSV BIT(7) -#define PCIE_PHY_TRSV0_LVCC 0x0dc -#define PCIE_PHY_TRSV1_EMP_LVL 0x144 -#define PCIE_PHY_TRSV1_RXCDR 0x16c -#define PCIE_PHY_TRSV1_POWER 0x184 -#define PCIE_PHY_TRSV1_PD_TSV BIT(7) -#define PCIE_PHY_TRSV1_LVCC 0x19c -#define PCIE_PHY_TRSV2_EMP_LVL 0x204 -#define PCIE_PHY_TRSV2_RXCDR 0x22c -#define PCIE_PHY_TRSV2_POWER 0x244 -#define PCIE_PHY_TRSV2_PD_TSV BIT(7) -#define PCIE_PHY_TRSV2_LVCC 0x25c -#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 -#define PCIE_PHY_TRSV3_RXCDR 0x2ec -#define PCIE_PHY_TRSV3_POWER 0x304 -#define PCIE_PHY_TRSV3_PD_TSV BIT(7) -#define PCIE_PHY_TRSV3_LVCC 0x31c - -struct exynos_pcie_phy_data { - const struct phy_ops *ops; -}; +#define PCIE_PHY_OFFSET(x) ((x) * 0x4) + +/* Sysreg FSYS register offsets and bits for Exynos5433 */ +#define PCIE_EXYNOS5433_PHY_MAC_RESET 0x0208 +#define PCIE_MAC_RESET_MASK 0xFF +#define PCIE_MAC_RESET BIT(4) +#define PCIE_EXYNOS5433_PHY_L1SUB_CM_CON 0x1010 +#define PCIE_REFCLK_GATING_EN BIT(0) +#define PCIE_EXYNOS5433_PHY_COMMON_RESET 0x1020 +#define PCIE_PHY_RESET BIT(0) +#define PCIE_EXYNOS5433_PHY_GLOBAL_RESET 0x1040 +#define PCIE_GLOBAL_RESET BIT(0) +#define PCIE_REFCLK BIT(1) +#define PCIE_REFCLK_MASK 0x16 +#define PCIE_APP_REQ_EXIT_L1_MODE BIT(5) + +/* PMU PCIE PHY isolation control */ +#define EXYNOS5433_PMU_PCIE_PHY_OFFSET 0x730 /* For Exynos pcie phy */ struct exynos_pcie_phy { - const struct exynos_pcie_phy_data *drv_data; - void __iomem *phy_base; - void __iomem *blk_base; /* For exynos5440 */ + void __iomem *base; + struct regmap *pmureg; + struct regmap *fsysreg; }; static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) @@ -75,153 +46,96 @@ static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) writel(val, base + offset); } -static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset) -{ - return readl(base + offset); -} - -/* For Exynos5440 specific functions */ -static int exynos5440_pcie_phy_init(struct phy *phy) +/* Exynos5433 specific functions */ +static int exynos5433_pcie_phy_init(struct phy *phy) { struct exynos_pcie_phy *ep = phy_get_drvdata(phy); - /* DCC feedback control off */ - exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); - - /* set TX/RX impedance */ - exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); - - /* set 50Mhz PHY clock */ - exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); - exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); - - /* set TX Differential output for lane 0 */ - exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); - - /* set TX Pre-emphasis Level Control for lane 0 to minimum */ - exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); - - /* set RX clock and data recovery bandwidth */ - exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); - exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); - exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); - exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); - exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); - - /* change TX Pre-emphasis Level Control for lanes */ - exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); - exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); - exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); - exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); - - /* set LVCC */ - exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); - exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); - exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); - exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); - - /* pulse for common reset */ - exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET); - udelay(500); - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); - + regmap_update_bits(ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET, + BIT(0), 1); + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET, + PCIE_APP_REQ_EXIT_L1_MODE, 0); + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON, + PCIE_REFCLK_GATING_EN, 0); + + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET, + PCIE_PHY_RESET, 1); + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET, + PCIE_MAC_RESET, 0); + + /* PHY refclk 24MHz */ + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET, + PCIE_REFCLK_MASK, PCIE_REFCLK); + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET, + PCIE_GLOBAL_RESET, 0); + + + exynos_pcie_phy_writel(ep->base, 0x11, PCIE_PHY_OFFSET(0x3)); + + /* band gap reference on */ + exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x20)); + exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x4b)); + + /* jitter tuning */ + exynos_pcie_phy_writel(ep->base, 0x34, PCIE_PHY_OFFSET(0x4)); + exynos_pcie_phy_writel(ep->base, 0x02, PCIE_PHY_OFFSET(0x7)); + exynos_pcie_phy_writel(ep->base, 0x41, PCIE_PHY_OFFSET(0x21)); + exynos_pcie_phy_writel(ep->base, 0x7F, PCIE_PHY_OFFSET(0x14)); + exynos_pcie_phy_writel(ep->base, 0xC0, PCIE_PHY_OFFSET(0x15)); + exynos_pcie_phy_writel(ep->base, 0x61, PCIE_PHY_OFFSET(0x36)); + + /* D0 uninit.. */ + exynos_pcie_phy_writel(ep->base, 0x44, PCIE_PHY_OFFSET(0x3D)); + + /* 24MHz */ + exynos_pcie_phy_writel(ep->base, 0x94, PCIE_PHY_OFFSET(0x8)); + exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x9)); + exynos_pcie_phy_writel(ep->base, 0x93, PCIE_PHY_OFFSET(0xA)); + exynos_pcie_phy_writel(ep->base, 0x6B, PCIE_PHY_OFFSET(0xC)); + exynos_pcie_phy_writel(ep->base, 0xA5, PCIE_PHY_OFFSET(0xF)); + exynos_pcie_phy_writel(ep->base, 0x34, PCIE_PHY_OFFSET(0x16)); + exynos_pcie_phy_writel(ep->base, 0xA3, PCIE_PHY_OFFSET(0x17)); + exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x1A)); + exynos_pcie_phy_writel(ep->base, 0x71, PCIE_PHY_OFFSET(0x23)); + exynos_pcie_phy_writel(ep->base, 0x4C, PCIE_PHY_OFFSET(0x24)); + + exynos_pcie_phy_writel(ep->base, 0x0E, PCIE_PHY_OFFSET(0x26)); + exynos_pcie_phy_writel(ep->base, 0x14, PCIE_PHY_OFFSET(0x7)); + exynos_pcie_phy_writel(ep->base, 0x48, PCIE_PHY_OFFSET(0x43)); + exynos_pcie_phy_writel(ep->base, 0x44, PCIE_PHY_OFFSET(0x44)); + exynos_pcie_phy_writel(ep->base, 0x03, PCIE_PHY_OFFSET(0x45)); + exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x48)); + exynos_pcie_phy_writel(ep->base, 0x13, PCIE_PHY_OFFSET(0x54)); + exynos_pcie_phy_writel(ep->base, 0x04, PCIE_PHY_OFFSET(0x31)); + exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x32)); + + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET, + PCIE_PHY_RESET, 0); + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET, + PCIE_MAC_RESET_MASK, PCIE_MAC_RESET); return 0; } -static int exynos5440_pcie_phy_power_on(struct phy *phy) +static int exynos5433_pcie_phy_exit(struct phy *phy) { struct exynos_pcie_phy *ep = phy_get_drvdata(phy); - u32 val; - - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG); - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET); - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); - val &= ~PCIE_PHY_COMMON_PD_CMN; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); - val &= ~PCIE_PHY_TRSV0_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); - val &= ~PCIE_PHY_TRSV1_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); - val &= ~PCIE_PHY_TRSV2_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); - val &= ~PCIE_PHY_TRSV3_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); + regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON, + PCIE_REFCLK_GATING_EN, PCIE_REFCLK_GATING_EN); + regmap_update_bits(ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET, + BIT(0), 0); return 0; } -static int exynos5440_pcie_phy_power_off(struct phy *phy) -{ - struct exynos_pcie_phy *ep = phy_get_drvdata(phy); - u32 val; - - if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val, - (val != 0), 1, 500)) { - dev_err(&phy->dev, "PLL Locked: 0x%x\n", val); - return -ETIMEDOUT; - } - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); - val |= PCIE_PHY_COMMON_PD_CMN; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); - val |= PCIE_PHY_TRSV0_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); - val |= PCIE_PHY_TRSV1_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); - val |= PCIE_PHY_TRSV2_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); - - val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); - val |= PCIE_PHY_TRSV3_PD_TSV; - exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); - - return 0; -} - -static int exynos5440_pcie_phy_reset(struct phy *phy) -{ - struct exynos_pcie_phy *ep = phy_get_drvdata(phy); - - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET); - exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET); - exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET); - - return 0; -} - -static const struct phy_ops exynos5440_phy_ops = { - .init = exynos5440_pcie_phy_init, - .power_on = exynos5440_pcie_phy_power_on, - .power_off = exynos5440_pcie_phy_power_off, - .reset = exynos5440_pcie_phy_reset, +static const struct phy_ops exynos5433_phy_ops = { + .init = exynos5433_pcie_phy_init, + .exit = exynos5433_pcie_phy_exit, .owner = THIS_MODULE, }; -static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = { - .ops = &exynos5440_phy_ops, -}; - static const struct of_device_id exynos_pcie_phy_match[] = { { - .compatible = "samsung,exynos5440-pcie-phy", - .data = &exynos5440_pcie_phy_data, + .compatible = "samsung,exynos5433-pcie-phy", }, {}, }; @@ -232,30 +146,30 @@ static int exynos_pcie_phy_probe(struct platform_device *pdev) struct exynos_pcie_phy *exynos_phy; struct phy *generic_phy; struct phy_provider *phy_provider; - struct resource *res; - const struct exynos_pcie_phy_data *drv_data; - - drv_data = of_device_get_match_data(dev); - if (!drv_data) - return -ENODEV; exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL); if (!exynos_phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - exynos_phy->phy_base = devm_ioremap_resource(dev, res); - if (IS_ERR(exynos_phy->phy_base)) - return PTR_ERR(exynos_phy->phy_base); + exynos_phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(exynos_phy->base)) + return PTR_ERR(exynos_phy->base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - exynos_phy->blk_base = devm_ioremap_resource(dev, res); - if (IS_ERR(exynos_phy->blk_base)) - return PTR_ERR(exynos_phy->blk_base); + exynos_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,pmu-syscon"); + if (IS_ERR(exynos_phy->pmureg)) { + dev_err(&pdev->dev, "PMU regmap lookup failed.\n"); + return PTR_ERR(exynos_phy->pmureg); + } - exynos_phy->drv_data = drv_data; + exynos_phy->fsysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,fsys-sysreg"); + if (IS_ERR(exynos_phy->fsysreg)) { + dev_err(&pdev->dev, "FSYS sysreg regmap lookup failed.\n"); + return PTR_ERR(exynos_phy->fsysreg); + } - generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops); + generic_phy = devm_phy_create(dev, dev->of_node, &exynos5433_phy_ops); if (IS_ERR(generic_phy)) { dev_err(dev, "failed to create PHY\n"); return PTR_ERR(generic_phy); @@ -275,5 +189,4 @@ static struct platform_driver exynos_pcie_phy_driver = { .suppress_bind_attrs = true, } }; - builtin_platform_driver(exynos_pcie_phy_driver); diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index e510732afb8b..ee0848fe8432 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS5 SoC series USB DRD PHY driver + * Samsung Exynos5 SoC series USB DRD PHY driver * * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series * @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/iopoll.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/mutex.h> @@ -33,7 +34,7 @@ #define EXYNOS5_FSEL_24MHZ 0x5 #define EXYNOS5_FSEL_50MHZ 0x7 -/* EXYNOS5: USB 3.0 DRD PHY registers */ +/* Exynos5: USB 3.0 DRD PHY registers */ #define EXYNOS5_DRD_LINKSYSTEM 0x04 #define LINKSYSTEM_FLADJ_MASK (0x3f << 1) @@ -180,14 +181,14 @@ struct exynos5_usbdrd_phy_drvdata { * @utmiclk: clock for utmi+ phy * @itpclk: clock for ITP generation * @drv_data: pointer to SoC level driver data structure - * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY + * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY * instances each with its 'phy' and 'phy_cfg'. * @extrefclk: frequency select settings when using 'separate * reference clocks' for SS and HS operations * @ref_clk: reference clock to PHY block from which PHY's * operational clocks are derived - * vbus: VBUS regulator for phy - * vbus_boost: Boost regulator for VBUS present on few Exynos boards + * @vbus: VBUS regulator for phy + * @vbus_boost: Boost regulator for VBUS present on few Exynos boards */ struct exynos5_usbdrd_phy { struct device *dev; @@ -556,41 +557,25 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy) static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd, u32 val, u32 cmd) { - u32 usec = 100; unsigned int result; + int err; writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); - do { - result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1); - if (result & PHYREG1_CR_ACK) - break; - - udelay(1); - } while (usec-- > 0); - - if (!usec) { - dev_err(phy_drd->dev, - "CRPORT handshake timeout1 (0x%08x)\n", val); - return -ETIME; + err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, + result, (result & PHYREG1_CR_ACK), 1, 100); + if (err == -ETIMEDOUT) { + dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val); + return err; } - usec = 100; - writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); - do { - result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1); - if (!(result & PHYREG1_CR_ACK)) - break; - - udelay(1); - } while (usec-- > 0); - - if (!usec) { - dev_err(phy_drd->dev, - "CRPORT handshake timeout2 (0x%08x)\n", val); - return -ETIME; + err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, + result, !(result & PHYREG1_CR_ACK), 1, 100); + if (err == -ETIMEDOUT) { + dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val); + return err; } return 0; @@ -714,7 +699,9 @@ static int exynos5_usbdrd_phy_calibrate(struct phy *phy) struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - return exynos5420_usbdrd_phy_calibrate(phy_drd); + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) + return exynos5420_usbdrd_phy_calibrate(phy_drd); + return 0; } static const struct phy_ops exynos5_usbdrd_phy_ops = { @@ -842,7 +829,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct exynos5_usbdrd_phy *phy_drd; struct phy_provider *phy_provider; - struct resource *res; const struct exynos5_usbdrd_phy_drvdata *drv_data; struct regmap *reg_pmu; u32 pmu_offset; @@ -856,8 +842,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, phy_drd); phy_drd->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy_drd->reg_phy = devm_ioremap_resource(dev, res); + phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(phy_drd->reg_phy)) return PTR_ERR(phy_drd->reg_phy); @@ -958,7 +943,7 @@ static struct platform_driver exynos5_usb3drd_phy = { }; module_platform_driver(exynos5_usb3drd_phy); -MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); +MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver"); MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/drivers/phy/samsung/phy-exynos5250-sata.c b/drivers/phy/samsung/phy-exynos5250-sata.c index 4dd7324d91b2..595adba5fb8f 100644 --- a/drivers/phy/samsung/phy-exynos5250-sata.c +++ b/drivers/phy/samsung/phy-exynos5250-sata.c @@ -162,7 +162,6 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) { struct exynos_sata_phy *sata_phy; struct device *dev = &pdev->dev; - struct resource *res; struct phy_provider *phy_provider; struct device_node *node; int ret = 0; @@ -171,9 +170,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) if (!sata_phy) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - sata_phy->regs = devm_ioremap_resource(dev, res); + sata_phy->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sata_phy->regs)) return PTR_ERR(sata_phy->regs); @@ -190,6 +187,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) return -EINVAL; sata_phy->client = of_find_i2c_device_by_node(node); + of_node_put(node); if (!sata_phy->client) return -EPROBE_DEFER; @@ -198,20 +196,21 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl"); if (IS_ERR(sata_phy->phyclk)) { dev_err(dev, "failed to get clk for PHY\n"); - return PTR_ERR(sata_phy->phyclk); + ret = PTR_ERR(sata_phy->phyclk); + goto put_dev; } ret = clk_prepare_enable(sata_phy->phyclk); if (ret < 0) { dev_err(dev, "failed to enable source clk\n"); - return ret; + goto put_dev; } sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); if (IS_ERR(sata_phy->phy)) { - clk_disable_unprepare(sata_phy->phyclk); dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(sata_phy->phy); + ret = PTR_ERR(sata_phy->phy); + goto clk_disable; } phy_set_drvdata(sata_phy->phy, sata_phy); @@ -219,11 +218,18 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (IS_ERR(phy_provider)) { - clk_disable_unprepare(sata_phy->phyclk); - return PTR_ERR(phy_provider); + ret = PTR_ERR(phy_provider); + goto clk_disable; } return 0; + +clk_disable: + clk_disable_unprepare(sata_phy->phyclk); +put_dev: + put_device(&sata_phy->client->dev); + + return ret; } static const struct of_device_id exynos_sata_phy_of_match[] = { diff --git a/drivers/phy/samsung/phy-exynos5250-usb2.c b/drivers/phy/samsung/phy-exynos5250-usb2.c index 4f53b711fd6f..e198010e1bfd 100644 --- a/drivers/phy/samsung/phy-exynos5250-usb2.c +++ b/drivers/phy/samsung/phy-exynos5250-usb2.c @@ -117,9 +117,9 @@ /* Isolation, configured in the power management unit */ #define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704 -#define EXYNOS_5250_USB_ISOL_OTG BIT(0) #define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708 -#define EXYNOS_5250_USB_ISOL_HOST BIT(0) +#define EXYNOS_5420_USB_ISOL_HOST_OFFSET 0x70C +#define EXYNOS_5250_USB_ISOL_ENABLE BIT(0) /* Mode swtich register */ #define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230 @@ -132,7 +132,6 @@ enum exynos4x12_phy_id { EXYNOS5250_HOST, EXYNOS5250_HSIC0, EXYNOS5250_HSIC1, - EXYNOS5250_NUM_PHYS, }; /* @@ -176,20 +175,19 @@ static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on) { struct samsung_usb2_phy_driver *drv = inst->drv; u32 offset; - u32 mask; + u32 mask = EXYNOS_5250_USB_ISOL_ENABLE; - switch (inst->cfg->id) { - case EXYNOS5250_DEVICE: + if (drv->cfg == &exynos5250_usb2_phy_config && + inst->cfg->id == EXYNOS5250_DEVICE) offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET; - mask = EXYNOS_5250_USB_ISOL_OTG; - break; - case EXYNOS5250_HOST: + else if (drv->cfg == &exynos5250_usb2_phy_config && + inst->cfg->id == EXYNOS5250_HOST) offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET; - mask = EXYNOS_5250_USB_ISOL_HOST; - break; - default: + else if (drv->cfg == &exynos5420_usb2_phy_config && + inst->cfg->id == EXYNOS5250_HOST) + offset = EXYNOS_5420_USB_ISOL_HOST_OFFSET; + else return; - } regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); } @@ -390,9 +388,31 @@ static const struct samsung_usb2_common_phy exynos5250_phys[] = { }, }; +static const struct samsung_usb2_common_phy exynos5420_phys[] = { + { + .label = "host", + .id = EXYNOS5250_HOST, + .power_on = exynos5250_power_on, + .power_off = exynos5250_power_off, + }, + { + .label = "hsic", + .id = EXYNOS5250_HSIC0, + .power_on = exynos5250_power_on, + .power_off = exynos5250_power_off, + }, +}; + const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { .has_mode_switch = 1, - .num_phys = EXYNOS5250_NUM_PHYS, + .num_phys = ARRAY_SIZE(exynos5250_phys), .phys = exynos5250_phys, .rate_to_clk = exynos5250_rate_to_clk, }; + +const struct samsung_usb2_phy_config exynos5420_usb2_phy_config = { + .has_mode_switch = 1, + .num_phys = ARRAY_SIZE(exynos5420_phys), + .phys = exynos5420_phys, + .rate_to_clk = exynos5250_rate_to_clk, +}; diff --git a/drivers/phy/samsung/phy-exynos7-ufs.c b/drivers/phy/samsung/phy-exynos7-ufs.c new file mode 100644 index 000000000000..a982e7c128c5 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos7-ufs.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver data for Samsung EXYNOS7 SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + */ + +#include "phy-samsung-ufs.h" + +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL 0x720 +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) + +#define EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e + +/* Calibration for phy initialization */ +static const struct samsung_ufs_phy_cfg exynos7_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x017, 0x84, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg exynos7_pre_pwr_hs_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_HS_ANY), + /* Setting order: 1st(0x16, 2nd(0x15) */ + PHY_COMN_REG_CFG(0x016, 0xff, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x015, 0x80, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x037, 0x43, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x038, 0x3f, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x042, 0xbb, PWR_MODE_HS_G2_SER_B), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x034, 0x35, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x034, 0x36, PWR_MODE_HS_G2_SER_B), + PHY_TRSV_REG_CFG(0x035, 0x5b, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x035, 0x5c, PWR_MODE_HS_G2_SER_B), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B atfer PMC */ +static const struct samsung_ufs_phy_cfg exynos7_post_pwr_hs_cfg[] = { + PHY_COMN_REG_CFG(0x015, 0x00, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_HS_ANY), + END_UFS_PHY_CFG +}; + +static const struct samsung_ufs_phy_cfg *exynos7_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = exynos7_pre_init_cfg, + [CFG_PRE_PWR_HS] = exynos7_pre_pwr_hs_cfg, + [CFG_POST_PWR_HS] = exynos7_post_pwr_hs_cfg, +}; + +static const char * const exynos7_ufs_phy_clks[] = { + "tx0_symbol_clk", "rx0_symbol_clk", "rx1_symbol_clk", "ref_clk", +}; + +const struct samsung_ufs_phy_drvdata exynos7_ufs_phy = { + .cfgs = exynos7_ufs_phy_cfgs, + .isol = { + .offset = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL, + .mask = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK, + .en = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN, + }, + .clk_list = exynos7_ufs_phy_clks, + .num_clks = ARRAY_SIZE(exynos7_ufs_phy_clks), + .cdr_lock_status_offset = EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, +}; diff --git a/drivers/phy/samsung/phy-exynosautov9-ufs.c b/drivers/phy/samsung/phy-exynosautov9-ufs.c new file mode 100644 index 000000000000..49e2bcbef0b4 --- /dev/null +++ b/drivers/phy/samsung/phy-exynosautov9-ufs.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver data for Samsung EXYNOSAUTO v9 SoC + * + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + */ + +#include "phy-samsung-ufs.h" + +#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL 0x728 +#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 +#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) +#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e + +#define PHY_TRSV_REG_CFG_AUTOV9(o, v, d) \ + PHY_TRSV_REG_CFG_OFFSET(o, v, d, 0x50) + +/* Calibration for phy initialization */ +static const struct samsung_ufs_phy_cfg exynosautov9_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x023, 0x80, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x01d, 0x10, PWR_MODE_ANY), + + PHY_TRSV_REG_CFG_AUTOV9(0x044, 0xb5, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x43, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x05b, 0x20, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x05e, 0xc0, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x038, 0x12, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x059, 0x58, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x06c, 0x18, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x06d, 0x02, PWR_MODE_ANY), + + PHY_COMN_REG_CFG(0x023, 0xc0, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x023, 0x00, PWR_MODE_ANY), + + PHY_TRSV_REG_CFG_AUTOV9(0x042, 0x5d, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x043, 0x80, PWR_MODE_ANY), + + END_UFS_PHY_CFG, +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg exynosautov9_pre_pwr_hs_cfg[] = { + PHY_TRSV_REG_CFG_AUTOV9(0x032, 0xbc, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x03c, 0x7f, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x048, 0xc0, PWR_MODE_HS_ANY), + + PHY_TRSV_REG_CFG_AUTOV9(0x04a, 0x00, PWR_MODE_HS_G3_SER_B), + PHY_TRSV_REG_CFG_AUTOV9(0x04b, 0x10, PWR_MODE_HS_G1_SER_B | + PWR_MODE_HS_G3_SER_B), + PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x63, PWR_MODE_HS_G3_SER_B), + + END_UFS_PHY_CFG, +}; + +static const struct samsung_ufs_phy_cfg *exynosautov9_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = exynosautov9_pre_init_cfg, + [CFG_PRE_PWR_HS] = exynosautov9_pre_pwr_hs_cfg, +}; + +static const char * const exynosautov9_ufs_phy_clks[] = { + "ref_clk", +}; + +const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy = { + .cfgs = exynosautov9_ufs_phy_cfgs, + .isol = { + .offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL, + .mask = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK, + .en = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN, + }, + .clk_list = exynosautov9_ufs_phy_clks, + .num_clks = ARRAY_SIZE(exynosautov9_ufs_phy_clks), + .cdr_lock_status_offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, +}; diff --git a/drivers/phy/samsung/phy-fsd-ufs.c b/drivers/phy/samsung/phy-fsd-ufs.c new file mode 100644 index 000000000000..d36cabd53434 --- /dev/null +++ b/drivers/phy/samsung/phy-fsd-ufs.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver data for FSD SoC + * + * Copyright (C) 2022 Samsung Electronics Co., Ltd. + * + */ +#include "phy-samsung-ufs.h" + +#define FSD_EMBEDDED_COMBO_PHY_CTRL 0x724 +#define FSD_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 +#define FSD_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) +#define FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x6e + +static const struct samsung_ufs_phy_cfg fsd_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg fsd_pre_pwr_hs_cfg[] = { + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B atfer PMC */ +static const struct samsung_ufs_phy_cfg fsd_post_pwr_hs_cfg[] = { + END_UFS_PHY_CFG +}; + +static const struct samsung_ufs_phy_cfg *fsd_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = fsd_pre_init_cfg, + [CFG_PRE_PWR_HS] = fsd_pre_pwr_hs_cfg, + [CFG_POST_PWR_HS] = fsd_post_pwr_hs_cfg, +}; + +static const char * const fsd_ufs_phy_clks[] = { + "ref_clk", +}; + +const struct samsung_ufs_phy_drvdata fsd_ufs_phy = { + .cfgs = fsd_ufs_phy_cfgs, + .isol = { + .offset = FSD_EMBEDDED_COMBO_PHY_CTRL, + .mask = FSD_EMBEDDED_COMBO_PHY_CTRL_MASK, + .en = FSD_EMBEDDED_COMBO_PHY_CTRL_EN, + }, + .clk_list = fsd_ufs_phy_clks, + .num_clks = ARRAY_SIZE(fsd_ufs_phy_clks), + .cdr_lock_status_offset = FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, +}; diff --git a/drivers/phy/samsung/phy-s5pv210-usb2.c b/drivers/phy/samsung/phy-s5pv210-usb2.c index 56a5083fe6f9..32be62e49804 100644 --- a/drivers/phy/samsung/phy-s5pv210-usb2.c +++ b/drivers/phy/samsung/phy-s5pv210-usb2.c @@ -139,6 +139,10 @@ static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) udelay(10); rst &= ~rstbits; writel(rst, drv->reg_phy + S5PV210_UPHYRST); + /* The following delay is necessary for the reset sequence to be + * completed + */ + udelay(80); } else { pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); pwr |= phypwr; diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c new file mode 100644 index 000000000000..183c88e3d1ec --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver for Samsung SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author: Seungwon Jeon <essuuj@gmail.com> + * Author: Alim Akhtar <alim.akhtar@samsung.com> + * + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "phy-samsung-ufs.h" + +#define for_each_phy_lane(phy, i) \ + for (i = 0; i < (phy)->lane_cnt; i++) +#define for_each_phy_cfg(cfg) \ + for (; (cfg)->id; (cfg)++) + +#define PHY_DEF_LANE_CNT 1 + +static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy, + const struct samsung_ufs_phy_cfg *cfg, + u8 lane) +{ + enum {LANE_0, LANE_1}; /* lane index */ + + switch (lane) { + case LANE_0: + writel(cfg->val, (phy)->reg_pma + cfg->off_0); + break; + case LANE_1: + if (cfg->id == PHY_TRSV_BLK) + writel(cfg->val, (phy)->reg_pma + cfg->off_1); + break; + } +} + +static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + const unsigned int timeout_us = 100000; + const unsigned int sleep_us = 10; + u32 val; + int err; + + err = readl_poll_timeout( + ufs_phy->reg_pma + PHY_APB_ADDR(PHY_PLL_LOCK_STATUS), + val, (val & PHY_PLL_LOCK_BIT), sleep_us, timeout_us); + if (err) { + dev_err(ufs_phy->dev, + "failed to get phy pll lock acquisition %d\n", err); + goto out; + } + + err = readl_poll_timeout( + ufs_phy->reg_pma + + PHY_APB_ADDR(ufs_phy->drvdata->cdr_lock_status_offset), + val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us); + if (err) + dev_err(ufs_phy->dev, + "failed to get phy cdr lock acquisition %d\n", err); +out: + return err; +} + +static int samsung_ufs_phy_calibrate(struct phy *phy) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + const struct samsung_ufs_phy_cfg * const *cfgs = ufs_phy->cfgs; + const struct samsung_ufs_phy_cfg *cfg; + int err = 0; + int i; + + if (unlikely(ufs_phy->ufs_phy_state < CFG_PRE_INIT || + ufs_phy->ufs_phy_state >= CFG_TAG_MAX)) { + dev_err(ufs_phy->dev, "invalid phy config index %d\n", ufs_phy->ufs_phy_state); + return -EINVAL; + } + + cfg = cfgs[ufs_phy->ufs_phy_state]; + if (!cfg) + goto out; + + for_each_phy_cfg(cfg) { + for_each_phy_lane(ufs_phy, i) { + samsung_ufs_phy_config(ufs_phy, cfg, i); + } + } + + if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS) + err = samsung_ufs_phy_wait_for_lock_acq(phy); + + /** + * In Samsung ufshci, PHY need to be calibrated at different + * stages / state mainly before Linkstartup, after Linkstartup, + * before power mode change and after power mode change. + * Below state machine to make sure to calibrate PHY in each + * state. Here after configuring PHY in a given state, will + * change the state to next state so that next state phy + * calibration value can be programed + */ +out: + switch (ufs_phy->ufs_phy_state) { + case CFG_PRE_INIT: + ufs_phy->ufs_phy_state = CFG_POST_INIT; + break; + case CFG_POST_INIT: + ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS; + break; + case CFG_PRE_PWR_HS: + ufs_phy->ufs_phy_state = CFG_POST_PWR_HS; + break; + case CFG_POST_PWR_HS: + /* Change back to INIT state */ + ufs_phy->ufs_phy_state = CFG_PRE_INIT; + break; + default: + dev_err(ufs_phy->dev, "wrong state for phy calibration\n"); + } + + return err; +} + +static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy) +{ + int i; + const struct samsung_ufs_phy_drvdata *drvdata = phy->drvdata; + int num_clks = drvdata->num_clks; + + phy->clks = devm_kcalloc(phy->dev, num_clks, sizeof(*phy->clks), + GFP_KERNEL); + if (!phy->clks) + return -ENOMEM; + + for (i = 0; i < num_clks; i++) + phy->clks[i].id = drvdata->clk_list[i]; + + return devm_clk_bulk_get(phy->dev, num_clks, phy->clks); +} + +static int samsung_ufs_phy_init(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + ss_phy->lane_cnt = phy->attrs.bus_width; + ss_phy->ufs_phy_state = CFG_PRE_INIT; + + return 0; +} + +static int samsung_ufs_phy_power_on(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + int ret; + + samsung_ufs_phy_ctrl_isol(ss_phy, false); + + ret = clk_bulk_prepare_enable(ss_phy->drvdata->num_clks, ss_phy->clks); + if (ret) { + dev_err(ss_phy->dev, "failed to enable ufs phy clocks\n"); + return ret; + } + + if (ss_phy->ufs_phy_state == CFG_PRE_INIT) { + ret = samsung_ufs_phy_calibrate(phy); + if (ret) + dev_err(ss_phy->dev, "ufs phy calibration failed\n"); + } + + return ret; +} + +static int samsung_ufs_phy_power_off(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + clk_bulk_disable_unprepare(ss_phy->drvdata->num_clks, ss_phy->clks); + + samsung_ufs_phy_ctrl_isol(ss_phy, true); + + return 0; +} + +static int samsung_ufs_phy_set_mode(struct phy *generic_phy, + enum phy_mode mode, int submode) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(generic_phy); + + ss_phy->mode = PHY_MODE_INVALID; + + if (mode > 0) + ss_phy->mode = mode; + + return 0; +} + +static int samsung_ufs_phy_exit(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + ss_phy->ufs_phy_state = CFG_TAG_MAX; + + return 0; +} + +static const struct phy_ops samsung_ufs_phy_ops = { + .init = samsung_ufs_phy_init, + .exit = samsung_ufs_phy_exit, + .power_on = samsung_ufs_phy_power_on, + .power_off = samsung_ufs_phy_power_off, + .calibrate = samsung_ufs_phy_calibrate, + .set_mode = samsung_ufs_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id samsung_ufs_phy_match[]; + +static int samsung_ufs_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct samsung_ufs_phy *phy; + struct phy *gen_phy; + struct phy_provider *phy_provider; + const struct samsung_ufs_phy_drvdata *drvdata; + u32 isol_offset; + int err = 0; + + match = of_match_node(samsung_ufs_phy_match, dev->of_node); + if (!match) { + err = -EINVAL; + dev_err(dev, "failed to get match_node\n"); + goto out; + } + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + phy->reg_pma = devm_platform_ioremap_resource_byname(pdev, "phy-pma"); + if (IS_ERR(phy->reg_pma)) { + err = PTR_ERR(phy->reg_pma); + goto out; + } + + phy->reg_pmu = syscon_regmap_lookup_by_phandle( + dev->of_node, "samsung,pmu-syscon"); + if (IS_ERR(phy->reg_pmu)) { + err = PTR_ERR(phy->reg_pmu); + dev_err(dev, "failed syscon remap for pmu\n"); + goto out; + } + + gen_phy = devm_phy_create(dev, NULL, &samsung_ufs_phy_ops); + if (IS_ERR(gen_phy)) { + err = PTR_ERR(gen_phy); + dev_err(dev, "failed to create PHY for ufs-phy\n"); + goto out; + } + + drvdata = match->data; + phy->dev = dev; + phy->drvdata = drvdata; + phy->cfgs = drvdata->cfgs; + memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol)); + + if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1, + &isol_offset)) + phy->isol.offset = isol_offset; + + phy->lane_cnt = PHY_DEF_LANE_CNT; + + err = samsung_ufs_phy_clks_init(phy); + if (err) { + dev_err(dev, "failed to get phy clocks\n"); + goto out; + } + + phy_set_drvdata(gen_phy, phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + err = PTR_ERR(phy_provider); + dev_err(dev, "failed to register phy-provider\n"); + goto out; + } +out: + return err; +} + +static const struct of_device_id samsung_ufs_phy_match[] = { + { + .compatible = "samsung,exynos7-ufs-phy", + .data = &exynos7_ufs_phy, + }, { + .compatible = "samsung,exynosautov9-ufs-phy", + .data = &exynosautov9_ufs_phy, + }, { + .compatible = "tesla,fsd-ufs-phy", + .data = &fsd_ufs_phy, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_ufs_phy_match); + +static struct platform_driver samsung_ufs_phy_driver = { + .probe = samsung_ufs_phy_probe, + .driver = { + .name = "samsung-ufs-phy", + .of_match_table = samsung_ufs_phy_match, + }, +}; +module_platform_driver(samsung_ufs_phy_driver); +MODULE_DESCRIPTION("Samsung SoC UFS PHY Driver"); +MODULE_AUTHOR("Seungwon Jeon <essuuj@gmail.com>"); +MODULE_AUTHOR("Alim Akhtar <alim.akhtar@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h new file mode 100644 index 000000000000..e122960cfee8 --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-ufs.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UFS PHY driver for Samsung EXYNOS SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author: Seungwon Jeon <essuuj@gmail.com> + * Author: Alim Akhtar <alim.akhtar@samsung.com> + * + */ +#ifndef _PHY_SAMSUNG_UFS_ +#define _PHY_SAMSUNG_UFS_ + +#include <linux/phy/phy.h> +#include <linux/regmap.h> + +#define PHY_COMN_BLK 1 +#define PHY_TRSV_BLK 2 +#define END_UFS_PHY_CFG { 0 } +#define PHY_TRSV_CH_OFFSET 0x30 +#define PHY_APB_ADDR(off) ((off) << 2) + +#define PHY_COMN_REG_CFG(o, v, d) { \ + .off_0 = PHY_APB_ADDR((o)), \ + .off_1 = 0, \ + .val = (v), \ + .desc = (d), \ + .id = PHY_COMN_BLK, \ +} + +#define PHY_TRSV_REG_CFG_OFFSET(o, v, d, c) { \ + .off_0 = PHY_APB_ADDR((o)), \ + .off_1 = PHY_APB_ADDR((o) + (c)), \ + .val = (v), \ + .desc = (d), \ + .id = PHY_TRSV_BLK, \ +} + +#define PHY_TRSV_REG_CFG(o, v, d) \ + PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_TRSV_CH_OFFSET) + +/* UFS PHY registers */ +#define PHY_PLL_LOCK_STATUS 0x1e + +#define PHY_PLL_LOCK_BIT BIT(5) +#define PHY_CDR_LOCK_BIT BIT(4) + +/* description for PHY calibration */ +enum { + /* applicable to any */ + PWR_DESC_ANY = 0, + /* mode */ + PWR_DESC_PWM = 1, + PWR_DESC_HS = 2, + /* series */ + PWR_DESC_SER_A = 1, + PWR_DESC_SER_B = 2, + /* gear */ + PWR_DESC_G1 = 1, + PWR_DESC_G2 = 2, + PWR_DESC_G3 = 3, + /* field mask */ + MD_MASK = 0x3, + SR_MASK = 0x3, + GR_MASK = 0x7, +}; + +#define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY) +#define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A) +#define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B) +#define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY) +#define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A) +#define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B) +#define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY) +#define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A) +#define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B) +#define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\ + (((s) & SR_MASK) << 2) | ((m) & MD_MASK)) +#define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_PWM) +#define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\ + (((s) & SR_MASK) << 2) | PWR_DESC_HS) +#define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_HS) +#define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_ANY) +/* PHY calibration point/state */ +enum { + CFG_PRE_INIT, + CFG_POST_INIT, + CFG_PRE_PWR_HS, + CFG_POST_PWR_HS, + CFG_TAG_MAX, +}; + +struct samsung_ufs_phy_cfg { + u32 off_0; + u32 off_1; + u32 val; + u8 desc; + u8 id; +}; + +struct samsung_ufs_phy_pmu_isol { + u32 offset; + u32 mask; + u32 en; +}; + +struct samsung_ufs_phy_drvdata { + const struct samsung_ufs_phy_cfg **cfgs; + struct samsung_ufs_phy_pmu_isol isol; + const char * const *clk_list; + int num_clks; + u32 cdr_lock_status_offset; +}; + +struct samsung_ufs_phy { + struct device *dev; + void __iomem *reg_pma; + struct regmap *reg_pmu; + struct clk_bulk_data *clks; + const struct samsung_ufs_phy_drvdata *drvdata; + const struct samsung_ufs_phy_cfg * const *cfgs; + struct samsung_ufs_phy_pmu_isol isol; + u8 lane_cnt; + int ufs_phy_state; + enum phy_mode mode; +}; + +static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy) +{ + return (struct samsung_ufs_phy *)phy_get_drvdata(phy); +} + +static inline void samsung_ufs_phy_ctrl_isol( + struct samsung_ufs_phy *phy, u32 isol) +{ + regmap_update_bits(phy->reg_pmu, phy->isol.offset, + phy->isol.mask, isol ? 0 : phy->isol.en); +} + +extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy; +extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy; +extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy; + +#endif /* _PHY_SAMSUNG_UFS_ */ diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c index 090aa02e02de..ec2befabeea6 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.c +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -127,6 +127,10 @@ static const struct of_device_id samsung_usb2_phy_of_match[] = { .compatible = "samsung,exynos5250-usb2-phy", .data = &exynos5250_usb2_phy_config, }, + { + .compatible = "samsung,exynos5420-usb2-phy", + .data = &exynos5420_usb2_phy_config, + }, #endif #ifdef CONFIG_PHY_S5PV210_USB2 { @@ -143,7 +147,6 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) const struct samsung_usb2_phy_config *cfg; struct device *dev = &pdev->dev; struct phy_provider *phy_provider; - struct resource *mem; struct samsung_usb2_phy_driver *drv; int i, ret; @@ -167,8 +170,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) drv->cfg = cfg; drv->dev = dev; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv->reg_phy = devm_ioremap_resource(dev, mem); + drv->reg_phy = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(drv->reg_phy)) { dev_err(dev, "Failed to map register memory (phy)\n"); return PTR_ERR(drv->reg_phy); @@ -255,7 +257,7 @@ static struct platform_driver samsung_usb2_phy_driver = { }; module_platform_driver(samsung_usb2_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); +MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver"); MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:samsung-usb2-phy"); diff --git a/drivers/phy/samsung/phy-samsung-usb2.h b/drivers/phy/samsung/phy-samsung-usb2.h index 2c1a7d71142b..ebaf43bfc5a2 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.h +++ b/drivers/phy/samsung/phy-samsung-usb2.h @@ -43,7 +43,7 @@ struct samsung_usb2_phy_driver { struct regmap *reg_pmu; struct regmap *reg_sys; spinlock_t lock; - struct samsung_usb2_phy_instance instances[0]; + struct samsung_usb2_phy_instance instances[]; }; struct samsung_usb2_common_phy { @@ -66,5 +66,6 @@ extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; +extern const struct samsung_usb2_phy_config exynos5420_usb2_phy_config; extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config; #endif diff --git a/drivers/phy/socionext/Kconfig b/drivers/phy/socionext/Kconfig index 8c9d7c37536a..8ae644756352 100644 --- a/drivers/phy/socionext/Kconfig +++ b/drivers/phy/socionext/Kconfig @@ -34,3 +34,13 @@ config PHY_UNIPHIER_PCIE help Enable this to support PHY implemented in PCIe controller on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs. + +config PHY_UNIPHIER_AHCI + tristate "UniPhier AHCI PHY driver" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on OF && HAS_IOMEM + default SATA_AHCI_PLATFORM + select GENERIC_PHY + help + Enable this to support PHY implemented in AHCI controller + on UniPhier SoCs. This driver supports Pro4, PXs2 and PXs3 SoCs. diff --git a/drivers/phy/socionext/Makefile b/drivers/phy/socionext/Makefile index 7dc9095b5bb7..e67c2da6675c 100644 --- a/drivers/phy/socionext/Makefile +++ b/drivers/phy/socionext/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_PHY_UNIPHIER_USB2) += phy-uniphier-usb2.o obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3hs.o phy-uniphier-usb3ss.o obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o +obj-$(CONFIG_PHY_UNIPHIER_AHCI) += phy-uniphier-ahci.o diff --git a/drivers/phy/socionext/phy-uniphier-ahci.c b/drivers/phy/socionext/phy-uniphier-ahci.c new file mode 100644 index 000000000000..28cf3efe0695 --- /dev/null +++ b/drivers/phy/socionext/phy-uniphier-ahci.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-uniphier-ahci.c - PHY driver for UniPhier AHCI controller + * Copyright 2016-2020, Socionext Inc. + * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +struct uniphier_ahciphy_priv { + struct device *dev; + void __iomem *base; + struct clk *clk, *clk_parent, *clk_parent_gio; + struct reset_control *rst, *rst_parent, *rst_parent_gio; + struct reset_control *rst_pm, *rst_tx, *rst_rx; + const struct uniphier_ahciphy_soc_data *data; +}; + +struct uniphier_ahciphy_soc_data { + int (*init)(struct uniphier_ahciphy_priv *priv); + int (*power_on)(struct uniphier_ahciphy_priv *priv); + int (*power_off)(struct uniphier_ahciphy_priv *priv); + bool is_legacy; + bool is_ready_high; + bool is_phy_clk; +}; + +/* for Pro4 */ +#define CKCTRL0 0x0 +#define CKCTRL0_CK_OFF BIT(9) +#define CKCTRL0_NCY_MASK GENMASK(8, 4) +#define CKCTRL0_NCY5_MASK GENMASK(3, 2) +#define CKCTRL0_PRESCALE_MASK GENMASK(1, 0) +#define CKCTRL1 0x4 +#define CKCTRL1_LOS_LVL_MASK GENMASK(20, 16) +#define CKCTRL1_TX_LVL_MASK GENMASK(12, 8) +#define RXTXCTRL 0x8 +#define RXTXCTRL_RX_EQ_VALL_MASK GENMASK(31, 29) +#define RXTXCTRL_RX_DPLL_MODE_MASK GENMASK(28, 26) +#define RXTXCTRL_TX_ATTEN_MASK GENMASK(14, 12) +#define RXTXCTRL_TX_BOOST_MASK GENMASK(11, 8) +#define RXTXCTRL_TX_EDGERATE_MASK GENMASK(3, 2) +#define RXTXCTRL_TX_CKO_EN BIT(0) +#define RSTPWR 0x30 +#define RSTPWR_RX_EN_VAL BIT(18) + +/* for PXs2/PXs3 */ +#define CKCTRL 0x0 +#define CKCTRL_P0_READY BIT(15) +#define CKCTRL_P0_RESET BIT(10) +#define CKCTRL_REF_SSP_EN BIT(9) +#define TXCTRL0 0x4 +#define TXCTRL0_AMP_G3_MASK GENMASK(22, 16) +#define TXCTRL0_AMP_G2_MASK GENMASK(14, 8) +#define TXCTRL0_AMP_G1_MASK GENMASK(6, 0) +#define TXCTRL1 0x8 +#define TXCTRL1_DEEMPH_G3_MASK GENMASK(21, 16) +#define TXCTRL1_DEEMPH_G2_MASK GENMASK(13, 8) +#define TXCTRL1_DEEMPH_G1_MASK GENMASK(5, 0) +#define RXCTRL 0xc +#define RXCTRL_LOS_LVL_MASK GENMASK(20, 16) +#define RXCTRL_LOS_BIAS_MASK GENMASK(10, 8) +#define RXCTRL_RX_EQ_MASK GENMASK(2, 0) + +static int uniphier_ahciphy_pro4_init(struct uniphier_ahciphy_priv *priv) +{ + u32 val; + + /* set phy MPLL parameters */ + val = readl(priv->base + CKCTRL0); + val &= ~CKCTRL0_NCY_MASK; + val |= FIELD_PREP(CKCTRL0_NCY_MASK, 0x6); + val &= ~CKCTRL0_NCY5_MASK; + val |= FIELD_PREP(CKCTRL0_NCY5_MASK, 0x2); + val &= ~CKCTRL0_PRESCALE_MASK; + val |= FIELD_PREP(CKCTRL0_PRESCALE_MASK, 0x1); + writel(val, priv->base + CKCTRL0); + + /* setup phy control parameters */ + val = readl(priv->base + CKCTRL1); + val &= ~CKCTRL1_LOS_LVL_MASK; + val |= FIELD_PREP(CKCTRL1_LOS_LVL_MASK, 0x10); + val &= ~CKCTRL1_TX_LVL_MASK; + val |= FIELD_PREP(CKCTRL1_TX_LVL_MASK, 0x06); + writel(val, priv->base + CKCTRL1); + + val = readl(priv->base + RXTXCTRL); + val &= ~RXTXCTRL_RX_EQ_VALL_MASK; + val |= FIELD_PREP(RXTXCTRL_RX_EQ_VALL_MASK, 0x6); + val &= ~RXTXCTRL_RX_DPLL_MODE_MASK; + val |= FIELD_PREP(RXTXCTRL_RX_DPLL_MODE_MASK, 0x3); + val &= ~RXTXCTRL_TX_ATTEN_MASK; + val |= FIELD_PREP(RXTXCTRL_TX_ATTEN_MASK, 0x3); + val &= ~RXTXCTRL_TX_BOOST_MASK; + val |= FIELD_PREP(RXTXCTRL_TX_BOOST_MASK, 0x5); + val &= ~RXTXCTRL_TX_EDGERATE_MASK; + val |= FIELD_PREP(RXTXCTRL_TX_EDGERATE_MASK, 0x0); + writel(val, priv->base + RXTXCTRL); + + return 0; +} + +static int uniphier_ahciphy_pro4_power_on(struct uniphier_ahciphy_priv *priv) +{ + u32 val; + int ret; + + /* enable reference clock for phy */ + val = readl(priv->base + CKCTRL0); + val &= ~CKCTRL0_CK_OFF; + writel(val, priv->base + CKCTRL0); + + /* enable TX clock */ + val = readl(priv->base + RXTXCTRL); + val |= RXTXCTRL_TX_CKO_EN; + writel(val, priv->base + RXTXCTRL); + + /* wait until RX is ready */ + ret = readl_poll_timeout(priv->base + RSTPWR, val, + !(val & RSTPWR_RX_EN_VAL), 200, 2000); + if (ret) { + dev_err(priv->dev, "Failed to check whether Rx is ready\n"); + goto out_disable_clock; + } + + /* release all reset */ + ret = reset_control_deassert(priv->rst_pm); + if (ret) { + dev_err(priv->dev, "Failed to release PM reset\n"); + goto out_disable_clock; + } + + ret = reset_control_deassert(priv->rst_tx); + if (ret) { + dev_err(priv->dev, "Failed to release Tx reset\n"); + goto out_reset_pm_assert; + } + + ret = reset_control_deassert(priv->rst_rx); + if (ret) { + dev_err(priv->dev, "Failed to release Rx reset\n"); + goto out_reset_tx_assert; + } + + return 0; + +out_reset_tx_assert: + reset_control_assert(priv->rst_tx); +out_reset_pm_assert: + reset_control_assert(priv->rst_pm); + +out_disable_clock: + /* disable TX clock */ + val = readl(priv->base + RXTXCTRL); + val &= ~RXTXCTRL_TX_CKO_EN; + writel(val, priv->base + RXTXCTRL); + + /* disable reference clock for phy */ + val = readl(priv->base + CKCTRL0); + val |= CKCTRL0_CK_OFF; + writel(val, priv->base + CKCTRL0); + + return ret; +} + +static int uniphier_ahciphy_pro4_power_off(struct uniphier_ahciphy_priv *priv) +{ + u32 val; + + reset_control_assert(priv->rst_rx); + reset_control_assert(priv->rst_tx); + reset_control_assert(priv->rst_pm); + + /* disable TX clock */ + val = readl(priv->base + RXTXCTRL); + val &= ~RXTXCTRL_TX_CKO_EN; + writel(val, priv->base + RXTXCTRL); + + /* disable reference clock for phy */ + val = readl(priv->base + CKCTRL0); + val |= CKCTRL0_CK_OFF; + writel(val, priv->base + CKCTRL0); + + return 0; +} + +static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv, + bool enable) +{ + u32 val; + + val = readl(priv->base + CKCTRL); + + if (enable) { + val |= CKCTRL_REF_SSP_EN; + writel(val, priv->base + CKCTRL); + val &= ~CKCTRL_P0_RESET; + writel(val, priv->base + CKCTRL); + } else { + val |= CKCTRL_P0_RESET; + writel(val, priv->base + CKCTRL); + val &= ~CKCTRL_REF_SSP_EN; + writel(val, priv->base + CKCTRL); + } +} + +static int uniphier_ahciphy_pxs2_power_on(struct uniphier_ahciphy_priv *priv) +{ + int ret; + u32 val; + + uniphier_ahciphy_pxs2_enable(priv, true); + + /* wait until PLL is ready */ + if (priv->data->is_ready_high) + ret = readl_poll_timeout(priv->base + CKCTRL, val, + (val & CKCTRL_P0_READY), 200, 400); + else + ret = readl_poll_timeout(priv->base + CKCTRL, val, + !(val & CKCTRL_P0_READY), 200, 400); + if (ret) { + dev_err(priv->dev, "Failed to check whether PHY PLL is ready\n"); + uniphier_ahciphy_pxs2_enable(priv, false); + } + + return ret; +} + +static int uniphier_ahciphy_pxs2_power_off(struct uniphier_ahciphy_priv *priv) +{ + uniphier_ahciphy_pxs2_enable(priv, false); + + return 0; +} + +static int uniphier_ahciphy_pxs3_init(struct uniphier_ahciphy_priv *priv) +{ + int i; + u32 val; + + /* setup port parameter */ + val = readl(priv->base + TXCTRL0); + val &= ~TXCTRL0_AMP_G3_MASK; + val |= FIELD_PREP(TXCTRL0_AMP_G3_MASK, 0x73); + val &= ~TXCTRL0_AMP_G2_MASK; + val |= FIELD_PREP(TXCTRL0_AMP_G2_MASK, 0x46); + val &= ~TXCTRL0_AMP_G1_MASK; + val |= FIELD_PREP(TXCTRL0_AMP_G1_MASK, 0x42); + writel(val, priv->base + TXCTRL0); + + val = readl(priv->base + TXCTRL1); + val &= ~TXCTRL1_DEEMPH_G3_MASK; + val |= FIELD_PREP(TXCTRL1_DEEMPH_G3_MASK, 0x23); + val &= ~TXCTRL1_DEEMPH_G2_MASK; + val |= FIELD_PREP(TXCTRL1_DEEMPH_G2_MASK, 0x05); + val &= ~TXCTRL1_DEEMPH_G1_MASK; + val |= FIELD_PREP(TXCTRL1_DEEMPH_G1_MASK, 0x05); + + val = readl(priv->base + RXCTRL); + val &= ~RXCTRL_LOS_LVL_MASK; + val |= FIELD_PREP(RXCTRL_LOS_LVL_MASK, 0x9); + val &= ~RXCTRL_LOS_BIAS_MASK; + val |= FIELD_PREP(RXCTRL_LOS_BIAS_MASK, 0x2); + val &= ~RXCTRL_RX_EQ_MASK; + val |= FIELD_PREP(RXCTRL_RX_EQ_MASK, 0x1); + + /* dummy read 25 times to make a wait time for the phy to stabilize */ + for (i = 0; i < 25; i++) + readl(priv->base + CKCTRL); + + return 0; +} + +static int uniphier_ahciphy_init(struct phy *phy) +{ + struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(priv->clk_parent_gio); + if (ret) + return ret; + + ret = clk_prepare_enable(priv->clk_parent); + if (ret) + goto out_clk_gio_disable; + + ret = reset_control_deassert(priv->rst_parent_gio); + if (ret) + goto out_clk_disable; + + ret = reset_control_deassert(priv->rst_parent); + if (ret) + goto out_rst_gio_assert; + + if (priv->data->init) { + ret = priv->data->init(priv); + if (ret) + goto out_rst_assert; + } + + return 0; + +out_rst_assert: + reset_control_assert(priv->rst_parent); +out_rst_gio_assert: + reset_control_assert(priv->rst_parent_gio); +out_clk_disable: + clk_disable_unprepare(priv->clk_parent); +out_clk_gio_disable: + clk_disable_unprepare(priv->clk_parent_gio); + + return ret; +} + +static int uniphier_ahciphy_exit(struct phy *phy) +{ + struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy); + + reset_control_assert(priv->rst_parent); + reset_control_assert(priv->rst_parent_gio); + clk_disable_unprepare(priv->clk_parent); + clk_disable_unprepare(priv->clk_parent_gio); + + return 0; +} + +static int uniphier_ahciphy_power_on(struct phy *phy) +{ + struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rst); + if (ret) + goto out_clk_disable; + + if (priv->data->power_on) { + ret = priv->data->power_on(priv); + if (ret) + goto out_reset_assert; + } + + return 0; + +out_reset_assert: + reset_control_assert(priv->rst); +out_clk_disable: + clk_disable_unprepare(priv->clk); + + return ret; +} + +static int uniphier_ahciphy_power_off(struct phy *phy) +{ + struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + if (priv->data->power_off) + ret = priv->data->power_off(priv); + + reset_control_assert(priv->rst); + clk_disable_unprepare(priv->clk); + + return ret; +} + +static const struct phy_ops uniphier_ahciphy_ops = { + .init = uniphier_ahciphy_init, + .exit = uniphier_ahciphy_exit, + .power_on = uniphier_ahciphy_power_on, + .power_off = uniphier_ahciphy_power_off, + .owner = THIS_MODULE, +}; + +static int uniphier_ahciphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_ahciphy_priv *priv; + struct phy *phy; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->data = of_device_get_match_data(dev); + if (WARN_ON(!priv->data)) + return -EINVAL; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk_parent = devm_clk_get(dev, "link"); + if (IS_ERR(priv->clk_parent)) + return PTR_ERR(priv->clk_parent); + + if (priv->data->is_phy_clk) { + priv->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + } + + priv->rst_parent = devm_reset_control_get_shared(dev, "link"); + if (IS_ERR(priv->rst_parent)) + return PTR_ERR(priv->rst_parent); + + priv->rst = devm_reset_control_get_shared(dev, "phy"); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + + if (priv->data->is_legacy) { + priv->clk_parent_gio = devm_clk_get(dev, "gio"); + if (IS_ERR(priv->clk_parent_gio)) + return PTR_ERR(priv->clk_parent_gio); + priv->rst_parent_gio = + devm_reset_control_get_shared(dev, "gio"); + if (IS_ERR(priv->rst_parent_gio)) + return PTR_ERR(priv->rst_parent_gio); + + priv->rst_pm = devm_reset_control_get_shared(dev, "pm"); + if (IS_ERR(priv->rst_pm)) + return PTR_ERR(priv->rst_pm); + + priv->rst_tx = devm_reset_control_get_shared(dev, "tx"); + if (IS_ERR(priv->rst_tx)) + return PTR_ERR(priv->rst_tx); + + priv->rst_rx = devm_reset_control_get_shared(dev, "rx"); + if (IS_ERR(priv->rst_rx)) + return PTR_ERR(priv->rst_rx); + } + + phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static const struct uniphier_ahciphy_soc_data uniphier_pro4_data = { + .init = uniphier_ahciphy_pro4_init, + .power_on = uniphier_ahciphy_pro4_power_on, + .power_off = uniphier_ahciphy_pro4_power_off, + .is_legacy = true, + .is_phy_clk = false, +}; + +static const struct uniphier_ahciphy_soc_data uniphier_pxs2_data = { + .power_on = uniphier_ahciphy_pxs2_power_on, + .power_off = uniphier_ahciphy_pxs2_power_off, + .is_legacy = false, + .is_ready_high = false, + .is_phy_clk = false, +}; + +static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = { + .init = uniphier_ahciphy_pxs3_init, + .power_on = uniphier_ahciphy_pxs2_power_on, + .power_off = uniphier_ahciphy_pxs2_power_off, + .is_legacy = false, + .is_ready_high = true, + .is_phy_clk = true, +}; + +static const struct of_device_id uniphier_ahciphy_match[] = { + { + .compatible = "socionext,uniphier-pro4-ahci-phy", + .data = &uniphier_pro4_data, + }, + { + .compatible = "socionext,uniphier-pxs2-ahci-phy", + .data = &uniphier_pxs2_data, + }, + { + .compatible = "socionext,uniphier-pxs3-ahci-phy", + .data = &uniphier_pxs3_data, + }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, uniphier_ahciphy_match); + +static struct platform_driver uniphier_ahciphy_driver = { + .probe = uniphier_ahciphy_probe, + .driver = { + .name = "uniphier-ahci-phy", + .of_match_table = uniphier_ahciphy_match, + }, +}; +module_platform_driver(uniphier_ahciphy_driver); + +MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); +MODULE_DESCRIPTION("UniPhier PHY driver for AHCI controller"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c index 93ffbd2940fa..ebca296ef123 100644 --- a/drivers/phy/socionext/phy-uniphier-pcie.c +++ b/drivers/phy/socionext/phy-uniphier-pcie.c @@ -19,11 +19,18 @@ #include <linux/resource.h> /* PHY */ +#define PCL_PHY_CLKCTRL 0x0000 +#define PORT_SEL_MASK GENMASK(11, 9) +#define PORT_SEL_1 FIELD_PREP(PORT_SEL_MASK, 1) + #define PCL_PHY_TEST_I 0x2000 -#define PCL_PHY_TEST_O 0x2004 #define TESTI_DAT_MASK GENMASK(13, 6) #define TESTI_ADR_MASK GENMASK(5, 1) #define TESTI_WR_EN BIT(0) +#define TESTIO_PHY_SHIFT 16 + +#define PCL_PHY_TEST_O 0x2004 +#define TESTO_DAT_MASK GENMASK(7, 0) #define PCL_PHY_RESET 0x200c #define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */ @@ -33,6 +40,10 @@ #define SG_USBPCIESEL 0x590 #define SG_USBPCIESEL_PCIE BIT(0) +/* SC */ +#define SC_US3SRCSEL 0x2244 +#define SC_US3SRCSEL_2LANE GENMASK(9, 8) + #define PCL_PHY_R00 0 #define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */ #define PCL_PHY_R06 6 @@ -41,52 +52,71 @@ #define PCL_PHY_R26 26 #define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */ #define VCO_CTRL_INIT_VAL 5 +#define PCL_PHY_R28 28 +#define VCOPLL_CLMP GENMASK(3, 2) /* Tx VCOPLL clamp mode */ +#define VCOPLL_CLMP_VAL 0 struct uniphier_pciephy_priv { void __iomem *base; struct device *dev; - struct clk *clk; - struct reset_control *rst; + struct clk *clk, *clk_gio; + struct reset_control *rst, *rst_gio; const struct uniphier_pciephy_soc_data *data; }; struct uniphier_pciephy_soc_data { - bool has_syscon; + bool is_legacy; + bool is_dual_phy; + void (*set_phymode)(struct regmap *regmap); }; static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv, - u32 data) + int id, u32 data) { + if (id) + data <<= TESTIO_PHY_SHIFT; + /* need to read TESTO twice after accessing TESTI */ writel(data, priv->base + PCL_PHY_TEST_I); readl(priv->base + PCL_PHY_TEST_O); readl(priv->base + PCL_PHY_TEST_O); } +static u32 uniphier_pciephy_testio_read(struct uniphier_pciephy_priv *priv, int id) +{ + u32 val = readl(priv->base + PCL_PHY_TEST_O); + + if (id) + val >>= TESTIO_PHY_SHIFT; + + return val & TESTO_DAT_MASK; +} + static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv, - u32 reg, u32 mask, u32 param) + int id, u32 reg, u32 mask, u32 param) { u32 val; /* read previous data */ val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, reg); - uniphier_pciephy_testio_write(priv, val); - val = readl(priv->base + PCL_PHY_TEST_O); + uniphier_pciephy_testio_write(priv, id, val); + val = uniphier_pciephy_testio_read(priv, id); /* update value */ - val &= ~FIELD_PREP(TESTI_DAT_MASK, mask); - val = FIELD_PREP(TESTI_DAT_MASK, mask & param); + val &= ~mask; + val |= mask & param; + val = FIELD_PREP(TESTI_DAT_MASK, val); val |= FIELD_PREP(TESTI_ADR_MASK, reg); - uniphier_pciephy_testio_write(priv, val); - uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN); - uniphier_pciephy_testio_write(priv, val); + uniphier_pciephy_testio_write(priv, id, val); + uniphier_pciephy_testio_write(priv, id, val | TESTI_WR_EN); + uniphier_pciephy_testio_write(priv, id, val); /* read current data as dummy */ val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, reg); - uniphier_pciephy_testio_write(priv, val); - readl(priv->base + PCL_PHY_TEST_O); + uniphier_pciephy_testio_write(priv, id, val); + uniphier_pciephy_testio_read(priv, id); } static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv) @@ -111,22 +141,45 @@ static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv) static int uniphier_pciephy_init(struct phy *phy) { struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); - int ret; + u32 val; + int ret, id; ret = clk_prepare_enable(priv->clk); if (ret) return ret; - ret = reset_control_deassert(priv->rst); + ret = clk_prepare_enable(priv->clk_gio); if (ret) goto out_clk_disable; - uniphier_pciephy_set_param(priv, PCL_PHY_R00, + ret = reset_control_deassert(priv->rst); + if (ret) + goto out_clk_gio_disable; + + ret = reset_control_deassert(priv->rst_gio); + if (ret) + goto out_rst_assert; + + /* support only 1 port */ + val = readl(priv->base + PCL_PHY_CLKCTRL); + val &= ~PORT_SEL_MASK; + val |= PORT_SEL_1; + writel(val, priv->base + PCL_PHY_CLKCTRL); + + /* legacy controller doesn't have phy_reset and parameters */ + if (priv->data->is_legacy) + return 0; + + for (id = 0; id < (priv->data->is_dual_phy ? 2 : 1); id++) { + uniphier_pciephy_set_param(priv, id, PCL_PHY_R00, RX_EQ_ADJ_EN, RX_EQ_ADJ_EN); - uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ, + uniphier_pciephy_set_param(priv, id, PCL_PHY_R06, RX_EQ_ADJ, FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL)); - uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL, + uniphier_pciephy_set_param(priv, id, PCL_PHY_R26, VCO_CTRL, FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL)); + uniphier_pciephy_set_param(priv, id, PCL_PHY_R28, VCOPLL_CLMP, + FIELD_PREP(VCOPLL_CLMP, VCOPLL_CLMP_VAL)); + } usleep_range(1, 10); uniphier_pciephy_deassert(priv); @@ -134,6 +187,10 @@ static int uniphier_pciephy_init(struct phy *phy) return 0; +out_rst_assert: + reset_control_assert(priv->rst); +out_clk_gio_disable: + clk_disable_unprepare(priv->clk_gio); out_clk_disable: clk_disable_unprepare(priv->clk); @@ -144,8 +201,11 @@ static int uniphier_pciephy_exit(struct phy *phy) { struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); - uniphier_pciephy_assert(priv); + if (!priv->data->is_legacy) + uniphier_pciephy_assert(priv); + reset_control_assert(priv->rst_gio); reset_control_assert(priv->rst); + clk_disable_unprepare(priv->clk_gio); clk_disable_unprepare(priv->clk); return 0; @@ -163,7 +223,6 @@ static int uniphier_pciephy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct regmap *regmap; - struct resource *res; struct phy *phy; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -176,18 +235,36 @@ static int uniphier_pciephy_probe(struct platform_device *pdev) priv->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - priv->rst = devm_reset_control_get_shared(dev, NULL); - if (IS_ERR(priv->rst)) - return PTR_ERR(priv->rst); + if (priv->data->is_legacy) { + priv->clk_gio = devm_clk_get(dev, "gio"); + if (IS_ERR(priv->clk_gio)) + return PTR_ERR(priv->clk_gio); + + priv->rst_gio = + devm_reset_control_get_shared(dev, "gio"); + if (IS_ERR(priv->rst_gio)) + return PTR_ERR(priv->rst_gio); + + priv->clk = devm_clk_get(dev, "link"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->rst = devm_reset_control_get_shared(dev, "link"); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + } else { + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->rst = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + } phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops); if (IS_ERR(phy)) @@ -195,9 +272,8 @@ static int uniphier_pciephy_probe(struct platform_device *pdev) regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "socionext,syscon"); - if (!IS_ERR(regmap) && priv->data->has_syscon) - regmap_update_bits(regmap, SG_USBPCIESEL, - SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE); + if (!IS_ERR(regmap) && priv->data->set_phymode) + priv->data->set_phymode(regmap); phy_set_drvdata(phy, priv); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); @@ -205,16 +281,45 @@ static int uniphier_pciephy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } +static void uniphier_pciephy_ld20_setmode(struct regmap *regmap) +{ + regmap_update_bits(regmap, SG_USBPCIESEL, + SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE); +} + +static void uniphier_pciephy_nx1_setmode(struct regmap *regmap) +{ + regmap_update_bits(regmap, SC_US3SRCSEL, + SC_US3SRCSEL_2LANE, SC_US3SRCSEL_2LANE); +} + +static const struct uniphier_pciephy_soc_data uniphier_pro5_data = { + .is_legacy = true, +}; + static const struct uniphier_pciephy_soc_data uniphier_ld20_data = { - .has_syscon = true, + .is_legacy = false, + .is_dual_phy = false, + .set_phymode = uniphier_pciephy_ld20_setmode, }; static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = { - .has_syscon = false, + .is_legacy = false, + .is_dual_phy = false, +}; + +static const struct uniphier_pciephy_soc_data uniphier_nx1_data = { + .is_legacy = false, + .is_dual_phy = true, + .set_phymode = uniphier_pciephy_nx1_setmode, }; static const struct of_device_id uniphier_pciephy_match[] = { { + .compatible = "socionext,uniphier-pro5-pcie-phy", + .data = &uniphier_pro5_data, + }, + { .compatible = "socionext,uniphier-ld20-pcie-phy", .data = &uniphier_ld20_data, }, @@ -222,6 +327,10 @@ static const struct of_device_id uniphier_pciephy_match[] = { .compatible = "socionext,uniphier-pxs3-pcie-phy", .data = &uniphier_pxs3_data, }, + { + .compatible = "socionext,uniphier-nx1-pcie-phy", + .data = &uniphier_nx1_data, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, uniphier_pciephy_match); diff --git a/drivers/phy/socionext/phy-uniphier-usb3hs.c b/drivers/phy/socionext/phy-uniphier-usb3hs.c index 50f379fc4e06..8c8673df0084 100644 --- a/drivers/phy/socionext/phy-uniphier-usb3hs.c +++ b/drivers/phy/socionext/phy-uniphier-usb3hs.c @@ -41,10 +41,12 @@ #define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) } +#define RX_CHK_SYNC PHY_F(0, 5, 5) /* RX sync mode */ +#define RX_SYNC_SEL PHY_F(1, 1, 0) /* RX sync length */ #define LS_SLEW PHY_F(10, 6, 6) /* LS mode slew rate */ #define FS_LS_DRV PHY_F(10, 5, 5) /* FS/LS slew rate */ -#define MAX_PHY_PARAMS 2 +#define MAX_PHY_PARAMS 4 struct uniphier_u3hsphy_param { struct { @@ -66,13 +68,14 @@ struct uniphier_u3hsphy_trim_param { struct uniphier_u3hsphy_priv { struct device *dev; void __iomem *base; - struct clk *clk, *clk_parent, *clk_ext; - struct reset_control *rst, *rst_parent; + struct clk *clk, *clk_parent, *clk_ext, *clk_parent_gio; + struct reset_control *rst, *rst_parent, *rst_parent_gio; struct regulator *vbus; const struct uniphier_u3hsphy_soc_data *data; }; struct uniphier_u3hsphy_soc_data { + bool is_legacy; int nparams; const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS]; u32 config0; @@ -256,11 +259,20 @@ static int uniphier_u3hsphy_init(struct phy *phy) if (ret) return ret; - ret = reset_control_deassert(priv->rst_parent); + ret = clk_prepare_enable(priv->clk_parent_gio); if (ret) goto out_clk_disable; - if (!priv->data->config0 && !priv->data->config1) + ret = reset_control_deassert(priv->rst_parent); + if (ret) + goto out_clk_gio_disable; + + ret = reset_control_deassert(priv->rst_parent_gio); + if (ret) + goto out_rst_assert; + + if ((priv->data->is_legacy) + || (!priv->data->config0 && !priv->data->config1)) return 0; config0 = priv->data->config0; @@ -280,6 +292,8 @@ static int uniphier_u3hsphy_init(struct phy *phy) out_rst_assert: reset_control_assert(priv->rst_parent); +out_clk_gio_disable: + clk_disable_unprepare(priv->clk_parent_gio); out_clk_disable: clk_disable_unprepare(priv->clk_parent); @@ -290,7 +304,9 @@ static int uniphier_u3hsphy_exit(struct phy *phy) { struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy); + reset_control_assert(priv->rst_parent_gio); reset_control_assert(priv->rst_parent); + clk_disable_unprepare(priv->clk_parent_gio); clk_disable_unprepare(priv->clk_parent); return 0; @@ -309,7 +325,6 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct uniphier_u3hsphy_priv *priv; struct phy_provider *phy_provider; - struct resource *res; struct phy *phy; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -322,27 +337,38 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev) priv->data->nparams > MAX_PHY_PARAMS)) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - priv->clk = devm_clk_get(dev, "phy"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); + if (!priv->data->is_legacy) { + priv->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->clk_ext = devm_clk_get_optional(dev, "phy-ext"); + if (IS_ERR(priv->clk_ext)) + return PTR_ERR(priv->clk_ext); + + priv->rst = devm_reset_control_get_shared(dev, "phy"); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + + } else { + priv->clk_parent_gio = devm_clk_get(dev, "gio"); + if (IS_ERR(priv->clk_parent_gio)) + return PTR_ERR(priv->clk_parent_gio); + + priv->rst_parent_gio = + devm_reset_control_get_shared(dev, "gio"); + if (IS_ERR(priv->rst_parent_gio)) + return PTR_ERR(priv->rst_parent_gio); + } priv->clk_parent = devm_clk_get(dev, "link"); if (IS_ERR(priv->clk_parent)) return PTR_ERR(priv->clk_parent); - priv->clk_ext = devm_clk_get_optional(dev, "phy-ext"); - if (IS_ERR(priv->clk_ext)) - return PTR_ERR(priv->clk_ext); - - priv->rst = devm_reset_control_get_shared(dev, "phy"); - if (IS_ERR(priv->rst)) - return PTR_ERR(priv->rst); - priv->rst_parent = devm_reset_control_get_shared(dev, "link"); if (IS_ERR(priv->rst_parent)) return PTR_ERR(priv->rst_parent); @@ -364,13 +390,26 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } -static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = { +static const struct uniphier_u3hsphy_soc_data uniphier_pro5_data = { + .is_legacy = true, .nparams = 0, }; -static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = { +static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = { + .is_legacy = false, .nparams = 2, .param = { + { RX_CHK_SYNC, 1 }, + { RX_SYNC_SEL, 1 }, + }, +}; + +static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = { + .is_legacy = false, + .nparams = 4, + .param = { + { RX_CHK_SYNC, 1 }, + { RX_SYNC_SEL, 1 }, { LS_SLEW, 1 }, { FS_LS_DRV, 1 }, }, @@ -380,7 +419,12 @@ static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = { }; static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = { - .nparams = 0, + .is_legacy = false, + .nparams = 2, + .param = { + { RX_CHK_SYNC, 1 }, + { RX_SYNC_SEL, 1 }, + }, .trim_func = uniphier_u3hsphy_trim_ld20, .config0 = 0x92316680, .config1 = 0x00000106, @@ -388,6 +432,10 @@ static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = { static const struct of_device_id uniphier_u3hsphy_match[] = { { + .compatible = "socionext,uniphier-pro5-usb3-hsphy", + .data = &uniphier_pro5_data, + }, + { .compatible = "socionext,uniphier-pxs2-usb3-hsphy", .data = &uniphier_pxs2_data, }, @@ -399,6 +447,10 @@ static const struct of_device_id uniphier_u3hsphy_match[] = { .compatible = "socionext,uniphier-pxs3-usb3-hsphy", .data = &uniphier_pxs3_data, }, + { + .compatible = "socionext,uniphier-nx1-usb3-hsphy", + .data = &uniphier_pxs3_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match); diff --git a/drivers/phy/socionext/phy-uniphier-usb3ss.c b/drivers/phy/socionext/phy-uniphier-usb3ss.c index ec231e40ef2a..f402ed8732fd 100644 --- a/drivers/phy/socionext/phy-uniphier-usb3ss.c +++ b/drivers/phy/socionext/phy-uniphier-usb3ss.c @@ -22,11 +22,13 @@ #include <linux/reset.h> #define SSPHY_TESTI 0x0 -#define SSPHY_TESTO 0x4 #define TESTI_DAT_MASK GENMASK(13, 6) #define TESTI_ADR_MASK GENMASK(5, 1) #define TESTI_WR_EN BIT(0) +#define SSPHY_TESTO 0x4 +#define TESTO_DAT_MASK GENMASK(7, 0) + #define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) } #define CDR_CPD_TRIM PHY_F(7, 3, 0) /* RxPLL charge pump current */ @@ -84,12 +86,12 @@ static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv, val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); uniphier_u3ssphy_testio_write(priv, val); - val = readl(priv->base + SSPHY_TESTO); + val = readl(priv->base + SSPHY_TESTO) & TESTO_DAT_MASK; /* update value */ - val &= ~FIELD_PREP(TESTI_DAT_MASK, field_mask); + val &= ~field_mask; data = field_mask & (p->value << p->field.lsb); - val = FIELD_PREP(TESTI_DAT_MASK, data); + val = FIELD_PREP(TESTI_DAT_MASK, data | val); val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); uniphier_u3ssphy_testio_write(priv, val); uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN); @@ -215,7 +217,6 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct uniphier_u3ssphy_priv *priv; struct phy_provider *phy_provider; - struct resource *res; struct phy *phy; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -228,8 +229,7 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev) priv->data->nparams > MAX_PHY_PARAMS)) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); @@ -315,6 +315,10 @@ static const struct of_device_id uniphier_u3ssphy_match[] = { .data = &uniphier_pro4_data, }, { + .compatible = "socionext,uniphier-pro5-usb3-ssphy", + .data = &uniphier_pro4_data, + }, + { .compatible = "socionext,uniphier-pxs2-usb3-ssphy", .data = &uniphier_pxs2_data, }, @@ -326,6 +330,10 @@ static const struct of_device_id uniphier_u3ssphy_match[] = { .compatible = "socionext,uniphier-pxs3-usb3-ssphy", .data = &uniphier_ld20_data, }, + { + .compatible = "socionext,uniphier-nx1-usb3-ssphy", + .data = &uniphier_ld20_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match); diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig index b32f44ff9033..3fc3d0781fb8 100644 --- a/drivers/phy/st/Kconfig +++ b/drivers/phy/st/Kconfig @@ -36,6 +36,7 @@ config PHY_STIH407_USB config PHY_STM32_USBPHYC tristate "STMicroelectronics STM32 USB HS PHY Controller driver" depends on ARCH_STM32 || COMPILE_TEST + depends on COMMON_CLK select GENERIC_PHY help Enable this to support the High-Speed USB transceivers that are part diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 56bdea4b0bd9..5bb9647b078f 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -7,16 +7,20 @@ */ #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/delay.h> -#include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/reset.h> +#include <linux/units.h> #define STM32_USBPHYC_PLL 0x0 #define STM32_USBPHYC_MISC 0x8 +#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100)) +#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100)) #define STM32_USBPHYC_VERSION 0x3F4 /* STM32_USBPHYC_PLL bit fields */ @@ -32,23 +36,96 @@ /* STM32_USBPHYC_MISC bit fields */ #define SWITHOST BIT(0) -/* STM32_USBPHYC_VERSION bit fields */ -#define MINREV GENMASK(3, 0) -#define MAJREV GENMASK(7, 4) +/* STM32_USBPHYC_MONITOR bit fields */ +#define STM32_USBPHYC_MON_OUT GENMASK(3, 0) +#define STM32_USBPHYC_MON_SEL GENMASK(8, 4) +#define STM32_USBPHYC_MON_SEL_LOCKP 0x1F +#define STM32_USBPHYC_MON_OUT_LOCKP BIT(3) + +/* STM32_USBPHYC_TUNE bit fields */ +#define INCURREN BIT(0) +#define INCURRINT BIT(1) +#define LFSCAPEN BIT(2) +#define HSDRVSLEW BIT(3) +#define HSDRVDCCUR BIT(4) +#define HSDRVDCLEV BIT(5) +#define HSDRVCURINCR BIT(6) +#define FSDRVRFADJ BIT(7) +#define HSDRVRFRED BIT(8) +#define HSDRVCHKITRM GENMASK(12, 9) +#define HSDRVCHKZTRM GENMASK(14, 13) +#define OTPCOMP GENMASK(19, 15) +#define SQLCHCTL GENMASK(21, 20) +#define HDRXGNEQEN BIT(22) +#define HSRXOFF GENMASK(24, 23) +#define HSFALLPREEM BIT(25) +#define SHTCCTCTLPROT BIT(26) +#define STAGSEL BIT(27) + +enum boosting_vals { + BOOST_1000_UA = 1000, + BOOST_2000_UA = 2000, +}; + +enum dc_level_vals { + DC_NOMINAL, + DC_PLUS_5_TO_7_MV, + DC_PLUS_10_TO_14_MV, + DC_MINUS_5_TO_7_MV, + DC_MAX, +}; -static const char * const supplies_names[] = { - "vdda1v1", /* 1V1 */ - "vdda1v8", /* 1V8 */ +enum current_trim { + CUR_NOMINAL, + CUR_PLUS_1_56_PCT, + CUR_PLUS_3_12_PCT, + CUR_PLUS_4_68_PCT, + CUR_PLUS_6_24_PCT, + CUR_PLUS_7_8_PCT, + CUR_PLUS_9_36_PCT, + CUR_PLUS_10_92_PCT, + CUR_PLUS_12_48_PCT, + CUR_PLUS_14_04_PCT, + CUR_PLUS_15_6_PCT, + CUR_PLUS_17_16_PCT, + CUR_PLUS_19_01_PCT, + CUR_PLUS_20_58_PCT, + CUR_PLUS_22_16_PCT, + CUR_PLUS_23_73_PCT, + CUR_MAX, }; -#define NUM_SUPPLIES ARRAY_SIZE(supplies_names) +enum impedance_trim { + IMP_NOMINAL, + IMP_MINUS_2_OHMS, + IMP_MINUS_4_OMHS, + IMP_MINUS_6_OHMS, + IMP_MAX, +}; + +enum squelch_level { + SQLCH_NOMINAL, + SQLCH_PLUS_7_MV, + SQLCH_MINUS_5_MV, + SQLCH_PLUS_14_MV, + SQLCH_MAX, +}; + +enum rx_offset { + NO_RX_OFFSET, + RX_OFFSET_PLUS_5_MV, + RX_OFFSET_PLUS_10_MV, + RX_OFFSET_MINUS_5_MV, + RX_OFFSET_MAX, +}; + +/* STM32_USBPHYC_VERSION bit fields */ +#define MINREV GENMASK(3, 0) +#define MAJREV GENMASK(7, 4) -#define PLL_LOCK_TIME_US 100 -#define PLL_PWR_DOWN_TIME_US 5 #define PLL_FVCO_MHZ 2880 #define PLL_INFF_MIN_RATE_HZ 19200000 #define PLL_INFF_MAX_RATE_HZ 38400000 -#define HZ_PER_MHZ 1000000L struct pll_params { u8 ndiv; @@ -58,9 +135,10 @@ struct pll_params { struct stm32_usbphyc_phy { struct phy *phy; struct stm32_usbphyc *usbphyc; - struct regulator_bulk_data supplies[NUM_SUPPLIES]; + struct regulator *vbus; u32 index; bool active; + u32 tune; }; struct stm32_usbphyc { @@ -70,6 +148,10 @@ struct stm32_usbphyc { struct reset_control *rst; struct stm32_usbphyc_phy **phys; int nphys; + struct regulator *vdda1v1; + struct regulator *vdda1v8; + atomic_t n_pll_cons; + struct clk_hw clk48_hw; int switch_setup; }; @@ -83,6 +165,41 @@ static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits) writel_relaxed(readl_relaxed(reg) & ~bits, reg); } +static int stm32_usbphyc_regulators_enable(struct stm32_usbphyc *usbphyc) +{ + int ret; + + ret = regulator_enable(usbphyc->vdda1v1); + if (ret) + return ret; + + ret = regulator_enable(usbphyc->vdda1v8); + if (ret) + goto vdda1v1_disable; + + return 0; + +vdda1v1_disable: + regulator_disable(usbphyc->vdda1v1); + + return ret; +} + +static int stm32_usbphyc_regulators_disable(struct stm32_usbphyc *usbphyc) +{ + int ret; + + ret = regulator_disable(usbphyc->vdda1v8); + if (ret) + return ret; + + ret = regulator_disable(usbphyc->vdda1v1); + if (ret) + return ret; + + return 0; +} + static void stm32_usbphyc_get_pll_params(u32 clk_rate, struct pll_params *pll_params) { @@ -142,83 +259,108 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc) return 0; } -static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc) +static int __stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) { - int i; + void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; + u32 pllen; + + stm32_usbphyc_clr_bits(pll_reg, PLLEN); - for (i = 0; i < usbphyc->nphys; i++) - if (usbphyc->phys[i]->active) - return true; + /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ + if (readl_relaxed_poll_timeout(pll_reg, pllen, !(pllen & PLLEN), 5, 50)) + dev_err(usbphyc->dev, "PLL not reset\n"); - return false; + return stm32_usbphyc_regulators_disable(usbphyc); +} + +static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) +{ + /* Check if a phy port is still active or clk48 in use */ + if (atomic_dec_return(&usbphyc->n_pll_cons) > 0) + return 0; + + return __stm32_usbphyc_pll_disable(usbphyc); } static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) { void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; - bool pllen = (readl_relaxed(pll_reg) & PLLEN); + bool pllen = readl_relaxed(pll_reg) & PLLEN; int ret; - /* Check if one phy port has already configured the pll */ - if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc)) + /* + * Check if a phy port or clk48 prepare has configured the pll + * and ensure the PLL is enabled + */ + if (atomic_inc_return(&usbphyc->n_pll_cons) > 1 && pllen) return 0; if (pllen) { - stm32_usbphyc_clr_bits(pll_reg, PLLEN); - /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ - udelay(PLL_PWR_DOWN_TIME_US); + /* + * PLL shouldn't be enabled without known consumer, + * disable it and reinit n_pll_cons + */ + dev_warn(usbphyc->dev, "PLL enabled without known consumers\n"); + + ret = __stm32_usbphyc_pll_disable(usbphyc); + if (ret) + goto dec_n_pll_cons; } + ret = stm32_usbphyc_regulators_enable(usbphyc); + if (ret) + goto dec_n_pll_cons; + ret = stm32_usbphyc_pll_init(usbphyc); if (ret) - return ret; + goto reg_disable; stm32_usbphyc_set_bits(pll_reg, PLLEN); - /* Wait for maximum lock time */ - udelay(PLL_LOCK_TIME_US); - - if (!(readl_relaxed(pll_reg) & PLLEN)) { - dev_err(usbphyc->dev, "PLLEN not set\n"); - return -EIO; - } - return 0; -} -static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) -{ - void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; +reg_disable: + stm32_usbphyc_regulators_disable(usbphyc); - /* Check if other phy port active */ - if (stm32_usbphyc_has_one_phy_active(usbphyc)) - return 0; +dec_n_pll_cons: + atomic_dec(&usbphyc->n_pll_cons); - stm32_usbphyc_clr_bits(pll_reg, PLLEN); - /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ - udelay(PLL_PWR_DOWN_TIME_US); - - if (readl_relaxed(pll_reg) & PLLEN) { - dev_err(usbphyc->dev, "PLL not reset\n"); - return -EIO; - } - - return 0; + return ret; } static int stm32_usbphyc_phy_init(struct phy *phy) { struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc; + u32 reg_mon = STM32_USBPHYC_MONITOR(usbphyc_phy->index); + u32 monsel = FIELD_PREP(STM32_USBPHYC_MON_SEL, + STM32_USBPHYC_MON_SEL_LOCKP); + u32 monout; int ret; ret = stm32_usbphyc_pll_enable(usbphyc); if (ret) return ret; + /* Check that PLL Lock input to PHY is High */ + writel_relaxed(monsel, usbphyc->base + reg_mon); + ret = readl_relaxed_poll_timeout(usbphyc->base + reg_mon, monout, + (monout & STM32_USBPHYC_MON_OUT_LOCKP), + 100, 1000); + if (ret) { + dev_err(usbphyc->dev, "PLL Lock input to PHY is Low (val=%x)\n", + (u32)(monout & STM32_USBPHYC_MON_OUT)); + goto pll_disable; + } + usbphyc_phy->active = true; return 0; + +pll_disable: + stm32_usbphyc_pll_disable(usbphyc); + + return ret; } static int stm32_usbphyc_phy_exit(struct phy *phy) @@ -235,14 +377,20 @@ static int stm32_usbphyc_phy_power_on(struct phy *phy) { struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); - return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies); + if (usbphyc_phy->vbus) + return regulator_enable(usbphyc_phy->vbus); + + return 0; } static int stm32_usbphyc_phy_power_off(struct phy *phy) { struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); - return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies); + if (usbphyc_phy->vbus) + return regulator_disable(usbphyc_phy->vbus); + + return 0; } static const struct phy_ops stm32_usbphyc_phy_ops = { @@ -253,6 +401,162 @@ static const struct phy_ops stm32_usbphyc_phy_ops = { .owner = THIS_MODULE, }; +static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw) +{ + struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw); + + return stm32_usbphyc_pll_enable(usbphyc); +} + +static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw) +{ + struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw); + + stm32_usbphyc_pll_disable(usbphyc); +} + +static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + return 48000000; +} + +static const struct clk_ops usbphyc_clk48_ops = { + .prepare = stm32_usbphyc_clk48_prepare, + .unprepare = stm32_usbphyc_clk48_unprepare, + .recalc_rate = stm32_usbphyc_clk48_recalc_rate, +}; + +static void stm32_usbphyc_clk48_unregister(void *data) +{ + struct stm32_usbphyc *usbphyc = data; + + of_clk_del_provider(usbphyc->dev->of_node); + clk_hw_unregister(&usbphyc->clk48_hw); +} + +static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc) +{ + struct device_node *node = usbphyc->dev->of_node; + struct clk_init_data init = { }; + int ret = 0; + + init.name = "ck_usbo_48m"; + init.ops = &usbphyc_clk48_ops; + + usbphyc->clk48_hw.init = &init; + + ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &usbphyc->clk48_hw); + if (ret) + clk_hw_unregister(&usbphyc->clk48_hw); + + return ret; +} + +static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc, + struct device_node *np, u32 index) +{ + struct stm32_usbphyc_phy *usbphyc_phy = usbphyc->phys[index]; + u32 reg = STM32_USBPHYC_TUNE(index); + u32 otpcomp, val; + int ret; + + /* Backup OTP compensation code */ + otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg)); + + ret = of_property_read_u32(np, "st,current-boost-microamp", &val); + if (ret != -EINVAL) { + if (!ret && (val == BOOST_1000_UA || val == BOOST_2000_UA)) { + val = (val == BOOST_2000_UA) ? 1 : 0; + usbphyc_phy->tune |= INCURREN | FIELD_PREP(INCURRINT, val); + } else { + dev_warn(usbphyc->dev, "phy%d: invalid st,current-boost-microamp\n", index); + } + } + + if (!of_property_read_bool(np, "st,no-lsfs-fb-cap")) + usbphyc_phy->tune |= LFSCAPEN; + + if (of_property_read_bool(np, "st,decrease-hs-slew-rate")) + usbphyc_phy->tune |= HSDRVSLEW; + + ret = of_property_read_u32(np, "st,tune-hs-dc-level", &val); + if (ret != -EINVAL) { + if (!ret && val < DC_MAX) { + if (val == DC_MINUS_5_TO_7_MV) {/* Decreases HS driver DC level */ + usbphyc_phy->tune |= HSDRVDCCUR; + } else if (val > 0) { /* Increases HS driver DC level */ + val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0; + usbphyc_phy->tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val); + } + } else { + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-dc-level\n", index); + } + } + + if (of_property_read_bool(np, "st,enable-fs-rftime-tuning")) + usbphyc_phy->tune |= FSDRVRFADJ; + + if (of_property_read_bool(np, "st,enable-hs-rftime-reduction")) + usbphyc_phy->tune |= HSDRVRFRED; + + ret = of_property_read_u32(np, "st,trim-hs-current", &val); + if (ret != -EINVAL) { + if (!ret && val < CUR_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKITRM, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-current\n", index); + } + + ret = of_property_read_u32(np, "st,trim-hs-impedance", &val); + if (ret != -EINVAL) { + if (!ret && val < IMP_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKZTRM, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-impedance\n", index); + } + + ret = of_property_read_u32(np, "st,tune-squelch-level", &val); + if (ret != -EINVAL) { + if (!ret && val < SQLCH_MAX) + usbphyc_phy->tune |= FIELD_PREP(SQLCHCTL, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-squelch\n", index); + } + + if (of_property_read_bool(np, "st,enable-hs-rx-gain-eq")) + usbphyc_phy->tune |= HDRXGNEQEN; + + ret = of_property_read_u32(np, "st,tune-hs-rx-offset", &val); + if (ret != -EINVAL) { + if (!ret && val < RX_OFFSET_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSRXOFF, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-rx-offset\n", index); + } + + if (of_property_read_bool(np, "st,no-hs-ftime-ctrl")) + usbphyc_phy->tune |= HSFALLPREEM; + + if (!of_property_read_bool(np, "st,no-lsfs-sc")) + usbphyc_phy->tune |= SHTCCTCTLPROT; + + if (of_property_read_bool(np, "st,enable-hs-tx-staggering")) + usbphyc_phy->tune |= STAGSEL; + + /* Restore OTP compensation code */ + usbphyc_phy->tune |= FIELD_PREP(OTPCOMP, otpcomp); + + /* + * By default, if no st,xxx tuning property is used, usbphyc_phy->tune is equal to + * STM32_USBPHYC_TUNE reset value (LFSCAPEN | SHTCCTCTLPROT | OTPCOMP). + */ + writel_relaxed(usbphyc_phy->tune, usbphyc->base + reg); +} + static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc, u32 utmi_switch) { @@ -311,9 +615,8 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) struct stm32_usbphyc *usbphyc; struct device *dev = &pdev->dev; struct device_node *child, *np = dev->of_node; - struct resource *res; struct phy_provider *phy_provider; - u32 version; + u32 pllen, version; int ret, port = 0; usbphyc = devm_kzalloc(dev, sizeof(*usbphyc), GFP_KERNEL); @@ -322,17 +625,13 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->dev = dev; dev_set_drvdata(dev, usbphyc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - usbphyc->base = devm_ioremap_resource(dev, res); + usbphyc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(usbphyc->base)) return PTR_ERR(usbphyc->base); - usbphyc->clk = devm_clk_get(dev, 0); - if (IS_ERR(usbphyc->clk)) { - ret = PTR_ERR(usbphyc->clk); - dev_err(dev, "clk get failed: %d\n", ret); - return ret; - } + usbphyc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(usbphyc->clk)) + return dev_err_probe(dev, PTR_ERR(usbphyc->clk), "clk get_failed\n"); ret = clk_prepare_enable(usbphyc->clk); if (ret) { @@ -340,11 +639,28 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) return ret; } - usbphyc->rst = devm_reset_control_get(dev, 0); + usbphyc->rst = devm_reset_control_get(dev, NULL); if (!IS_ERR(usbphyc->rst)) { reset_control_assert(usbphyc->rst); udelay(2); reset_control_deassert(usbphyc->rst); + } else { + ret = PTR_ERR(usbphyc->rst); + if (ret == -EPROBE_DEFER) + goto clk_disable; + + stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_PLL, PLLEN); + } + + /* + * Wait for minimum width of powerdown pulse (ENABLE = Low): + * we have to ensure the PLL is disabled before phys initialization. + */ + if (readl_relaxed_poll_timeout(usbphyc->base + STM32_USBPHYC_PLL, + pllen, !(pllen & PLLEN), 5, 50)) { + dev_warn(usbphyc->dev, "PLL not reset\n"); + ret = -EPROBE_DEFER; + goto clk_disable; } usbphyc->switch_setup = -EINVAL; @@ -356,11 +672,24 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) goto clk_disable; } + usbphyc->vdda1v1 = devm_regulator_get(dev, "vdda1v1"); + if (IS_ERR(usbphyc->vdda1v1)) { + ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v1), + "failed to get vdda1v1 supply\n"); + goto clk_disable; + } + + usbphyc->vdda1v8 = devm_regulator_get(dev, "vdda1v8"); + if (IS_ERR(usbphyc->vdda1v8)) { + ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v8), + "failed to get vdda1v8 supply\n"); + goto clk_disable; + } + for_each_child_of_node(np, child) { struct stm32_usbphyc_phy *usbphyc_phy; struct phy *phy; u32 index; - int i; phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops); if (IS_ERR(phy)) { @@ -378,21 +707,11 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) goto put_child; } - for (i = 0; i < NUM_SUPPLIES; i++) - usbphyc_phy->supplies[i].supply = supplies_names[i]; - - ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES, - usbphyc_phy->supplies); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&phy->dev, - "failed to get regulators: %d\n", ret); - goto put_child; - } - ret = of_property_read_u32(child, "reg", &index); if (ret || index > usbphyc->nphys) { dev_err(&phy->dev, "invalid reg property: %d\n", ret); + if (!ret) + ret = -EINVAL; goto put_child; } @@ -405,6 +724,17 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->phys[port]->index = index; usbphyc->phys[port]->active = false; + usbphyc->phys[port]->vbus = devm_regulator_get_optional(&phy->dev, "vbus"); + if (IS_ERR(usbphyc->phys[port]->vbus)) { + ret = PTR_ERR(usbphyc->phys[port]->vbus); + if (ret == -EPROBE_DEFER) + goto put_child; + usbphyc->phys[port]->vbus = NULL; + } + + /* Configure phy tuning */ + stm32_usbphyc_phy_tuning(usbphyc, child, index); + port++; } @@ -416,6 +746,12 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) goto clk_disable; } + ret = stm32_usbphyc_clk48_register(usbphyc); + if (ret) { + dev_err(dev, "failed to register ck_usbo_48m clock: %d\n", ret); + goto clk_disable; + } + version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION); dev_info(dev, "registered rev:%lu.%lu\n", FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version)); @@ -433,12 +769,39 @@ clk_disable: static int stm32_usbphyc_remove(struct platform_device *pdev) { struct stm32_usbphyc *usbphyc = dev_get_drvdata(&pdev->dev); + int port; + + /* Ensure PHYs are not active, to allow PLL disabling */ + for (port = 0; port < usbphyc->nphys; port++) + if (usbphyc->phys[port]->active) + stm32_usbphyc_phy_exit(usbphyc->phys[port]->phy); + + stm32_usbphyc_clk48_unregister(usbphyc); clk_disable_unprepare(usbphyc->clk); return 0; } +static int __maybe_unused stm32_usbphyc_resume(struct device *dev) +{ + struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); + struct stm32_usbphyc_phy *usbphyc_phy; + int port; + + if (usbphyc->switch_setup >= 0) + stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); + + for (port = 0; port < usbphyc->nphys; port++) { + usbphyc_phy = usbphyc->phys[port]; + writel_relaxed(usbphyc_phy->tune, usbphyc->base + STM32_USBPHYC_TUNE(port)); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_usbphyc_pm_ops, NULL, stm32_usbphyc_resume); + static const struct of_device_id stm32_usbphyc_of_match[] = { { .compatible = "st,stm32mp1-usbphyc", }, { }, @@ -451,6 +814,7 @@ static struct platform_driver stm32_usbphyc_driver = { .driver = { .of_match_table = stm32_usbphyc_of_match, .name = "stm32-usbphyc", + .pm = &stm32_usbphyc_pm_ops, } }; module_platform_driver(stm32_usbphyc_driver); diff --git a/drivers/phy/sunplus/Kconfig b/drivers/phy/sunplus/Kconfig new file mode 100644 index 000000000000..3bd3cfb53a63 --- /dev/null +++ b/drivers/phy/sunplus/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config PHY_SUNPLUS_USB + tristate "Sunplus SP7021 USB 2.0 PHY driver" + depends on OF && (SOC_SP7021 || COMPILE_TEST) + select GENERIC_PHY + help + Enable this to support the USB 2.0 PHY on Sunplus SP7021 + SoC. The USB 2.0 PHY controller supports battery charger + and synchronous signals, various power down modes including + operating, partial and suspend modes, and high-speed, + full-speed and low-speed data transfer. diff --git a/drivers/phy/sunplus/Makefile b/drivers/phy/sunplus/Makefile new file mode 100644 index 000000000000..71754d5cb545 --- /dev/null +++ b/drivers/phy/sunplus/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_PHY_SUNPLUS_USB) += phy-sunplus-usb2.o diff --git a/drivers/phy/sunplus/phy-sunplus-usb2.c b/drivers/phy/sunplus/phy-sunplus-usb2.c new file mode 100644 index 000000000000..e827b79f6d49 --- /dev/null +++ b/drivers/phy/sunplus/phy-sunplus-usb2.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Sunplus SP7021 USB 2.0 phy driver + * + * Copyright (C) 2022 Sunplus Technology Inc., All rights reserved. + * + * Note 1 : non-posted write command for the registers accesses of + * Sunplus SP7021. + * + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#define HIGH_MASK_BITS GENMASK(31, 16) +#define LOW_MASK_BITS GENMASK(15, 0) +#define OTP_DISC_LEVEL_DEFAULT 0xd + +/* GROUP UPHY */ +#define CONFIG1 0x4 +#define J_HS_TX_PWRSAV BIT(5) +#define CONFIG3 0xc +#define J_FORCE_DISC_ON BIT(5) +#define J_DEBUG_CTRL_ADDR_MACRO BIT(0) +#define CONFIG7 0x1c +#define J_DISC 0X1f +#define CONFIG9 0x24 +#define J_ECO_PATH BIT(6) +#define CONFIG16 0x40 +#define J_TBCWAIT_MASK GENMASK(6, 5) +#define J_TBCWAIT_1P1_MS FIELD_PREP(J_TBCWAIT_MASK, 0) +#define J_TVDM_SRC_DIS_MASK GENMASK(4, 3) +#define J_TVDM_SRC_DIS_8P2_MS FIELD_PREP(J_TVDM_SRC_DIS_MASK, 3) +#define J_TVDM_SRC_EN_MASK GENMASK(2, 1) +#define J_TVDM_SRC_EN_1P6_MS FIELD_PREP(J_TVDM_SRC_EN_MASK, 0) +#define J_BC_EN BIT(0) +#define CONFIG17 0x44 +#define IBG_TRIM0_MASK GENMASK(7, 5) +#define IBG_TRIM0_SSLVHT FIELD_PREP(IBG_TRIM0_MASK, 4) +#define J_VDATREE_TRIM_MASK GENMASK(4, 1) +#define J_VDATREE_TRIM_DEFAULT FIELD_PREP(J_VDATREE_TRIM_MASK, 9) +#define CONFIG23 0x5c +#define PROB_MASK GENMASK(5, 3) +#define PROB FIELD_PREP(PROB_MASK, 7) + +/* GROUP MOON4 */ +#define UPHY_CONTROL0 0x0 +#define UPHY_CONTROL1 0x4 +#define UPHY_CONTROL2 0x8 +#define MO1_UPHY_RX_CLK_SEL BIT(6) +#define MASK_MO1_UPHY_RX_CLK_SEL BIT(6 + 16) +#define UPHY_CONTROL3 0xc +#define MO1_UPHY_PLL_POWER_OFF_SEL BIT(7) +#define MASK_MO1_UPHY_PLL_POWER_OFF_SEL BIT(7 + 16) +#define MO1_UPHY_PLL_POWER_OFF BIT(3) +#define MASK_UPHY_PLL_POWER_OFF BIT(3 + 16) + +struct sp_usbphy { + struct device *dev; + struct resource *phy_res_mem; + struct resource *moon4_res_mem; + struct reset_control *rstc; + struct clk *phy_clk; + void __iomem *phy_regs; + void __iomem *moon4_regs; + u32 disc_vol_addr_off; +}; + +static int update_disc_vol(struct sp_usbphy *usbphy) +{ + struct nvmem_cell *cell; + char *disc_name = "disc_vol"; + ssize_t otp_l = 0; + char *otp_v; + u32 val, set; + + cell = nvmem_cell_get(usbphy->dev, disc_name); + if (IS_ERR_OR_NULL(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + otp_v = nvmem_cell_read(cell, &otp_l); + nvmem_cell_put(cell); + + if (!IS_ERR(otp_v)) { + set = *(otp_v + 1); + set = (set << (sizeof(char) * 8)) | *otp_v; + set = (set >> usbphy->disc_vol_addr_off) & J_DISC; + } + + if (IS_ERR(otp_v) || set == 0) + set = OTP_DISC_LEVEL_DEFAULT; + + val = readl(usbphy->phy_regs + CONFIG7); + val = (val & ~J_DISC) | set; + writel(val, usbphy->phy_regs + CONFIG7); + + return 0; +} + +static int sp_uphy_init(struct phy *phy) +{ + struct sp_usbphy *usbphy = phy_get_drvdata(phy); + u32 val; + int ret; + + ret = clk_prepare_enable(usbphy->phy_clk); + if (ret) + goto err_clk; + + ret = reset_control_deassert(usbphy->rstc); + if (ret) + goto err_reset; + + /* Default value modification */ + writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0); + writel(HIGH_MASK_BITS | 0x8747, usbphy->moon4_regs + UPHY_CONTROL1); + + /* disconnect voltage */ + ret = update_disc_vol(usbphy); + if (ret < 0) + return ret; + + /* board uphy 0 internal register modification for tid certification */ + val = readl(usbphy->phy_regs + CONFIG9); + val &= ~(J_ECO_PATH); + writel(val, usbphy->phy_regs + CONFIG9); + + val = readl(usbphy->phy_regs + CONFIG1); + val &= ~(J_HS_TX_PWRSAV); + writel(val, usbphy->phy_regs + CONFIG1); + + val = readl(usbphy->phy_regs + CONFIG23); + val = (val & ~PROB) | PROB; + writel(val, usbphy->phy_regs + CONFIG23); + + /* port 0 uphy clk fix */ + writel(MASK_MO1_UPHY_RX_CLK_SEL | MO1_UPHY_RX_CLK_SEL, + usbphy->moon4_regs + UPHY_CONTROL2); + + /* battery charger */ + writel(J_TBCWAIT_1P1_MS | J_TVDM_SRC_DIS_8P2_MS | J_TVDM_SRC_EN_1P6_MS | J_BC_EN, + usbphy->phy_regs + CONFIG16); + writel(IBG_TRIM0_SSLVHT | J_VDATREE_TRIM_DEFAULT, usbphy->phy_regs + CONFIG17); + + /* chirp mode */ + writel(J_FORCE_DISC_ON | J_DEBUG_CTRL_ADDR_MACRO, usbphy->phy_regs + CONFIG3); + + return 0; + +err_reset: + reset_control_assert(usbphy->rstc); +err_clk: + clk_disable_unprepare(usbphy->phy_clk); + + return ret; +} + +static int sp_uphy_power_on(struct phy *phy) +{ + struct sp_usbphy *usbphy = phy_get_drvdata(phy); + u32 pll_pwr_on, pll_pwr_off; + + /* PLL power off/on twice */ + pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) + | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF; + pll_pwr_on = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) + | MO1_UPHY_PLL_POWER_OFF_SEL; + + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, + usbphy->moon4_regs + UPHY_CONTROL3); + mdelay(1); + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on, + usbphy->moon4_regs + UPHY_CONTROL3); + mdelay(1); + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, + usbphy->moon4_regs + UPHY_CONTROL3); + mdelay(1); + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on, + usbphy->moon4_regs + UPHY_CONTROL3); + mdelay(1); + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0, + usbphy->moon4_regs + UPHY_CONTROL3); + + return 0; +} + +static int sp_uphy_power_off(struct phy *phy) +{ + struct sp_usbphy *usbphy = phy_get_drvdata(phy); + u32 pll_pwr_off; + + pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) + | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF; + + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, + usbphy->moon4_regs + UPHY_CONTROL3); + mdelay(1); + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0, + usbphy->moon4_regs + UPHY_CONTROL3); + + return 0; +} + +static int sp_uphy_exit(struct phy *phy) +{ + struct sp_usbphy *usbphy = phy_get_drvdata(phy); + + reset_control_assert(usbphy->rstc); + clk_disable_unprepare(usbphy->phy_clk); + + return 0; +} + +static const struct phy_ops sp_uphy_ops = { + .init = sp_uphy_init, + .power_on = sp_uphy_power_on, + .power_off = sp_uphy_power_off, + .exit = sp_uphy_exit, +}; + +static const struct of_device_id sp_uphy_dt_ids[] = { + {.compatible = "sunplus,sp7021-usb2-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, sp_uphy_dt_ids); + +static int sp_usb_phy_probe(struct platform_device *pdev) +{ + struct sp_usbphy *usbphy; + struct phy_provider *phy_provider; + struct phy *phy; + int ret; + + usbphy = devm_kzalloc(&pdev->dev, sizeof(*usbphy), GFP_KERNEL); + if (!usbphy) + return -ENOMEM; + + usbphy->dev = &pdev->dev; + + usbphy->phy_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + usbphy->phy_regs = devm_ioremap_resource(&pdev->dev, usbphy->phy_res_mem); + if (IS_ERR(usbphy->phy_regs)) + return PTR_ERR(usbphy->phy_regs); + + usbphy->moon4_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon4"); + usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start, + resource_size(usbphy->moon4_res_mem)); + if (!usbphy->moon4_regs) + return -ENOMEM; + + usbphy->phy_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(usbphy->phy_clk)) + return PTR_ERR(usbphy->phy_clk); + + usbphy->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(usbphy->rstc)) + return PTR_ERR(usbphy->rstc); + + of_property_read_u32(pdev->dev.of_node, "sunplus,disc-vol-addr-off", + &usbphy->disc_vol_addr_off); + + phy = devm_phy_create(&pdev->dev, NULL, &sp_uphy_ops); + if (IS_ERR(phy)) { + ret = -PTR_ERR(phy); + return ret; + } + + phy_set_drvdata(phy, usbphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver sunplus_usb_phy_driver = { + .probe = sp_usb_phy_probe, + .driver = { + .name = "sunplus-usb2-phy", + .of_match_table = sp_uphy_dt_ids, + }, +}; +module_platform_driver(sunplus_usb_phy_driver); + +MODULE_AUTHOR("Vincent Shih <vincent.shih@sunplus.com>"); +MODULE_DESCRIPTION("Sunplus USB 2.0 phy driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig index f9817c3ae85f..c591c958f1eb 100644 --- a/drivers/phy/tegra/Kconfig +++ b/drivers/phy/tegra/Kconfig @@ -1,7 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only config PHY_TEGRA_XUSB tristate "NVIDIA Tegra XUSB pad controller driver" - depends on ARCH_TEGRA + depends on ARCH_TEGRA && USB_SUPPORT + select USB_COMMON + select USB_CONN_GPIO + select USB_PHY help Choose this option if you have an NVIDIA Tegra SoC. diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile index 320dd389f34d..89b84067cb4c 100644 --- a/drivers/phy/tegra/Makefile +++ b/drivers/phy/tegra/Makefile @@ -6,4 +6,5 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o diff --git a/drivers/phy/tegra/phy-tegra194-p2u.c b/drivers/phy/tegra/phy-tegra194-p2u.c index 7042bed9feaa..1415ca71de38 100644 --- a/drivers/phy/tegra/phy-tegra194-p2u.c +++ b/drivers/phy/tegra/phy-tegra194-p2u.c @@ -2,7 +2,7 @@ /* * P2U (PIPE to UPHY) driver for Tegra T194 SoC * - * Copyright (C) 2019 NVIDIA Corporation. + * Copyright (C) 2019-2022 NVIDIA Corporation. * * Author: Vidya Sagar <vidyas@nvidia.com> */ @@ -14,6 +14,9 @@ #include <linux/of_platform.h> #include <linux/phy/phy.h> +#define P2U_CONTROL_CMN 0x74 +#define P2U_CONTROL_CMN_SKP_SIZE_PROTECTION_EN BIT(20) + #define P2U_PERIODIC_EQ_CTRL_GEN3 0xc0 #define P2U_PERIODIC_EQ_CTRL_GEN3_PERIODIC_EQ_EN BIT(0) #define P2U_PERIODIC_EQ_CTRL_GEN3_INIT_PRESET_EQ_TRAIN_EN BIT(1) @@ -24,8 +27,17 @@ #define P2U_RX_DEBOUNCE_TIME_DEBOUNCE_TIMER_MASK 0xffff #define P2U_RX_DEBOUNCE_TIME_DEBOUNCE_TIMER_VAL 160 +#define P2U_DIR_SEARCH_CTRL 0xd4 +#define P2U_DIR_SEARCH_CTRL_GEN4_FINE_GRAIN_SEARCH_TWICE BIT(18) + +struct tegra_p2u_of_data { + bool one_dir_search; +}; + struct tegra_p2u { void __iomem *base; + bool skip_sz_protection_en; /* Needed to support two retimers */ + struct tegra_p2u_of_data *of_data; }; static inline void p2u_writel(struct tegra_p2u *phy, const u32 value, @@ -44,6 +56,12 @@ static int tegra_p2u_power_on(struct phy *x) struct tegra_p2u *phy = phy_get_drvdata(x); u32 val; + if (phy->skip_sz_protection_en) { + val = p2u_readl(phy, P2U_CONTROL_CMN); + val |= P2U_CONTROL_CMN_SKP_SIZE_PROTECTION_EN; + p2u_writel(phy, val, P2U_CONTROL_CMN); + } + val = p2u_readl(phy, P2U_PERIODIC_EQ_CTRL_GEN3); val &= ~P2U_PERIODIC_EQ_CTRL_GEN3_PERIODIC_EQ_EN; val |= P2U_PERIODIC_EQ_CTRL_GEN3_INIT_PRESET_EQ_TRAIN_EN; @@ -58,6 +76,12 @@ static int tegra_p2u_power_on(struct phy *x) val |= P2U_RX_DEBOUNCE_TIME_DEBOUNCE_TIMER_VAL; p2u_writel(phy, val, P2U_RX_DEBOUNCE_TIME); + if (phy->of_data->one_dir_search) { + val = p2u_readl(phy, P2U_DIR_SEARCH_CTRL); + val &= ~P2U_DIR_SEARCH_CTRL_GEN4_FINE_GRAIN_SEARCH_TWICE; + p2u_writel(phy, val, P2U_DIR_SEARCH_CTRL); + } + return 0; } @@ -72,17 +96,24 @@ static int tegra_p2u_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct phy *generic_phy; struct tegra_p2u *phy; - struct resource *res; phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) return -ENOMEM; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctl"); - phy->base = devm_ioremap_resource(dev, res); + phy->of_data = + (struct tegra_p2u_of_data *)of_device_get_match_data(dev); + if (!phy->of_data) + return -EINVAL; + + phy->base = devm_platform_ioremap_resource_byname(pdev, "ctl"); if (IS_ERR(phy->base)) return PTR_ERR(phy->base); + phy->skip_sz_protection_en = + of_property_read_bool(dev->of_node, + "nvidia,skip-sz-protect-en"); + platform_set_drvdata(pdev, phy); generic_phy = devm_phy_create(dev, NULL, &ops); @@ -98,9 +129,22 @@ static int tegra_p2u_probe(struct platform_device *pdev) return 0; } +static const struct tegra_p2u_of_data tegra194_p2u_of_data = { + .one_dir_search = false, +}; + +static const struct tegra_p2u_of_data tegra234_p2u_of_data = { + .one_dir_search = true, +}; + static const struct of_device_id tegra_p2u_id_table[] = { { .compatible = "nvidia,tegra194-p2u", + .data = &tegra194_p2u_of_data, + }, + { + .compatible = "nvidia,tegra234-p2u", + .data = &tegra234_p2u_of_data, }, {} }; diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c index 98d84920c676..db56c7fbe60b 100644 --- a/drivers/phy/tegra/xusb-tegra124.c +++ b/drivers/phy/tegra/xusb-tegra124.c @@ -1422,6 +1422,8 @@ tegra124_usb2_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = { + .release = tegra_xusb_usb2_port_release, + .remove = tegra_xusb_usb2_port_remove, .enable = tegra124_usb2_port_enable, .disable = tegra124_usb2_port_disable, .map = tegra124_usb2_port_map, @@ -1443,6 +1445,7 @@ tegra124_ulpi_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = { + .release = tegra_xusb_ulpi_port_release, .enable = tegra124_ulpi_port_enable, .disable = tegra124_ulpi_port_disable, .map = tegra124_ulpi_port_map, @@ -1464,6 +1467,7 @@ tegra124_hsic_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = { + .release = tegra_xusb_hsic_port_release, .enable = tegra124_hsic_port_enable, .disable = tegra124_hsic_port_disable, .map = tegra124_hsic_port_map, @@ -1647,6 +1651,8 @@ tegra124_usb3_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = { + .release = tegra_xusb_usb3_port_release, + .remove = tegra_xusb_usb3_port_remove, .enable = tegra124_usb3_port_enable, .disable = tegra124_usb3_port_disable, .map = tegra124_usb3_port_map, diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c index 84c27394c181..0996ede63387 100644 --- a/drivers/phy/tegra/xusb-tegra186.c +++ b/drivers/phy/tegra/xusb-tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved. */ #include <linux/delay.h> @@ -63,6 +63,10 @@ #define SSPX_ELPG_CLAMP_EN(x) BIT(0 + (x) * 3) #define SSPX_ELPG_CLAMP_EN_EARLY(x) BIT(1 + (x) * 3) #define SSPX_ELPG_VCORE_DOWN(x) BIT(2 + (x) * 3) +#define XUSB_PADCTL_SS_PORT_CFG 0x2c +#define PORTX_SPEED_SUPPORT_SHIFT(x) ((x) * 4) +#define PORTX_SPEED_SUPPORT_MASK (0x3) +#define PORT_SPEED_SUPPORT_GEN1 (0x0) #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x88 + (x) * 0x40) #define HS_CURR_LEVEL(x) ((x) & 0x3f) @@ -109,6 +113,117 @@ #define ID_OVERRIDE_FLOATING ID_OVERRIDE(8) #define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0) +/* XUSB AO registers */ +#define XUSB_AO_USB_DEBOUNCE_DEL (0x4) +#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4) +#define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf) + +#define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4) +#define CLR_WALK_PTR BIT(0) +#define CAP_CFG BIT(1) +#define CLR_WAKE_ALARM BIT(3) + +#define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4) +#define HSIC_CLR_WALK_PTR BIT(0) +#define HSIC_CLR_WAKE_ALARM BIT(3) +#define HSIC_CAP_CFG BIT(4) + +#define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4) +#define SPEED(x) ((x) & 0x3) +#define UTMI_HS SPEED(0) +#define UTMI_FS SPEED(1) +#define UTMI_LS SPEED(2) +#define UTMI_RST SPEED(3) + +#define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4) +#define MODE(x) ((x) & 0x1) +#define MODE_HS MODE(0) +#define MODE_RST MODE(1) + +#define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4) +#define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4) +#define FAKE_USBOP_VAL BIT(0) +#define FAKE_USBON_VAL BIT(1) +#define FAKE_USBOP_EN BIT(2) +#define FAKE_USBON_EN BIT(3) +#define FAKE_STROBE_VAL BIT(0) +#define FAKE_DATA_VAL BIT(1) +#define FAKE_STROBE_EN BIT(2) +#define FAKE_DATA_EN BIT(3) +#define WAKE_WALK_EN BIT(14) +#define MASTER_ENABLE BIT(15) +#define LINEVAL_WALK_EN BIT(16) +#define WAKE_VAL(x) (((x) & 0xf) << 17) +#define WAKE_VAL_NONE WAKE_VAL(12) +#define WAKE_VAL_ANY WAKE_VAL(15) +#define WAKE_VAL_DS10 WAKE_VAL(2) +#define LINE_WAKEUP_EN BIT(21) +#define MASTER_CFG_SEL BIT(22) + +#define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4) +/* phase A */ +#define USBOP_RPD_A BIT(0) +#define USBON_RPD_A BIT(1) +#define AP_A BIT(4) +#define AN_A BIT(5) +#define HIGHZ_A BIT(6) +/* phase B */ +#define USBOP_RPD_B BIT(8) +#define USBON_RPD_B BIT(9) +#define AP_B BIT(12) +#define AN_B BIT(13) +#define HIGHZ_B BIT(14) +/* phase C */ +#define USBOP_RPD_C BIT(16) +#define USBON_RPD_C BIT(17) +#define AP_C BIT(20) +#define AN_C BIT(21) +#define HIGHZ_C BIT(22) +/* phase D */ +#define USBOP_RPD_D BIT(24) +#define USBON_RPD_D BIT(25) +#define AP_D BIT(28) +#define AN_D BIT(29) +#define HIGHZ_D BIT(30) + +#define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4) +/* phase A */ +#define RPD_STROBE_A BIT(0) +#define RPD_DATA0_A BIT(1) +#define RPU_STROBE_A BIT(2) +#define RPU_DATA0_A BIT(3) +/* phase B */ +#define RPD_STROBE_B BIT(8) +#define RPD_DATA0_B BIT(9) +#define RPU_STROBE_B BIT(10) +#define RPU_DATA0_B BIT(11) +/* phase C */ +#define RPD_STROBE_C BIT(16) +#define RPD_DATA0_C BIT(17) +#define RPU_STROBE_C BIT(18) +#define RPU_DATA0_C BIT(19) +/* phase D */ +#define RPD_STROBE_D BIT(24) +#define RPD_DATA0_D BIT(25) +#define RPU_STROBE_D BIT(26) +#define RPU_DATA0_D BIT(27) + +#define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4) +#define FSLS_USE_XUSB_AO BIT(3) +#define TRK_CTRL_USE_XUSB_AO BIT(4) +#define RPD_CTRL_USE_XUSB_AO BIT(5) +#define RPU_USE_XUSB_AO BIT(6) +#define VREG_USE_XUSB_AO BIT(7) +#define USBOP_VAL_PD BIT(8) +#define USBON_VAL_PD BIT(9) +#define E_DPD_OVRD_EN BIT(10) +#define E_DPD_OVRD_VAL BIT(11) + +#define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4) +#define STROBE_VAL_PD BIT(0) +#define DATA0_VAL_PD BIT(1) +#define USE_XUSB_AO BIT(4) + #define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \ { \ .name = _name, \ @@ -126,16 +241,37 @@ struct tegra_xusb_fuse_calibration { u32 rpd_ctrl; }; +struct tegra186_xusb_padctl_context { + u32 vbus_id; + u32 usb2_pad_mux; + u32 usb2_port_cap; + u32 ss_port_cap; +}; + struct tegra186_xusb_padctl { struct tegra_xusb_padctl base; + void __iomem *ao_regs; struct tegra_xusb_fuse_calibration calib; /* UTMI bias and tracking */ struct clk *usb2_trk_clk; unsigned int bias_pad_enable; + + /* padctl context */ + struct tegra186_xusb_padctl_context context; }; +static inline void ao_writel(struct tegra186_xusb_padctl *priv, u32 value, unsigned int offset) +{ + writel(value, priv->ao_regs + offset); +} + +static inline u32 ao_readl(struct tegra186_xusb_padctl *priv, unsigned int offset) +{ + return readl(priv->ao_regs + offset); +} + static inline struct tegra186_xusb_padctl * to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl) { @@ -176,9 +312,264 @@ static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane) kfree(usb2); } +static int tegra186_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + /* ensure sleepwalk logic is disabled */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~MASTER_ENABLE; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* ensure sleepwalk logics are in low power mode */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value |= MASTER_CFG_SEL; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* set debounce time */ + value = ao_readl(priv, XUSB_AO_USB_DEBOUNCE_DEL); + value &= ~UTMIP_LINE_DEB_CNT(~0); + value |= UTMIP_LINE_DEB_CNT(1); + ao_writel(priv, value, XUSB_AO_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL | + FAKE_USBOP_EN | FAKE_USBON_EN); + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* ensure wake events of sleepwalk logic are not latched */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~LINE_WAKEUP_EN; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* disable wake event triggers of sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~WAKE_VAL(~0); + value |= WAKE_VAL_NONE; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* power down the line state detectors of the pad */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value |= (USBOP_VAL_PD | USBON_VAL_PD); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* save state per speed */ + value = ao_readl(priv, XUSB_AO_UTMIP_SAVED_STATE(index)); + value &= ~SPEED(~0); + + switch (speed) { + case USB_SPEED_HIGH: + value |= UTMI_HS; + break; + + case USB_SPEED_FULL: + value |= UTMI_FS; + break; + + case USB_SPEED_LOW: + value |= UTMI_LS; + break; + + default: + value |= UTMI_RST; + break; + } + + ao_writel(priv, value, XUSB_AO_UTMIP_SAVED_STATE(index)); + + /* enable the trigger of the sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value |= LINEVAL_WALK_EN; + value &= ~WAKE_WALK_EN; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 pad + */ + value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); + value |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG); + ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); + + /* setup the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. + * if device is connected, program sleepwalk logic to maintain a J and + * keep driving K upon seeing remote wake. + */ + value = USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D; + value |= USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D; + + switch (speed) { + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + /* J state: D+/D- = high/low, K state: D+/D- = low/high */ + value |= HIGHZ_A; + value |= AP_A; + value |= AN_B | AN_C | AN_D; + break; + + case USB_SPEED_LOW: + /* J state: D+/D- = low/high, K state: D+/D- = high/low */ + value |= HIGHZ_A; + value |= AN_A; + value |= AP_B | AP_C | AP_D; + break; + + default: + value |= HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D; + break; + } + + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK(index)); + + /* power up the line state detectors of the pad */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value &= ~(USBOP_VAL_PD | USBON_VAL_PD); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + usleep_range(150, 200); + + /* switch the electric control of the USB2.0 pad to XUSB_AO */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value |= FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | + RPU_USE_XUSB_AO | VREG_USE_XUSB_AO; + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* set the wake signaling trigger events */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~WAKE_VAL(~0); + value |= WAKE_VAL_ANY; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* enable the wake detection */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value |= MASTER_ENABLE | LINE_WAKEUP_EN; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + /* disable the wake detection */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~(MASTER_ENABLE | LINE_WAKEUP_EN); + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* switch the electric control of the USB2.0 pad to XUSB vcore logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | + RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* disable wake event triggers of sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + value &= ~WAKE_VAL(~0); + value |= WAKE_VAL_NONE; + ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); + + /* power down the line state detectors of the port */ + value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); + value |= USBOP_VAL_PD | USBON_VAL_PD; + ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); + + /* clear alarm of the sleepwalk logic */ + value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); + value |= CLR_WAKE_ALARM; + ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_utmi_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_utmi_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra186_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) && + (value & USB2_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = { .probe = tegra186_usb2_lane_probe, .remove = tegra186_usb2_lane_remove, + .enable_phy_sleepwalk = tegra186_utmi_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra186_utmi_disable_phy_sleepwalk, + .enable_phy_wake = tegra186_utmi_enable_phy_wake, + .disable_phy_wake = tegra186_utmi_disable_phy_wake, + .remote_wake_detected = tegra186_utmi_phy_remote_wake_detected, }; static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl) @@ -247,7 +638,7 @@ static void tegra186_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl) mutex_unlock(&padctl->lock); } -static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy) +static void tegra186_utmi_pad_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; @@ -265,6 +656,8 @@ static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy) return; } + dev_dbg(dev, "power on UTMI pad %u\n", index); + tegra186_utmi_bias_pad_power_on(padctl); udelay(2); @@ -278,7 +671,7 @@ static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); } -static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy) +static void tegra186_utmi_pad_power_down(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; @@ -288,6 +681,8 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy) if (!phy) return; + dev_dbg(padctl->dev, "power down UTMI pad %u\n", index); + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); value |= USB2_OTG_PD; padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); @@ -301,6 +696,97 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy) tegra186_utmi_bias_pad_power_off(padctl); } +static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, USB2_VBUS_ID); + + if (status) { + value |= VBUS_OVERRIDE; + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_FLOATING; + } else { + value &= ~VBUS_OVERRIDE; + } + + padctl_writel(padctl, value, USB2_VBUS_ID); + + return 0; +} + +static int tegra186_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, USB2_VBUS_ID); + + if (status) { + if (value & VBUS_OVERRIDE) { + value &= ~VBUS_OVERRIDE; + padctl_writel(padctl, value, USB2_VBUS_ID); + usleep_range(1000, 2000); + + value = padctl_readl(padctl, USB2_VBUS_ID); + } + + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_GROUNDED; + } else { + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_FLOATING; + } + + padctl_writel(padctl, value, USB2_VBUS_ID); + + return 0; +} + +static int tegra186_utmi_phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl, + lane->index); + int err = 0; + + mutex_lock(&padctl->lock); + + dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode); + + if (mode == PHY_MODE_USB_OTG) { + if (submode == USB_ROLE_HOST) { + tegra186_xusb_padctl_id_override(padctl, true); + + err = regulator_enable(port->supply); + } else if (submode == USB_ROLE_DEVICE) { + tegra186_xusb_padctl_vbus_override(padctl, true); + } else if (submode == USB_ROLE_NONE) { + /* + * When port is peripheral only or role transitions to + * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not + * enabled. + */ + if (regulator_is_enabled(port->supply)) + regulator_disable(port->supply); + + tegra186_xusb_padctl_id_override(padctl, false); + tegra186_xusb_padctl_vbus_override(padctl, false); + } + } + + mutex_unlock(&padctl->lock); + + return err; +} + static int tegra186_utmi_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); @@ -367,15 +853,14 @@ static int tegra186_utmi_phy_power_on(struct phy *phy) value |= RPD_CTRL(priv->calib.rpd_ctrl); padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); - /* TODO: pad power saving */ - tegra_phy_xusb_utmi_pad_power_on(phy); + tegra186_utmi_pad_power_on(phy); + return 0; } static int tegra186_utmi_phy_power_off(struct phy *phy) { - /* TODO: pad power saving */ - tegra_phy_xusb_utmi_pad_power_down(phy); + tegra186_utmi_pad_power_down(phy); return 0; } @@ -439,6 +924,7 @@ static const struct phy_ops utmi_phy_ops = { .exit = tegra186_utmi_phy_exit, .power_on = tegra186_utmi_phy_power_on, .power_off = tegra186_utmi_phy_power_off, + .set_mode = tegra186_utmi_phy_set_mode, .owner = THIS_MODULE, }; @@ -503,19 +989,6 @@ static const char * const tegra186_usb2_functions[] = { "xusb", }; -static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = { - TEGRA186_LANE("usb2-0", 0, 0, 0, usb2), - TEGRA186_LANE("usb2-1", 0, 0, 0, usb2), - TEGRA186_LANE("usb2-2", 0, 0, 0, usb2), -}; - -static const struct tegra_xusb_pad_soc tegra186_usb2_pad = { - .name = "usb2", - .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes), - .lanes = tegra186_usb2_lanes, - .ops = &tegra186_usb2_pad_ops, -}; - static int tegra186_usb2_port_enable(struct tegra_xusb_port *port) { return 0; @@ -532,6 +1005,8 @@ tegra186_usb2_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = { + .release = tegra_xusb_usb2_port_release, + .remove = tegra_xusb_usb2_port_remove, .enable = tegra186_usb2_port_enable, .disable = tegra186_usb2_port_disable, .map = tegra186_usb2_port_map, @@ -571,10 +1046,128 @@ static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane) kfree(usb3); } +static int tegra186_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value |= SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value |= SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + usleep_range(250, 350); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); + value &= ~SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_usb3_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra186_usb3_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra186_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = { .probe = tegra186_usb3_lane_probe, .remove = tegra186_usb3_lane_remove, + .enable_phy_sleepwalk = tegra186_usb3_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra186_usb3_disable_phy_sleepwalk, + .enable_phy_wake = tegra186_usb3_enable_phy_wake, + .disable_phy_wake = tegra186_usb3_disable_phy_wake, + .remote_wake_detected = tegra186_usb3_phy_remote_wake_detected, }; + static int tegra186_usb3_port_enable(struct tegra_xusb_port *port) { return 0; @@ -591,6 +1184,8 @@ tegra186_usb3_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = { + .release = tegra_xusb_usb3_port_release, + .remove = tegra_xusb_usb3_port_remove, .enable = tegra186_usb3_port_enable, .disable = tegra186_usb3_port_disable, .map = tegra186_usb3_port_map, @@ -635,6 +1230,15 @@ static int tegra186_usb3_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CAP); + if (padctl->soc->supports_gen2 && port->disable_gen2) { + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CFG); + value &= ~(PORTX_SPEED_SUPPORT_MASK << + PORTX_SPEED_SUPPORT_SHIFT(index)); + value |= (PORT_SPEED_SUPPORT_GEN1 << + PORTX_SPEED_SUPPORT_SHIFT(index)); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CFG); + } + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value &= ~SSPX_ELPG_VCORE_DOWN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); @@ -765,27 +1369,6 @@ static const char * const tegra186_usb3_functions[] = { "xusb", }; -static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = { - TEGRA186_LANE("usb3-0", 0, 0, 0, usb3), - TEGRA186_LANE("usb3-1", 0, 0, 0, usb3), - TEGRA186_LANE("usb3-2", 0, 0, 0, usb3), -}; - -static const struct tegra_xusb_pad_soc tegra186_usb3_pad = { - .name = "usb3", - .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes), - .lanes = tegra186_usb3_lanes, - .ops = &tegra186_usb3_pad_ops, -}; - -static const struct tegra_xusb_pad_soc * const tegra186_pads[] = { - &tegra186_usb2_pad, - &tegra186_usb3_pad, -#if 0 /* TODO implement */ - &tegra186_hsic_pad, -#endif -}; - static int tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) { @@ -801,10 +1384,9 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) return -ENOMEM; err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value); - if (err) { - dev_err(dev, "failed to read calibration fuse: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "failed to read calibration fuse\n"); dev_dbg(dev, "FUSE_USB_CALIB_0 %#x\n", value); @@ -836,7 +1418,9 @@ static struct tegra_xusb_padctl * tegra186_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { + struct platform_device *pdev = to_platform_device(dev); struct tegra186_xusb_padctl *priv; + struct resource *res; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -846,6 +1430,11 @@ tegra186_xusb_padctl_probe(struct device *dev, priv->base.dev = dev; priv->base.soc = soc; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao"); + priv->ao_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ao_regs)) + return ERR_CAST(priv->ao_regs); + err = tegra186_xusb_read_fuse_calibration(priv); if (err < 0) return ERR_PTR(err); @@ -853,38 +1442,55 @@ tegra186_xusb_padctl_probe(struct device *dev, return &priv->base; } -static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) +static void tegra186_xusb_padctl_save(struct tegra_xusb_padctl *padctl) { + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); + + priv->context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID); + priv->context.usb2_pad_mux = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); + priv->context.usb2_port_cap = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); + priv->context.ss_port_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); } -static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, - bool status) +static void tegra186_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) { - u32 value; + struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); - dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + padctl_writel(padctl, priv->context.usb2_pad_mux, XUSB_PADCTL_USB2_PAD_MUX); + padctl_writel(padctl, priv->context.usb2_port_cap, XUSB_PADCTL_USB2_PORT_CAP); + padctl_writel(padctl, priv->context.ss_port_cap, XUSB_PADCTL_SS_PORT_CAP); + padctl_writel(padctl, priv->context.vbus_id, USB2_VBUS_ID); +} - value = padctl_readl(padctl, USB2_VBUS_ID); +static int tegra186_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) +{ + tegra186_xusb_padctl_save(padctl); - if (status) { - value |= VBUS_OVERRIDE; - value &= ~ID_OVERRIDE(~0); - value |= ID_OVERRIDE_FLOATING; - } else { - value &= ~VBUS_OVERRIDE; - } + return 0; +} - padctl_writel(padctl, value, USB2_VBUS_ID); +static int tegra186_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) +{ + tegra186_xusb_padctl_restore(padctl); return 0; } +static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) +{ +} + static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = { .probe = tegra186_xusb_padctl_probe, .remove = tegra186_xusb_padctl_remove, + .suspend_noirq = tegra186_xusb_padctl_suspend_noirq, + .resume_noirq = tegra186_xusb_padctl_resume_noirq, .vbus_override = tegra186_xusb_padctl_vbus_override, + .utmi_pad_power_on = tegra186_utmi_pad_power_on, + .utmi_pad_power_down = tegra186_utmi_pad_power_down, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) static const char * const tegra186_xusb_padctl_supply_names[] = { "avdd-pll-erefeut", "avdd-usb", @@ -892,6 +1498,40 @@ static const char * const tegra186_xusb_padctl_supply_names[] = { "vddio-hsic", }; +static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = { + TEGRA186_LANE("usb2-0", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-1", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-2", 0, 0, 0, usb2), +}; + +static const struct tegra_xusb_pad_soc tegra186_usb2_pad = { + .name = "usb2", + .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes), + .lanes = tegra186_usb2_lanes, + .ops = &tegra186_usb2_pad_ops, +}; + +static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = { + TEGRA186_LANE("usb3-0", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-1", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-2", 0, 0, 0, usb3), +}; + +static const struct tegra_xusb_pad_soc tegra186_usb3_pad = { + .name = "usb3", + .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes), + .lanes = tegra186_usb3_lanes, + .ops = &tegra186_usb3_pad_ops, +}; + +static const struct tegra_xusb_pad_soc * const tegra186_pads[] = { + &tegra186_usb2_pad, + &tegra186_usb3_pad, +#if 0 /* TODO implement */ + &tegra186_hsic_pad, +#endif +}; + const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = { .num_pads = ARRAY_SIZE(tegra186_pads), .pads = tegra186_pads, @@ -916,6 +1556,67 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = { .num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names), }; EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc); +#endif + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) +static const char * const tegra194_xusb_padctl_supply_names[] = { + "avdd-usb", + "vclamp-usb", +}; + +static const struct tegra_xusb_lane_soc tegra194_usb2_lanes[] = { + TEGRA186_LANE("usb2-0", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-1", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-2", 0, 0, 0, usb2), + TEGRA186_LANE("usb2-3", 0, 0, 0, usb2), +}; + +static const struct tegra_xusb_pad_soc tegra194_usb2_pad = { + .name = "usb2", + .num_lanes = ARRAY_SIZE(tegra194_usb2_lanes), + .lanes = tegra194_usb2_lanes, + .ops = &tegra186_usb2_pad_ops, +}; + +static const struct tegra_xusb_lane_soc tegra194_usb3_lanes[] = { + TEGRA186_LANE("usb3-0", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-1", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-2", 0, 0, 0, usb3), + TEGRA186_LANE("usb3-3", 0, 0, 0, usb3), +}; + +static const struct tegra_xusb_pad_soc tegra194_usb3_pad = { + .name = "usb3", + .num_lanes = ARRAY_SIZE(tegra194_usb3_lanes), + .lanes = tegra194_usb3_lanes, + .ops = &tegra186_usb3_pad_ops, +}; + +static const struct tegra_xusb_pad_soc * const tegra194_pads[] = { + &tegra194_usb2_pad, + &tegra194_usb3_pad, +}; + +const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = { + .num_pads = ARRAY_SIZE(tegra194_pads), + .pads = tegra194_pads, + .ports = { + .usb2 = { + .ops = &tegra186_usb2_port_ops, + .count = 4, + }, + .usb3 = { + .ops = &tegra186_usb3_port_ops, + .count = 4, + }, + }, + .ops = &tegra186_xusb_padctl_ops, + .supply_names = tegra194_xusb_padctl_supply_names, + .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names), + .supports_gen2 = true, +}; +EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc); +#endif MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>"); MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver"); diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 394913bb2f20..eedfc7c2cc05 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. * Copyright (C) 2015 Google, Inc. */ @@ -11,8 +11,10 @@ #include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> @@ -52,6 +54,20 @@ #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5)) #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7 +#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20 +#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x)) +#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7) +#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14) +#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21) +#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28) +#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30) +#define ALL_WAKE_EVENTS ( \ + USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \ + USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \ + SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \ + SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \ + USB2_HSIC_PORT_WAKEUP_EVENT(0)) + #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) @@ -90,6 +106,8 @@ #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) +#define RPD_CTRL(x) (((x) & 0x1f) << 26) +#define RPD_CTRL_VALUE(x) (((x) >> 26) & 0x1f) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11) @@ -108,6 +126,8 @@ #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e +#define TCTRL_VALUE(x) (((x) & 0x3f) >> 0) +#define PCTRL_VALUE(x) (((x) >> 6) & 0x3f) #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20) #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18) @@ -198,6 +218,18 @@ #define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18) #define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13) +#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(x) (0x464 + (x) * 0x40) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ BIT(0) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD BIT(1) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK GENMASK(5, 4) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL GENMASK(5, 4) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD BIT(24) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ BIT(8) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD BIT(9) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK GENMASK(13, 12) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL GENMASK(13, 12) +#define XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD BIT(25) + #define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860 #define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864 @@ -209,6 +241,7 @@ #define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c #define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960 +#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2 0x964 #define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16 @@ -236,6 +269,143 @@ #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT 18 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK 0xf #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8 +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0 + +/* USB2 SLEEPWALK registers */ +#define UTMIP(_port, _offset1, _offset2) \ + (((_port) <= 2) ? (_offset1) : (_offset2)) + +#define PMC_UTMIP_UHSIC_SLEEP_CFG(x) UTMIP(x, 0x1fc, 0x4d0) +#define UTMIP_MASTER_ENABLE(x) UTMIP(x, BIT(8 * (x)), BIT(0)) +#define UTMIP_FSLS_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 1), \ + BIT(1)) +#define UTMIP_PCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 2), \ + BIT(2)) +#define UTMIP_TCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 3), \ + BIT(3)) +#define UTMIP_WAKE_VAL(_port, _value) (((_value) & 0xf) << \ + (UTMIP(_port, 8 * (_port) + 4, 4))) +#define UTMIP_WAKE_VAL_NONE(_port) UTMIP_WAKE_VAL(_port, 12) +#define UTMIP_WAKE_VAL_ANY(_port) UTMIP_WAKE_VAL(_port, 15) + +#define PMC_UTMIP_UHSIC_SLEEP_CFG1 (0x4d0) +#define UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x) BIT((x) + 8) +#define UTMIP_RPD_CTRL_USE_PMC_PX(x) BIT((x) + 16) + +#define PMC_UTMIP_MASTER_CONFIG (0x274) +#define UTMIP_PWR(x) UTMIP(x, BIT(x), BIT(4)) +#define UHSIC_PWR BIT(3) + +#define PMC_USB_DEBOUNCE_DEL (0xec) +#define DEBOUNCE_VAL(x) (((x) & 0xffff) << 0) +#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16) +#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 20) + +#define PMC_UTMIP_UHSIC_FAKE(x) UTMIP(x, 0x218, 0x294) +#define UTMIP_FAKE_USBOP_VAL(x) UTMIP(x, BIT(4 * (x)), BIT(8)) +#define UTMIP_FAKE_USBON_VAL(x) UTMIP(x, BIT(4 * (x) + 1), \ + BIT(9)) +#define UTMIP_FAKE_USBOP_EN(x) UTMIP(x, BIT(4 * (x) + 2), \ + BIT(10)) +#define UTMIP_FAKE_USBON_EN(x) UTMIP(x, BIT(4 * (x) + 3), \ + BIT(11)) + +#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x) UTMIP(x, 0x200, 0x288) +#define UTMIP_LINEVAL_WALK_EN(x) UTMIP(x, BIT(8 * (x) + 7), \ + BIT(15)) + +#define PMC_USB_AO (0xf0) +#define USBOP_VAL_PD(x) UTMIP(x, BIT(4 * (x)), BIT(20)) +#define USBON_VAL_PD(x) UTMIP(x, BIT(4 * (x) + 1), \ + BIT(21)) +#define STROBE_VAL_PD BIT(12) +#define DATA0_VAL_PD BIT(13) +#define DATA1_VAL_PD BIT(24) + +#define PMC_UTMIP_UHSIC_SAVED_STATE(x) UTMIP(x, 0x1f0, 0x280) +#define SPEED(_port, _value) (((_value) & 0x3) << \ + (UTMIP(_port, 8 * (_port), 8))) +#define UTMI_HS(_port) SPEED(_port, 0) +#define UTMI_FS(_port) SPEED(_port, 1) +#define UTMI_LS(_port) SPEED(_port, 2) +#define UTMI_RST(_port) SPEED(_port, 3) + +#define PMC_UTMIP_UHSIC_TRIGGERS (0x1ec) +#define UTMIP_CLR_WALK_PTR(x) UTMIP(x, BIT(x), BIT(16)) +#define UTMIP_CAP_CFG(x) UTMIP(x, BIT((x) + 4), BIT(17)) +#define UTMIP_CLR_WAKE_ALARM(x) UTMIP(x, BIT((x) + 12), \ + BIT(19)) +#define UHSIC_CLR_WALK_PTR BIT(3) +#define UHSIC_CLR_WAKE_ALARM BIT(15) + +#define PMC_UTMIP_SLEEPWALK_PX(x) UTMIP(x, 0x204 + (4 * (x)), \ + 0x4e0) +/* phase A */ +#define UTMIP_USBOP_RPD_A BIT(0) +#define UTMIP_USBON_RPD_A BIT(1) +#define UTMIP_AP_A BIT(4) +#define UTMIP_AN_A BIT(5) +#define UTMIP_HIGHZ_A BIT(6) +/* phase B */ +#define UTMIP_USBOP_RPD_B BIT(8) +#define UTMIP_USBON_RPD_B BIT(9) +#define UTMIP_AP_B BIT(12) +#define UTMIP_AN_B BIT(13) +#define UTMIP_HIGHZ_B BIT(14) +/* phase C */ +#define UTMIP_USBOP_RPD_C BIT(16) +#define UTMIP_USBON_RPD_C BIT(17) +#define UTMIP_AP_C BIT(20) +#define UTMIP_AN_C BIT(21) +#define UTMIP_HIGHZ_C BIT(22) +/* phase D */ +#define UTMIP_USBOP_RPD_D BIT(24) +#define UTMIP_USBON_RPD_D BIT(25) +#define UTMIP_AP_D BIT(28) +#define UTMIP_AN_D BIT(29) +#define UTMIP_HIGHZ_D BIT(30) + +#define PMC_UTMIP_UHSIC_LINE_WAKEUP (0x26c) +#define UTMIP_LINE_WAKEUP_EN(x) UTMIP(x, BIT(x), BIT(4)) +#define UHSIC_LINE_WAKEUP_EN BIT(3) + +#define PMC_UTMIP_TERM_PAD_CFG (0x1f8) +#define PCTRL_VAL(x) (((x) & 0x3f) << 1) +#define TCTRL_VAL(x) (((x) & 0x3f) << 7) + +#define PMC_UTMIP_PAD_CFGX(x) (0x4c0 + (4 * (x))) +#define RPD_CTRL_PX(x) (((x) & 0x1f) << 22) + +#define PMC_UHSIC_SLEEP_CFG PMC_UTMIP_UHSIC_SLEEP_CFG(0) +#define UHSIC_MASTER_ENABLE BIT(24) +#define UHSIC_WAKE_VAL(_value) (((_value) & 0xf) << 28) +#define UHSIC_WAKE_VAL_SD10 UHSIC_WAKE_VAL(2) +#define UHSIC_WAKE_VAL_NONE UHSIC_WAKE_VAL(12) + +#define PMC_UHSIC_FAKE PMC_UTMIP_UHSIC_FAKE(0) +#define UHSIC_FAKE_STROBE_VAL BIT(12) +#define UHSIC_FAKE_DATA_VAL BIT(13) +#define UHSIC_FAKE_STROBE_EN BIT(14) +#define UHSIC_FAKE_DATA_EN BIT(15) + +#define PMC_UHSIC_SAVED_STATE PMC_UTMIP_UHSIC_SAVED_STATE(0) +#define UHSIC_MODE(_value) (((_value) & 0x1) << 24) +#define UHSIC_HS UHSIC_MODE(0) +#define UHSIC_RST UHSIC_MODE(1) + +#define PMC_UHSIC_SLEEPWALK_CFG PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0) +#define UHSIC_WAKE_WALK_EN BIT(30) +#define UHSIC_LINEVAL_WALK_EN BIT(31) + +#define PMC_UHSIC_SLEEPWALK_P0 (0x210) +#define UHSIC_DATA0_RPD_A BIT(1) +#define UHSIC_DATA0_RPU_B BIT(11) +#define UHSIC_DATA0_RPU_C BIT(19) +#define UHSIC_DATA0_RPU_D BIT(27) +#define UHSIC_STROBE_RPU_A BIT(2) +#define UHSIC_STROBE_RPD_B BIT(8) +#define UHSIC_STROBE_RPD_C BIT(16) +#define UHSIC_STROBE_RPD_D BIT(24) struct tegra210_xusb_fuse_calibration { u32 hs_curr_level[4]; @@ -243,10 +413,19 @@ struct tegra210_xusb_fuse_calibration { u32 rpd_ctrl; }; +struct tegra210_xusb_padctl_context { + u32 usb2_pad_mux; + u32 usb2_port_cap; + u32 ss_port_map; + u32 usb3_pad_mux; +}; + struct tegra210_xusb_padctl { struct tegra_xusb_padctl base; + struct regmap *regmap; struct tegra210_xusb_fuse_calibration fuse; + struct tegra210_xusb_padctl_context context; }; static inline struct tegra210_xusb_padctl * @@ -255,23 +434,51 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl) return container_of(padctl, struct tegra210_xusb_padctl, base); } +static const struct tegra_xusb_lane_map tegra210_usb3_map[] = { + { 0, "pcie", 6 }, + { 1, "pcie", 5 }, + { 2, "pcie", 0 }, + { 2, "pcie", 3 }, + { 3, "pcie", 4 }, + { 3, "sata", 0 }, + { 0, NULL, 0 } +}; + +static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane) +{ + const struct tegra_xusb_lane_map *map; + + for (map = tegra210_usb3_map; map->type; map++) { + if (map->index == lane->index && + strcmp(map->type, lane->pad->soc->name) == 0) { + dev_dbg(lane->pad->padctl->dev, "lane = %s map to port = usb3-%d\n", + lane->pad->soc->lanes[lane->index].name, map->port); + return map->port; + } + } + + return -EINVAL; +} + /* must be called under padctl->lock */ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); unsigned long timeout; u32 value; + unsigned int i; int err; - if (pcie->enable > 0) { - pcie->enable++; + if (pcie->enable) return 0; - } err = clk_prepare_enable(pcie->pll); if (err < 0) return err; + if (tegra210_plle_hw_sequence_is_enabled()) + goto skip_pll_init; + err = reset_control_deassert(pcie->rst); if (err < 0) goto disable; @@ -454,7 +661,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) tegra210_xusb_pll_hw_sequence_start(); - pcie->enable++; +skip_pll_init: + pcie->enable = true; + + for (i = 0; i < padctl->pcie->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } return 0; @@ -468,34 +682,44 @@ disable: static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie); + u32 value; + unsigned int i; - mutex_lock(&padctl->lock); + if (WARN_ON(!pcie->enable)) + return; - if (WARN_ON(pcie->enable == 0)) - goto unlock; + pcie->enable = false; - if (--pcie->enable > 0) - goto unlock; + for (i = 0; i < padctl->pcie->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } - reset_control_assert(pcie->rst); clk_disable_unprepare(pcie->pll); - -unlock: - mutex_unlock(&padctl->lock); } /* must be called under padctl->lock */ -static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb) +static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); + struct tegra_xusb_lane *lane = tegra_xusb_find_lane(padctl, "sata", 0); unsigned long timeout; u32 value; + unsigned int i; int err; + bool usb; - if (sata->enable > 0) { - sata->enable++; + if (sata->enable) return 0; - } + + if (IS_ERR(lane)) + return 0; + + if (tegra210_plle_hw_sequence_is_enabled()) + goto skip_pll_init; + + usb = tegra_xusb_lane_check(lane, "usb3-ss"); err = clk_prepare_enable(sata->pll); if (err < 0) @@ -696,7 +920,14 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb) tegra210_sata_pll_hw_sequence_start(); - sata->enable++; +skip_pll_init: + sata->enable = true; + + for (i = 0; i < padctl->sata->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } return 0; @@ -710,31 +941,27 @@ disable: static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata); + u32 value; + unsigned int i; - mutex_lock(&padctl->lock); + if (WARN_ON(!sata->enable)) + return; - if (WARN_ON(sata->enable == 0)) - goto unlock; + sata->enable = false; - if (--sata->enable > 0) - goto unlock; + for (i = 0; i < padctl->sata->soc->num_lanes; i++) { + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); + value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i); + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + } - reset_control_assert(sata->rst); clk_disable_unprepare(sata->pll); - -unlock: - mutex_unlock(&padctl->lock); } -static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) +static void tegra210_aux_mux_lp0_clamp_disable(struct tegra_xusb_padctl *padctl) { u32 value; - mutex_lock(&padctl->lock); - - if (padctl->enable++ > 0) - goto out; - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); @@ -750,24 +977,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - -out: - mutex_unlock(&padctl->lock); - return 0; } -static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) +static void tegra210_aux_mux_lp0_clamp_enable(struct tegra_xusb_padctl *padctl) { u32 value; - mutex_lock(&padctl->lock); - - if (WARN_ON(padctl->enable == 0)) - goto out; - - if (--padctl->enable > 0) - goto out; - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); @@ -783,12 +998,38 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); +} + +static int tegra210_uphy_init(struct tegra_xusb_padctl *padctl) +{ + if (padctl->pcie) + tegra210_pex_uphy_enable(padctl); + + if (padctl->sata) + tegra210_sata_uphy_enable(padctl); + + if (!tegra210_plle_hw_sequence_is_enabled()) + tegra210_plle_hw_sequence_start(); + else + dev_dbg(padctl->dev, "PLLE is already in HW control\n"); + + tegra210_aux_mux_lp0_clamp_disable(padctl); -out: - mutex_unlock(&padctl->lock); return 0; } +static void __maybe_unused +tegra210_uphy_deinit(struct tegra_xusb_padctl *padctl) +{ + tegra210_aux_mux_lp0_clamp_enable(padctl); + + if (padctl->sata) + tegra210_sata_uphy_disable(padctl); + + if (padctl->pcie) + tegra210_pex_uphy_disable(padctl); +} + static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, unsigned int index, bool idle) { @@ -814,6 +1055,643 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, return 0; } +static int tegra210_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKE_INTERRUPT_ENABLE(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra210_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int index = tegra210_usb3_lane_map(lane); + u32 value; + + if (index < 0) + return false; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + +static int tegra210_utmi_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_utmi_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra210_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) && + (value & USB2_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + +static int tegra210_hsic_enable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_hsic_disable_phy_wake(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static bool tegra210_hsic_phy_remote_wake_detected(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index)) && + (value & USB2_HSIC_PORT_WAKEUP_EVENT(index))) + return true; + + return false; +} + +#define padctl_pmc_readl(_priv, _offset) \ +({ \ + u32 value; \ + WARN(regmap_read(_priv->regmap, _offset, &value), "read %s failed\n", #_offset);\ + value; \ +}) + +#define padctl_pmc_writel(_priv, _value, _offset) \ + WARN(regmap_write(_priv->regmap, _offset, _value), "write %s failed\n", #_offset) + +static int tegra210_pmc_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + unsigned int port = lane->index; + u32 value, tctrl, pctrl, rpd_ctrl; + + if (!priv->regmap) + return -EOPNOTSUPP; + + if (speed > USB_SPEED_HIGH) + return -EINVAL; + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + tctrl = TCTRL_VALUE(value); + pctrl = PCTRL_VALUE(value); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); + rpd_ctrl = RPD_CTRL_VALUE(value); + + /* ensure sleepwalk logic is disabled */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* ensure sleepwalk logics are in low power mode */ + value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + value |= UTMIP_PWR(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + value &= ~UTMIP_LINE_DEB_CNT(~0); + value |= UTMIP_LINE_DEB_CNT(0x1); + padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port)); + value &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) | + UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_FAKE(port)); + + /* ensure wake events of sleepwalk logic are not latched */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_WAKE_VAL(port, ~0); + value |= UTMIP_WAKE_VAL_NONE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* power down the line state detectors of the pad */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* save state per speed */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + value &= ~SPEED(port, ~0); + + switch (speed) { + case USB_SPEED_HIGH: + value |= UTMI_HS(port); + break; + + case USB_SPEED_FULL: + value |= UTMI_FS(port); + break; + + case USB_SPEED_LOW: + value |= UTMI_LS(port); + break; + + default: + value |= UTMI_RST(port); + break; + } + + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + + /* enable the trigger of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + value |= UTMIP_LINEVAL_WALK_EN(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + + /* + * Reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 pad. + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | UTMIP_CAP_CFG(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + /* program electrical parameters read from XUSB PADCTL */ + value = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG); + value &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0)); + value |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl)); + padctl_pmc_writel(priv, value, PMC_UTMIP_TERM_PAD_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port)); + value &= ~RPD_CTRL_PX(~0); + value |= RPD_CTRL_PX(rpd_ctrl); + padctl_pmc_writel(priv, value, PMC_UTMIP_PAD_CFGX(port)); + + /* + * Set up the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. If a device is connected, program sleepwalk + * logic to maintain a J and keep driving K upon seeing remote wake. + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port)); + value = UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | UTMIP_USBOP_RPD_D; + value |= UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | UTMIP_USBON_RPD_D; + + switch (speed) { + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + /* J state: D+/D- = high/low, K state: D+/D- = low/high */ + value |= UTMIP_HIGHZ_A; + value |= UTMIP_AP_A; + value |= UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D; + break; + + case USB_SPEED_LOW: + /* J state: D+/D- = low/high, K state: D+/D- = high/low */ + value |= UTMIP_HIGHZ_A; + value |= UTMIP_AN_A; + value |= UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D; + break; + + default: + value |= UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | UTMIP_HIGHZ_D; + break; + } + + padctl_pmc_writel(priv, value, PMC_UTMIP_SLEEPWALK_PX(port)); + + /* power up the line state detectors of the pad */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + usleep_range(50, 100); + + /* switch the electric control of the USB2.0 pad to PMC */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value |= UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | UTMIP_TCTRL_USE_PMC(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); + value |= UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1); + + /* set the wake signaling trigger events */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_WAKE_VAL(port, ~0); + value |= UTMIP_WAKE_VAL_ANY(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* enable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value |= UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value |= UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + return 0; +} + +static int tegra210_pmc_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + unsigned int port = lane->index; + u32 value; + + if (!priv->regmap) + return -EOPNOTSUPP; + + /* disable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* switch the electric control of the USB2.0 pad to XUSB or USB2 */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | + UTMIP_TCTRL_USE_PMC(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); + value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) | UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_WAKE_VAL(port, ~0); + value |= UTMIP_WAKE_VAL_NONE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* clear alarm of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UTMIP_CLR_WAKE_ALARM(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + return 0; +} + +static int tegra210_pmc_hsic_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, + enum usb_device_speed speed) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + u32 value; + + if (!priv->regmap) + return -EOPNOTSUPP; + + /* ensure sleepwalk logic is disabled */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* ensure sleepwalk logics are in low power mode */ + value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + value |= UHSIC_PWR; + padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + value &= ~UHSIC_LINE_DEB_CNT(~0); + value |= UHSIC_LINE_DEB_CNT(0x1); + padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + value = padctl_pmc_readl(priv, PMC_UHSIC_FAKE); + value &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL | + UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN); + padctl_pmc_writel(priv, value, PMC_UHSIC_FAKE); + + /* ensure wake events of sleepwalk logic are not latched */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_NONE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD; + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* save state, HSIC always comes up as HS */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SAVED_STATE); + value &= ~UHSIC_MODE(~0); + value |= UHSIC_HS; + padctl_pmc_writel(priv, value, PMC_UHSIC_SAVED_STATE); + + /* enable the trigger of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_CFG); + value |= UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_CFG); + + /* + * Reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 port. + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + /* + * Set up the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. Maintain a HSIC IDLE and keep driving HSIC + * RESUME upon remote wake. + */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_P0); + value = UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C | UHSIC_DATA0_RPU_D | + UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C | UHSIC_STROBE_RPD_D; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_P0); + + /* power up the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value &= ~(STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + usleep_range(50, 100); + + /* set the wake signaling trigger events */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_SD10; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* enable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value |= UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value |= UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + return 0; +} + +static int tegra210_pmc_hsic_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + u32 value; + + if (!priv->regmap) + return -EOPNOTSUPP; + + /* disable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_NONE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= STROBE_VAL_PD | DATA0_VAL_PD | DATA1_VAL_PD; + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* clear alarm of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UHSIC_CLR_WAKE_ALARM; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + return 0; +} + static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int index, bool enable) { @@ -910,14 +1788,36 @@ static void tegra210_usb2_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_usb2_lane_ops = { .probe = tegra210_usb2_lane_probe, .remove = tegra210_usb2_lane_remove, + .enable_phy_sleepwalk = tegra210_pmc_utmi_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_pmc_utmi_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_utmi_enable_phy_wake, + .disable_phy_wake = tegra210_utmi_disable_phy_wake, + .remote_wake_detected = tegra210_utmi_phy_remote_wake_detected, }; static int tegra210_usb2_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct tegra_xusb_usb2_port *port; + int err; u32 value; + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + if (port->supply && port->mode == USB_DR_MODE_HOST) { + err = regulator_enable(port->supply); + if (err) + return err; + } + + mutex_lock(&padctl->lock); + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK << XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT); @@ -925,14 +1825,128 @@ static int tegra210_usb2_phy_init(struct phy *phy) XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT; padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); - return tegra210_xusb_padctl_enable(padctl); + mutex_unlock(&padctl->lock); + + return 0; } static int tegra210_usb2_phy_exit(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port; + int err; + + port = tegra_xusb_find_usb2_port(padctl, lane->index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", lane->index); + return -ENODEV; + } + + if (port->supply && port->mode == USB_DR_MODE_HOST) { + err = regulator_disable(port->supply); + if (err) + return err; + } + + return 0; +} + +static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; - return tegra210_xusb_padctl_disable(lane->pad->padctl); + dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + + if (status) { + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } else { + value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + } + + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + + return 0; +} + +static int tegra210_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + + if (status) { + if (value & XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON) { + value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + usleep_range(1000, 2000); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + } + + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } else { + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } + + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + + return 0; +} + +static int tegra210_usb2_phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl, + lane->index); + int err = 0; + + mutex_lock(&padctl->lock); + + dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode); + + if (mode == PHY_MODE_USB_OTG) { + if (submode == USB_ROLE_HOST) { + tegra210_xusb_padctl_id_override(padctl, true); + + err = regulator_enable(port->supply); + } else if (submode == USB_ROLE_DEVICE) { + tegra210_xusb_padctl_vbus_override(padctl, true); + } else if (submode == USB_ROLE_NONE) { + /* + * When port is peripheral only or role transitions to + * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not + * be enabled. + */ + if (regulator_is_enabled(port->supply)) + regulator_disable(port->supply); + + tegra210_xusb_padctl_id_override(padctl, false); + tegra210_xusb_padctl_vbus_override(padctl, false); + } + } + + mutex_unlock(&padctl->lock); + + return err; } static int tegra210_usb2_phy_power_on(struct phy *phy) @@ -955,6 +1969,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) priv = to_tegra210_xusb_padctl(padctl); + mutex_lock(&padctl->lock); + if (port->usb3_port_fake != -1) { value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( @@ -1048,12 +2064,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); - err = regulator_enable(port->supply); - if (err) - return err; - - mutex_lock(&padctl->lock); - if (pad->enable > 0) { pad->enable++; mutex_unlock(&padctl->lock); @@ -1062,7 +2072,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) err = clk_prepare_enable(pad->clk); if (err) - goto disable_regulator; + goto out; value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << @@ -1094,8 +2104,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) return 0; -disable_regulator: - regulator_disable(port->supply); +out: mutex_unlock(&padctl->lock); return err; } @@ -1154,7 +2163,6 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); out: - regulator_disable(port->supply); mutex_unlock(&padctl->lock); return 0; } @@ -1164,6 +2172,7 @@ static const struct phy_ops tegra210_usb2_phy_ops = { .exit = tegra210_usb2_phy_exit, .power_on = tegra210_usb2_phy_power_on, .power_off = tegra210_usb2_phy_power_off, + .set_mode = tegra210_usb2_phy_set_mode, .owner = THIS_MODULE, }; @@ -1275,6 +2284,11 @@ static void tegra210_hsic_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_hsic_lane_ops = { .probe = tegra210_hsic_lane_probe, .remove = tegra210_hsic_lane_remove, + .enable_phy_sleepwalk = tegra210_pmc_hsic_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_pmc_hsic_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_hsic_enable_phy_wake, + .disable_phy_wake = tegra210_hsic_disable_phy_wake, + .remote_wake_detected = tegra210_hsic_phy_remote_wake_detected, }; static int tegra210_hsic_phy_init(struct phy *phy) @@ -1290,14 +2304,12 @@ static int tegra210_hsic_phy_init(struct phy *phy) XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT; padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); - return tegra210_xusb_padctl_enable(padctl); + return 0; } static int tegra210_hsic_phy_exit(struct phy *phy) { - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); - - return tegra210_xusb_padctl_disable(lane->pad->padctl); + return 0; } static int tegra210_hsic_phy_power_on(struct phy *phy) @@ -1481,6 +2493,55 @@ static const struct tegra_xusb_pad_soc tegra210_hsic_pad = { .ops = &tegra210_hsic_ops, }; +static void tegra210_uphy_lane_iddq_enable(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, lane->soc->regs.misc_ctl2); + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL; + padctl_writel(padctl, value, lane->soc->regs.misc_ctl2); +} + +static void tegra210_uphy_lane_iddq_disable(struct tegra_xusb_lane *lane) +{ + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + u32 value; + + value = padctl_readl(padctl, lane->soc->regs.misc_ctl2); + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ_OVRD; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ_OVRD; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_PWR_OVRD; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_PWR_OVRD; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_TX_SLEEP_VAL; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_IDDQ; + value &= ~XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_MASK; + value |= XUSB_PADCTL_UPHY_MISC_PAD_CTL2_RX_SLEEP_VAL; + padctl_writel(padctl, value, lane->soc->regs.misc_ctl2); +} + +#define TEGRA210_UPHY_LANE(_name, _offset, _shift, _mask, _type, _misc) \ + { \ + .name = _name, \ + .offset = _offset, \ + .shift = _shift, \ + .mask = _mask, \ + .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions), \ + .funcs = tegra210_##_type##_functions, \ + .regs.misc_ctl2 = _misc, \ + } + static const char *tegra210_pcie_functions[] = { "pcie-x1", "usb3-ss", @@ -1489,15 +2550,137 @@ static const char *tegra210_pcie_functions[] = { }; static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = { - TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, pcie), - TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, pcie), - TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, pcie), - TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, pcie), - TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, pcie), - TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, pcie), - TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie), + TEGRA210_UPHY_LANE("pcie-0", 0x028, 12, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(0)), + TEGRA210_UPHY_LANE("pcie-1", 0x028, 14, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(1)), + TEGRA210_UPHY_LANE("pcie-2", 0x028, 16, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(2)), + TEGRA210_UPHY_LANE("pcie-3", 0x028, 18, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(3)), + TEGRA210_UPHY_LANE("pcie-4", 0x028, 20, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(4)), + TEGRA210_UPHY_LANE("pcie-5", 0x028, 22, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(5)), + TEGRA210_UPHY_LANE("pcie-6", 0x028, 24, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL2(6)), }; +static struct tegra_xusb_usb3_port * +tegra210_lane_to_usb3_port(struct tegra_xusb_lane *lane) +{ + int port; + + if (!lane || !lane->pad || !lane->pad->padctl) + return NULL; + + port = tegra210_usb3_lane_map(lane); + if (port < 0) + return NULL; + + return tegra_xusb_find_usb3_port(lane->pad->padctl, port); +} + +static int tegra210_usb3_phy_power_on(struct phy *phy) +{ + struct device *dev = &phy->dev; + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane); + unsigned int index; + u32 value; + + if (!usb3) { + dev_err(dev, "no USB3 port found for lane %u\n", lane->index); + return -ENODEV; + } + + index = usb3->base.index; + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + + if (!usb3->internal) + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + else + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); + + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); + + padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL, + XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); + value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT); + value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL << + XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT; + padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); + + padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL, + XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + return 0; +} + +static int tegra210_usb3_phy_power_off(struct phy *phy) +{ + struct device *dev = &phy->dev; + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_usb3_port *usb3 = tegra210_lane_to_usb3_port(lane); + unsigned int index; + u32 value; + + if (!usb3) { + dev_err(dev, "no USB3 port found for lane %u\n", lane->index); + return -ENODEV; + } + + index = usb3->base.index; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + return 0; +} static struct tegra_xusb_lane * tegra210_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np, unsigned int index) @@ -1534,40 +2717,40 @@ static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = { .probe = tegra210_pcie_lane_probe, .remove = tegra210_pcie_lane_remove, + .iddq_enable = tegra210_uphy_lane_iddq_enable, + .iddq_disable = tegra210_uphy_lane_iddq_disable, + .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_usb3_enable_phy_wake, + .disable_phy_wake = tegra210_usb3_disable_phy_wake, + .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected, }; static int tegra210_pcie_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; - return tegra210_xusb_padctl_enable(lane->pad->padctl); -} + mutex_lock(&padctl->lock); -static int tegra210_pcie_phy_exit(struct phy *phy) -{ - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + tegra210_uphy_init(padctl); + + mutex_unlock(&padctl->lock); - return tegra210_xusb_padctl_disable(lane->pad->padctl); + return 0; } static int tegra210_pcie_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - u32 value; - int err; + int err = 0; mutex_lock(&padctl->lock); - err = tegra210_pex_uphy_enable(padctl); - if (err < 0) - goto unlock; - - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_on(phy); -unlock: mutex_unlock(&padctl->lock); return err; } @@ -1576,20 +2759,19 @@ static int tegra210_pcie_phy_power_off(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - u32 value; + int err = 0; - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + mutex_lock(&padctl->lock); - tegra210_pex_uphy_disable(padctl); + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_off(phy); - return 0; + mutex_unlock(&padctl->lock); + return err; } static const struct phy_ops tegra210_pcie_phy_ops = { .init = tegra210_pcie_phy_init, - .exit = tegra210_pcie_phy_exit, .power_on = tegra210_pcie_phy_power_on, .power_off = tegra210_pcie_phy_power_off, .owner = THIS_MODULE, @@ -1666,7 +2848,7 @@ static const struct tegra_xusb_pad_soc tegra210_pcie_pad = { }; static const struct tegra_xusb_lane_soc tegra210_sata_lanes[] = { - TEGRA210_LANE("sata-0", 0x028, 30, 0x3, pcie), + TEGRA210_UPHY_LANE("sata-0", 0x028, 30, 0x3, pcie, XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2), }; static struct tegra_xusb_lane * @@ -1705,40 +2887,39 @@ static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane) static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = { .probe = tegra210_sata_lane_probe, .remove = tegra210_sata_lane_remove, + .iddq_enable = tegra210_uphy_lane_iddq_enable, + .iddq_disable = tegra210_uphy_lane_iddq_disable, + .enable_phy_sleepwalk = tegra210_usb3_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_usb3_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_usb3_enable_phy_wake, + .disable_phy_wake = tegra210_usb3_disable_phy_wake, + .remote_wake_detected = tegra210_usb3_phy_remote_wake_detected, }; static int tegra210_sata_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; - return tegra210_xusb_padctl_enable(lane->pad->padctl); -} + mutex_lock(&padctl->lock); -static int tegra210_sata_phy_exit(struct phy *phy) -{ - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + tegra210_uphy_init(padctl); - return tegra210_xusb_padctl_disable(lane->pad->padctl); + mutex_unlock(&padctl->lock); + return 0; } static int tegra210_sata_phy_power_on(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - u32 value; - int err; + int err = 0; mutex_lock(&padctl->lock); - err = tegra210_sata_uphy_enable(padctl, false); - if (err < 0) - goto unlock; - - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_on(phy); -unlock: mutex_unlock(&padctl->lock); return err; } @@ -1747,20 +2928,19 @@ static int tegra210_sata_phy_power_off(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - u32 value; + int err = 0; - value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); - value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index); - padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX); + mutex_lock(&padctl->lock); - tegra210_sata_uphy_disable(lane->pad->padctl); + if (tegra_xusb_lane_check(lane, "usb3-ss")) + err = tegra210_usb3_phy_power_off(phy); - return 0; + mutex_unlock(&padctl->lock); + return err; } static const struct phy_ops tegra210_sata_phy_ops = { .init = tegra210_sata_phy_init, - .exit = tegra210_sata_phy_exit, .power_on = tegra210_sata_phy_power_on, .power_off = tegra210_sata_phy_power_off, .owner = THIS_MODULE, @@ -1852,6 +3032,8 @@ tegra210_usb2_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = { + .release = tegra_xusb_usb2_port_release, + .remove = tegra_xusb_usb2_port_remove, .enable = tegra210_usb2_port_enable, .disable = tegra210_usb2_port_disable, .map = tegra210_usb2_port_map, @@ -1873,6 +3055,7 @@ tegra210_hsic_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = { + .release = tegra_xusb_hsic_port_release, .enable = tegra210_hsic_port_enable, .disable = tegra210_hsic_port_disable, .map = tegra210_hsic_port_map, @@ -1880,137 +3063,13 @@ static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = { static int tegra210_usb3_port_enable(struct tegra_xusb_port *port) { - struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); - struct tegra_xusb_padctl *padctl = port->padctl; - struct tegra_xusb_lane *lane = usb3->base.lane; - unsigned int index = port->index; - u32 value; - int err; - - value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); - - if (!usb3->internal) - value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); - else - value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index); - - value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); - value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port); - padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); - - /* - * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks - * and conditionalize based on mux function? This seems to work, but - * might not be the exact proper sequence. - */ - err = regulator_enable(usb3->supply); - if (err < 0) - return err; - - value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); - value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT); - value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT; - padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index)); - - value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); - value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT); - value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT; - padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index)); - - padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL, - XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index)); - - value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); - value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT); - value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL << - XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT; - padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index)); - - padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL, - XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index)); - - if (lane->pad == padctl->sata) - err = tegra210_sata_uphy_enable(padctl, true); - else - err = tegra210_pex_uphy_enable(padctl); - - if (err) { - dev_err(&port->dev, "%s: failed to enable UPHY: %d\n", - __func__, err); - return err; - } - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - usleep_range(100, 200); - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - usleep_range(100, 200); - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - return 0; } static void tegra210_usb3_port_disable(struct tegra_xusb_port *port) { - struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); - struct tegra_xusb_padctl *padctl = port->padctl; - struct tegra_xusb_lane *lane = port->lane; - unsigned int index = port->index; - u32 value; - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - usleep_range(100, 200); - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - usleep_range(250, 350); - - value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); - value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index); - padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); - - if (lane->pad == padctl->sata) - tegra210_sata_uphy_disable(padctl); - else - tegra210_pex_uphy_disable(padctl); - - regulator_disable(usb3->supply); - - value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); - value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index); - value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, 0x7); - padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); } -static const struct tegra_xusb_lane_map tegra210_usb3_map[] = { - { 0, "pcie", 6 }, - { 1, "pcie", 5 }, - { 2, "pcie", 0 }, - { 2, "pcie", 3 }, - { 3, "pcie", 4 }, - { 3, "pcie", 4 }, - { 0, NULL, 0 } -}; - static struct tegra_xusb_lane * tegra210_usb3_port_map(struct tegra_xusb_port *port) { @@ -2018,35 +3077,13 @@ tegra210_usb3_port_map(struct tegra_xusb_port *port) } static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = { + .release = tegra_xusb_usb3_port_release, + .remove = tegra_xusb_usb3_port_remove, .enable = tegra210_usb3_port_enable, .disable = tegra210_usb3_port_disable, .map = tegra210_usb3_port_map, }; -static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, - bool status) -{ - u32 value; - - dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); - - value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); - - if (status) { - value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; - value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << - XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); - value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << - XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; - } else { - value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; - } - - padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); - - return 0; -} - static int tegra210_utmi_port_reset(struct phy *phy) { struct tegra_xusb_padctl *padctl; @@ -2106,6 +3143,8 @@ tegra210_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { struct tegra210_xusb_padctl *padctl; + struct platform_device *pdev; + struct device_node *np; int err; padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); @@ -2119,6 +3158,26 @@ tegra210_xusb_padctl_probe(struct device *dev, if (err < 0) return ERR_PTR(err); + np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0); + if (!np) { + dev_warn(dev, "nvidia,pmc property is missing\n"); + goto out; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + dev_warn(dev, "PMC device is not available\n"); + goto out; + } + + if (!platform_get_drvdata(pdev)) + return ERR_PTR(-EPROBE_DEFER); + + padctl->regmap = dev_get_regmap(&pdev->dev, "usb_sleepwalk"); + if (!padctl->regmap) + dev_info(dev, "failed to find PMC regmap\n"); + +out: return &padctl->base; } @@ -2126,9 +3185,75 @@ static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } +static void tegra210_xusb_padctl_save(struct tegra_xusb_padctl *padctl) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + + priv->context.usb2_pad_mux = + padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); + priv->context.usb2_port_cap = + padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); + priv->context.ss_port_map = + padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + priv->context.usb3_pad_mux = + padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); +} + +static void tegra210_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct tegra_xusb_lane *lane; + + padctl_writel(padctl, priv->context.usb2_pad_mux, + XUSB_PADCTL_USB2_PAD_MUX); + padctl_writel(padctl, priv->context.usb2_port_cap, + XUSB_PADCTL_USB2_PORT_CAP); + padctl_writel(padctl, priv->context.ss_port_map, + XUSB_PADCTL_SS_PORT_MAP); + + list_for_each_entry(lane, &padctl->lanes, list) { + if (lane->pad->ops->iddq_enable) + tegra210_uphy_lane_iddq_enable(lane); + } + + padctl_writel(padctl, priv->context.usb3_pad_mux, + XUSB_PADCTL_USB3_PAD_MUX); + + list_for_each_entry(lane, &padctl->lanes, list) { + if (lane->pad->ops->iddq_disable) + tegra210_uphy_lane_iddq_disable(lane); + } +} + +static int tegra210_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + + tegra210_uphy_deinit(padctl); + + tegra210_xusb_padctl_save(padctl); + + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra210_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + + tegra210_xusb_padctl_restore(padctl); + + tegra210_uphy_init(padctl); + + mutex_unlock(&padctl->lock); + return 0; +} + static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { .probe = tegra210_xusb_padctl_probe, .remove = tegra210_xusb_padctl_remove, + .suspend_noirq = tegra210_xusb_padctl_suspend_noirq, + .resume_noirq = tegra210_xusb_padctl_resume_noirq, .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, .hsic_set_idle = tegra210_hsic_set_idle, .vbus_override = tegra210_xusb_padctl_vbus_override, diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index f98ec3922c02..dce45fbbd699 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. */ #include <linux/delay.h> @@ -66,6 +66,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = { .data = &tegra186_xusb_padctl_soc, }, #endif +#if defined(CONFIG_ARCH_TEGRA_194_SOC) + { + .compatible = "nvidia,tegra194-xusb-padctl", + .data = &tegra194_xusb_padctl_soc, + }, +#endif { } }; MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match); @@ -140,7 +146,7 @@ static void tegra_xusb_pad_release(struct device *dev) pad->soc->ops->remove(pad); } -static struct device_type tegra_xusb_pad_type = { +static const struct device_type tegra_xusb_pad_type = { .release = tegra_xusb_pad_release, }; @@ -315,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) if (soc->num_funcs < 2) return; + if (lane->pad->ops->iddq_enable) + lane->pad->ops->iddq_enable(lane); + /* choose function */ value = padctl_readl(padctl, soc->offset); value &= ~(soc->mask << soc->shift); value |= lane->function << soc->shift; padctl_writel(padctl, value, soc->offset); + + if (lane->pad->ops->iddq_disable) + lane->pad->ops->iddq_disable(lane); } static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) @@ -370,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) return 0; } -static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, +bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function) { const char *func = lane->soc->funcs[lane->function]; @@ -443,7 +455,7 @@ tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type, name = kasprintf(GFP_KERNEL, "%s-%u", type, index); if (!name) { of_node_put(ports); - return ERR_PTR(-ENOMEM); + return NULL; } np = of_get_child_by_name(ports, name); kfree(name); @@ -501,9 +513,13 @@ tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index) static void tegra_xusb_port_release(struct device *dev) { + struct tegra_xusb_port *port = to_tegra_xusb_port(dev); + + if (port->ops->release) + port->ops->release(port); } -static struct device_type tegra_xusb_port_type = { +static const struct device_type tegra_xusb_port_type = { .release = tegra_xusb_port_release, }; @@ -541,6 +557,16 @@ unregister: static void tegra_xusb_port_unregister(struct tegra_xusb_port *port) { + if (!IS_ERR_OR_NULL(port->usb_role_sw)) { + of_platform_depopulate(&port->dev); + usb_role_switch_unregister(port->usb_role_sw); + cancel_work_sync(&port->usb_phy_work); + usb_remove_phy(&port->usb_phy); + } + + if (port->ops->remove) + port->ops->remove(port); + device_unregister(&port->dev); } @@ -551,11 +577,147 @@ static const char *const modes[] = { [USB_DR_MODE_OTG] = "otg", }; +static const char * const usb_roles[] = { + [USB_ROLE_NONE] = "none", + [USB_ROLE_HOST] = "host", + [USB_ROLE_DEVICE] = "device", +}; + +static enum usb_phy_events to_usb_phy_event(enum usb_role role) +{ + switch (role) { + case USB_ROLE_DEVICE: + return USB_EVENT_VBUS; + + case USB_ROLE_HOST: + return USB_EVENT_ID; + + default: + return USB_EVENT_NONE; + } +} + +static void tegra_xusb_usb_phy_work(struct work_struct *work) +{ + struct tegra_xusb_port *port = container_of(work, + struct tegra_xusb_port, + usb_phy_work); + enum usb_role role = usb_role_switch_get_role(port->usb_role_sw); + + usb_phy_set_event(&port->usb_phy, to_usb_phy_event(role)); + + dev_dbg(&port->dev, "%s(): calling notifier for role %s\n", __func__, + usb_roles[role]); + + atomic_notifier_call_chain(&port->usb_phy.notifier, 0, &port->usb_phy); +} + +static int tegra_xusb_role_sw_set(struct usb_role_switch *sw, + enum usb_role role) +{ + struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw); + + dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]); + + schedule_work(&port->usb_phy_work); + + return 0; +} + +static int tegra_xusb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct tegra_xusb_port *port = container_of(otg->usb_phy, + struct tegra_xusb_port, + usb_phy); + + if (gadget != NULL) + schedule_work(&port->usb_phy_work); + + return 0; +} + +static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct tegra_xusb_port *port = container_of(otg->usb_phy, + struct tegra_xusb_port, + usb_phy); + + if (host != NULL) + schedule_work(&port->usb_phy_work); + + return 0; +} + + +static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port) +{ + struct tegra_xusb_lane *lane; + struct usb_role_switch_desc role_sx_desc = { + .fwnode = dev_fwnode(&port->dev), + .set = tegra_xusb_role_sw_set, + .allow_userspace_control = true, + }; + int err = 0; + + /* + * USB role switch driver needs parent driver owner info. This is a + * suboptimal solution. TODO: Need to revisit this in a follow-up patch + * where an optimal solution is possible with changes to USB role + * switch driver. + */ + port->dev.driver = devm_kzalloc(&port->dev, + sizeof(struct device_driver), + GFP_KERNEL); + port->dev.driver->owner = THIS_MODULE; + + port->usb_role_sw = usb_role_switch_register(&port->dev, + &role_sx_desc); + if (IS_ERR(port->usb_role_sw)) { + err = PTR_ERR(port->usb_role_sw); + dev_err(&port->dev, "failed to register USB role switch: %d", + err); + return err; + } + + INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work); + usb_role_switch_set_drvdata(port->usb_role_sw, port); + + port->usb_phy.otg = devm_kzalloc(&port->dev, sizeof(struct usb_otg), + GFP_KERNEL); + if (!port->usb_phy.otg) + return -ENOMEM; + + lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index); + + /* + * Assign phy dev to usb-phy dev. Host/device drivers can use phy + * reference to retrieve usb-phy details. + */ + port->usb_phy.dev = &lane->pad->lanes[port->index]->dev; + port->usb_phy.dev->driver = port->dev.driver; + port->usb_phy.otg->usb_phy = &port->usb_phy; + port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral; + port->usb_phy.otg->set_host = tegra_xusb_set_host; + + err = usb_add_phy_dev(&port->usb_phy); + if (err < 0) { + dev_err(&port->dev, "Failed to add USB PHY: %d\n", err); + return err; + } + + /* populate connector entry */ + of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev); + + return err; +} + static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) { struct tegra_xusb_port *port = &usb2->base; struct device_node *np = port->dev.of_node; const char *mode; + int err; usb2->internal = of_property_read_bool(np, "nvidia,internal"); @@ -572,7 +734,21 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) usb2->mode = USB_DR_MODE_HOST; } - usb2->supply = devm_regulator_get(&port->dev, "vbus"); + /* usb-role-switch property is mandatory for OTG/Peripheral modes */ + if (usb2->mode == USB_DR_MODE_PERIPHERAL || + usb2->mode == USB_DR_MODE_OTG) { + if (of_property_read_bool(np, "usb-role-switch")) { + err = tegra_xusb_setup_usb_role_switch(port); + if (err < 0) + return err; + } else { + dev_err(&port->dev, "usb-role-switch not found for %s mode", + modes[usb2->mode]); + return -EINVAL; + } + } + + usb2->supply = regulator_get(&port->dev, "vbus"); return PTR_ERR_OR_ZERO(usb2->supply); } @@ -591,7 +767,7 @@ static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL); + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); if (!usb2) { err = -ENOMEM; goto out; @@ -622,6 +798,20 @@ out: return err; } +void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); + + kfree(usb2); +} + +void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); + + regulator_put(usb2->supply); +} + static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi) { struct tegra_xusb_port *port = &ulpi->base; @@ -643,7 +833,7 @@ static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL); + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); if (!ulpi) { err = -ENOMEM; goto out; @@ -674,6 +864,13 @@ out: return err; } +void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_ulpi_port *ulpi = to_ulpi_port(port); + + kfree(ulpi); +} + static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic) { /* XXX */ @@ -691,7 +888,7 @@ static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL); + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); if (!hsic) { err = -ENOMEM; goto out; @@ -722,10 +919,18 @@ out: return err; } +void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_hsic_port *hsic = to_hsic_port(port); + + kfree(hsic); +} + static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) { struct tegra_xusb_port *port = &usb3->base; struct device_node *np = port->dev.of_node; + enum usb_device_speed maximum_speed; u32 value; int err; @@ -739,7 +944,17 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) usb3->internal = of_property_read_bool(np, "nvidia,internal"); - usb3->supply = devm_regulator_get(&port->dev, "vbus"); + if (device_property_present(&port->dev, "maximum-speed")) { + maximum_speed = usb_get_maximum_speed(&port->dev); + if (maximum_speed == USB_SPEED_SUPER) + usb3->disable_gen2 = true; + else if (maximum_speed == USB_SPEED_SUPER_PLUS) + usb3->disable_gen2 = false; + else + return -EINVAL; + } + + usb3->supply = regulator_get(&port->dev, "vbus"); return PTR_ERR_OR_ZERO(usb3->supply); } @@ -759,7 +974,7 @@ static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl, if (!np || !of_device_is_available(np)) goto out; - usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL); + usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL); if (!usb3) { err = -ENOMEM; goto out; @@ -790,6 +1005,20 @@ out: return err; } +void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + + kfree(usb3); +} + +void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port) +{ + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); + + regulator_put(usb3->supply); +} + static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_port *port, *tmp; @@ -926,7 +1155,6 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) const struct tegra_xusb_padctl_soc *soc; struct tegra_xusb_padctl *padctl; const struct of_device_id *match; - struct resource *res; int err; /* for backwards compatibility with old device trees */ @@ -951,8 +1179,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) INIT_LIST_HEAD(&padctl->pads); mutex_init(&padctl->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - padctl->regs = devm_ioremap_resource(&pdev->dev, res); + padctl->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(padctl->regs)) { err = PTR_ERR(padctl->regs); goto remove; @@ -978,7 +1205,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) err = devm_regulator_bulk_get(&pdev->dev, padctl->soc->num_supplies, padctl->supplies); if (err < 0) { - dev_err(&pdev->dev, "failed to get regulators: %d\n", err); + dev_err_probe(&pdev->dev, err, "failed to get regulators\n"); goto remove; } @@ -1001,7 +1228,13 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) err = tegra_xusb_setup_ports(padctl); if (err) { - dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err); + const char *level = KERN_ERR; + + if (err == -EPROBE_DEFER) + level = KERN_DEBUG; + + dev_printk(level, &pdev->dev, + dev_fmt("failed to setup XUSB ports: %d\n"), err); goto remove_pads; } @@ -1014,6 +1247,7 @@ power_down: reset: reset_control_assert(padctl->rst); remove: + platform_set_drvdata(pdev, NULL); soc->ops->remove(padctl); return err; } @@ -1037,13 +1271,39 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev) padctl->soc->ops->remove(padctl); - return err; + return 0; } +static __maybe_unused int tegra_xusb_padctl_suspend_noirq(struct device *dev) +{ + struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); + + if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq) + return padctl->soc->ops->suspend_noirq(padctl); + + return 0; +} + +static __maybe_unused int tegra_xusb_padctl_resume_noirq(struct device *dev) +{ + struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); + + if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq) + return padctl->soc->ops->resume_noirq(padctl); + + return 0; +} + +static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq, + tegra_xusb_padctl_resume_noirq) +}; + static struct platform_driver tegra_xusb_padctl_driver = { .driver = { .name = "tegra-xusb-padctl", .of_match_table = tegra_xusb_padctl_of_match, + .pm = &tegra_xusb_padctl_pm_ops, }, .probe = tegra_xusb_padctl_probe, .remove = tegra_xusb_padctl_remove, @@ -1110,6 +1370,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); +int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy, + enum usb_device_speed speed) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->enable_phy_sleepwalk) + return lane->pad->ops->enable_phy_sleepwalk(lane, speed); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk); + +int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->disable_phy_sleepwalk) + return lane->pad->ops->disable_phy_sleepwalk(lane); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk); + +int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->enable_phy_wake) + return lane->pad->ops->enable_phy_wake(lane); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake); + +int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->disable_phy_wake) + return lane->pad->ops->disable_phy_wake(lane); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake); + +bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + + if (lane->pad->ops->remote_wake_detected) + return lane->pad->ops->remote_wake_detected(lane); + + return false; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected); + int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int port, bool enable) { @@ -1143,6 +1459,59 @@ int tegra_phy_xusb_utmi_port_reset(struct phy *phy) } EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset); +void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy) +{ + struct tegra_xusb_lane *lane; + struct tegra_xusb_padctl *padctl; + + if (!phy) + return; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; + + if (padctl->soc->ops->utmi_pad_power_on) + padctl->soc->ops->utmi_pad_power_on(phy); +} +EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on); + +void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy) +{ + struct tegra_xusb_lane *lane; + struct tegra_xusb_padctl *padctl; + + if (!phy) + return; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; + + if (padctl->soc->ops->utmi_pad_power_down) + padctl->soc->ops->utmi_pad_power_down(phy); +} +EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_down); + +int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, + unsigned int port) +{ + struct tegra_xusb_usb2_port *usb2; + struct tegra_xusb_usb3_port *usb3; + int i; + + usb2 = tegra_xusb_find_usb2_port(padctl, port); + if (!usb2) + return -EINVAL; + + for (i = 0; i < padctl->soc->ports.usb3.count; i++) { + usb3 = tegra_xusb_find_usb3_port(padctl, i); + if (usb3 && usb3->port == usb2->base.index) + return usb3->base.index; + } + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion); + MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index da94fcce6307..8cfbbdbd6e0c 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2015, Google Inc. */ @@ -11,7 +11,9 @@ #include <linux/mutex.h> #include <linux/workqueue.h> +#include <linux/usb/ch9.h> #include <linux/usb/otg.h> +#include <linux/usb/role.h> /* legacy entry points for backwards-compatibility */ int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev); @@ -34,6 +36,10 @@ struct tegra_xusb_lane_soc { const char * const *funcs; unsigned int num_funcs; + + struct { + unsigned int misc_ctl2; + } regs; }; struct tegra_xusb_lane { @@ -125,8 +131,17 @@ struct tegra_xusb_lane_ops { struct device_node *np, unsigned int index); void (*remove)(struct tegra_xusb_lane *lane); + void (*iddq_enable)(struct tegra_xusb_lane *lane); + void (*iddq_disable)(struct tegra_xusb_lane *lane); + int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed); + int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane); + int (*enable_phy_wake)(struct tegra_xusb_lane *lane); + int (*disable_phy_wake)(struct tegra_xusb_lane *lane); + bool (*remote_wake_detected)(struct tegra_xusb_lane *lane); }; +bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function); + /* * pads */ @@ -229,7 +244,7 @@ struct tegra_xusb_pcie_pad { struct reset_control *rst; struct clk *pll; - unsigned int enable; + bool enable; }; static inline struct tegra_xusb_pcie_pad * @@ -244,7 +259,7 @@ struct tegra_xusb_sata_pad { struct reset_control *rst; struct clk *pll; - unsigned int enable; + bool enable; }; static inline struct tegra_xusb_sata_pad * @@ -266,9 +281,18 @@ struct tegra_xusb_port { struct list_head list; struct device dev; + struct usb_role_switch *usb_role_sw; + struct work_struct usb_phy_work; + struct usb_phy usb_phy; + const struct tegra_xusb_port_ops *ops; }; +static inline struct tegra_xusb_port *to_tegra_xusb_port(struct device *dev) +{ + return container_of(dev, struct tegra_xusb_port, dev); +} + struct tegra_xusb_lane_map { unsigned int port; const char *type; @@ -303,6 +327,8 @@ to_usb2_port(struct tegra_xusb_port *port) struct tegra_xusb_usb2_port * tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index); +void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port); +void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port); struct tegra_xusb_ulpi_port { struct tegra_xusb_port base; @@ -317,6 +343,8 @@ to_ulpi_port(struct tegra_xusb_port *port) return container_of(port, struct tegra_xusb_ulpi_port, base); } +void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port); + struct tegra_xusb_hsic_port { struct tegra_xusb_port base; }; @@ -327,12 +355,15 @@ to_hsic_port(struct tegra_xusb_port *port) return container_of(port, struct tegra_xusb_hsic_port, base); } +void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port); + struct tegra_xusb_usb3_port { struct tegra_xusb_port base; struct regulator *supply; bool context_saved; unsigned int port; bool internal; + bool disable_gen2; u32 tap1; u32 amp; @@ -349,8 +380,12 @@ to_usb3_port(struct tegra_xusb_port *port) struct tegra_xusb_usb3_port * tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index); +void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port); +void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port); struct tegra_xusb_port_ops { + void (*release)(struct tegra_xusb_port *port); + void (*remove)(struct tegra_xusb_port *port); int (*enable)(struct tegra_xusb_port *port); void (*disable)(struct tegra_xusb_port *port); struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port); @@ -367,6 +402,8 @@ struct tegra_xusb_padctl_ops { const struct tegra_xusb_padctl_soc *soc); void (*remove)(struct tegra_xusb_padctl *padctl); + int (*suspend_noirq)(struct tegra_xusb_padctl *padctl); + int (*resume_noirq)(struct tegra_xusb_padctl *padctl); int (*usb3_save_context)(struct tegra_xusb_padctl *padctl, unsigned int index); int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl, @@ -375,6 +412,8 @@ struct tegra_xusb_padctl_ops { unsigned int index, bool enable); int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set); int (*utmi_port_reset)(struct phy *phy); + void (*utmi_pad_power_on)(struct phy *phy); + void (*utmi_pad_power_down)(struct phy *phy); }; struct tegra_xusb_padctl_soc { @@ -392,6 +431,7 @@ struct tegra_xusb_padctl_soc { const char * const *supply_names; unsigned int num_supplies; + bool supports_gen2; bool need_fake_usb3_port; }; @@ -448,5 +488,8 @@ extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc; #if defined(CONFIG_ARCH_TEGRA_186_SOC) extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc; #endif +#if defined(CONFIG_ARCH_TEGRA_194_SOC) +extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc; +#endif #endif /* __PHY_TEGRA_XUSB_H */ diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index 6dbe9d0b9ff3..15a3bcf32308 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -106,11 +106,8 @@ config TWL4030_USB config PHY_TI_GMII_SEL tristate - default y if TI_CPSW=y || TI_CPSW_SWITCHDEV=y - depends on TI_CPSW || TI_CPSW_SWITCHDEV || COMPILE_TEST select GENERIC_PHY select REGMAP - default m help This driver supports configuring of the TI CPSW Port mode depending on the Ethernet PHY connected to the CPSW Port. diff --git a/drivers/phy/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c index 88a047b9fa6f..0be727bb9f79 100644 --- a/drivers/phy/ti/phy-am654-serdes.c +++ b/drivers/phy/ti/phy-am654-serdes.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * PCIe SERDES driver for AM654x SoC * * Copyright (C) 2018 - 2019 Texas Instruments Incorporated - http://www.ti.com/ @@ -19,15 +19,38 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> +#define CMU_R004 0x4 +#define CMU_R060 0x60 #define CMU_R07C 0x7c - +#define CMU_R088 0x88 +#define CMU_R0D0 0xd0 +#define CMU_R0E8 0xe8 + +#define LANE_R048 0x248 +#define LANE_R058 0x258 +#define LANE_R06c 0x26c +#define LANE_R070 0x270 +#define LANE_R070 0x270 +#define LANE_R19C 0x39c + +#define COMLANE_R004 0xa04 #define COMLANE_R138 0xb38 -#define VERSION 0x70 +#define VERSION_VAL 0x70 #define COMLANE_R190 0xb90 - #define COMLANE_R194 0xb94 +#define COMRXEQ_R004 0x1404 +#define COMRXEQ_R008 0x1408 +#define COMRXEQ_R00C 0x140c +#define COMRXEQ_R014 0x1414 +#define COMRXEQ_R018 0x1418 +#define COMRXEQ_R01C 0x141c +#define COMRXEQ_R04C 0x144c +#define COMRXEQ_R088 0x1488 +#define COMRXEQ_R094 0x1494 +#define COMRXEQ_R098 0x1498 + #define SERDES_CTRL 0x1fd0 #define WIZ_LANEXCTL_STS 0x1fe0 @@ -72,34 +95,144 @@ struct serdes_am654_clk_mux { #define to_serdes_am654_clk_mux(_hw) \ container_of(_hw, struct serdes_am654_clk_mux, hw) -static struct regmap_config serdes_am654_regmap_config = { +static const struct regmap_config serdes_am654_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .fast_io = true, + .max_register = 0x1ffc, }; -static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24); -static const struct reg_field config_version = REG_FIELD(COMLANE_R138, 16, 23); -static const struct reg_field l1_master_cdn_o = REG_FIELD(COMLANE_R190, 9, 9); -static const struct reg_field cmu_ok_i_0 = REG_FIELD(COMLANE_R194, 19, 19); -static const struct reg_field por_en = REG_FIELD(SERDES_CTRL, 29, 29); -static const struct reg_field tx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31); -static const struct reg_field rx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15); -static const struct reg_field pll_enable = REG_FIELD(WIZ_PLL_CTRL, 29, 31); -static const struct reg_field pll_ok = REG_FIELD(WIZ_PLL_CTRL, 28, 28); +enum serdes_am654_fields { + /* CMU PLL Control */ + CMU_PLL_CTRL, + + LANE_PLL_CTRL_RXEQ_RXIDLE, + + /* CMU VCO bias current and VREG setting */ + AHB_PMA_CM_VCO_VBIAS_VREG, + AHB_PMA_CM_VCO_BIAS_VREG, + + AHB_PMA_CM_SR, + AHB_SSC_GEN_Z_O_20_13, + + /* AHB PMA Lane Configuration */ + AHB_PMA_LN_AGC_THSEL_VREGH, + + /* AGC and Signal detect threshold for Gen3 */ + AHB_PMA_LN_GEN3_AGC_SD_THSEL, + + AHB_PMA_LN_RX_SELR_GEN3, + AHB_PMA_LN_TX_DRV, + + /* CMU Master Reset */ + CMU_MASTER_CDN, + + /* P2S ring buffer initial startup pointer difference */ + P2S_RBUF_PTR_DIFF, + + CONFIG_VERSION, + + /* Lane 1 Master Reset */ + L1_MASTER_CDN, + + /* CMU OK Status */ + CMU_OK_I_0, + + /* Mid-speed initial calibration control */ + COMRXEQ_MS_INIT_CTRL_7_0, + + /* High-speed initial calibration control */ + COMRXEQ_HS_INIT_CAL_7_0, + + /* Mid-speed recalibration control */ + COMRXEQ_MS_RECAL_CTRL_7_0, + + /* High-speed recalibration control */ + COMRXEQ_HS_RECAL_CTRL_7_0, + + /* ATT configuration */ + COMRXEQ_CSR_ATT_CONFIG, + + /* Edge based boost adaptation window length */ + COMRXEQ_CSR_EBSTADAPT_WIN_LEN, + + /* COMRXEQ control 3 & 4 */ + COMRXEQ_CTRL_3_4, + + /* COMRXEQ control 14, 15 and 16*/ + COMRXEQ_CTRL_14_15_16, + + /* Threshold for errors in pattern data */ + COMRXEQ_CSR_DLEV_ERR_THRESH, + + /* COMRXEQ control 25 */ + COMRXEQ_CTRL_25, + + /* Mid-speed rate change calibration control */ + CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O, + + /* High-speed rate change calibration control */ + COMRXEQ_HS_RCHANGE_CTRL_7_0, + + /* Serdes reset */ + POR_EN, + + /* Tx Enable Value */ + TX0_ENABLE, + + /* Rx Enable Value */ + RX0_ENABLE, + + /* PLL Enable Value */ + PLL_ENABLE, + + /* PLL ready for use */ + PLL_OK, + + /* sentinel */ + MAX_FIELDS + +}; + +static const struct reg_field serdes_am654_reg_fields[] = { + [CMU_PLL_CTRL] = REG_FIELD(CMU_R004, 8, 15), + [AHB_PMA_CM_VCO_VBIAS_VREG] = REG_FIELD(CMU_R060, 8, 15), + [CMU_MASTER_CDN] = REG_FIELD(CMU_R07C, 24, 31), + [AHB_PMA_CM_VCO_BIAS_VREG] = REG_FIELD(CMU_R088, 24, 31), + [AHB_PMA_CM_SR] = REG_FIELD(CMU_R0D0, 24, 31), + [AHB_SSC_GEN_Z_O_20_13] = REG_FIELD(CMU_R0E8, 8, 15), + [LANE_PLL_CTRL_RXEQ_RXIDLE] = REG_FIELD(LANE_R048, 8, 15), + [AHB_PMA_LN_AGC_THSEL_VREGH] = REG_FIELD(LANE_R058, 16, 23), + [AHB_PMA_LN_GEN3_AGC_SD_THSEL] = REG_FIELD(LANE_R06c, 0, 7), + [AHB_PMA_LN_RX_SELR_GEN3] = REG_FIELD(LANE_R070, 16, 23), + [AHB_PMA_LN_TX_DRV] = REG_FIELD(LANE_R19C, 16, 23), + [P2S_RBUF_PTR_DIFF] = REG_FIELD(COMLANE_R004, 0, 7), + [CONFIG_VERSION] = REG_FIELD(COMLANE_R138, 16, 23), + [L1_MASTER_CDN] = REG_FIELD(COMLANE_R190, 8, 15), + [CMU_OK_I_0] = REG_FIELD(COMLANE_R194, 19, 19), + [COMRXEQ_MS_INIT_CTRL_7_0] = REG_FIELD(COMRXEQ_R004, 24, 31), + [COMRXEQ_HS_INIT_CAL_7_0] = REG_FIELD(COMRXEQ_R008, 0, 7), + [COMRXEQ_MS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 8, 15), + [COMRXEQ_HS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 16, 23), + [COMRXEQ_CSR_ATT_CONFIG] = REG_FIELD(COMRXEQ_R014, 16, 23), + [COMRXEQ_CSR_EBSTADAPT_WIN_LEN] = REG_FIELD(COMRXEQ_R018, 16, 23), + [COMRXEQ_CTRL_3_4] = REG_FIELD(COMRXEQ_R01C, 8, 15), + [COMRXEQ_CTRL_14_15_16] = REG_FIELD(COMRXEQ_R04C, 0, 7), + [COMRXEQ_CSR_DLEV_ERR_THRESH] = REG_FIELD(COMRXEQ_R088, 16, 23), + [COMRXEQ_CTRL_25] = REG_FIELD(COMRXEQ_R094, 24, 31), + [CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O] = REG_FIELD(COMRXEQ_R098, 8, 15), + [COMRXEQ_HS_RCHANGE_CTRL_7_0] = REG_FIELD(COMRXEQ_R098, 16, 23), + [POR_EN] = REG_FIELD(SERDES_CTRL, 29, 29), + [TX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31), + [RX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15), + [PLL_ENABLE] = REG_FIELD(WIZ_PLL_CTRL, 29, 31), + [PLL_OK] = REG_FIELD(WIZ_PLL_CTRL, 28, 28), +}; struct serdes_am654 { struct regmap *regmap; - struct regmap_field *cmu_master_cdn_o; - struct regmap_field *config_version; - struct regmap_field *l1_master_cdn_o; - struct regmap_field *cmu_ok_i_0; - struct regmap_field *por_en; - struct regmap_field *tx0_enable; - struct regmap_field *rx0_enable; - struct regmap_field *pll_enable; - struct regmap_field *pll_ok; + struct regmap_field *fields[MAX_FIELDS]; struct device *dev; struct mux_control *control; @@ -115,12 +248,12 @@ static int serdes_am654_enable_pll(struct serdes_am654 *phy) int ret; u32 val; - ret = regmap_field_write(phy->pll_enable, PLL_ENABLE_STATE); + ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_ENABLE_STATE); if (ret) return ret; - return regmap_field_read_poll_timeout(phy->pll_ok, val, val, 1000, - PLL_LOCK_TIME); + return regmap_field_read_poll_timeout(phy->fields[PLL_OK], val, val, + 1000, PLL_LOCK_TIME); } static void serdes_am654_disable_pll(struct serdes_am654 *phy) @@ -128,41 +261,39 @@ static void serdes_am654_disable_pll(struct serdes_am654 *phy) struct device *dev = phy->dev; int ret; - ret = regmap_field_write(phy->pll_enable, PLL_DISABLE_STATE); + ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_DISABLE_STATE); if (ret) dev_err(dev, "Failed to disable PLL\n"); } static int serdes_am654_enable_txrx(struct serdes_am654 *phy) { - int ret; + int ret = 0; /* Enable TX */ - ret = regmap_field_write(phy->tx0_enable, TX0_ENABLE_STATE); - if (ret) - return ret; + ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_ENABLE_STATE); /* Enable RX */ - ret = regmap_field_write(phy->rx0_enable, RX0_ENABLE_STATE); + ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_ENABLE_STATE); + if (ret) - return ret; + return -EIO; return 0; } static int serdes_am654_disable_txrx(struct serdes_am654 *phy) { - int ret; + int ret = 0; /* Disable TX */ - ret = regmap_field_write(phy->tx0_enable, TX0_DISABLE_STATE); - if (ret) - return ret; + ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_DISABLE_STATE); /* Disable RX */ - ret = regmap_field_write(phy->rx0_enable, RX0_DISABLE_STATE); + ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_DISABLE_STATE); + if (ret) - return ret; + return -EIO; return 0; } @@ -186,8 +317,8 @@ static int serdes_am654_power_on(struct phy *x) return ret; } - return regmap_field_read_poll_timeout(phy->cmu_ok_i_0, val, val, - SLEEP_TIME, PLL_LOCK_TIME); + return regmap_field_read_poll_timeout(phy->fields[CMU_OK_I_0], val, + val, SLEEP_TIME, PLL_LOCK_TIME); } static int serdes_am654_power_off(struct phy *x) @@ -200,40 +331,156 @@ static int serdes_am654_power_off(struct phy *x) return 0; } -static int serdes_am654_init(struct phy *x) +#define SERDES_AM654_CFG(offset, a, b, val) \ + regmap_update_bits(phy->regmap, (offset),\ + GENMASK((a), (b)), (val) << (b)) + +static int serdes_am654_usb3_init(struct serdes_am654 *phy) { - struct serdes_am654 *phy = phy_get_drvdata(x); - int ret; + SERDES_AM654_CFG(0x0000, 31, 24, 0x17); + SERDES_AM654_CFG(0x0004, 15, 8, 0x02); + SERDES_AM654_CFG(0x0004, 7, 0, 0x0e); + SERDES_AM654_CFG(0x0008, 23, 16, 0x2e); + SERDES_AM654_CFG(0x0008, 31, 24, 0x2e); + SERDES_AM654_CFG(0x0060, 7, 0, 0x4b); + SERDES_AM654_CFG(0x0060, 15, 8, 0x98); + SERDES_AM654_CFG(0x0060, 23, 16, 0x60); + SERDES_AM654_CFG(0x00d0, 31, 24, 0x45); + SERDES_AM654_CFG(0x00e8, 15, 8, 0x0e); + SERDES_AM654_CFG(0x0220, 7, 0, 0x34); + SERDES_AM654_CFG(0x0220, 15, 8, 0x34); + SERDES_AM654_CFG(0x0220, 31, 24, 0x37); + SERDES_AM654_CFG(0x0224, 7, 0, 0x37); + SERDES_AM654_CFG(0x0224, 15, 8, 0x37); + SERDES_AM654_CFG(0x0228, 23, 16, 0x37); + SERDES_AM654_CFG(0x0228, 31, 24, 0x37); + SERDES_AM654_CFG(0x022c, 7, 0, 0x37); + SERDES_AM654_CFG(0x022c, 15, 8, 0x37); + SERDES_AM654_CFG(0x0230, 15, 8, 0x2a); + SERDES_AM654_CFG(0x0230, 23, 16, 0x2a); + SERDES_AM654_CFG(0x0240, 23, 16, 0x10); + SERDES_AM654_CFG(0x0240, 31, 24, 0x34); + SERDES_AM654_CFG(0x0244, 7, 0, 0x40); + SERDES_AM654_CFG(0x0244, 23, 16, 0x34); + SERDES_AM654_CFG(0x0248, 15, 8, 0x0d); + SERDES_AM654_CFG(0x0258, 15, 8, 0x16); + SERDES_AM654_CFG(0x0258, 23, 16, 0x84); + SERDES_AM654_CFG(0x0258, 31, 24, 0xf2); + SERDES_AM654_CFG(0x025c, 7, 0, 0x21); + SERDES_AM654_CFG(0x0260, 7, 0, 0x27); + SERDES_AM654_CFG(0x0260, 15, 8, 0x04); + SERDES_AM654_CFG(0x0268, 15, 8, 0x04); + SERDES_AM654_CFG(0x0288, 15, 8, 0x2c); + SERDES_AM654_CFG(0x0330, 31, 24, 0xa0); + SERDES_AM654_CFG(0x0338, 23, 16, 0x03); + SERDES_AM654_CFG(0x0338, 31, 24, 0x00); + SERDES_AM654_CFG(0x033c, 7, 0, 0x00); + SERDES_AM654_CFG(0x0344, 31, 24, 0x18); + SERDES_AM654_CFG(0x034c, 7, 0, 0x18); + SERDES_AM654_CFG(0x039c, 23, 16, 0x3b); + SERDES_AM654_CFG(0x0a04, 7, 0, 0x03); + SERDES_AM654_CFG(0x0a14, 31, 24, 0x3c); + SERDES_AM654_CFG(0x0a18, 15, 8, 0x3c); + SERDES_AM654_CFG(0x0a38, 7, 0, 0x3e); + SERDES_AM654_CFG(0x0a38, 15, 8, 0x3e); + SERDES_AM654_CFG(0x0ae0, 7, 0, 0x07); + SERDES_AM654_CFG(0x0b6c, 23, 16, 0xcd); + SERDES_AM654_CFG(0x0b6c, 31, 24, 0x04); + SERDES_AM654_CFG(0x0b98, 23, 16, 0x03); + SERDES_AM654_CFG(0x1400, 7, 0, 0x3f); + SERDES_AM654_CFG(0x1404, 23, 16, 0x6f); + SERDES_AM654_CFG(0x1404, 31, 24, 0x6f); + SERDES_AM654_CFG(0x140c, 7, 0, 0x6f); + SERDES_AM654_CFG(0x140c, 15, 8, 0x6f); + SERDES_AM654_CFG(0x1410, 15, 8, 0x27); + SERDES_AM654_CFG(0x1414, 7, 0, 0x0c); + SERDES_AM654_CFG(0x1414, 23, 16, 0x07); + SERDES_AM654_CFG(0x1418, 23, 16, 0x40); + SERDES_AM654_CFG(0x141c, 7, 0, 0x00); + SERDES_AM654_CFG(0x141c, 15, 8, 0x1f); + SERDES_AM654_CFG(0x1428, 31, 24, 0x08); + SERDES_AM654_CFG(0x1434, 31, 24, 0x00); + SERDES_AM654_CFG(0x1444, 7, 0, 0x94); + SERDES_AM654_CFG(0x1460, 31, 24, 0x7f); + SERDES_AM654_CFG(0x1464, 7, 0, 0x43); + SERDES_AM654_CFG(0x1464, 23, 16, 0x6f); + SERDES_AM654_CFG(0x1464, 31, 24, 0x43); + SERDES_AM654_CFG(0x1484, 23, 16, 0x8f); + SERDES_AM654_CFG(0x1498, 7, 0, 0x4f); + SERDES_AM654_CFG(0x1498, 23, 16, 0x4f); + SERDES_AM654_CFG(0x007c, 31, 24, 0x0d); + SERDES_AM654_CFG(0x0b90, 15, 8, 0x0f); - ret = regmap_field_write(phy->config_version, VERSION); - if (ret) - return ret; + return 0; +} - ret = regmap_field_write(phy->cmu_master_cdn_o, 0x1); - if (ret) - return ret; +static int serdes_am654_pcie_init(struct serdes_am654 *phy) +{ + int ret = 0; + + ret |= regmap_field_write(phy->fields[CMU_PLL_CTRL], 0x2); + ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_VBIAS_VREG], 0x98); + ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_BIAS_VREG], 0x98); + ret |= regmap_field_write(phy->fields[AHB_PMA_CM_SR], 0x45); + ret |= regmap_field_write(phy->fields[AHB_SSC_GEN_Z_O_20_13], 0xe); + ret |= regmap_field_write(phy->fields[LANE_PLL_CTRL_RXEQ_RXIDLE], 0x5); + ret |= regmap_field_write(phy->fields[AHB_PMA_LN_AGC_THSEL_VREGH], 0x83); + ret |= regmap_field_write(phy->fields[AHB_PMA_LN_GEN3_AGC_SD_THSEL], 0x83); + ret |= regmap_field_write(phy->fields[AHB_PMA_LN_RX_SELR_GEN3], 0x81); + ret |= regmap_field_write(phy->fields[AHB_PMA_LN_TX_DRV], 0x3b); + ret |= regmap_field_write(phy->fields[P2S_RBUF_PTR_DIFF], 0x3); + ret |= regmap_field_write(phy->fields[CONFIG_VERSION], VERSION_VAL); + ret |= regmap_field_write(phy->fields[COMRXEQ_MS_INIT_CTRL_7_0], 0xf); + ret |= regmap_field_write(phy->fields[COMRXEQ_HS_INIT_CAL_7_0], 0x4f); + ret |= regmap_field_write(phy->fields[COMRXEQ_MS_RECAL_CTRL_7_0], 0xf); + ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RECAL_CTRL_7_0], 0x4f); + ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_ATT_CONFIG], 0x7); + ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_EBSTADAPT_WIN_LEN], 0x7f); + ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_3_4], 0xf); + ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_14_15_16], 0x9a); + ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_DLEV_ERR_THRESH], 0x32); + ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_25], 0x80); + ret |= regmap_field_write(phy->fields[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O], 0xf); + ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RCHANGE_CTRL_7_0], 0x4f); + ret |= regmap_field_write(phy->fields[CMU_MASTER_CDN], 0x1); + ret |= regmap_field_write(phy->fields[L1_MASTER_CDN], 0x2); - ret = regmap_field_write(phy->l1_master_cdn_o, 0x1); if (ret) - return ret; + return -EIO; return 0; } +static int serdes_am654_init(struct phy *x) +{ + struct serdes_am654 *phy = phy_get_drvdata(x); + + switch (phy->type) { + case PHY_TYPE_PCIE: + return serdes_am654_pcie_init(phy); + case PHY_TYPE_USB3: + return serdes_am654_usb3_init(phy); + default: + return -EINVAL; + } +} + static int serdes_am654_reset(struct phy *x) { struct serdes_am654 *phy = phy_get_drvdata(x); - int ret; + int ret = 0; - ret = regmap_field_write(phy->por_en, 0x1); - if (ret) - return ret; + serdes_am654_disable_pll(phy); + serdes_am654_disable_txrx(phy); + + ret |= regmap_field_write(phy->fields[POR_EN], 0x1); mdelay(1); - ret = regmap_field_write(phy->por_en, 0x0); + ret |= regmap_field_write(phy->fields[POR_EN], 0x0); + if (ret) - return ret; + return -EIO; return 0; } @@ -487,66 +734,16 @@ static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy) { struct regmap *regmap = am654_phy->regmap; struct device *dev = am654_phy->dev; + int i; - am654_phy->cmu_master_cdn_o = devm_regmap_field_alloc(dev, regmap, - cmu_master_cdn_o); - if (IS_ERR(am654_phy->cmu_master_cdn_o)) { - dev_err(dev, "CMU_MASTER_CDN_O reg field init failed\n"); - return PTR_ERR(am654_phy->cmu_master_cdn_o); - } - - am654_phy->config_version = devm_regmap_field_alloc(dev, regmap, - config_version); - if (IS_ERR(am654_phy->config_version)) { - dev_err(dev, "CONFIG_VERSION reg field init failed\n"); - return PTR_ERR(am654_phy->config_version); - } - - am654_phy->l1_master_cdn_o = devm_regmap_field_alloc(dev, regmap, - l1_master_cdn_o); - if (IS_ERR(am654_phy->l1_master_cdn_o)) { - dev_err(dev, "L1_MASTER_CDN_O reg field init failed\n"); - return PTR_ERR(am654_phy->l1_master_cdn_o); - } - - am654_phy->cmu_ok_i_0 = devm_regmap_field_alloc(dev, regmap, - cmu_ok_i_0); - if (IS_ERR(am654_phy->cmu_ok_i_0)) { - dev_err(dev, "CMU_OK_I_0 reg field init failed\n"); - return PTR_ERR(am654_phy->cmu_ok_i_0); - } - - am654_phy->por_en = devm_regmap_field_alloc(dev, regmap, por_en); - if (IS_ERR(am654_phy->por_en)) { - dev_err(dev, "POR_EN reg field init failed\n"); - return PTR_ERR(am654_phy->por_en); - } - - am654_phy->tx0_enable = devm_regmap_field_alloc(dev, regmap, - tx0_enable); - if (IS_ERR(am654_phy->tx0_enable)) { - dev_err(dev, "TX0_ENABLE reg field init failed\n"); - return PTR_ERR(am654_phy->tx0_enable); - } - - am654_phy->rx0_enable = devm_regmap_field_alloc(dev, regmap, - rx0_enable); - if (IS_ERR(am654_phy->rx0_enable)) { - dev_err(dev, "RX0_ENABLE reg field init failed\n"); - return PTR_ERR(am654_phy->rx0_enable); - } - - am654_phy->pll_enable = devm_regmap_field_alloc(dev, regmap, - pll_enable); - if (IS_ERR(am654_phy->pll_enable)) { - dev_err(dev, "PLL_ENABLE reg field init failed\n"); - return PTR_ERR(am654_phy->pll_enable); - } - - am654_phy->pll_ok = devm_regmap_field_alloc(dev, regmap, pll_ok); - if (IS_ERR(am654_phy->pll_ok)) { - dev_err(dev, "PLL_OK reg field init failed\n"); - return PTR_ERR(am654_phy->pll_ok); + for (i = 0; i < MAX_FIELDS; i++) { + am654_phy->fields[i] = devm_regmap_field_alloc(dev, + regmap, + serdes_am654_reg_fields[i]); + if (IS_ERR(am654_phy->fields[i])) { + dev_err(dev, "Unable to allocate regmap field %d\n", i); + return PTR_ERR(am654_phy->fields[i]); + } } return 0; @@ -625,8 +822,10 @@ static int serdes_am654_probe(struct platform_device *pdev) pm_runtime_enable(dev); phy = devm_phy_create(dev, NULL, &ops); - if (IS_ERR(phy)) - return PTR_ERR(phy); + if (IS_ERR(phy)) { + ret = PTR_ERR(phy); + goto clk_err; + } phy_set_drvdata(phy, am654_phy); phy_provider = devm_of_phy_provider_register(dev, serdes_am654_xlate); @@ -639,7 +838,7 @@ static int serdes_am654_probe(struct platform_device *pdev) clk_err: of_clk_del_provider(node); - + pm_runtime_disable(dev); return ret; } diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index 26f194779064..fb619908f912 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #include <linux/module.h> #include <linux/platform_device.h> @@ -82,17 +73,16 @@ static int dm816x_usb_phy_init(struct phy *x) { struct dm816x_usb_phy *phy = phy_get_drvdata(x); unsigned int val; - int error; if (clk_get_rate(phy->refclk) != 24000000) dev_warn(phy->dev, "nonstandard phy refclk\n"); /* Set PLL ref clock and put phys to sleep */ - error = regmap_update_bits(phy->syscon, phy->usb_ctrl, - DM816X_USB_CTRL_PHYCLKSRC | - DM816X_USB_CTRL_PHYSLEEP1 | - DM816X_USB_CTRL_PHYSLEEP0, - 0); + regmap_update_bits(phy->syscon, phy->usb_ctrl, + DM816X_USB_CTRL_PHYCLKSRC | + DM816X_USB_CTRL_PHYSLEEP1 | + DM816X_USB_CTRL_PHYSLEEP0, + 0); regmap_read(phy->syscon, phy->usb_ctrl, &val); if ((val & 3) != 0) dev_info(phy->dev, @@ -243,19 +233,28 @@ static int dm816x_usb_phy_probe(struct platform_device *pdev) pm_runtime_enable(phy->dev); generic_phy = devm_phy_create(phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) - return PTR_ERR(generic_phy); + if (IS_ERR(generic_phy)) { + error = PTR_ERR(generic_phy); + goto clk_unprepare; + } phy_set_drvdata(generic_phy, phy); phy_provider = devm_of_phy_provider_register(phy->dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); + if (IS_ERR(phy_provider)) { + error = PTR_ERR(phy_provider); + goto clk_unprepare; + } usb_add_phy_dev(&phy->phy); return 0; + +clk_unprepare: + pm_runtime_disable(phy->dev); + clk_unprepare(phy->refclk); + return error; } static int dm816x_usb_phy_remove(struct platform_device *pdev) diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 1c536fc03c83..0bcfd6d96b4d 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/mfd/syscon.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_net.h> #include <linux/phy.h> #include <linux/phy/phy.h> @@ -21,8 +22,14 @@ #define AM33XX_GMII_SEL_MODE_RMII 1 #define AM33XX_GMII_SEL_MODE_RGMII 2 +/* J72xx SoC specific definitions for the CONTROL port */ +#define J72XX_GMII_SEL_MODE_QSGMII 4 +#define J72XX_GMII_SEL_MODE_QSGMII_SUB 6 + +#define PHY_GMII_PORT(n) BIT((n) - 1) + enum { - PHY_GMII_SEL_PORT_MODE, + PHY_GMII_SEL_PORT_MODE = 0, PHY_GMII_SEL_RGMII_ID_MODE, PHY_GMII_SEL_RMII_IO_CLK_EN, PHY_GMII_SEL_LAST, @@ -41,6 +48,8 @@ struct phy_gmii_sel_soc_data { u32 num_ports; u32 features; const struct reg_field (*regfields)[PHY_GMII_SEL_LAST]; + bool use_of_data; + u64 extra_modes; }; struct phy_gmii_sel_priv { @@ -49,6 +58,9 @@ struct phy_gmii_sel_priv { struct regmap *regmap; struct phy_provider *phy_provider; struct phy_gmii_sel_phy_priv *if_phys; + u32 num_ports; + u32 reg_offset; + u32 qsgmii_main_ports; }; static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) @@ -84,10 +96,17 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) gmii_sel_mode = AM33XX_GMII_SEL_MODE_MII; break; + case PHY_INTERFACE_MODE_QSGMII: + if (!(soc_data->extra_modes & BIT(PHY_INTERFACE_MODE_QSGMII))) + goto unsupported; + if (if_phy->priv->qsgmii_main_ports & BIT(if_phy->id - 1)) + gmii_sel_mode = J72XX_GMII_SEL_MODE_QSGMII; + else + gmii_sel_mode = J72XX_GMII_SEL_MODE_QSGMII_SUB; + break; + default: - dev_warn(dev, "port%u: unsupported mode: \"%s\"\n", - if_phy->id, phy_modes(submode)); - return -EINVAL; + goto unsupported; } if_phy->phy_if_mode = submode; @@ -119,6 +138,11 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) } return 0; + +unsupported: + dev_warn(dev, "port%u: unsupported mode: \"%s\"\n", + if_phy->id, phy_modes(submode)); + return -EINVAL; } static const @@ -147,13 +171,9 @@ static const struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = { { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1), - [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0), - [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0), }, { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5), - [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0), - [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0), }, }; @@ -170,6 +190,31 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = { .regfields = phy_gmii_sel_fields_am33xx, }; +static const +struct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = { + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x0, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x8, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0xC, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x10, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x14, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x18, 0, 2), }, + { [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x1C, 0, 2), }, +}; + +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = { + .use_of_data = true, + .regfields = phy_gmii_sel_fields_am654, +}; + +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = { + .use_of_data = true, + .regfields = phy_gmii_sel_fields_am654, + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII), +}; + static const struct of_device_id phy_gmii_sel_id_table[] = { { .compatible = "ti,am3352-phy-gmii-sel", @@ -187,6 +232,14 @@ static const struct of_device_id phy_gmii_sel_id_table[] = { .compatible = "ti,dm814-phy-gmii-sel", .data = &phy_gmii_sel_soc_dm814, }, + { + .compatible = "ti,am654-phy-gmii-sel", + .data = &phy_gmii_sel_soc_am654, + }, + { + .compatible = "ti,j7200-cpsw5g-phy-gmii-sel", + .data = &phy_gmii_sel_cpsw5g_soc_j7200, + }, {} }; MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table); @@ -209,7 +262,7 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev, if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) && args->args_count < 2) return ERR_PTR(-EINVAL); - if (phy_id > priv->soc_data->num_ports) + if (phy_id > priv->num_ports) return ERR_PTR(-EINVAL); if (phy_id != priv->if_phys[phy_id - 1].id) return ERR_PTR(-EINVAL); @@ -223,68 +276,99 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev, return priv->if_phys[phy_id].if_phy; } -static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv) +static int phy_gmii_init_phy(struct phy_gmii_sel_priv *priv, int port, + struct phy_gmii_sel_phy_priv *if_phy) { const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data; struct device *dev = priv->dev; - struct phy_gmii_sel_phy_priv *if_phys; - int i, num_ports, ret; + const struct reg_field *fields; + struct regmap_field *regfield; + struct reg_field field; + int ret; - num_ports = priv->soc_data->num_ports; + if_phy->id = port; + if_phy->priv = priv; + + fields = soc_data->regfields[port - 1]; + field = *fields++; + field.reg += priv->reg_offset; + dev_dbg(dev, "%s field %x %d %d\n", __func__, + field.reg, field.msb, field.lsb); + + regfield = devm_regmap_field_alloc(dev, priv->regmap, field); + if (IS_ERR(regfield)) + return PTR_ERR(regfield); + if_phy->fields[PHY_GMII_SEL_PORT_MODE] = regfield; + + field = *fields++; + field.reg += priv->reg_offset; + if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE)) { + regfield = devm_regmap_field_alloc(dev, + priv->regmap, + field); + if (IS_ERR(regfield)) + return PTR_ERR(regfield); + if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE] = regfield; + dev_dbg(dev, "%s field %x %d %d\n", __func__, + field.reg, field.msb, field.lsb); + } - if_phys = devm_kcalloc(priv->dev, num_ports, - sizeof(*if_phys), GFP_KERNEL); - if (!if_phys) - return -ENOMEM; - dev_dbg(dev, "%s %d\n", __func__, num_ports); + field = *fields; + field.reg += priv->reg_offset; + if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN)) { + regfield = devm_regmap_field_alloc(dev, + priv->regmap, + field); + if (IS_ERR(regfield)) + return PTR_ERR(regfield); + if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = regfield; + dev_dbg(dev, "%s field %x %d %d\n", __func__, + field.reg, field.msb, field.lsb); + } - for (i = 0; i < num_ports; i++) { - const struct reg_field *field; - struct regmap_field *regfield; + if_phy->if_phy = devm_phy_create(dev, + priv->dev->of_node, + &phy_gmii_sel_ops); + if (IS_ERR(if_phy->if_phy)) { + ret = PTR_ERR(if_phy->if_phy); + dev_err(dev, "Failed to create phy%d %d\n", port, ret); + return ret; + } + phy_set_drvdata(if_phy->if_phy, if_phy); - if_phys[i].id = i + 1; - if_phys[i].priv = priv; + return 0; +} - field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE]; - dev_dbg(dev, "%s field %x %d %d\n", __func__, - field->reg, field->msb, field->lsb); +static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv) +{ + const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data; + struct phy_gmii_sel_phy_priv *if_phys; + struct device *dev = priv->dev; + int i, ret; + + if (soc_data->use_of_data) { + const __be32 *offset; + u64 size; + + offset = of_get_address(dev->of_node, 0, &size, NULL); + if (!offset) + return -EINVAL; + priv->num_ports = size / sizeof(u32); + if (!priv->num_ports) + return -EINVAL; + priv->reg_offset = __be32_to_cpu(*offset); + } - regfield = devm_regmap_field_alloc(dev, priv->regmap, *field); - if (IS_ERR(regfield)) - return PTR_ERR(regfield); - if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield; - - field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE]; - if (field->reg != (~0)) { - regfield = devm_regmap_field_alloc(dev, - priv->regmap, - *field); - if (IS_ERR(regfield)) - return PTR_ERR(regfield); - if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] = - regfield; - } - - field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN]; - if (field->reg != (~0)) { - regfield = devm_regmap_field_alloc(dev, - priv->regmap, - *field); - if (IS_ERR(regfield)) - return PTR_ERR(regfield); - if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = - regfield; - } - - if_phys[i].if_phy = devm_phy_create(dev, - priv->dev->of_node, - &phy_gmii_sel_ops); - if (IS_ERR(if_phys[i].if_phy)) { - ret = PTR_ERR(if_phys[i].if_phy); - dev_err(dev, "Failed to create phy%d %d\n", i, ret); + if_phys = devm_kcalloc(dev, priv->num_ports, + sizeof(*if_phys), GFP_KERNEL); + if (!if_phys) + return -ENOMEM; + dev_dbg(dev, "%s %d\n", __func__, priv->num_ports); + + for (i = 0; i < priv->num_ports; i++) { + ret = phy_gmii_init_phy(priv, i + 1, &if_phys[i]); + if (ret) return ret; - } - phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]); } priv->if_phys = if_phys; @@ -297,6 +381,7 @@ static int phy_gmii_sel_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; const struct of_device_id *of_id; struct phy_gmii_sel_priv *priv; + u32 main_ports = 1; int ret; of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node); @@ -309,6 +394,16 @@ static int phy_gmii_sel_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->soc_data = of_id->data; + priv->num_ports = priv->soc_data->num_ports; + of_property_read_u32(node, "ti,qsgmii-main-ports", &main_ports); + /* + * Ensure that main_ports is within bounds. If the property + * ti,qsgmii-main-ports is not mentioned, or the value mentioned + * is out of bounds, default to 1. + */ + if (main_ports < 1 || main_ports > 4) + main_ports = 1; + priv->qsgmii_main_ports = PHY_GMII_PORT(main_ports); priv->regmap = syscon_node_to_regmap(node->parent); if (IS_ERR(priv->regmap)) { diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index 7b51045df783..41725c6bcdf6 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * Wrapper driver for SERDES used in J721E * * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ @@ -7,12 +7,15 @@ */ #include <dt-bindings/phy/phy.h> +#include <dt-bindings/phy/phy-ti.h> +#include <linux/slab.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/mfd/syscon.h> #include <linux/mux/consumer.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -21,11 +24,25 @@ #include <linux/regmap.h> #include <linux/reset-controller.h> +#define REF_CLK_19_2MHZ 19200000 +#define REF_CLK_25MHZ 25000000 +#define REF_CLK_100MHZ 100000000 +#define REF_CLK_156_25MHZ 156250000 + +/* SCM offsets */ +#define SERDES_SUP_CTRL 0x4400 + +/* SERDES offsets */ #define WIZ_SERDES_CTRL 0x404 #define WIZ_SERDES_TOP_CTRL 0x408 #define WIZ_SERDES_RST 0x40c #define WIZ_SERDES_TYPEC 0x410 #define WIZ_LANECTL(n) (0x480 + (0x40 * (n))) +#define WIZ_LANEDIV(n) (0x484 + (0x40 * (n))) + +#define WIZ_MAX_INPUT_CLOCKS 4 +/* To include mux clocks, divider clocks and gate clocks */ +#define WIZ_MAX_OUTPUT_CLOCKS 32 #define WIZ_MAX_LANES 4 #define WIZ_MUX_NUM_CLOCKS 3 @@ -52,8 +69,16 @@ enum wiz_refclk_div_sel { CMN_REFCLK1_DIG_DIV, }; +enum wiz_clock_input { + WIZ_CORE_REFCLK, + WIZ_EXT_REFCLK, + WIZ_CORE_REFCLK1, + WIZ_EXT_REFCLK1, +}; + static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31); static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31); +static const struct reg_field phy_en_refclk = REG_FIELD(WIZ_SERDES_RST, 30, 30); static const struct reg_field pll1_refclk_mux_sel = REG_FIELD(WIZ_SERDES_RST, 29, 29); static const struct reg_field pll0_refclk_mux_sel = @@ -71,6 +96,24 @@ static const struct reg_field pma_cmn_refclk_dig_div = static const struct reg_field pma_cmn_refclk1_dig_div = REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25); +static const struct reg_field sup_pll0_refclk_mux_sel = + REG_FIELD(SERDES_SUP_CTRL, 0, 1); +static const struct reg_field sup_pll1_refclk_mux_sel = + REG_FIELD(SERDES_SUP_CTRL, 2, 3); +static const struct reg_field sup_pma_cmn_refclk1_int_mode = + REG_FIELD(SERDES_SUP_CTRL, 4, 5); +static const struct reg_field sup_refclk_dig_sel_10g = + REG_FIELD(SERDES_SUP_CTRL, 6, 7); +static const struct reg_field sup_legacy_clk_override = + REG_FIELD(SERDES_SUP_CTRL, 8, 8); + +static const char * const output_clk_names[] = { + [TI_WIZ_PLL0_REFCLK] = "pll0-refclk", + [TI_WIZ_PLL1_REFCLK] = "pll1-refclk", + [TI_WIZ_REFCLK_DIG] = "refclk-dig", + [TI_WIZ_PHY_EN_REFCLK] = "phy-en-refclk", +}; + static const struct reg_field p_enable[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(0), 30, 31), REG_FIELD(WIZ_LANECTL(1), 30, 31), @@ -78,6 +121,8 @@ static const struct reg_field p_enable[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(3), 30, 31), }; +enum p_enable { P_ENABLE = 2, P_ENABLE_FORCE = 1, P_ENABLE_DISABLE = 0 }; + static const struct reg_field p_align[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(0), 29, 29), REG_FIELD(WIZ_LANECTL(1), 29, 29), @@ -99,13 +144,54 @@ static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(3), 24, 25), }; +static const struct reg_field p0_fullrt_div[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 22, 23), + REG_FIELD(WIZ_LANECTL(1), 22, 23), + REG_FIELD(WIZ_LANECTL(2), 22, 23), + REG_FIELD(WIZ_LANECTL(3), 22, 23), +}; + +static const struct reg_field p0_mac_src_sel[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 20, 21), + REG_FIELD(WIZ_LANECTL(1), 20, 21), + REG_FIELD(WIZ_LANECTL(2), 20, 21), + REG_FIELD(WIZ_LANECTL(3), 20, 21), +}; + +static const struct reg_field p0_rxfclk_sel[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 6, 7), + REG_FIELD(WIZ_LANECTL(1), 6, 7), + REG_FIELD(WIZ_LANECTL(2), 6, 7), + REG_FIELD(WIZ_LANECTL(3), 6, 7), +}; + +static const struct reg_field p0_refclk_sel[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 18, 19), + REG_FIELD(WIZ_LANECTL(1), 18, 19), + REG_FIELD(WIZ_LANECTL(2), 18, 19), + REG_FIELD(WIZ_LANECTL(3), 18, 19), +}; +static const struct reg_field p_mac_div_sel0[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANEDIV(0), 16, 22), + REG_FIELD(WIZ_LANEDIV(1), 16, 22), + REG_FIELD(WIZ_LANEDIV(2), 16, 22), + REG_FIELD(WIZ_LANEDIV(3), 16, 22), +}; + +static const struct reg_field p_mac_div_sel1[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANEDIV(0), 0, 8), + REG_FIELD(WIZ_LANEDIV(1), 0, 8), + REG_FIELD(WIZ_LANEDIV(2), 0, 8), + REG_FIELD(WIZ_LANEDIV(3), 0, 8), +}; + static const struct reg_field typec_ln10_swap = REG_FIELD(WIZ_SERDES_TYPEC, 30, 30); struct wiz_clk_mux { struct clk_hw hw; struct regmap_field *field; - u32 *table; + const u32 *table; struct clk_init_data clk_data; }; @@ -114,25 +200,33 @@ struct wiz_clk_mux { struct wiz_clk_divider { struct clk_hw hw; struct regmap_field *field; - struct clk_div_table *table; + const struct clk_div_table *table; struct clk_init_data clk_data; }; #define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw) struct wiz_clk_mux_sel { - struct regmap_field *field; - u32 table[4]; + u32 table[WIZ_MAX_INPUT_CLOCKS]; const char *node_name; + u32 num_parents; + u32 parents[WIZ_MAX_INPUT_CLOCKS]; }; struct wiz_clk_div_sel { - struct regmap_field *field; - struct clk_div_table *table; + const struct clk_div_table *table; const char *node_name; }; -static struct wiz_clk_mux_sel clk_mux_sel_16g[] = { +struct wiz_phy_en_refclk { + struct clk_hw hw; + struct regmap_field *phy_en_refclk; + struct clk_init_data clk_data; +}; + +#define to_wiz_phy_en_refclk(_hw) container_of(_hw, struct wiz_phy_en_refclk, hw) + +static const struct wiz_clk_mux_sel clk_mux_sel_16g[] = { { /* * Mux value to be configured for each of the input clocks @@ -151,33 +245,61 @@ static struct wiz_clk_mux_sel clk_mux_sel_16g[] = { }, }; -static struct wiz_clk_mux_sel clk_mux_sel_10g[] = { +static const struct wiz_clk_mux_sel clk_mux_sel_10g[] = { { /* * Mux value to be configured for each of the input clocks * in the order populated in device tree */ + .num_parents = 2, + .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK }, .table = { 1, 0 }, .node_name = "pll0-refclk", }, { + .num_parents = 2, + .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK }, .table = { 1, 0 }, .node_name = "pll1-refclk", }, { + .num_parents = 2, + .parents = { WIZ_CORE_REFCLK, WIZ_EXT_REFCLK }, .table = { 1, 0 }, .node_name = "refclk-dig", }, }; -static struct clk_div_table clk_div_table[] = { +static const struct wiz_clk_mux_sel clk_mux_sel_10g_2_refclk[] = { + { + .num_parents = 3, + .parents = { WIZ_CORE_REFCLK, WIZ_CORE_REFCLK1, WIZ_EXT_REFCLK }, + .table = { 2, 3, 0 }, + .node_name = "pll0-refclk", + }, + { + .num_parents = 3, + .parents = { WIZ_CORE_REFCLK, WIZ_CORE_REFCLK1, WIZ_EXT_REFCLK }, + .table = { 2, 3, 0 }, + .node_name = "pll1-refclk", + }, + { + .num_parents = 3, + .parents = { WIZ_CORE_REFCLK, WIZ_CORE_REFCLK1, WIZ_EXT_REFCLK }, + .table = { 2, 3, 0 }, + .node_name = "refclk-dig", + }, +}; + +static const struct clk_div_table clk_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 4, }, { .val = 3, .div = 8, }, + { /* sentinel */ }, }; -static struct wiz_clk_div_sel clk_div_sel[] = { +static const struct wiz_clk_div_sel clk_div_sel[] = { { .table = clk_div_table, .node_name = "cmn-refclk-dig-div", @@ -190,7 +312,20 @@ static struct wiz_clk_div_sel clk_div_sel[] = { enum wiz_type { J721E_WIZ_16G, - J721E_WIZ_10G, + J721E_WIZ_10G, /* Also for J7200 SR1.0 */ + AM64_WIZ_10G, + J7200_WIZ_10G, /* J7200 SR2.0 */ +}; + +struct wiz_data { + enum wiz_type type; + const struct reg_field *pll0_refclk_mux_sel; + const struct reg_field *pll1_refclk_mux_sel; + const struct reg_field *refclk_dig_sel; + const struct reg_field *pma_cmn_refclk1_dig_div; + const struct reg_field *pma_cmn_refclk1_int_mode; + const struct wiz_clk_mux_sel *clk_mux_sel; + unsigned int clk_div_sel_num; }; #define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */ @@ -198,21 +333,33 @@ enum wiz_type { struct wiz { struct regmap *regmap; + struct regmap *scm_regmap; enum wiz_type type; - struct wiz_clk_mux_sel *clk_mux_sel; - struct wiz_clk_div_sel *clk_div_sel; + const struct wiz_clk_mux_sel *clk_mux_sel; + const struct wiz_clk_div_sel *clk_div_sel; unsigned int clk_div_sel_num; struct regmap_field *por_en; struct regmap_field *phy_reset_n; + struct regmap_field *phy_en_refclk; struct regmap_field *p_enable[WIZ_MAX_LANES]; struct regmap_field *p_align[WIZ_MAX_LANES]; struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES]; struct regmap_field *p_standard_mode[WIZ_MAX_LANES]; + struct regmap_field *p_mac_div_sel0[WIZ_MAX_LANES]; + struct regmap_field *p_mac_div_sel1[WIZ_MAX_LANES]; + struct regmap_field *p0_fullrt_div[WIZ_MAX_LANES]; + struct regmap_field *p0_mac_src_sel[WIZ_MAX_LANES]; + struct regmap_field *p0_rxfclk_sel[WIZ_MAX_LANES]; + struct regmap_field *p0_refclk_sel[WIZ_MAX_LANES]; struct regmap_field *pma_cmn_refclk_int_mode; + struct regmap_field *pma_cmn_refclk1_int_mode; struct regmap_field *pma_cmn_refclk_mode; struct regmap_field *pma_cmn_refclk_dig_div; struct regmap_field *pma_cmn_refclk1_dig_div; + struct regmap_field *mux_sel_field[WIZ_MUX_NUM_CLOCKS]; + struct regmap_field *div_sel_field[WIZ_DIV_NUM_CLOCKS_16G]; struct regmap_field *typec_ln10_swap; + struct regmap_field *sup_legacy_clk_override; struct device *dev; u32 num_lanes; @@ -220,6 +367,11 @@ struct wiz { struct reset_controller_dev wiz_phy_reset_dev; struct gpio_desc *gpio_typec_dir; int typec_dir_delay; + u32 lane_phy_type[WIZ_MAX_LANES]; + struct clk *input_clks[WIZ_MAX_INPUT_CLOCKS]; + struct clk *output_clks[WIZ_MAX_OUTPUT_CLOCKS]; + struct clk_onecell_data clk_data; + const struct wiz_data *data; }; static int wiz_reset(struct wiz *wiz) @@ -239,15 +391,52 @@ static int wiz_reset(struct wiz *wiz) return 0; } +static int wiz_p_mac_div_sel(struct wiz *wiz) +{ + u32 num_lanes = wiz->num_lanes; + int ret; + int i; + + for (i = 0; i < num_lanes; i++) { + if (wiz->lane_phy_type[i] == PHY_TYPE_SGMII || + wiz->lane_phy_type[i] == PHY_TYPE_QSGMII || + wiz->lane_phy_type[i] == PHY_TYPE_USXGMII) { + ret = regmap_field_write(wiz->p_mac_div_sel0[i], 1); + if (ret) + return ret; + + ret = regmap_field_write(wiz->p_mac_div_sel1[i], 2); + if (ret) + return ret; + } + } + + return 0; +} + static int wiz_mode_select(struct wiz *wiz) { u32 num_lanes = wiz->num_lanes; + enum wiz_lane_standard_mode mode; int ret; int i; for (i = 0; i < num_lanes; i++) { - ret = regmap_field_write(wiz->p_standard_mode[i], - LANE_MODE_GEN4); + if (wiz->lane_phy_type[i] == PHY_TYPE_DP) + mode = LANE_MODE_GEN1; + else if (wiz->lane_phy_type[i] == PHY_TYPE_QSGMII) + mode = LANE_MODE_GEN2; + else + continue; + + if (wiz->lane_phy_type[i] == PHY_TYPE_USXGMII) { + ret = regmap_field_write(wiz->p0_mac_src_sel[i], 0x3); + ret = regmap_field_write(wiz->p0_rxfclk_sel[i], 0x3); + ret = regmap_field_write(wiz->p0_refclk_sel[i], 0x3); + mode = LANE_MODE_GEN1; + } + + ret = regmap_field_write(wiz->p_standard_mode[i], mode); if (ret) return ret; } @@ -291,6 +480,12 @@ static int wiz_init(struct wiz *wiz) return ret; } + ret = wiz_p_mac_div_sel(wiz); + if (ret) { + dev_err(dev, "Configuring P0 MAC DIV SEL failed\n"); + return ret; + } + ret = wiz_init_raw_interface(wiz, true); if (ret) { dev_err(dev, "WIZ interface initialization failed\n"); @@ -302,11 +497,11 @@ static int wiz_init(struct wiz *wiz) static int wiz_regfield_init(struct wiz *wiz) { - struct wiz_clk_mux_sel *clk_mux_sel; - struct wiz_clk_div_sel *clk_div_sel; struct regmap *regmap = wiz->regmap; + struct regmap *scm_regmap = wiz->regmap; /* updated later to scm_regmap if applicable */ int num_lanes = wiz->num_lanes; struct device *dev = wiz->dev; + const struct wiz_data *data = wiz->data; int i; wiz->por_en = devm_regmap_field_alloc(dev, regmap, por_en); @@ -336,54 +531,61 @@ static int wiz_regfield_init(struct wiz *wiz) return PTR_ERR(wiz->pma_cmn_refclk_mode); } - clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV]; - clk_div_sel->field = devm_regmap_field_alloc(dev, regmap, - pma_cmn_refclk_dig_div); - if (IS_ERR(clk_div_sel->field)) { + wiz->div_sel_field[CMN_REFCLK_DIG_DIV] = + devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_dig_div); + if (IS_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV])) { dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n"); - return PTR_ERR(clk_div_sel->field); + return PTR_ERR(wiz->div_sel_field[CMN_REFCLK_DIG_DIV]); } - if (wiz->type == J721E_WIZ_16G) { - clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV]; - clk_div_sel->field = + if (data->pma_cmn_refclk1_dig_div) { + wiz->div_sel_field[CMN_REFCLK1_DIG_DIV] = devm_regmap_field_alloc(dev, regmap, - pma_cmn_refclk1_dig_div); - if (IS_ERR(clk_div_sel->field)) { + *data->pma_cmn_refclk1_dig_div); + if (IS_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV])) { dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n"); - return PTR_ERR(clk_div_sel->field); + return PTR_ERR(wiz->div_sel_field[CMN_REFCLK1_DIG_DIV]); } } - clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK]; - clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap, - pll0_refclk_mux_sel); - if (IS_ERR(clk_mux_sel->field)) { + if (wiz->scm_regmap) { + scm_regmap = wiz->scm_regmap; + wiz->sup_legacy_clk_override = + devm_regmap_field_alloc(dev, scm_regmap, sup_legacy_clk_override); + if (IS_ERR(wiz->sup_legacy_clk_override)) { + dev_err(dev, "SUP_LEGACY_CLK_OVERRIDE reg field init failed\n"); + return PTR_ERR(wiz->sup_legacy_clk_override); + } + } + + wiz->mux_sel_field[PLL0_REFCLK] = + devm_regmap_field_alloc(dev, scm_regmap, *data->pll0_refclk_mux_sel); + if (IS_ERR(wiz->mux_sel_field[PLL0_REFCLK])) { dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n"); - return PTR_ERR(clk_mux_sel->field); + return PTR_ERR(wiz->mux_sel_field[PLL0_REFCLK]); } - clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK]; - clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap, - pll1_refclk_mux_sel); - if (IS_ERR(clk_mux_sel->field)) { + wiz->mux_sel_field[PLL1_REFCLK] = + devm_regmap_field_alloc(dev, scm_regmap, *data->pll1_refclk_mux_sel); + if (IS_ERR(wiz->mux_sel_field[PLL1_REFCLK])) { dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n"); - return PTR_ERR(clk_mux_sel->field); + return PTR_ERR(wiz->mux_sel_field[PLL1_REFCLK]); } - clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG]; - if (wiz->type == J721E_WIZ_10G) - clk_mux_sel->field = - devm_regmap_field_alloc(dev, regmap, - refclk_dig_sel_10g); - else - clk_mux_sel->field = - devm_regmap_field_alloc(dev, regmap, - refclk_dig_sel_16g); - - if (IS_ERR(clk_mux_sel->field)) { + wiz->mux_sel_field[REFCLK_DIG] = devm_regmap_field_alloc(dev, scm_regmap, + *data->refclk_dig_sel); + if (IS_ERR(wiz->mux_sel_field[REFCLK_DIG])) { dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n"); - return PTR_ERR(clk_mux_sel->field); + return PTR_ERR(wiz->mux_sel_field[REFCLK_DIG]); + } + + if (data->pma_cmn_refclk1_int_mode) { + wiz->pma_cmn_refclk1_int_mode = + devm_regmap_field_alloc(dev, scm_regmap, *data->pma_cmn_refclk1_int_mode); + if (IS_ERR(wiz->pma_cmn_refclk1_int_mode)) { + dev_err(dev, "PMA_CMN_REFCLK1_INT_MODE reg field init failed\n"); + return PTR_ERR(wiz->pma_cmn_refclk1_int_mode); + } } for (i = 0; i < num_lanes; i++) { @@ -416,6 +618,46 @@ static int wiz_regfield_init(struct wiz *wiz) i); return PTR_ERR(wiz->p_standard_mode[i]); } + + wiz->p0_fullrt_div[i] = devm_regmap_field_alloc(dev, regmap, p0_fullrt_div[i]); + if (IS_ERR(wiz->p0_fullrt_div[i])) { + dev_err(dev, "P%d_FULLRT_DIV reg field init failed\n", i); + return PTR_ERR(wiz->p0_fullrt_div[i]); + } + + wiz->p0_mac_src_sel[i] = devm_regmap_field_alloc(dev, regmap, p0_mac_src_sel[i]); + if (IS_ERR(wiz->p0_mac_src_sel[i])) { + dev_err(dev, "P%d_MAC_SRC_SEL reg field init failed\n", i); + return PTR_ERR(wiz->p0_mac_src_sel[i]); + } + + wiz->p0_rxfclk_sel[i] = devm_regmap_field_alloc(dev, regmap, p0_rxfclk_sel[i]); + if (IS_ERR(wiz->p0_rxfclk_sel[i])) { + dev_err(dev, "P%d_RXFCLK_SEL reg field init failed\n", i); + return PTR_ERR(wiz->p0_rxfclk_sel[i]); + } + + wiz->p0_refclk_sel[i] = devm_regmap_field_alloc(dev, regmap, p0_refclk_sel[i]); + if (IS_ERR(wiz->p0_refclk_sel[i])) { + dev_err(dev, "P%d_REFCLK_SEL reg field init failed\n", i); + return PTR_ERR(wiz->p0_refclk_sel[i]); + } + + wiz->p_mac_div_sel0[i] = + devm_regmap_field_alloc(dev, regmap, p_mac_div_sel0[i]); + if (IS_ERR(wiz->p_mac_div_sel0[i])) { + dev_err(dev, "P%d_MAC_DIV_SEL0 reg field init fail\n", + i); + return PTR_ERR(wiz->p_mac_div_sel0[i]); + } + + wiz->p_mac_div_sel1[i] = + devm_regmap_field_alloc(dev, regmap, p_mac_div_sel1[i]); + if (IS_ERR(wiz->p_mac_div_sel1[i])) { + dev_err(dev, "P%d_MAC_DIV_SEL1 reg field init fail\n", + i); + return PTR_ERR(wiz->p_mac_div_sel1[i]); + } } wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap, @@ -425,6 +667,89 @@ static int wiz_regfield_init(struct wiz *wiz) return PTR_ERR(wiz->typec_ln10_swap); } + wiz->phy_en_refclk = devm_regmap_field_alloc(dev, regmap, phy_en_refclk); + if (IS_ERR(wiz->phy_en_refclk)) { + dev_err(dev, "PHY_EN_REFCLK reg field init failed\n"); + return PTR_ERR(wiz->phy_en_refclk); + } + + return 0; +} + +static int wiz_phy_en_refclk_enable(struct clk_hw *hw) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw); + struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk; + + regmap_field_write(phy_en_refclk, 1); + + return 0; +} + +static void wiz_phy_en_refclk_disable(struct clk_hw *hw) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw); + struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk; + + regmap_field_write(phy_en_refclk, 0); +} + +static int wiz_phy_en_refclk_is_enabled(struct clk_hw *hw) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk = to_wiz_phy_en_refclk(hw); + struct regmap_field *phy_en_refclk = wiz_phy_en_refclk->phy_en_refclk; + int val; + + regmap_field_read(phy_en_refclk, &val); + + return !!val; +} + +static const struct clk_ops wiz_phy_en_refclk_ops = { + .enable = wiz_phy_en_refclk_enable, + .disable = wiz_phy_en_refclk_disable, + .is_enabled = wiz_phy_en_refclk_is_enabled, +}; + +static int wiz_phy_en_refclk_register(struct wiz *wiz) +{ + struct wiz_phy_en_refclk *wiz_phy_en_refclk; + struct device *dev = wiz->dev; + struct clk_init_data *init; + struct clk *clk; + char *clk_name; + unsigned int sz; + + wiz_phy_en_refclk = devm_kzalloc(dev, sizeof(*wiz_phy_en_refclk), GFP_KERNEL); + if (!wiz_phy_en_refclk) + return -ENOMEM; + + init = &wiz_phy_en_refclk->clk_data; + + init->ops = &wiz_phy_en_refclk_ops; + init->flags = 0; + + sz = strlen(dev_name(dev)) + strlen(output_clk_names[TI_WIZ_PHY_EN_REFCLK]) + 2; + + clk_name = kzalloc(sz, GFP_KERNEL); + if (!clk_name) + return -ENOMEM; + + snprintf(clk_name, sz, "%s_%s", dev_name(dev), output_clk_names[TI_WIZ_PHY_EN_REFCLK]); + init->name = clk_name; + + wiz_phy_en_refclk->phy_en_refclk = wiz->phy_en_refclk; + wiz_phy_en_refclk->hw.init = init; + + clk = devm_clk_register(dev, &wiz_phy_en_refclk->hw); + + kfree(clk_name); + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + wiz->output_clks[TI_WIZ_PHY_EN_REFCLK] = clk; + return 0; } @@ -435,7 +760,7 @@ static u8 wiz_clk_mux_get_parent(struct clk_hw *hw) unsigned int val; regmap_field_read(field, &val); - return clk_mux_val_to_index(hw, mux->table, 0, val); + return clk_mux_val_to_index(hw, (u32 *)mux->table, 0, val); } static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index) @@ -453,8 +778,69 @@ static const struct clk_ops wiz_clk_mux_ops = { .get_parent = wiz_clk_mux_get_parent, }; -static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node, - struct regmap_field *field, u32 *table) +static int wiz_mux_clk_register(struct wiz *wiz, struct regmap_field *field, + const struct wiz_clk_mux_sel *mux_sel, int clk_index) +{ + struct device *dev = wiz->dev; + struct clk_init_data *init; + const char **parent_names; + unsigned int num_parents; + struct wiz_clk_mux *mux; + char clk_name[100]; + struct clk *clk; + int ret = 0, i; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + num_parents = mux_sel->num_parents; + + parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + clk = wiz->input_clks[mux_sel->parents[i]]; + if (IS_ERR_OR_NULL(clk)) { + dev_err(dev, "Failed to get parent clk for %s\n", + output_clk_names[clk_index]); + ret = -EINVAL; + goto err; + } + parent_names[i] = __clk_get_name(clk); + } + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), output_clk_names[clk_index]); + + init = &mux->clk_data; + + init->ops = &wiz_clk_mux_ops; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + init->name = clk_name; + + mux->field = field; + mux->table = mux_sel->table; + mux->hw.init = init; + + clk = devm_clk_register(dev, &mux->hw); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err; + } + + wiz->output_clks[clk_index] = clk; + +err: + kfree(parent_names); + + return ret; +} + +static int wiz_mux_of_clk_register(struct wiz *wiz, struct device_node *node, + struct regmap_field *field, const u32 *table) { struct device *dev = wiz->dev; struct clk_init_data *init; @@ -550,7 +936,7 @@ static const struct clk_ops wiz_clk_div_ops = { static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node, struct regmap_field *field, - struct clk_div_table *table) + const struct clk_div_table *table) { struct device *dev = wiz->dev; struct wiz_clk_divider *div; @@ -598,20 +984,71 @@ static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node, static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node) { - struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + struct device *dev = wiz->dev; struct device_node *clk_node; int i; + switch (wiz->type) { + case AM64_WIZ_10G: + case J7200_WIZ_10G: + of_clk_del_provider(dev->of_node); + return; + default: + break; + } + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) { clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name); of_clk_del_provider(clk_node); of_node_put(clk_node); } + + for (i = 0; i < wiz->clk_div_sel_num; i++) { + clk_node = of_get_child_by_name(node, clk_div_sel[i].node_name); + of_clk_del_provider(clk_node); + of_node_put(clk_node); + } + + of_clk_del_provider(wiz->dev->of_node); +} + +static int wiz_clock_register(struct wiz *wiz) +{ + const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + struct device *dev = wiz->dev; + struct device_node *node = dev->of_node; + int clk_index; + int ret; + int i; + + clk_index = TI_WIZ_PLL0_REFCLK; + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++, clk_index++) { + ret = wiz_mux_clk_register(wiz, wiz->mux_sel_field[i], &clk_mux_sel[i], clk_index); + if (ret) { + dev_err(dev, "Failed to register clk: %s\n", output_clk_names[clk_index]); + return ret; + } + } + + ret = wiz_phy_en_refclk_register(wiz); + if (ret) { + dev_err(dev, "Failed to add phy-en-refclk\n"); + return ret; + } + + wiz->clk_data.clks = wiz->output_clks; + wiz->clk_data.clk_num = WIZ_MAX_OUTPUT_CLOCKS; + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &wiz->clk_data); + if (ret) + dev_err(dev, "Failed to add clock provider: %s\n", node->name); + + return ret; } static int wiz_clock_init(struct wiz *wiz, struct device_node *node) { - struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + const struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; struct device *dev = wiz->dev; struct device_node *clk_node; const char *node_name; @@ -626,6 +1063,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node) ret = PTR_ERR(clk); return ret; } + wiz->input_clks[WIZ_CORE_REFCLK] = clk; rate = clk_get_rate(clk); if (rate >= 100000000) @@ -633,12 +1071,48 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node) else regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x3); + switch (wiz->type) { + case AM64_WIZ_10G: + case J7200_WIZ_10G: + switch (rate) { + case REF_CLK_100MHZ: + regmap_field_write(wiz->div_sel_field[CMN_REFCLK_DIG_DIV], 0x2); + break; + case REF_CLK_156_25MHZ: + regmap_field_write(wiz->div_sel_field[CMN_REFCLK_DIG_DIV], 0x3); + break; + default: + regmap_field_write(wiz->div_sel_field[CMN_REFCLK_DIG_DIV], 0); + break; + } + break; + default: + break; + } + + if (wiz->data->pma_cmn_refclk1_int_mode) { + clk = devm_clk_get(dev, "core_ref1_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "core_ref1_clk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + wiz->input_clks[WIZ_CORE_REFCLK1] = clk; + + rate = clk_get_rate(clk); + if (rate >= 100000000) + regmap_field_write(wiz->pma_cmn_refclk1_int_mode, 0x1); + else + regmap_field_write(wiz->pma_cmn_refclk1_int_mode, 0x3); + } + clk = devm_clk_get(dev, "ext_ref_clk"); if (IS_ERR(clk)) { dev_err(dev, "ext_ref_clk clock not found\n"); ret = PTR_ERR(clk); return ret; } + wiz->input_clks[WIZ_EXT_REFCLK] = clk; rate = clk_get_rate(clk); if (rate >= 100000000) @@ -646,6 +1120,17 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node) else regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2); + switch (wiz->type) { + case AM64_WIZ_10G: + case J7200_WIZ_10G: + ret = wiz_clock_register(wiz); + if (ret) + dev_err(dev, "Failed to register wiz clocks\n"); + return ret; + default: + break; + } + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) { node_name = clk_mux_sel[i].node_name; clk_node = of_get_child_by_name(node, node_name); @@ -655,8 +1140,8 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node) goto err; } - ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field, - clk_mux_sel[i].table); + ret = wiz_mux_of_clk_register(wiz, clk_node, wiz->mux_sel_field[i], + clk_mux_sel[i].table); if (ret) { dev_err(dev, "Failed to register %s clock\n", node_name); @@ -676,7 +1161,7 @@ static int wiz_clock_init(struct wiz *wiz, struct device_node *node) goto err; } - ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field, + ret = wiz_div_clk_register(wiz, clk_node, wiz->div_sel_field[i], clk_div_sel[i].table); if (ret) { dev_err(dev, "Failed to register %s clock\n", @@ -707,10 +1192,28 @@ static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev, return ret; } - ret = regmap_field_write(wiz->p_enable[id - 1], false); + ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_DISABLE); return ret; } +static int wiz_phy_fullrt_div(struct wiz *wiz, int lane) +{ + switch (wiz->type) { + case AM64_WIZ_10G: + if (wiz->lane_phy_type[lane] == PHY_TYPE_PCIE) + return regmap_field_write(wiz->p0_fullrt_div[lane], 0x1); + break; + case J721E_WIZ_10G: + case J7200_WIZ_10G: + if (wiz->lane_phy_type[lane] == PHY_TYPE_SGMII) + return regmap_field_write(wiz->p0_fullrt_div[lane], 0x2); + break; + default: + return 0; + } + return 0; +} + static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) { @@ -734,7 +1237,15 @@ static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev, return ret; } - ret = regmap_field_write(wiz->p_enable[id - 1], true); + ret = wiz_phy_fullrt_div(wiz, id - 1); + if (ret) + return ret; + + if (wiz->lane_phy_type[id - 1] == PHY_TYPE_DP) + ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE); + else + ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_FORCE); + return ret; } @@ -743,43 +1254,135 @@ static const struct reset_control_ops wiz_phy_reset_ops = { .deassert = wiz_phy_reset_deassert, }; -static struct regmap_config wiz_regmap_config = { +static const struct regmap_config wiz_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .fast_io = true, }; +static struct wiz_data j721e_16g_data = { + .type = J721E_WIZ_16G, + .pll0_refclk_mux_sel = &pll0_refclk_mux_sel, + .pll1_refclk_mux_sel = &pll1_refclk_mux_sel, + .refclk_dig_sel = &refclk_dig_sel_16g, + .pma_cmn_refclk1_dig_div = &pma_cmn_refclk1_dig_div, + .clk_mux_sel = clk_mux_sel_16g, + .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G, +}; + +static struct wiz_data j721e_10g_data = { + .type = J721E_WIZ_10G, + .pll0_refclk_mux_sel = &pll0_refclk_mux_sel, + .pll1_refclk_mux_sel = &pll1_refclk_mux_sel, + .refclk_dig_sel = &refclk_dig_sel_10g, + .clk_mux_sel = clk_mux_sel_10g, + .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G, +}; + +static struct wiz_data am64_10g_data = { + .type = AM64_WIZ_10G, + .pll0_refclk_mux_sel = &pll0_refclk_mux_sel, + .pll1_refclk_mux_sel = &pll1_refclk_mux_sel, + .refclk_dig_sel = &refclk_dig_sel_10g, + .clk_mux_sel = clk_mux_sel_10g, + .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G, +}; + +static struct wiz_data j7200_pg2_10g_data = { + .type = J7200_WIZ_10G, + .pll0_refclk_mux_sel = &sup_pll0_refclk_mux_sel, + .pll1_refclk_mux_sel = &sup_pll1_refclk_mux_sel, + .refclk_dig_sel = &sup_refclk_dig_sel_10g, + .pma_cmn_refclk1_int_mode = &sup_pma_cmn_refclk1_int_mode, + .clk_mux_sel = clk_mux_sel_10g_2_refclk, + .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G, +}; + static const struct of_device_id wiz_id_table[] = { { - .compatible = "ti,j721e-wiz-16g", .data = (void *)J721E_WIZ_16G + .compatible = "ti,j721e-wiz-16g", .data = &j721e_16g_data, + }, + { + .compatible = "ti,j721e-wiz-10g", .data = &j721e_10g_data, + }, + { + .compatible = "ti,am64-wiz-10g", .data = &am64_10g_data, }, { - .compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G + .compatible = "ti,j7200-wiz-10g", .data = &j7200_pg2_10g_data, }, {} }; MODULE_DEVICE_TABLE(of, wiz_id_table); +static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz) +{ + struct device_node *serdes, *subnode; + + serdes = of_get_child_by_name(dev->of_node, "serdes"); + if (!serdes) { + dev_err(dev, "%s: Getting \"serdes\"-node failed\n", __func__); + return -EINVAL; + } + + for_each_child_of_node(serdes, subnode) { + u32 reg, num_lanes = 1, phy_type = PHY_NONE; + int ret, i; + + if (!(of_node_name_eq(subnode, "phy") || + of_node_name_eq(subnode, "link"))) + continue; + + ret = of_property_read_u32(subnode, "reg", ®); + if (ret) { + of_node_put(subnode); + dev_err(dev, + "%s: Reading \"reg\" from \"%s\" failed: %d\n", + __func__, subnode->name, ret); + return ret; + } + of_property_read_u32(subnode, "cdns,num-lanes", &num_lanes); + of_property_read_u32(subnode, "cdns,phy-type", &phy_type); + + dev_dbg(dev, "%s: Lanes %u-%u have phy-type %u\n", __func__, + reg, reg + num_lanes - 1, phy_type); + + for (i = reg; i < reg + num_lanes; i++) + wiz->lane_phy_type[i] = phy_type; + } + + return 0; +} + static int wiz_probe(struct platform_device *pdev) { struct reset_controller_dev *phy_reset_dev; struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct platform_device *serdes_pdev; + bool already_configured = false; struct device_node *child_node; struct regmap *regmap; struct resource res; void __iomem *base; struct wiz *wiz; + int ret, val, i; u32 num_lanes; - int ret; + const struct wiz_data *data; wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL); if (!wiz) return -ENOMEM; - wiz->type = (enum wiz_type)of_device_get_match_data(dev); + data = of_device_get_match_data(dev); + if (!data) { + dev_err(dev, "NULL device data\n"); + return -EINVAL; + } + + wiz->data = data; + wiz->type = data->type; child_node = of_get_child_by_name(node, "serdes"); if (!child_node) { @@ -794,8 +1397,10 @@ static int wiz_probe(struct platform_device *pdev) } base = devm_ioremap(dev, res.start, resource_size(&res)); - if (!base) + if (!base) { + ret = -ENOMEM; goto err_addr_to_resource; + } regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config); if (IS_ERR(regmap)) { @@ -804,6 +1409,17 @@ static int wiz_probe(struct platform_device *pdev) goto err_addr_to_resource; } + wiz->scm_regmap = syscon_regmap_lookup_by_phandle(node, "ti,scm"); + if (IS_ERR(wiz->scm_regmap)) { + if (wiz->type == J7200_WIZ_10G) { + dev_err(dev, "Couldn't get ti,scm regmap\n"); + ret = -ENODEV; + goto err_addr_to_resource; + } + + wiz->scm_regmap = NULL; + } + ret = of_property_read_u32(node, "num-lanes", &num_lanes); if (ret) { dev_err(dev, "Failed to read num-lanes property\n"); @@ -812,6 +1428,7 @@ static int wiz_probe(struct platform_device *pdev) if (num_lanes > WIZ_MAX_LANES) { dev_err(dev, "Cannot support %d lanes\n", num_lanes); + ret = -ENODEV; goto err_addr_to_resource; } @@ -839,25 +1456,22 @@ static int wiz_probe(struct platform_device *pdev) if (wiz->typec_dir_delay < WIZ_TYPEC_DIR_DEBOUNCE_MIN || wiz->typec_dir_delay > WIZ_TYPEC_DIR_DEBOUNCE_MAX) { + ret = -EINVAL; dev_err(dev, "Invalid typec-dir-debounce property\n"); goto err_addr_to_resource; } } + ret = wiz_get_lane_phy_types(dev, wiz); + if (ret) + goto err_addr_to_resource; + wiz->dev = dev; wiz->regmap = regmap; wiz->num_lanes = num_lanes; - if (wiz->type == J721E_WIZ_10G) - wiz->clk_mux_sel = clk_mux_sel_10g; - else - wiz->clk_mux_sel = clk_mux_sel_16g; - + wiz->clk_mux_sel = data->clk_mux_sel; wiz->clk_div_sel = clk_div_sel; - - if (wiz->type == J721E_WIZ_10G) - wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G; - else - wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G; + wiz->clk_div_sel_num = data->clk_div_sel_num; platform_set_drvdata(pdev, wiz); @@ -867,6 +1481,10 @@ static int wiz_probe(struct platform_device *pdev) goto err_addr_to_resource; } + /* Enable supplemental Control override if available */ + if (wiz->scm_regmap) + regmap_field_write(wiz->sup_legacy_clk_override, 1); + phy_reset_dev = &wiz->wiz_phy_reset_dev; phy_reset_dev->dev = dev; phy_reset_dev->ops = &wiz_phy_reset_ops, @@ -894,26 +1512,34 @@ static int wiz_probe(struct platform_device *pdev) goto err_get_sync; } + for (i = 0; i < wiz->num_lanes; i++) { + regmap_field_read(wiz->p_enable[i], &val); + if (val & (P_ENABLE | P_ENABLE_FORCE)) { + already_configured = true; + break; + } + } + + if (!already_configured) { + ret = wiz_init(wiz); + if (ret) { + dev_err(dev, "WIZ initialization failed\n"); + goto err_wiz_init; + } + } + serdes_pdev = of_platform_device_create(child_node, NULL, dev); if (!serdes_pdev) { dev_WARN(dev, "Unable to create SERDES platform device\n"); - goto err_pdev_create; - } - wiz->serdes_pdev = serdes_pdev; - - ret = wiz_init(wiz); - if (ret) { - dev_err(dev, "WIZ initialization failed\n"); + ret = -ENOMEM; goto err_wiz_init; } + wiz->serdes_pdev = serdes_pdev; of_node_put(child_node); return 0; err_wiz_init: - of_platform_device_destroy(&serdes_pdev->dev, NULL); - -err_pdev_create: wiz_clock_cleanup(wiz, node); err_get_sync: diff --git a/drivers/phy/ti/phy-omap-control.c b/drivers/phy/ti/phy-omap-control.c index ccd0e4e00451..76c5595f0859 100644 --- a/drivers/phy/ti/phy-omap-control.c +++ b/drivers/phy/ti/phy-omap-control.c @@ -26,7 +26,7 @@ void omap_control_pcie_pcs(struct device *dev, u8 delay) u32 val; struct omap_control_phy *control_phy; - if (IS_ERR(dev) || !dev) { + if (IS_ERR_OR_NULL(dev)) { pr_err("%s: invalid device\n", __func__); return; } @@ -61,7 +61,7 @@ void omap_control_phy_power(struct device *dev, int on) unsigned long rate; struct omap_control_phy *control_phy; - if (IS_ERR(dev) || !dev) { + if (IS_ERR_OR_NULL(dev)) { pr_err("%s: invalid device\n", __func__); return; } @@ -202,7 +202,7 @@ void omap_control_usb_set_mode(struct device *dev, { struct omap_control_phy *ctrl_phy; - if (IS_ERR(dev) || !dev) + if (IS_ERR_OR_NULL(dev)) return; ctrl_phy = dev_get_drvdata(dev); @@ -268,7 +268,6 @@ MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); static int omap_control_phy_probe(struct platform_device *pdev) { - struct resource *res; const struct of_device_id *of_id; struct omap_control_phy *control_phy; @@ -285,16 +284,13 @@ static int omap_control_phy_probe(struct platform_device *pdev) control_phy->type = *(enum omap_control_phy_type *)of_id->data; if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "otghs_control"); - control_phy->otghs_control = devm_ioremap_resource( - &pdev->dev, res); + control_phy->otghs_control = + devm_platform_ioremap_resource_byname(pdev, "otghs_control"); if (IS_ERR(control_phy->otghs_control)) return PTR_ERR(control_phy->otghs_control); } else { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "power"); - control_phy->power = devm_ioremap_resource(&pdev->dev, res); + control_phy->power = + devm_platform_ioremap_resource_byname(pdev, "power"); if (IS_ERR(control_phy->power)) { dev_err(&pdev->dev, "Couldn't get power register\n"); return PTR_ERR(control_phy->power); @@ -312,9 +308,8 @@ static int omap_control_phy_probe(struct platform_device *pdev) } if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "pcie_pcs"); - control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); + control_phy->pcie_pcs = + devm_platform_ioremap_resource_byname(pdev, "pcie_pcs"); if (IS_ERR(control_phy->pcie_pcs)) return PTR_ERR(control_phy->pcie_pcs); } diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index 3d74629d7423..31a775877f6e 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -1,39 +1,97 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * omap-usb2.c - USB PHY, talking to musb controller in OMAP. + * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs. * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com * Author: Kishon Vijay Abraham I <kishon@ti.com> */ -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/of.h> -#include <linux/io.h> -#include <linux/phy/omap_usb.h> -#include <linux/usb/phy_companion.h> #include <linux/clk.h> -#include <linux/err.h> -#include <linux/pm_runtime.h> #include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/phy/omap_control_phy.h> +#include <linux/phy/omap_usb.h> #include <linux/phy/phy.h> -#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/of_platform.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> +#include <linux/usb/phy_companion.h> + +#define USB2PHY_ANA_CONFIG1 0x4c +#define USB2PHY_DISCON_BYP_LATCH BIT(31) -#define USB2PHY_DISCON_BYP_LATCH (1 << 31) -#define USB2PHY_ANA_CONFIG1 0x4c +#define USB2PHY_CHRG_DET 0x14 +#define USB2PHY_CHRG_DET_USE_CHG_DET_REG BIT(29) +#define USB2PHY_CHRG_DET_DIS_CHG_DET BIT(28) +/* SoC Specific USB2_OTG register definitions */ #define AM654_USB2_OTG_PD BIT(8) #define AM654_USB2_VBUS_DET_EN BIT(5) #define AM654_USB2_VBUSVALID_DET_EN BIT(4) +#define OMAP_DEV_PHY_PD BIT(0) +#define OMAP_USB2_PHY_PD BIT(28) + +#define AM437X_USB2_PHY_PD BIT(0) +#define AM437X_USB2_OTG_PD BIT(1) +#define AM437X_USB2_OTGVDET_EN BIT(19) +#define AM437X_USB2_OTGSESSEND_EN BIT(20) + +/* Driver Flags */ +#define OMAP_USB2_HAS_START_SRP BIT(0) +#define OMAP_USB2_HAS_SET_VBUS BIT(1) +#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT BIT(2) +#define OMAP_USB2_DISABLE_CHRG_DET BIT(3) + +struct omap_usb { + struct usb_phy phy; + struct phy_companion *comparator; + void __iomem *pll_ctrl_base; + void __iomem *phy_base; + struct device *dev; + struct device *control_dev; + struct clk *wkupclk; + struct clk *optclk; + u8 flags; + struct regmap *syscon_phy_power; /* ctrl. reg. acces */ + unsigned int power_reg; /* power reg. index within syscon */ + u32 mask; + u32 power_on; + u32 power_off; +}; + +#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy) + +struct usb_phy_data { + const char *label; + u8 flags; + u32 mask; + u32 power_on; + u32 power_off; +}; + +static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset) +{ + return __raw_readl(addr + offset); +} + +static inline void omap_usb_writel(void __iomem *addr, unsigned int offset, + u32 data) +{ + __raw_writel(data, addr + offset); +} + /** - * omap_usb2_set_comparator - links the comparator present in the sytem with - * this phy - * @comparator - the companion phy(comparator) for this phy + * omap_usb2_set_comparator() - links the comparator present in the system with this phy + * + * @comparator: the companion phy(comparator) for this phy * * The phy companion driver should call this API passing the phy_companion * filled with set_vbus and start_srp to be used by usb phy. @@ -84,7 +142,7 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) } static int omap_usb_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) + struct usb_gadget *gadget) { otg->gadget = gadget; if (!gadget) @@ -157,7 +215,7 @@ static int omap_usb2_enable_clocks(struct omap_usb *phy) return 0; err1: - clk_disable(phy->wkupclk); + clk_disable_unprepare(phy->wkupclk); err0: return ret; @@ -184,6 +242,13 @@ static int omap_usb_init(struct phy *x) omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val); } + if (phy->flags & OMAP_USB2_DISABLE_CHRG_DET) { + val = omap_usb_readl(phy->phy_base, USB2PHY_CHRG_DET); + val |= USB2PHY_CHRG_DET_USE_CHG_DET_REG | + USB2PHY_CHRG_DET_DIS_CHG_DET; + omap_usb_writel(phy->phy_base, USB2PHY_CHRG_DET, val); + } + return 0; } @@ -277,11 +342,30 @@ static const struct of_device_id omap_usb2_id_table[] = { }; MODULE_DEVICE_TABLE(of, omap_usb2_id_table); +static void omap_usb2_init_errata(struct omap_usb *phy) +{ + static const struct soc_device_attribute am65x_sr10_soc_devices[] = { + { .family = "AM65X", .revision = "SR1.0" }, + { /* sentinel */ } + }; + + /* + * Errata i2075: USB2PHY: USB2PHY Charger Detect is Enabled by + * Default Without VBUS Presence. + * + * AM654x SR1.0 has a silicon bug due to which D+ is pulled high after + * POR, which could cause enumeration failure with some USB hubs. + * Disabling the USB2_PHY Charger Detect function will put D+ + * into the normal state. + */ + if (soc_device_match(am65x_sr10_soc_devices)) + phy->flags |= OMAP_USB2_DISABLE_CHRG_DET; +} + static int omap_usb2_probe(struct platform_device *pdev) { struct omap_usb *phy; struct phy *generic_phy; - struct resource *res; struct phy_provider *phy_provider; struct usb_otg *otg; struct device_node *node = pdev->dev.of_node; @@ -314,17 +398,16 @@ static int omap_usb2_probe(struct platform_device *pdev) phy->mask = phy_data->mask; phy->power_on = phy_data->power_on; phy->power_off = phy_data->power_off; + phy->flags = phy_data->flags; - if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->phy_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->phy_base)) - return PTR_ERR(phy->phy_base); - phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; - } + omap_usb2_init_errata(phy); + + phy->phy_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->phy_base)) + return PTR_ERR(phy->phy_base); phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, - "syscon-phy-power"); + "syscon-phy-power"); if (IS_ERR(phy->syscon_phy_power)) { dev_dbg(&pdev->dev, "can't get syscon-phy-power, using control device\n"); @@ -353,7 +436,6 @@ static int omap_usb2_probe(struct platform_device *pdev) } } - phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); if (IS_ERR(phy->wkupclk)) { if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER) @@ -367,10 +449,10 @@ static int omap_usb2_probe(struct platform_device *pdev) if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER) dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); return PTR_ERR(phy->wkupclk); - } else { - dev_warn(&pdev->dev, - "found usb_phy_cm_clk32k, please fix DTS\n"); } + + dev_warn(&pdev->dev, + "found usb_phy_cm_clk32k, please fix DTS\n"); } phy->optclk = devm_clk_get(phy->dev, "refclk"); @@ -419,7 +501,6 @@ static int omap_usb2_probe(struct platform_device *pdev) return PTR_ERR(phy_provider); } - usb_add_phy_dev(&phy->phy); return 0; diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index a87946589eb7..f502c36f3be5 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -337,7 +337,6 @@ static int ti_pipe3_power_on(struct phy *x) { u32 val; u32 mask; - int ret; unsigned long rate; struct ti_pipe3 *phy = phy_get_drvdata(x); bool rx_pending = false; @@ -355,8 +354,8 @@ static int ti_pipe3_power_on(struct phy *x) rate = rate / 1000000; mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; val = rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; - ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, - mask, val); + regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + mask, val); /* * For PCIe, TX and RX must be powered on simultaneously. * For USB and SATA, TX must be powered on before RX @@ -697,6 +696,7 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) } control_pdev = of_find_device_by_node(control_node); + of_node_put(control_node); if (!control_pdev) { dev_err(dev, "Failed to get control device\n"); return -EINVAL; @@ -746,35 +746,28 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy) { - struct resource *res; struct device *dev = phy->dev; struct platform_device *pdev = to_platform_device(dev); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "phy_rx"); - phy->phy_rx = devm_ioremap_resource(dev, res); + phy->phy_rx = devm_platform_ioremap_resource_byname(pdev, "phy_rx"); 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); + phy->phy_tx = devm_platform_ioremap_resource_byname(pdev, "phy_tx"); return PTR_ERR_OR_ZERO(phy->phy_tx); } static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) { - struct resource *res; struct device *dev = phy->dev; struct platform_device *pdev = to_platform_device(dev); if (phy->mode == PIPE3_MODE_PCIE) return 0; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(dev, res); + phy->pll_ctrl_base = + devm_platform_ioremap_resource_byname(pdev, "pll_ctrl"); return PTR_ERR_OR_ZERO(phy->pll_ctrl_base); } diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c index d8d0cc11d187..669c13d6e402 100644 --- a/drivers/phy/ti/phy-tusb1210.c +++ b/drivers/phy/ti/phy-tusb1210.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * tusb1210.c - TUSB1210 USB ULPI PHY driver * * Copyright (C) 2015 Intel Corporation @@ -7,24 +7,94 @@ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> */ #include <linux/module.h> +#include <linux/bitfield.h> +#include <linux/delay.h> #include <linux/ulpi/driver.h> #include <linux/ulpi/regs.h> #include <linux/gpio/consumer.h> #include <linux/phy/ulpi_phy.h> - -#define TUSB1210_VENDOR_SPECIFIC2 0x80 -#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 -#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 -#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 +#include <linux/power_supply.h> +#include <linux/workqueue.h> + +#define TUSB1211_POWER_CONTROL 0x3d +#define TUSB1211_POWER_CONTROL_SET 0x3e +#define TUSB1211_POWER_CONTROL_CLEAR 0x3f +#define TUSB1211_POWER_CONTROL_SW_CONTROL BIT(0) +#define TUSB1211_POWER_CONTROL_DET_COMP BIT(1) +#define TUSB1211_POWER_CONTROL_DP_VSRC_EN BIT(6) + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0) +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4) +#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6) + +#define TUSB1211_VENDOR_SPECIFIC3 0x85 +#define TUSB1211_VENDOR_SPECIFIC3_SET 0x86 +#define TUSB1211_VENDOR_SPECIFIC3_CLEAR 0x87 +#define TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET BIT(4) +#define TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN BIT(6) + +#define TUSB1210_RESET_TIME_MS 50 + +#define TUSB1210_CHG_DET_MAX_RETRIES 5 + +/* TUSB1210 charger detection work states */ +enum tusb1210_chg_det_state { + TUSB1210_CHG_DET_CONNECTING, + TUSB1210_CHG_DET_START_DET, + TUSB1210_CHG_DET_READ_DET, + TUSB1210_CHG_DET_FINISH_DET, + TUSB1210_CHG_DET_CONNECTED, + TUSB1210_CHG_DET_DISCONNECTING, + TUSB1210_CHG_DET_DISCONNECTING_DONE, + TUSB1210_CHG_DET_DISCONNECTED, +}; struct tusb1210 { struct ulpi *ulpi; struct phy *phy; struct gpio_desc *gpio_reset; struct gpio_desc *gpio_cs; + u8 otg_ctrl; u8 vendor_specific2; +#ifdef CONFIG_POWER_SUPPLY + enum power_supply_usb_type chg_type; + enum tusb1210_chg_det_state chg_det_state; + int chg_det_retries; + struct delayed_work chg_det_work; + struct notifier_block psy_nb; + struct power_supply *psy; + struct power_supply *charger; +#endif }; +static int tusb1210_ulpi_write(struct tusb1210 *tusb, u8 reg, u8 val) +{ + int ret; + + ret = ulpi_write(tusb->ulpi, reg, val); + if (ret) + dev_err(&tusb->ulpi->dev, "error %d writing val 0x%02x to reg 0x%02x\n", + ret, val, reg); + + return ret; +} + +static int tusb1210_ulpi_read(struct tusb1210 *tusb, u8 reg, u8 *val) +{ + int ret; + + ret = ulpi_read(tusb->ulpi, reg); + if (ret >= 0) { + *val = ret; + ret = 0; + } else { + dev_err(&tusb->ulpi->dev, "error %d reading reg 0x%02x\n", ret, reg); + } + + return ret; +} + static int tusb1210_power_on(struct phy *phy) { struct tusb1210 *tusb = phy_get_drvdata(phy); @@ -32,10 +102,10 @@ static int tusb1210_power_on(struct phy *phy) gpiod_set_value_cansleep(tusb->gpio_reset, 1); gpiod_set_value_cansleep(tusb->gpio_cs, 1); + msleep(TUSB1210_RESET_TIME_MS); + /* Restore the optional eye diagram optimization value */ - if (tusb->vendor_specific2) - ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, - tusb->vendor_specific2); + tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, tusb->vendor_specific2); return 0; } @@ -54,35 +124,357 @@ static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct tusb1210 *tusb = phy_get_drvdata(phy); int ret; + u8 reg; - ret = ulpi_read(tusb->ulpi, ULPI_OTG_CTRL); + ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, ®); if (ret < 0) return ret; switch (mode) { case PHY_MODE_USB_HOST: - ret |= (ULPI_OTG_CTRL_DRVVBUS_EXT + reg |= (ULPI_OTG_CTRL_DRVVBUS_EXT | ULPI_OTG_CTRL_ID_PULLUP | ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN); - ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); - ret |= ULPI_OTG_CTRL_DRVVBUS; + tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); + reg |= ULPI_OTG_CTRL_DRVVBUS; break; case PHY_MODE_USB_DEVICE: - ret &= ~(ULPI_OTG_CTRL_DRVVBUS + reg &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN); - ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); - ret &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; + tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); + reg &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; break; default: /* nothing */ return 0; } - return ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); + tusb->otg_ctrl = reg; + return tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); +} + +#ifdef CONFIG_POWER_SUPPLY +static const char * const tusb1210_chg_det_states[] = { + "CHG_DET_CONNECTING", + "CHG_DET_START_DET", + "CHG_DET_READ_DET", + "CHG_DET_FINISH_DET", + "CHG_DET_CONNECTED", + "CHG_DET_DISCONNECTING", + "CHG_DET_DISCONNECTING_DONE", + "CHG_DET_DISCONNECTED", +}; + +static void tusb1210_reset(struct tusb1210 *tusb) +{ + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + usleep_range(200, 500); + gpiod_set_value_cansleep(tusb->gpio_reset, 1); +} + +static void tusb1210_chg_det_set_type(struct tusb1210 *tusb, + enum power_supply_usb_type type) +{ + dev_dbg(&tusb->ulpi->dev, "charger type: %d\n", type); + tusb->chg_type = type; + tusb->chg_det_retries = 0; + power_supply_changed(tusb->psy); +} + +static void tusb1210_chg_det_set_state(struct tusb1210 *tusb, + enum tusb1210_chg_det_state new_state, + int delay_ms) +{ + if (delay_ms) + dev_dbg(&tusb->ulpi->dev, "chg_det new state %s in %d ms\n", + tusb1210_chg_det_states[new_state], delay_ms); + + tusb->chg_det_state = new_state; + mod_delayed_work(system_long_wq, &tusb->chg_det_work, + msecs_to_jiffies(delay_ms)); +} + +static void tusb1210_chg_det_handle_ulpi_error(struct tusb1210 *tusb) +{ + tusb1210_reset(tusb); + if (tusb->chg_det_retries < TUSB1210_CHG_DET_MAX_RETRIES) { + tusb->chg_det_retries++; + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, + TUSB1210_RESET_TIME_MS); + } else { + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, + TUSB1210_RESET_TIME_MS); + } +} + +/* + * Boards using a TUSB121x for charger-detection have 3 power_supply class devs: + * + * tusb1211-charger-detect(1) -> charger -> fuel-gauge + * + * To determine if an USB charger is connected to the board, the online prop of + * the charger psy needs to be read. Since the tusb1211-charger-detect psy is + * the start of the supplier -> supplied-to chain, power_supply_am_i_supplied() + * cannot be used here. + * + * Instead, below is a list of the power_supply names of known chargers for + * these boards and the charger psy is looked up by name from this list. + * + * (1) modelling the external USB charger + */ +static const char * const tusb1210_chargers[] = { + "bq24190-charger", +}; + +static bool tusb1210_get_online(struct tusb1210 *tusb) +{ + union power_supply_propval val; + int i; + + for (i = 0; i < ARRAY_SIZE(tusb1210_chargers) && !tusb->charger; i++) + tusb->charger = power_supply_get_by_name(tusb1210_chargers[i]); + + if (!tusb->charger) + return false; + + if (power_supply_get_property(tusb->charger, POWER_SUPPLY_PROP_ONLINE, &val)) + return false; + + return val.intval; +} + +static void tusb1210_chg_det_work(struct work_struct *work) +{ + struct tusb1210 *tusb = container_of(work, struct tusb1210, chg_det_work.work); + bool vbus_present = tusb1210_get_online(tusb); + int ret; + u8 val; + + dev_dbg(&tusb->ulpi->dev, "chg_det state %s vbus_present %d\n", + tusb1210_chg_det_states[tusb->chg_det_state], vbus_present); + + switch (tusb->chg_det_state) { + case TUSB1210_CHG_DET_CONNECTING: + tusb->chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + tusb->chg_det_retries = 0; + /* Power on USB controller for ulpi_read()/_write() */ + ret = pm_runtime_resume_and_get(tusb->ulpi->dev.parent); + if (ret < 0) { + dev_err(&tusb->ulpi->dev, "error %d runtime-resuming\n", ret); + /* Should never happen, skip charger detection */ + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0); + return; + } + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 0); + break; + case TUSB1210_CHG_DET_START_DET: + /* + * Use the builtin charger detection FSM to keep things simple. + * This only detects DCP / SDP. This is good enough for the few + * boards which actually rely on the phy for charger detection. + */ + mutex_lock(&tusb->phy->mutex); + ret = tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_SET, + TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET); + mutex_unlock(&tusb->phy->mutex); + if (ret) { + tusb1210_chg_det_handle_ulpi_error(tusb); + break; + } + + /* Wait 400 ms for the charger detection FSM to finish */ + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_READ_DET, 400); + break; + case TUSB1210_CHG_DET_READ_DET: + mutex_lock(&tusb->phy->mutex); + ret = tusb1210_ulpi_read(tusb, TUSB1211_POWER_CONTROL, &val); + mutex_unlock(&tusb->phy->mutex); + if (ret) { + tusb1210_chg_det_handle_ulpi_error(tusb); + break; + } + + if (val & TUSB1211_POWER_CONTROL_DET_COMP) + tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_DCP); + else + tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_SDP); + + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 0); + break; + case TUSB1210_CHG_DET_FINISH_DET: + mutex_lock(&tusb->phy->mutex); + + /* Set SW_CONTROL to stop the charger-det FSM */ + ret = tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_SET, + TUSB1211_POWER_CONTROL_SW_CONTROL); + + /* Clear DP_VSRC_EN which may have been enabled by the charger-det FSM */ + ret |= tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_CLEAR, + TUSB1211_POWER_CONTROL_DP_VSRC_EN); + + /* Clear CHGD_IDP_SRC_EN (may have been enabled by the charger-det FSM) */ + ret |= tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_CLEAR, + TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN); + + /* If any of the above fails reset the phy */ + if (ret) { + tusb1210_reset(tusb); + msleep(TUSB1210_RESET_TIME_MS); + } + + /* Restore phy-parameters and OTG_CTRL register */ + tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, tusb->otg_ctrl); + tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + mutex_unlock(&tusb->phy->mutex); + + pm_runtime_put(tusb->ulpi->dev.parent); + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0); + break; + case TUSB1210_CHG_DET_CONNECTED: + if (!vbus_present) + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING, 0); + break; + case TUSB1210_CHG_DET_DISCONNECTING: + /* + * The phy seems to take approx. 600ms longer then the charger + * chip (which is used to get vbus_present) to determine Vbus + * session end. Wait 800ms to ensure the phy has detected and + * signalled Vbus session end. + */ + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING_DONE, 800); + break; + case TUSB1210_CHG_DET_DISCONNECTING_DONE: + /* + * The phy often stops reacting to ulpi_read()/_write requests + * after a Vbus-session end. Reset it to work around this. + */ + tusb1210_reset(tusb); + tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_UNKNOWN); + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTED, 0); + break; + case TUSB1210_CHG_DET_DISCONNECTED: + if (vbus_present) + tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTING, 0); + break; + } +} + +static int tusb1210_psy_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct tusb1210 *tusb = container_of(nb, struct tusb1210, psy_nb); + struct power_supply *psy = ptr; + + if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB) + queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0); + + return NOTIFY_OK; } +static int tusb1210_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct tusb1210 *tusb = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = tusb1210_get_online(tusb); + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = tusb->chg_type; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (tusb->chg_type == POWER_SUPPLY_USB_TYPE_DCP) + val->intval = 2000000; + else + val->intval = 500000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const enum power_supply_usb_type tusb1210_psy_usb_types[] = { + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_UNKNOWN, +}; + +static const enum power_supply_property tusb1210_psy_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static const struct power_supply_desc tusb1210_psy_desc = { + .name = "tusb1211-charger-detect", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = tusb1210_psy_usb_types, + .num_usb_types = ARRAY_SIZE(tusb1210_psy_usb_types), + .properties = tusb1210_psy_props, + .num_properties = ARRAY_SIZE(tusb1210_psy_props), + .get_property = tusb1210_psy_get_prop, +}; + +/* Setup charger detection if requested, on errors continue without chg-det */ +static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) +{ + struct power_supply_config psy_cfg = { .drv_data = tusb }; + struct device *dev = &tusb->ulpi->dev; + int ret; + + if (!device_property_read_bool(dev->parent, "linux,phy_charger_detect")) + return; + + if (tusb->ulpi->id.product != 0x1508) { + dev_err(dev, "error charger detection is only supported on the TUSB1211\n"); + return; + } + + ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &tusb->otg_ctrl); + if (ret) + return; + + tusb->psy = power_supply_register(dev, &tusb1210_psy_desc, &psy_cfg); + if (IS_ERR(tusb->psy)) + return; + + /* + * Delay initial run by 2 seconds to allow the charger driver, + * which is used to determine vbus_present, to load. + */ + tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED; + INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work); + queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ); + + tusb->psy_nb.notifier_call = tusb1210_psy_notifier; + power_supply_reg_notifier(&tusb->psy_nb); +} + +static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) +{ + + if (!IS_ERR_OR_NULL(tusb->psy)) { + power_supply_unreg_notifier(&tusb->psy_nb); + cancel_delayed_work_sync(&tusb->chg_det_work); + power_supply_unregister(tusb->psy); + } + + if (tusb->charger) + power_supply_put(tusb->charger); +} +#else +static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) { } +static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) { } +#endif + static const struct phy_ops phy_ops = { .power_on = tusb1210_power_on, .power_off = tusb1210_power_off, @@ -94,11 +486,14 @@ static int tusb1210_probe(struct ulpi *ulpi) { struct tusb1210 *tusb; u8 val, reg; + int ret; tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); if (!tusb) return -ENOMEM; + tusb->ulpi = ulpi; + tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(tusb->gpio_reset)) @@ -118,32 +513,43 @@ static int tusb1210_probe(struct ulpi *ulpi) * diagram optimization and DP/DM swap. */ + ret = tusb1210_ulpi_read(tusb, TUSB1210_VENDOR_SPECIFIC2, ®); + if (ret) + return ret; + /* High speed output drive strength configuration */ - device_property_read_u8(&ulpi->dev, "ihstx", &val); - reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; + if (!device_property_read_u8(&ulpi->dev, "ihstx", &val)) + u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK); /* High speed output impedance configuration */ - device_property_read_u8(&ulpi->dev, "zhsdrv", &val); - reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; + if (!device_property_read_u8(&ulpi->dev, "zhsdrv", &val)) + u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK); /* DP/DM swap control */ - device_property_read_u8(&ulpi->dev, "datapolarity", &val); - reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; + if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val)) + u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK); - if (reg) { - ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); - tusb->vendor_specific2 = reg; - } + ret = tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, reg); + if (ret) + return ret; - tusb->phy = ulpi_phy_create(ulpi, &phy_ops); - if (IS_ERR(tusb->phy)) - return PTR_ERR(tusb->phy); + tusb->vendor_specific2 = reg; - tusb->ulpi = ulpi; + tusb1210_probe_charger_detect(tusb); + + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); + if (IS_ERR(tusb->phy)) { + ret = PTR_ERR(tusb->phy); + goto err_remove_charger; + } phy_set_drvdata(tusb->phy, tusb); ulpi_set_drvdata(ulpi, tusb); return 0; + +err_remove_charger: + tusb1210_remove_charger_detect(tusb); + return ret; } static void tusb1210_remove(struct ulpi *ulpi) @@ -151,6 +557,7 @@ static void tusb1210_remove(struct ulpi *ulpi) struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); ulpi_phy_destroy(ulpi, tusb->phy); + tusb1210_remove_charger_detect(tusb); } #define TI_VENDOR_ID 0x0451 diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c index 9887f908f540..ac71017a0bc1 100644 --- a/drivers/phy/ti/phy-twl4030-usb.c +++ b/drivers/phy/ti/phy-twl4030-usb.c @@ -162,6 +162,8 @@ struct twl4030_usb { atomic_t connected; bool vbus_supplied; bool musb_mailbox_pending; + unsigned long runtime_suspended:1; + unsigned long needs_resume:1; struct delayed_work id_workaround_work; }; @@ -384,6 +386,9 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on) WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); } +static int twl4030_usb_runtime_suspend(struct device *dev); +static int twl4030_usb_runtime_resume(struct device *dev); + static int __maybe_unused twl4030_usb_suspend(struct device *dev) { struct twl4030_usb *twl = dev_get_drvdata(dev); @@ -395,6 +400,10 @@ static int __maybe_unused twl4030_usb_suspend(struct device *dev) */ dev_dbg(twl->dev, "%s\n", __func__); disable_irq(twl->irq); + if (!twl->runtime_suspended && !atomic_read(&twl->connected)) { + twl4030_usb_runtime_suspend(dev); + twl->needs_resume = 1; + } return 0; } @@ -405,9 +414,13 @@ static int __maybe_unused twl4030_usb_resume(struct device *dev) dev_dbg(twl->dev, "%s\n", __func__); enable_irq(twl->irq); + if (twl->needs_resume) + twl4030_usb_runtime_resume(dev); /* check whether cable status changed */ twl4030_usb_irq(0, twl); + twl->runtime_suspended = 0; + return 0; } @@ -422,6 +435,8 @@ static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) regulator_disable(twl->usb1v8); regulator_disable(twl->usb3v1); + twl->runtime_suspended = 1; + return 0; } @@ -544,8 +559,8 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl) return 0; } -static ssize_t twl4030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct twl4030_usb *twl = dev_get_drvdata(dev); int ret = -EINVAL; @@ -557,7 +572,7 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, return ret; } -static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +static DEVICE_ATTR_RO(vbus); static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { @@ -779,7 +794,7 @@ static int twl4030_usb_remove(struct platform_device *pdev) usb_remove_phy(&twl->phy); pm_runtime_get_sync(twl->dev); - cancel_delayed_work(&twl->id_workaround_work); + cancel_delayed_work_sync(&twl->id_workaround_work); device_remove_file(twl->dev, &dev_attr_vbus); /* set transceiver mode to power on defaults */ diff --git a/drivers/phy/xilinx/Kconfig b/drivers/phy/xilinx/Kconfig new file mode 100644 index 000000000000..d8b0d46b2b4d --- /dev/null +++ b/drivers/phy/xilinx/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# +# PHY drivers for Xilinx platforms +# + +config PHY_XILINX_ZYNQMP + tristate "Xilinx ZynqMP PHY driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support ZynqMP High Speed Gigabit Transceiver + that is part of ZynqMP SoC. diff --git a/drivers/phy/xilinx/Makefile b/drivers/phy/xilinx/Makefile new file mode 100644 index 000000000000..3f1f6a2a9b45 --- /dev/null +++ b/drivers/phy/xilinx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c new file mode 100644 index 000000000000..9be9535ad7ab --- /dev/null +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -0,0 +1,1038 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT. + * + * Copyright (C) 2018-2020 Xilinx Inc. + * + * Author: Anurag Kumar Vulisha <anuragku@xilinx.com> + * Author: Subbaraya Sundeep <sundeep.lkml@gmail.com> + * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This driver is tested for USB, SATA and Display Port currently. + * Other controllers PCIe and SGMII should also work but that is + * experimental as of now. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <dt-bindings/phy/phy.h> + +/* + * Lane Registers + */ + +/* TX De-emphasis parameters */ +#define L0_TX_ANA_TM_18 0x0048 +#define L0_TX_ANA_TM_118 0x01d8 +#define L0_TX_ANA_TM_118_FORCE_17_0 BIT(0) + +/* DN Resistor calibration code parameters */ +#define L0_TXPMA_ST_3 0x0b0c +#define L0_DN_CALIB_CODE 0x3f + +/* PMA control parameters */ +#define L0_TXPMD_TM_45 0x0cb4 +#define L0_TXPMD_TM_48 0x0cc0 +#define L0_TXPMD_TM_45_OVER_DP_MAIN BIT(0) +#define L0_TXPMD_TM_45_ENABLE_DP_MAIN BIT(1) +#define L0_TXPMD_TM_45_OVER_DP_POST1 BIT(2) +#define L0_TXPMD_TM_45_ENABLE_DP_POST1 BIT(3) +#define L0_TXPMD_TM_45_OVER_DP_POST2 BIT(4) +#define L0_TXPMD_TM_45_ENABLE_DP_POST2 BIT(5) + +/* PCS control parameters */ +#define L0_TM_DIG_6 0x106c +#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f +#define L0_TX_DIG_61 0x00f4 +#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f + +/* PLL Test Mode register parameters */ +#define L0_TM_PLL_DIG_37 0x2094 +#define L0_TM_COARSE_CODE_LIMIT 0x10 + +/* PLL SSC step size offsets */ +#define L0_PLL_SS_STEPS_0_LSB 0x2368 +#define L0_PLL_SS_STEPS_1_MSB 0x236c +#define L0_PLL_SS_STEP_SIZE_0_LSB 0x2370 +#define L0_PLL_SS_STEP_SIZE_1 0x2374 +#define L0_PLL_SS_STEP_SIZE_2 0x2378 +#define L0_PLL_SS_STEP_SIZE_3_MSB 0x237c +#define L0_PLL_STATUS_READ_1 0x23e4 + +/* SSC step size parameters */ +#define STEP_SIZE_0_MASK 0xff +#define STEP_SIZE_1_MASK 0xff +#define STEP_SIZE_2_MASK 0xff +#define STEP_SIZE_3_MASK 0x3 +#define STEP_SIZE_SHIFT 8 +#define FORCE_STEP_SIZE 0x10 +#define FORCE_STEPS 0x20 +#define STEPS_0_MASK 0xff +#define STEPS_1_MASK 0x07 + +/* Reference clock selection parameters */ +#define L0_Ln_REF_CLK_SEL(n) (0x2860 + (n) * 4) +#define L0_REF_CLK_SEL_MASK 0x8f + +/* Calibration digital logic parameters */ +#define L3_TM_CALIB_DIG19 0xec4c +#define L3_CALIB_DONE_STATUS 0xef14 +#define L3_TM_CALIB_DIG18 0xec48 +#define L3_TM_CALIB_DIG19_NSW 0x07 +#define L3_TM_CALIB_DIG18_NSW 0xe0 +#define L3_TM_OVERRIDE_NSW_CODE 0x20 +#define L3_CALIB_DONE 0x02 +#define L3_NSW_SHIFT 5 +#define L3_NSW_PIPE_SHIFT 4 +#define L3_NSW_CALIB_SHIFT 3 + +#define PHY_REG_OFFSET 0x4000 + +/* + * Global Registers + */ + +/* Refclk selection parameters */ +#define PLL_REF_SEL(n) (0x10000 + (n) * 4) +#define PLL_FREQ_MASK 0x1f +#define PLL_STATUS_LOCKED 0x10 + +/* Inter Connect Matrix parameters */ +#define ICM_CFG0 0x10010 +#define ICM_CFG1 0x10014 +#define ICM_CFG0_L0_MASK 0x07 +#define ICM_CFG0_L1_MASK 0x70 +#define ICM_CFG1_L2_MASK 0x07 +#define ICM_CFG2_L3_MASK 0x70 +#define ICM_CFG_SHIFT 4 + +/* Inter Connect Matrix allowed protocols */ +#define ICM_PROTOCOL_PD 0x0 +#define ICM_PROTOCOL_PCIE 0x1 +#define ICM_PROTOCOL_SATA 0x2 +#define ICM_PROTOCOL_USB 0x3 +#define ICM_PROTOCOL_DP 0x4 +#define ICM_PROTOCOL_SGMII 0x5 + +/* Test Mode common reset control parameters */ +#define TM_CMN_RST 0x10018 +#define TM_CMN_RST_EN 0x1 +#define TM_CMN_RST_SET 0x2 +#define TM_CMN_RST_MASK 0x3 + +/* Bus width parameters */ +#define TX_PROT_BUS_WIDTH 0x10040 +#define RX_PROT_BUS_WIDTH 0x10044 +#define PROT_BUS_WIDTH_10 0x0 +#define PROT_BUS_WIDTH_20 0x1 +#define PROT_BUS_WIDTH_40 0x2 +#define PROT_BUS_WIDTH_SHIFT(n) ((n) * 2) +#define PROT_BUS_WIDTH_MASK(n) GENMASK((n) * 2 + 1, (n) * 2) + +/* Number of GT lanes */ +#define NUM_LANES 4 + +/* SIOU SATA control register */ +#define SATA_CONTROL_OFFSET 0x0100 + +/* Total number of controllers */ +#define CONTROLLERS_PER_LANE 5 + +/* Protocol Type parameters */ +#define XPSGTR_TYPE_USB0 0 /* USB controller 0 */ +#define XPSGTR_TYPE_USB1 1 /* USB controller 1 */ +#define XPSGTR_TYPE_SATA_0 2 /* SATA controller lane 0 */ +#define XPSGTR_TYPE_SATA_1 3 /* SATA controller lane 1 */ +#define XPSGTR_TYPE_PCIE_0 4 /* PCIe controller lane 0 */ +#define XPSGTR_TYPE_PCIE_1 5 /* PCIe controller lane 1 */ +#define XPSGTR_TYPE_PCIE_2 6 /* PCIe controller lane 2 */ +#define XPSGTR_TYPE_PCIE_3 7 /* PCIe controller lane 3 */ +#define XPSGTR_TYPE_DP_0 8 /* Display Port controller lane 0 */ +#define XPSGTR_TYPE_DP_1 9 /* Display Port controller lane 1 */ +#define XPSGTR_TYPE_SGMII0 10 /* Ethernet SGMII controller 0 */ +#define XPSGTR_TYPE_SGMII1 11 /* Ethernet SGMII controller 1 */ +#define XPSGTR_TYPE_SGMII2 12 /* Ethernet SGMII controller 2 */ +#define XPSGTR_TYPE_SGMII3 13 /* Ethernet SGMII controller 3 */ + +/* Timeout values */ +#define TIMEOUT_US 1000 + +struct xpsgtr_dev; + +/** + * struct xpsgtr_ssc - structure to hold SSC settings for a lane + * @refclk_rate: PLL reference clock frequency + * @pll_ref_clk: value to be written to register for corresponding ref clk rate + * @steps: number of steps of SSC (Spread Spectrum Clock) + * @step_size: step size of each step + */ +struct xpsgtr_ssc { + u32 refclk_rate; + u8 pll_ref_clk; + u32 steps; + u32 step_size; +}; + +/** + * struct xpsgtr_phy - representation of a lane + * @phy: pointer to the kernel PHY device + * @type: controller which uses this lane + * @lane: lane number + * @protocol: protocol in which the lane operates + * @skip_phy_init: skip phy_init() if true + * @dev: pointer to the xpsgtr_dev instance + * @refclk: reference clock index + */ +struct xpsgtr_phy { + struct phy *phy; + u8 type; + u8 lane; + u8 protocol; + bool skip_phy_init; + struct xpsgtr_dev *dev; + unsigned int refclk; +}; + +/** + * struct xpsgtr_dev - representation of a ZynMP GT device + * @dev: pointer to device + * @serdes: serdes base address + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes + * @refclk_sscs: spread spectrum settings for the reference clocks + * @clk: reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register + * @saved_icm_cfg1: stored value of ICM CFG1 register + */ +struct xpsgtr_dev { + struct device *dev; + void __iomem *serdes; + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; + const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + struct clk *clk[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; + unsigned int saved_icm_cfg1; +}; + +/* + * Configuration Data + */ + +/* lookup table to hold all settings needed for a ref clock frequency */ +static const struct xpsgtr_ssc ssc_lookup[] = { + { 19200000, 0x05, 608, 264020 }, + { 20000000, 0x06, 634, 243454 }, + { 24000000, 0x07, 760, 168973 }, + { 26000000, 0x08, 824, 143860 }, + { 27000000, 0x09, 856, 86551 }, + { 38400000, 0x0a, 1218, 65896 }, + { 40000000, 0x0b, 634, 243454 }, + { 52000000, 0x0c, 824, 143860 }, + { 100000000, 0x0d, 1058, 87533 }, + { 108000000, 0x0e, 856, 86551 }, + { 125000000, 0x0f, 992, 119497 }, + { 135000000, 0x10, 1070, 55393 }, + { 150000000, 0x11, 792, 187091 } +}; + +/* + * I/O Accessors + */ + +static inline u32 xpsgtr_read(struct xpsgtr_dev *gtr_dev, u32 reg) +{ + return readl(gtr_dev->serdes + reg); +} + +static inline void xpsgtr_write(struct xpsgtr_dev *gtr_dev, u32 reg, u32 value) +{ + writel(value, gtr_dev->serdes + reg); +} + +static inline void xpsgtr_clr_set(struct xpsgtr_dev *gtr_dev, u32 reg, + u32 clr, u32 set) +{ + u32 value = xpsgtr_read(gtr_dev, reg); + + value &= ~clr; + value |= set; + xpsgtr_write(gtr_dev, reg, value); +} + +static inline u32 xpsgtr_read_phy(struct xpsgtr_phy *gtr_phy, u32 reg) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + return readl(addr); +} + +static inline void xpsgtr_write_phy(struct xpsgtr_phy *gtr_phy, + u32 reg, u32 value) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + writel(value, addr); +} + +static inline void xpsgtr_clr_set_phy(struct xpsgtr_phy *gtr_phy, + u32 reg, u32 clr, u32 set) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + writel((readl(addr) & ~clr) | set, addr); +} + +/* + * Hardware Configuration + */ + +/* Wait for the PLL to lock (with a timeout). */ +static int xpsgtr_wait_pll_lock(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + unsigned int timeout = TIMEOUT_US; + int ret; + + dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n"); + + while (1) { + u32 reg = xpsgtr_read_phy(gtr_phy, L0_PLL_STATUS_READ_1); + + if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) { + ret = 0; + break; + } + + if (--timeout == 0) { + ret = -ETIMEDOUT; + break; + } + + udelay(1); + } + + if (ret == -ETIMEDOUT) + dev_err(gtr_dev->dev, + "lane %u (type %u, protocol %u): PLL lock timeout\n", + gtr_phy->lane, gtr_phy->type, gtr_phy->protocol); + + return ret; +} + +/* Configure PLL and spread-sprectrum clock. */ +static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) +{ + const struct xpsgtr_ssc *ssc; + u32 step_size; + + ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), + PLL_FREQ_MASK, ssc->pll_ref_clk); + + /* Enable lane clock sharing, if required */ + if (gtr_phy->refclk != gtr_phy->lane) { + /* Lane3 Ref Clock Selection Register */ + xpsgtr_clr_set(gtr_phy->dev, L0_Ln_REF_CLK_SEL(gtr_phy->lane), + L0_REF_CLK_SEL_MASK, 1 << gtr_phy->refclk); + } + + /* SSC step size [7:0] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_0_LSB, + STEP_SIZE_0_MASK, step_size & STEP_SIZE_0_MASK); + + /* SSC step size [15:8] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_1, + STEP_SIZE_1_MASK, step_size & STEP_SIZE_1_MASK); + + /* SSC step size [23:16] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_2, + STEP_SIZE_2_MASK, step_size & STEP_SIZE_2_MASK); + + /* SSC steps [7:0] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_0_LSB, + STEPS_0_MASK, ssc->steps & STEPS_0_MASK); + + /* SSC steps [10:8] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_1_MSB, + STEPS_1_MASK, + (ssc->steps >> STEP_SIZE_SHIFT) & STEPS_1_MASK); + + /* SSC step size [24:25] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); +} + +/* Configure the lane protocol. */ +static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u8 protocol = gtr_phy->protocol; + + switch (gtr_phy->lane) { + case 0: + xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L0_MASK, protocol); + break; + case 1: + xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L1_MASK, + protocol << ICM_CFG_SHIFT); + break; + case 2: + xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L0_MASK, protocol); + break; + case 3: + xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L1_MASK, + protocol << ICM_CFG_SHIFT); + break; + default: + /* We already checked 0 <= lane <= 3 */ + break; + } +} + +/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ +static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) +{ + xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); +} + +/* DP-specific initialization. */ +static void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy) +{ + xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_45, + L0_TXPMD_TM_45_OVER_DP_MAIN | + L0_TXPMD_TM_45_ENABLE_DP_MAIN | + L0_TXPMD_TM_45_OVER_DP_POST1 | + L0_TXPMD_TM_45_OVER_DP_POST2 | + L0_TXPMD_TM_45_ENABLE_DP_POST2); + xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118, + L0_TX_ANA_TM_118_FORCE_17_0); +} + +/* SATA-specific initialization. */ +static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + + xpsgtr_bypass_scrambler_8b10b(gtr_phy); + + writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); +} + +/* SGMII-specific initialization. */ +static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane); + u32 val = PROT_BUS_WIDTH_10 << PROT_BUS_WIDTH_SHIFT(gtr_phy->lane); + + /* Set SGMII protocol TX and RX bus width to 10 bits. */ + xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val); + xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val); + + xpsgtr_bypass_scrambler_8b10b(gtr_phy); +} + +/* Configure TX de-emphasis and margining for DP. */ +static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre, + unsigned int voltage) +{ + static const u8 voltage_swing[4][4] = { + { 0x2a, 0x27, 0x24, 0x20 }, + { 0x27, 0x23, 0x20, 0xff }, + { 0x24, 0x20, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff } + }; + static const u8 pre_emphasis[4][4] = { + { 0x02, 0x02, 0x02, 0x02 }, + { 0x01, 0x01, 0x01, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff } + }; + + xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_48, voltage_swing[pre][voltage]); + xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, pre_emphasis[pre][voltage]); +} + +/* + * PHY Operations + */ + +static bool xpsgtr_phy_init_required(struct xpsgtr_phy *gtr_phy) +{ + /* + * As USB may save the snapshot of the states during hibernation, doing + * phy_init() will put the USB controller into reset, resulting in the + * losing of the saved snapshot. So try to avoid phy_init() for USB + * except when gtr_phy->skip_phy_init is false (this happens when FPD is + * shutdown during suspend or when gt lane is changed from current one) + */ + if (gtr_phy->protocol == ICM_PROTOCOL_USB && gtr_phy->skip_phy_init) + return false; + else + return true; +} + +/* + * There is a functional issue in the GT. The TX termination resistance can be + * out of spec due to a issue in the calibration logic. This is the workaround + * to fix it, required for XCZU9EG silicon. + */ +static int xpsgtr_phy_tx_term_fix(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u32 timeout = TIMEOUT_US; + u32 nsw; + + /* Enabling Test Mode control for CMN Rest */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + /* Set Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); + + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, 0x00); + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, L3_TM_OVERRIDE_NSW_CODE); + + /* + * As a part of work around sequence for PMOS calibration fix, + * we need to configure any lane ICM_CFG to valid protocol. This + * will deassert the CMN_Resetn signal. + */ + xpsgtr_lane_set_protocol(gtr_phy); + + /* Clear Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + dev_dbg(gtr_dev->dev, "calibrating...\n"); + + do { + u32 reg = xpsgtr_read(gtr_dev, L3_CALIB_DONE_STATUS); + + if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE) + break; + + if (!--timeout) { + dev_err(gtr_dev->dev, "calibration time out\n"); + return -ETIMEDOUT; + } + + udelay(1); + } while (timeout > 0); + + dev_dbg(gtr_dev->dev, "calibration done\n"); + + /* Reading NMOS Register Code */ + nsw = xpsgtr_read(gtr_dev, L0_TXPMA_ST_3) & L0_DN_CALIB_CODE; + + /* Set Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); + + /* Writing NMOS register values back [5:3] */ + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, nsw >> L3_NSW_CALIB_SHIFT); + + /* Writing NMOS register value [2:0] */ + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, + ((nsw & L3_TM_CALIB_DIG19_NSW) << L3_NSW_SHIFT) | + (1 << L3_NSW_PIPE_SHIFT)); + + /* Clear Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + return 0; +} + +static int xpsgtr_phy_init(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + int ret = 0; + + mutex_lock(>r_dev->gtr_mutex); + + /* Skip initialization if not required. */ + if (!xpsgtr_phy_init_required(gtr_phy)) + goto out; + + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) + goto out; + + gtr_dev->tx_term_fix = false; + } + + /* Enable coarse code saturation limiting logic. */ + xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT); + + /* + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ + xpsgtr_configure_pll(gtr_phy); + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { + case ICM_PROTOCOL_DP: + xpsgtr_phy_init_dp(gtr_phy); + break; + + case ICM_PROTOCOL_SATA: + xpsgtr_phy_init_sata(gtr_phy); + break; + + case ICM_PROTOCOL_SGMII: + xpsgtr_phy_init_sgmii(gtr_phy); + break; + } + +out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +} + +static int xpsgtr_phy_exit(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + + gtr_phy->skip_phy_init = false; + + return 0; +} + +static int xpsgtr_phy_power_on(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + int ret = 0; + + /* Skip initialization if not required. */ + if (!xpsgtr_phy_init_required(gtr_phy)) + return ret; + /* + * Wait for the PLL to lock. For DP, only wait on DP0 to avoid + * cumulating waits for both lanes. The user is expected to initialize + * lane 0 last. + */ + if (gtr_phy->protocol != ICM_PROTOCOL_DP || + gtr_phy->type == XPSGTR_TYPE_DP_0) + ret = xpsgtr_wait_pll_lock(phy); + + return ret; +} + +static int xpsgtr_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + + if (gtr_phy->protocol != ICM_PROTOCOL_DP) + return 0; + + xpsgtr_phy_configure_dp(gtr_phy, opts->dp.pre[0], opts->dp.voltage[0]); + + return 0; +} + +static const struct phy_ops xpsgtr_phyops = { + .init = xpsgtr_phy_init, + .exit = xpsgtr_phy_exit, + .power_on = xpsgtr_phy_power_on, + .configure = xpsgtr_phy_configure, + .owner = THIS_MODULE, +}; + +/* + * OF Xlate Support + */ + +/* Set the lane type and protocol based on the PHY type and instance number. */ +static int xpsgtr_set_lane_type(struct xpsgtr_phy *gtr_phy, u8 phy_type, + unsigned int phy_instance) +{ + unsigned int num_phy_types; + const int *phy_types; + + switch (phy_type) { + case PHY_TYPE_SATA: { + static const int types[] = { + XPSGTR_TYPE_SATA_0, + XPSGTR_TYPE_SATA_1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_SATA; + break; + } + case PHY_TYPE_USB3: { + static const int types[] = { + XPSGTR_TYPE_USB0, + XPSGTR_TYPE_USB1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_USB; + break; + } + case PHY_TYPE_DP: { + static const int types[] = { + XPSGTR_TYPE_DP_0, + XPSGTR_TYPE_DP_1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_DP; + break; + } + case PHY_TYPE_PCIE: { + static const int types[] = { + XPSGTR_TYPE_PCIE_0, + XPSGTR_TYPE_PCIE_1, + XPSGTR_TYPE_PCIE_2, + XPSGTR_TYPE_PCIE_3, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_PCIE; + break; + } + case PHY_TYPE_SGMII: { + static const int types[] = { + XPSGTR_TYPE_SGMII0, + XPSGTR_TYPE_SGMII1, + XPSGTR_TYPE_SGMII2, + XPSGTR_TYPE_SGMII3, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_SGMII; + break; + } + default: + return -EINVAL; + } + + if (phy_instance >= num_phy_types) + return -EINVAL; + + gtr_phy->type = phy_types[phy_instance]; + return 0; +} + +/* + * Valid combinations of controllers and lanes (Interconnect Matrix). + */ +static const unsigned int icm_matrix[NUM_LANES][CONTROLLERS_PER_LANE] = { + { XPSGTR_TYPE_PCIE_0, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII0 }, + { XPSGTR_TYPE_PCIE_1, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII1 }, + { XPSGTR_TYPE_PCIE_2, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII2 }, + { XPSGTR_TYPE_PCIE_3, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB1, + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII3 } +}; + +/* Translate OF phandle and args to PHY instance. */ +static struct phy *xpsgtr_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + struct xpsgtr_phy *gtr_phy; + unsigned int phy_instance; + unsigned int phy_lane; + unsigned int phy_type; + unsigned int refclk; + unsigned int i; + int ret; + + if (args->args_count != 4) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + /* + * Get the PHY parameters from the OF arguments and derive the lane + * type. + */ + phy_lane = args->args[0]; + if (phy_lane >= ARRAY_SIZE(gtr_dev->phys)) { + dev_err(dev, "Invalid lane number %u\n", phy_lane); + return ERR_PTR(-ENODEV); + } + + gtr_phy = >r_dev->phys[phy_lane]; + phy_type = args->args[1]; + phy_instance = args->args[2]; + + ret = xpsgtr_set_lane_type(gtr_phy, phy_type, phy_instance); + if (ret < 0) { + dev_err(gtr_dev->dev, "Invalid PHY type and/or instance\n"); + return ERR_PTR(ret); + } + + refclk = args->args[3]; + if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || + !gtr_dev->refclk_sscs[refclk]) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } + + gtr_phy->refclk = refclk; + + /* + * Ensure that the Interconnect Matrix is obeyed, i.e a given lane type + * is allowed to operate on the lane. + */ + for (i = 0; i < CONTROLLERS_PER_LANE; i++) { + if (icm_matrix[phy_lane][i] == gtr_phy->type) + return gtr_phy->phy; + } + + return ERR_PTR(-EINVAL); +} + +/* + * Power Management + */ + +static int __maybe_unused xpsgtr_suspend(struct device *dev) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + unsigned int i; + + /* Save the snapshot ICM_CFG registers. */ + gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); + gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); + + for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) + clk_disable_unprepare(gtr_dev->clk[i]); + + return 0; +} + +static int __maybe_unused xpsgtr_resume(struct device *dev) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + unsigned int icm_cfg0, icm_cfg1; + unsigned int i; + bool skip_phy_init; + int err; + + for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) { + err = clk_prepare_enable(gtr_dev->clk[i]); + if (err) + goto err_clk_put; + } + + icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); + icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); + + /* Return if no GT lanes got configured before suspend. */ + if (!gtr_dev->saved_icm_cfg0 && !gtr_dev->saved_icm_cfg1) + return 0; + + /* Check if the ICM configurations changed after suspend. */ + if (icm_cfg0 == gtr_dev->saved_icm_cfg0 && + icm_cfg1 == gtr_dev->saved_icm_cfg1) + skip_phy_init = true; + else + skip_phy_init = false; + + /* Update the skip_phy_init for all gtr_phy instances. */ + for (i = 0; i < ARRAY_SIZE(gtr_dev->phys); i++) + gtr_dev->phys[i].skip_phy_init = skip_phy_init; + + return 0; + +err_clk_put: + while (i--) + clk_disable_unprepare(gtr_dev->clk[i]); + + return err; +} + +static const struct dev_pm_ops xpsgtr_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) +}; + +/* + * Probe & Platform Driver + */ + +static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) +{ + unsigned int refclk; + int ret; + + for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { + unsigned long rate; + unsigned int i; + struct clk *clk; + char name[8]; + + snprintf(name, sizeof(name), "ref%u", refclk); + clk = devm_clk_get_optional(gtr_dev->dev, name); + if (IS_ERR(clk)) { + ret = dev_err_probe(gtr_dev->dev, PTR_ERR(clk), + "Failed to get reference clock %u\n", + refclk); + goto err_clk_put; + } + + if (!clk) + continue; + + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk_put; + + gtr_dev->clk[refclk] = clk; + + /* + * Get the spread spectrum (SSC) settings for the reference + * clock rate. + */ + rate = clk_get_rate(clk); + + for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { + if (rate == ssc_lookup[i].refclk_rate) { + gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; + break; + } + } + + if (i == ARRAY_SIZE(ssc_lookup)) { + dev_err(gtr_dev->dev, + "Invalid rate %lu for reference clock %u\n", + rate, refclk); + ret = -EINVAL; + goto err_clk_put; + } + } + + return 0; + +err_clk_put: + while (refclk--) + clk_disable_unprepare(gtr_dev->clk[refclk]); + + return ret; +} + +static int xpsgtr_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct xpsgtr_dev *gtr_dev; + struct phy_provider *provider; + unsigned int port; + unsigned int i; + int ret; + + gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL); + if (!gtr_dev) + return -ENOMEM; + + gtr_dev->dev = &pdev->dev; + platform_set_drvdata(pdev, gtr_dev); + + mutex_init(>r_dev->gtr_mutex); + + if (of_device_is_compatible(np, "xlnx,zynqmp-psgtr")) + gtr_dev->tx_term_fix = + of_property_read_bool(np, "xlnx,tx-termination-fix"); + + /* Acquire resources. */ + gtr_dev->serdes = devm_platform_ioremap_resource_byname(pdev, "serdes"); + if (IS_ERR(gtr_dev->serdes)) + return PTR_ERR(gtr_dev->serdes); + + gtr_dev->siou = devm_platform_ioremap_resource_byname(pdev, "siou"); + if (IS_ERR(gtr_dev->siou)) + return PTR_ERR(gtr_dev->siou); + + ret = xpsgtr_get_ref_clocks(gtr_dev); + if (ret) + return ret; + + /* Create PHYs. */ + for (port = 0; port < ARRAY_SIZE(gtr_dev->phys); ++port) { + struct xpsgtr_phy *gtr_phy = >r_dev->phys[port]; + struct phy *phy; + + gtr_phy->lane = port; + gtr_phy->dev = gtr_dev; + + phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + ret = PTR_ERR(phy); + goto err_clk_put; + } + + gtr_phy->phy = phy; + phy_set_drvdata(phy, gtr_phy); + } + + /* Register the PHY provider. */ + provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate); + if (IS_ERR(provider)) { + dev_err(&pdev->dev, "registering provider failed\n"); + ret = PTR_ERR(provider); + goto err_clk_put; + } + return 0; + +err_clk_put: + for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) + clk_disable_unprepare(gtr_dev->clk[i]); + + return ret; +} + +static const struct of_device_id xpsgtr_of_match[] = { + { .compatible = "xlnx,zynqmp-psgtr", }, + { .compatible = "xlnx,zynqmp-psgtr-v1.1", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xpsgtr_of_match); + +static struct platform_driver xpsgtr_driver = { + .probe = xpsgtr_probe, + .driver = { + .name = "xilinx-psgtr", + .of_match_table = xpsgtr_of_match, + .pm = &xpsgtr_pm_ops, + }, +}; + +module_platform_driver(xpsgtr_driver); + +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Xilinx ZynqMP High speed Gigabit Transceiver"); |