diff options
Diffstat (limited to 'drivers/phy')
43 files changed, 5649 insertions, 619 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index de9362c25c07..01b53f86004c 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -49,6 +49,17 @@ 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 + 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. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c27408e4daae..6eb2916773c5 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.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_USB_LGM_PHY) += phy-lgm-usb.o obj-y += allwinner/ \ amlogic/ \ broadcom/ \ diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c index 14f45bc35cc5..47b029fbebbd 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> @@ -258,29 +259,24 @@ static struct mdio_driver bcm_ns_usb3_mdio_driver = { **************************************************/ static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, - u32 mask, u32 value, unsigned long timeout) + u32 mask, u32 value, int usec) { - unsigned long deadline = jiffies + timeout; u32 val; + int ret; - do { - val = readl(addr); - if ((val & mask) == value) - return 0; - cpu_relax(); - udelay(10); - } while (!time_after_eq(jiffies, deadline)); + ret = readl_poll_timeout_atomic(addr, val, ((val & mask) == value), + 10, usec); + if (ret) + dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); - dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); - - return -EBUSY; + return ret; } 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)); + BCM_NS_USB3_MII_MNG_TIMEOUT_US); } static int bcm_ns_usb3_platform_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c index 527625912b78..9630ac127366 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c @@ -18,6 +18,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 +88,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) diff --git a/drivers/phy/broadcom/phy-bcm-sr-usb.c b/drivers/phy/broadcom/phy-bcm-sr-usb.c index 77c025a0720c..c3e99ad17487 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> @@ -109,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) diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c index 016514e4aa54..88e239adc3b8 100644 --- a/drivers/phy/cadence/phy-cadence-salvo.c +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -97,7 +97,7 @@ struct cdns_reg_pairs { struct cdns_salvo_data { u8 reg_offset_shift; - struct cdns_reg_pairs *init_sequence_val; + const struct cdns_reg_pairs *init_sequence_val; u8 init_sequence_length; }; @@ -126,7 +126,7 @@ static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, * Below bringup sequence pair are from Cadence PHY's User Guide * and NXP platform tuning results. */ -static struct cdns_reg_pairs cdns_nxp_sequence_pair[] = { +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}, @@ -217,7 +217,7 @@ static int cdns_salvo_phy_init(struct phy *phy) return ret; for (i = 0; i < data->init_sequence_length; i++) { - struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i; + const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i; cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val); } @@ -251,7 +251,7 @@ static int cdns_salvo_phy_power_off(struct phy *phy) return 0; } -static struct phy_ops cdns_salvo_phy_ops = { +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, diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index faed652b73f7..453ef26fa1c7 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -172,10 +172,10 @@ struct cdns_sierra_data { 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; + const struct cdns_reg_pairs *pcie_cmn_vals; + const struct cdns_reg_pairs *pcie_ln_vals; + const struct cdns_reg_pairs *usb_cmn_vals; + const struct cdns_reg_pairs *usb_ln_vals; }; struct cdns_regmap_cdb_context { @@ -233,7 +233,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 +252,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,7 +260,7 @@ static struct regmap_config cdns_sierra_common_cdb_config = { .reg_read = cdns_regmap_read, }; -static struct regmap_config cdns_sierra_phy_config_ctrl_config = { +static const struct regmap_config cdns_sierra_phy_config_ctrl_config = { .name = "sierra_phy_config_ctrl", .reg_stride = 1, .fast_io = true, @@ -274,7 +274,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); struct regmap *regmap; int i, j; - struct cdns_reg_pairs *cmn_vals, *ln_vals; + const struct cdns_reg_pairs *cmn_vals, *ln_vals; u32 num_cmn_regs, num_ln_regs; /* Initialise the PHY registers, unless auto configured */ @@ -654,7 +654,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev) } /* 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,7 +663,7 @@ 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[] = { {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, @@ -674,7 +674,7 @@ static struct cdns_reg_pairs 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,7 +682,7 @@ 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}, {0x55A5, SIERRA_DET_STANDEC_C_PREG}, diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 7116127358ee..f310e15d94cb 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -25,11 +25,14 @@ #define REF_CLK_19_2MHz 19200000 #define REF_CLK_25MHz 25000000 -#define DEFAULT_NUM_LANES 4 #define MAX_NUM_LANES 4 #define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */ +#define NUM_SSC_MODE 3 +#define NUM_PHY_TYPE 6 + #define POLL_TIMEOUT_US 5000 +#define PLL_LOCK_TIMEOUT 100000 #define TORRENT_COMMON_CDB_OFFSET 0x0 @@ -79,6 +82,8 @@ #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_BGCAL_INIT_TMR 0x0064U #define CMN_BGCAL_ITER_TMR 0x0065U #define CMN_IBCAL_INIT_TMR 0x0074U @@ -99,6 +104,14 @@ #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 @@ -116,8 +129,10 @@ #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 @@ -131,24 +146,31 @@ #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 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_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 @@ -157,23 +179,49 @@ /* 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_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 PMA common registers */ +#define PHY_PMA_CMN_CTRL1 0x0000U #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U 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); @@ -183,14 +231,28 @@ static const struct reg_field phy_pma_pll_raw_ctrl = static const struct reg_field phy_reset_ctrl = REG_FIELD(PHY_RESET, 8, 8); -static const struct of_device_id cdns_torrent_phy_of_match[]; +enum cdns_torrent_phy_type { + TYPE_NONE, + TYPE_DP, + TYPE_PCIE, + TYPE_SGMII, + TYPE_QSGMII, + TYPE_USB, +}; + +enum cdns_torrent_ssc_mode { + NO_SSC, + EXTERNAL_SSC, + INTERNAL_SSC +}; struct cdns_torrent_inst { struct phy *phy; u32 mlane; - u32 phy_type; + 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 { @@ -198,11 +260,13 @@ struct cdns_torrent_phy { 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; unsigned long ref_clk_rate; struct cdns_torrent_inst phys[MAX_NUM_LANES]; int nsubnodes; + const struct cdns_torrent_data *init_data; struct regmap *regmap; struct regmap *regmap_common_cdb; struct regmap *regmap_phy_pcs_common_cdb; @@ -211,6 +275,7 @@ struct cdns_torrent_phy { struct regmap *regmap_rx_lane_cdb[MAX_NUM_LANES]; struct regmap *regmap_dptx_phy_reg; struct regmap_field *phy_pll_cfg; + 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; @@ -223,8 +288,8 @@ enum phy_powerstate { POWERSTATE_A3 = 3, }; +static int cdns_torrent_phy_init(struct phy *phy); static int cdns_torrent_dp_init(struct phy *phy); -static int cdns_torrent_dp_exit(struct phy *phy); static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes); static @@ -254,17 +319,38 @@ static int cdns_torrent_phy_on(struct phy *phy); static int cdns_torrent_phy_off(struct phy *phy); static const struct phy_ops cdns_torrent_phy_ops = { - .init = cdns_torrent_dp_init, - .exit = cdns_torrent_dp_exit, + .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, }; +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; + 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_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_torrent_vals *tx_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_torrent_vals *rx_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; }; struct cdns_regmap_cdb_context { @@ -331,21 +417,21 @@ static int cdns_regmap_dptx_read(void *context, unsigned int reg, .reg_read = cdns_regmap_read, \ } -static struct regmap_config cdns_torrent_tx_lane_cdb_config[] = { +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 struct regmap_config cdns_torrent_rx_lane_cdb_config[] = { +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 struct regmap_config cdns_torrent_common_cdb_config = { +static const struct regmap_config cdns_torrent_common_cdb_config = { .name = "torrent_common_cdb", .reg_stride = 1, .fast_io = true, @@ -353,7 +439,7 @@ static struct regmap_config cdns_torrent_common_cdb_config = { .reg_read = cdns_regmap_read, }; -static struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = { +static const struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = { .name = "torrent_phy_pcs_cmn_cdb", .reg_stride = 1, .fast_io = true, @@ -361,7 +447,7 @@ static struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = { .reg_read = cdns_regmap_read, }; -static struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = { +static const struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = { .name = "torrent_phy_pma_cmn_cdb", .reg_stride = 1, .fast_io = true, @@ -369,7 +455,7 @@ static struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = { .reg_read = cdns_regmap_read, }; -static struct regmap_config cdns_torrent_dptx_phy_config = { +static const struct regmap_config cdns_torrent_dptx_phy_config = { .name = "torrent_dptx_phy", .reg_stride = 1, .fast_io = true, @@ -848,19 +934,6 @@ static int cdns_torrent_dp_init(struct phy *phy) struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; - ret = clk_prepare_enable(cdns_phy->clk); - if (ret) { - dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); - return ret; - } - - cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk); - if (!(cdns_phy->ref_clk_rate)) { - dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); - clk_disable_unprepare(cdns_phy->clk); - return -EINVAL; - } - switch (cdns_phy->ref_clk_rate) { case REF_CLK_19_2MHz: case REF_CLK_25MHz: @@ -920,14 +993,6 @@ static int cdns_torrent_dp_init(struct phy *phy) return ret; } -static int cdns_torrent_dp_exit(struct phy *phy) -{ - struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); - - clk_disable_unprepare(cdns_phy->clk); - return 0; -} - static int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy) { @@ -1543,15 +1608,34 @@ 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; - /* Take the PHY out of reset */ - ret = reset_control_deassert(cdns_phy->phy_rst); - if (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; + } + + mdelay(10); - /* Take the PHY lane group out of reset */ - return reset_control_deassert(inst->lnk_rst); + return 0; } static int cdns_torrent_phy_off(struct phy *phy) @@ -1560,6 +1644,9 @@ static int cdns_torrent_phy_off(struct phy *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; @@ -1585,7 +1672,24 @@ static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, return devm_regmap_init(dev, NULL, ctx, config); } -static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy) +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; @@ -1600,6 +1704,14 @@ static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy) cdns_phy->phy_pll_cfg = 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"); @@ -1615,28 +1727,44 @@ static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy) } cdns_phy->phy_pma_pll_raw_ctrl = field; - 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); + 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->phy_reset_ctrl = field; + cdns_phy->regmap_dptx_phy_reg = regmap; return 0; } -static int cdns_regmap_init_torrent_dp(struct cdns_torrent_phy *cdns_phy, - void __iomem *sd_base, - void __iomem *base, - u8 block_offset_shift, - u8 reg_offset_shift) +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); @@ -1691,43 +1819,282 @@ static int cdns_regmap_init_torrent_dp(struct cdns_torrent_phy *cdns_phy, } cdns_phy->regmap_phy_pma_common_cdb = regmap; - 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); + 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; + 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; + + if (phy_type == TYPE_DP) + return cdns_torrent_dp_init(phy); + + /** + * 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[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[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[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); + } } - cdns_phy->regmap_dptx_phy_reg = regmap; + + return 0; +} + +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; + struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals; + enum cdns_torrent_phy_type phy_t1, phy_t2, tmp_phy_type; + 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]. + */ + tmp_phy_type = phy_t1; + phy_t1 = phy_t2; + phy_t2 = tmp_phy_type; + } + + 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[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[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[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 int cdns_torrent_phy_probe(struct platform_device *pdev) { - struct resource *regs; struct cdns_torrent_phy *cdns_phy; struct device *dev = &pdev->dev; struct phy_provider *phy_provider; - const struct of_device_id *match; - struct cdns_torrent_data *data; + const struct cdns_torrent_data *data; struct device_node *child; int ret, subnodes, node = 0, i; + u32 total_num_lanes = 0; + u8 init_dp_regmap = 0; + u32 phy_type; /* Get init data for this PHY */ - match = of_match_device(cdns_torrent_phy_of_match, dev); - if (!match) + data = of_device_get_match_data(dev); + if (!data) return -EINVAL; - data = (struct cdns_torrent_data *)match->data; - 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->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0); if (IS_ERR(cdns_phy->phy_rst)) { @@ -1736,14 +2103,20 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) return PTR_ERR(cdns_phy->phy_rst); } + cdns_phy->apb_rst = devm_reset_control_get_optional(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); + } + 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); } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs); + cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cdns_phy->sd_base)) return PTR_ERR(cdns_phy->sd_base); @@ -1751,14 +2124,39 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) if (subnodes == 0) { dev_err(dev, "No available link subnodes found\n"); return -EINVAL; - } else if (subnodes != 1) { - dev_err(dev, "Driver supports only one link subnode.\n"); + } + + ret = cdns_torrent_regmap_init(cdns_phy); + if (ret) + return ret; + + ret = cdns_torrent_regfield_init(cdns_phy); + if (ret) + return ret; + + ret = clk_prepare_enable(cdns_phy->clk); + if (ret) { + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); + return ret; + } + + cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk); + if (!(cdns_phy->ref_clk_rate)) { + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); + clk_disable_unprepare(cdns_phy->clk); return -EINVAL; } + /* 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)) { @@ -1776,27 +2174,57 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) goto put_child; } - if (cdns_phy->phys[node].mlane != 0) { - dev_err(dev, - "%s: Driver supports only lane-0 as master lane.\n", + 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; } - if (of_property_read_u32(child, "cdns,phy-type", - &cdns_phy->phys[node].phy_type)) { - dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n", + 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; } - cdns_phy->phys[node].num_lanes = DEFAULT_NUM_LANES; - of_property_read_u32(child, "cdns,num-lanes", - &cdns_phy->phys[node].num_lanes); + 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); + + gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops); + if (IS_ERR(gphy)) { + ret = PTR_ERR(gphy); + goto put_child; + } - if (cdns_phy->phys[node].phy_type == PHY_TYPE_DP) { + if (cdns_phy->phys[node].phy_type == TYPE_DP) { switch (cdns_phy->phys[node].num_lanes) { case 1: case 2: @@ -1833,30 +2261,34 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) } /* DPTX registers */ - regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); - cdns_phy->base = devm_ioremap_resource(&pdev->dev, - regs); + cdns_phy->base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(cdns_phy->base)) { ret = PTR_ERR(cdns_phy->base); goto put_child; } - gphy = devm_phy_create(dev, child, - &cdns_torrent_phy_ops); - if (IS_ERR(gphy)) { - ret = PTR_ERR(gphy); - 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_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n", cdns_phy->phys[node].num_lanes, cdns_phy->max_bit_rate / 1000, cdns_phy->max_bit_rate % 1000); - } else { - dev_err(dev, "Driver supports only PHY_TYPE_DP\n"); - ret = -ENOTSUPP; - goto put_child; + + 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]); @@ -1864,16 +2296,16 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) } cdns_phy->nsubnodes = node; - ret = cdns_regmap_init_torrent_dp(cdns_phy, cdns_phy->sd_base, - cdns_phy->base, - data->block_offset_shift, - data->reg_offset_shift); - if (ret) + if (total_num_lanes > MAX_NUM_LANES) { + dev_err(dev, "Invalid lane configuration\n"); goto put_lnk_rst; + } - ret = cdns_regfield_init(cdns_phy); - if (ret) - goto put_lnk_rst; + if (cdns_phy->nsubnodes > 1) { + 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)) { @@ -1889,6 +2321,8 @@ 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); return ret; } @@ -1898,22 +2332,1505 @@ static int cdns_torrent_phy_remove(struct platform_device *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); + return 0; } +/* 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}, + {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 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 usb_100_no_ssc_cmn_regs[] = { + {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_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 sgmii_100_no_ssc_cmn_regs[] = { + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL}, + {0x3700, CMN_DIAG_BIAS_OVRD1}, + {0x0008, CMN_TXPUCAL_TUNE}, + {0x0008, 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 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 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}, + {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}, + {0x3700, CMN_DIAG_BIAS_OVRD1}, + {0x0008, CMN_TXPUCAL_TUNE}, + {0x0008, 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 qsgmii_100_no_ssc_cmn_regs[] = { + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +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}, + {0x0003, DRV_DIAG_TX_DRV} +}; + +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 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}, + {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} +}; + +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}, + {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} +}; + +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}, + {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} +}; + +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[] = { + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL} +}; + +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_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_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 = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [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] = &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] = &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] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &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] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + }, + }, + .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 = { + [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_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_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 = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, + [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] = &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] = &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] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &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] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + [TYPE_QSGMII] = { + [NO_SSC] = &usb_100_no_ssc_cmn_vals, + [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, + [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, + }, + }, + }, + .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 = { + [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[] = { diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 0c4833da7be0..62d6d6849ad6 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,36 @@ 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 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) @@ -105,7 +170,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) 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 +189,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/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/intel/Kconfig b/drivers/phy/intel/Kconfig index 7b47682a4e0e..58ec695c92ec 100644 --- a/drivers/phy/intel/Kconfig +++ b/drivers/phy/intel/Kconfig @@ -1,9 +1,21 @@ # SPDX-License-Identifier: GPL-2.0 # -# Phy drivers for Intel Lightning Mountain(LGM) platform +# Phy drivers for Intel platforms # -config PHY_INTEL_COMBO - bool "Intel ComboPHY driver" +config PHY_INTEL_KEEMBAY_EMMC + tristate "Intel Keem Bay EMMC PHY driver" + depends on (OF && ARM64) || 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_LGM_COMBO + bool "Intel Lightning Mountain ComboPHY driver" depends on X86 || COMPILE_TEST depends on OF && HAS_IOMEM select MFD_SYSCON @@ -16,8 +28,8 @@ config PHY_INTEL_COMBO chipsets which provides PHYs for various controllers, EMAC, SATA and PCIe. -config PHY_INTEL_EMMC - tristate "Intel EMMC PHY driver" +config PHY_INTEL_LGM_EMMC + tristate "Intel Lightning Mountain EMMC PHY driver" depends on X86 || COMPILE_TEST select GENERIC_PHY help diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile index 233d530dadde..a5e0af5ccd75 100644 --- a/drivers/phy/intel/Makefile +++ b/drivers/phy/intel/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PHY_INTEL_COMBO) += phy-intel-combo.o -obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o +obj-$(CONFIG_PHY_INTEL_KEEMBAY_EMMC) += phy-intel-keembay-emmc.o +obj-$(CONFIG_PHY_INTEL_LGM_COMBO) += phy-intel-lgm-combo.o +obj-$(CONFIG_PHY_INTEL_LGM_EMMC) += phy-intel-lgm-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..eb7c635ed89a --- /dev/null +++ b/drivers/phy/intel/phy-intel-keembay-emmc.c @@ -0,0 +1,307 @@ +// 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; + + if (mhz < 50 || mhz > 200) + 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-combo.c b/drivers/phy/intel/phy-intel-lgm-combo.c index 360b1eb2ebd6..360b1eb2ebd6 100644 --- a/drivers/phy/intel/phy-intel-combo.c +++ b/drivers/phy/intel/phy-intel-lgm-combo.c 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/lantiq/phy-lantiq-rcu-usb2.c b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c index be09b1530ae6..a7d126192cf1 100644 --- a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c +++ b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c @@ -141,7 +141,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..22c5698123cf 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, diff --git a/drivers/phy/marvell/phy-pxa-28nm-hsic.c b/drivers/phy/marvell/phy-pxa-28nm-hsic.c index ae8370af59c0..31b43d2ee39a 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) diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c index 9fd881787fa6..a175ae915f02 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; } diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index dee757c957f2..50c5e9306e19 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -35,3 +35,10 @@ config PHY_MTK_XSPHY 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 && OF + select GENERIC_PHY + help + Support HDMI PHY for Mediatek SoCs. diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 08a8e6a97b1e..6325e38709ed 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -6,3 +6,8 @@ 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 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..b74c65a1762c --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunhui Dai <chunhui.dai@mediatek.com> + */ + +#include "phy-mtk-hdmi.h" + +#define HDMI_CON0 0x00 +#define RG_HDMITX_DRV_IBIAS 0 +#define RG_HDMITX_DRV_IBIAS_MASK (0x3f << 0) +#define RG_HDMITX_EN_SER 12 +#define RG_HDMITX_EN_SER_MASK (0x0f << 12) +#define RG_HDMITX_EN_SLDO 16 +#define RG_HDMITX_EN_SLDO_MASK (0x0f << 16) +#define RG_HDMITX_EN_PRED 20 +#define RG_HDMITX_EN_PRED_MASK (0x0f << 20) +#define RG_HDMITX_EN_IMP 24 +#define RG_HDMITX_EN_IMP_MASK (0x0f << 24) +#define RG_HDMITX_EN_DRV 28 +#define RG_HDMITX_EN_DRV_MASK (0x0f << 28) + +#define HDMI_CON1 0x04 +#define RG_HDMITX_PRED_IBIAS 18 +#define RG_HDMITX_PRED_IBIAS_MASK (0x0f << 18) +#define RG_HDMITX_PRED_IMP (0x01 << 22) +#define RG_HDMITX_DRV_IMP 26 +#define RG_HDMITX_DRV_IMP_MASK (0x3f << 26) + +#define HDMI_CON2 0x08 +#define RG_HDMITX_EN_TX_CKLDO (0x01 << 0) +#define RG_HDMITX_EN_TX_POSDIV (0x01 << 1) +#define RG_HDMITX_TX_POSDIV 3 +#define RG_HDMITX_TX_POSDIV_MASK (0x03 << 3) +#define RG_HDMITX_EN_MBIAS (0x01 << 6) +#define RG_HDMITX_MBIAS_LPF_EN (0x01 << 7) + +#define HDMI_CON4 0x10 +#define RG_HDMITX_RESERVE_MASK (0xffffffff << 0) + +#define HDMI_CON6 0x18 +#define RG_HTPLL_BR 0 +#define RG_HTPLL_BR_MASK (0x03 << 0) +#define RG_HTPLL_BC 2 +#define RG_HTPLL_BC_MASK (0x03 << 2) +#define RG_HTPLL_BP 4 +#define RG_HTPLL_BP_MASK (0x0f << 4) +#define RG_HTPLL_IR 8 +#define RG_HTPLL_IR_MASK (0x0f << 8) +#define RG_HTPLL_IC 12 +#define RG_HTPLL_IC_MASK (0x0f << 12) +#define RG_HTPLL_POSDIV 16 +#define RG_HTPLL_POSDIV_MASK (0x03 << 16) +#define RG_HTPLL_PREDIV 18 +#define RG_HTPLL_PREDIV_MASK (0x03 << 18) +#define RG_HTPLL_FBKSEL 20 +#define RG_HTPLL_FBKSEL_MASK (0x03 << 20) +#define RG_HTPLL_RLH_EN (0x01 << 22) +#define RG_HTPLL_FBKDIV 24 +#define RG_HTPLL_FBKDIV_MASK (0x7f << 24) +#define RG_HTPLL_EN (0x01 << 31) + +#define HDMI_CON7 0x1c +#define RG_HTPLL_AUTOK_EN (0x01 << 23) +#define RG_HTPLL_DIVEN 28 +#define RG_HTPLL_DIVEN_MASK (0x07 << 28) + +static int mtk_hdmi_pll_prepare(struct clk_hw *hw) +{ + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON7, RG_HTPLL_AUTOK_EN); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_MBIAS); + usleep_range(80, 100); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + usleep_range(80, 100); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, 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); + + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + usleep_range(80, 100); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_EN); + usleep_range(80, 100); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_MBIAS); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_hdmi_phy_clear_bits(hdmi_phy, 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); + u32 pos_div; + + if (rate <= 64000000) + pos_div = 3; + else if (rate <= 128000000) + pos_div = 2; + else + pos_div = 1; + + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_PREDIV_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_TX_POSDIV); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (0x1 << RG_HTPLL_IC), + RG_HTPLL_IC_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (0x1 << RG_HTPLL_IR), + RG_HTPLL_IR_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON2, (pos_div << RG_HDMITX_TX_POSDIV), + RG_HDMITX_TX_POSDIV_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (1 << RG_HTPLL_FBKSEL), + RG_HTPLL_FBKSEL_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (19 << RG_HTPLL_FBKDIV), + RG_HTPLL_FBKDIV_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON7, (0x2 << RG_HTPLL_DIVEN), + RG_HTPLL_DIVEN_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (0xc << RG_HTPLL_BP), + RG_HTPLL_BP_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (0x2 << RG_HTPLL_BC), + RG_HTPLL_BC_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, (0x1 << RG_HTPLL_BR), + RG_HTPLL_BR_MASK); + + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PRED_IMP); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, (0x3 << RG_HDMITX_PRED_IBIAS), + RG_HDMITX_PRED_IBIAS_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_IMP_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, (0x28 << RG_HDMITX_DRV_IMP), + RG_HDMITX_DRV_IMP_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4, 0x28, RG_HDMITX_RESERVE_MASK); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, (0xa << RG_HDMITX_DRV_IBIAS), + RG_HDMITX_DRV_IBIAS_MASK); + 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; + + val = (readl(hdmi_phy->regs + HDMI_CON6) + & RG_HTPLL_PREDIV_MASK) >> RG_HTPLL_PREDIV; + 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 = (readl(hdmi_phy->regs + HDMI_CON6) + & RG_HTPLL_FBKDIV_MASK) >> RG_HTPLL_FBKDIV; + out_rate *= (val + 1) * 2; + val = (readl(hdmi_phy->regs + HDMI_CON2) + & RG_HDMITX_TX_POSDIV_MASK); + out_rate >>= (val >> RG_HDMITX_TX_POSDIV); + + if (readl(hdmi_phy->regs + HDMI_CON2) & 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) +{ + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON7, RG_HTPLL_AUTOK_EN); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_MBIAS); + usleep_range(80, 100); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + usleep_range(80, 100); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + usleep_range(80, 100); +} + +static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) +{ + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_DRV_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_PRED_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SER_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_MBIAS_LPF_EN); + usleep_range(80, 100); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_EN_SLDO_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_TX_CKLDO); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_EN); + usleep_range(80, 100); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON2, RG_HDMITX_EN_MBIAS); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_POSDIV_MASK); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON6, RG_HTPLL_RLH_EN); + mtk_hdmi_phy_clear_bits(hdmi_phy, 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..6cdfdf5a698a --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt8173.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Jie Qiu <jie.qiu@mediatek.com> + */ + +#include "phy-mtk-hdmi.h" + +#define HDMI_CON0 0x00 +#define RG_HDMITX_PLL_EN BIT(31) +#define RG_HDMITX_PLL_FBKDIV (0x7f << 24) +#define PLL_FBKDIV_SHIFT 24 +#define RG_HDMITX_PLL_FBKSEL (0x3 << 22) +#define PLL_FBKSEL_SHIFT 22 +#define RG_HDMITX_PLL_PREDIV (0x3 << 20) +#define PREDIV_SHIFT 20 +#define RG_HDMITX_PLL_POSDIV (0x3 << 18) +#define POSDIV_SHIFT 18 +#define RG_HDMITX_PLL_RST_DLY (0x3 << 16) +#define RG_HDMITX_PLL_IR (0xf << 12) +#define PLL_IR_SHIFT 12 +#define RG_HDMITX_PLL_IC (0xf << 8) +#define PLL_IC_SHIFT 8 +#define RG_HDMITX_PLL_BP (0xf << 4) +#define PLL_BP_SHIFT 4 +#define RG_HDMITX_PLL_BR (0x3 << 2) +#define PLL_BR_SHIFT 2 +#define RG_HDMITX_PLL_BC (0x3 << 0) +#define PLL_BC_SHIFT 0 +#define HDMI_CON1 0x04 +#define RG_HDMITX_PLL_DIVEN (0x7 << 29) +#define PLL_DIVEN_SHIFT 29 +#define RG_HDMITX_PLL_AUTOK_EN BIT(28) +#define RG_HDMITX_PLL_AUTOK_KF (0x3 << 26) +#define RG_HDMITX_PLL_AUTOK_KS (0x3 << 24) +#define RG_HDMITX_PLL_AUTOK_LOAD BIT(23) +#define RG_HDMITX_PLL_BAND (0x3f << 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 (0x3 << 10) +#define PLL_TXDIV_SHIFT 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 (0xf << 0) +#define HDMI_CON2 0x08 +#define RGS_HDMITX_PLL_AUTOK_BAND (0x7f << 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 (0xf << 28) +#define RG_HDMITX_PRD_EN (0xf << 24) +#define RG_HDMITX_PRD_IMP_EN (0xf << 20) +#define RG_HDMITX_DRV_EN (0xf << 16) +#define RG_HDMITX_DRV_IMP_EN (0xf << 12) +#define DRV_IMP_EN_SHIFT 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 (0xf << 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 (0xf << 24) +#define RG_HDMITX_PRD_IBIAS_D2 (0xf << 16) +#define RG_HDMITX_PRD_IBIAS_D1 (0xf << 8) +#define RG_HDMITX_PRD_IBIAS_D0 (0xf << 0) +#define PRD_IBIAS_CLK_SHIFT 24 +#define PRD_IBIAS_D2_SHIFT 16 +#define PRD_IBIAS_D1_SHIFT 8 +#define PRD_IBIAS_D0_SHIFT 0 +#define HDMI_CON5 0x14 +#define RG_HDMITX_DRV_IBIAS_CLK (0x3f << 24) +#define RG_HDMITX_DRV_IBIAS_D2 (0x3f << 16) +#define RG_HDMITX_DRV_IBIAS_D1 (0x3f << 8) +#define RG_HDMITX_DRV_IBIAS_D0 (0x3f << 0) +#define DRV_IBIAS_CLK_SHIFT 24 +#define DRV_IBIAS_D2_SHIFT 16 +#define DRV_IBIAS_D1_SHIFT 8 +#define DRV_IBIAS_D0_SHIFT 0 +#define HDMI_CON6 0x18 +#define RG_HDMITX_DRV_IMP_CLK (0x3f << 24) +#define RG_HDMITX_DRV_IMP_D2 (0x3f << 16) +#define RG_HDMITX_DRV_IMP_D1 (0x3f << 8) +#define RG_HDMITX_DRV_IMP_D0 (0x3f << 0) +#define DRV_IMP_CLK_SHIFT 24 +#define DRV_IMP_D2_SHIFT 16 +#define DRV_IMP_D1_SHIFT 8 +#define DRV_IMP_D0_SHIFT 0 +#define HDMI_CON7 0x1c +#define RG_HDMITX_MHLCK_DRV_IBIAS (0x1f << 27) +#define RG_HDMITX_SER_DIN (0x3ff << 16) +#define RG_HDMITX_CHLDC_TST (0xf << 12) +#define RG_HDMITX_CHLCK_TST (0xf << 8) +#define RG_HDMITX_RESERVE (0xff << 0) +#define HDMI_CON8 0x20 +#define RGS_HDMITX_2T1_LEV (0xf << 16) +#define RGS_HDMITX_2T1_EDG (0xf << 12) +#define RGS_HDMITX_5T1_LEV (0xf << 8) +#define RGS_HDMITX_5T1_EDG (0xf << 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); + + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_MHLCK_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN); + usleep_range(100, 150); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN); + usleep_range(100, 150); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN); + mtk_hdmi_phy_set_bits(hdmi_phy, 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); + + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN); + usleep_range(100, 150); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN); + usleep_range(100, 150); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN); + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV); + mtk_hdmi_phy_clear_bits(hdmi_phy, 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); + 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_hdmi_phy_mask(hdmi_phy, HDMI_CON0, + (pre_div << PREDIV_SHIFT), RG_HDMITX_PLL_PREDIV); + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, + (0x1 << PLL_IC_SHIFT) | (0x1 << PLL_IR_SHIFT), + RG_HDMITX_PLL_IC | RG_HDMITX_PLL_IR); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, + (div << PLL_TXDIV_SHIFT), RG_HDMITX_PLL_TXDIV); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, + (0x1 << PLL_FBKSEL_SHIFT) | (19 << PLL_FBKDIV_SHIFT), + RG_HDMITX_PLL_FBKSEL | RG_HDMITX_PLL_FBKDIV); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, + (0x2 << PLL_DIVEN_SHIFT), RG_HDMITX_PLL_DIVEN); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, + (0xc << PLL_BP_SHIFT) | (0x2 << PLL_BC_SHIFT) | + (0x1 << PLL_BR_SHIFT), + RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC | + RG_HDMITX_PLL_BR); + if (rate < 165000000) { + mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, + RG_HDMITX_PRD_IMP_EN); + pre_ibias = 0x3; + imp_en = 0x0; + hdmi_ibias = hdmi_phy->ibias; + } else { + mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON3, + RG_HDMITX_PRD_IMP_EN); + pre_ibias = 0x6; + imp_en = 0xf; + hdmi_ibias = hdmi_phy->ibias_up; + } + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4, + (pre_ibias << PRD_IBIAS_CLK_SHIFT) | + (pre_ibias << PRD_IBIAS_D2_SHIFT) | + (pre_ibias << PRD_IBIAS_D1_SHIFT) | + (pre_ibias << PRD_IBIAS_D0_SHIFT), + RG_HDMITX_PRD_IBIAS_CLK | + RG_HDMITX_PRD_IBIAS_D2 | + RG_HDMITX_PRD_IBIAS_D1 | + RG_HDMITX_PRD_IBIAS_D0); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, + (imp_en << DRV_IMP_EN_SHIFT), + RG_HDMITX_DRV_IMP_EN); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, + (hdmi_phy->drv_imp_clk << DRV_IMP_CLK_SHIFT) | + (hdmi_phy->drv_imp_d2 << DRV_IMP_D2_SHIFT) | + (hdmi_phy->drv_imp_d1 << DRV_IMP_D1_SHIFT) | + (hdmi_phy->drv_imp_d0 << DRV_IMP_D0_SHIFT), + RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 | + RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON5, + (hdmi_ibias << DRV_IBIAS_CLK_SHIFT) | + (hdmi_ibias << DRV_IBIAS_D2_SHIFT) | + (hdmi_ibias << DRV_IBIAS_D1_SHIFT) | + (hdmi_ibias << DRV_IBIAS_D0_SHIFT), + RG_HDMITX_DRV_IBIAS_CLK | + RG_HDMITX_DRV_IBIAS_D2 | + RG_HDMITX_DRV_IBIAS_D1 | + RG_HDMITX_DRV_IBIAS_D0); + 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_hdmi_phy_set_bits(hdmi_phy, 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_hdmi_phy_clear_bits(hdmi_phy, 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..47c029d4b270 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi.c @@ -0,0 +1,214 @@ +// 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, +}; + +void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits) +{ + void __iomem *reg = hdmi_phy->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp &= ~bits; + writel(tmp, reg); +} + +void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits) +{ + void __iomem *reg = hdmi_phy->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp |= bits; + writel(tmp, reg); +} + +void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 val, u32 mask) +{ + void __iomem *reg = hdmi_phy->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp = (tmp & ~mask) | (val & mask); + writel(tmp, reg); +} + +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; + + 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 resource *mem; + 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; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi_phy->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(hdmi_phy->regs)) { + ret = PTR_ERR(hdmi_phy->regs); + dev_err(dev, "Failed to get memory resource: %d\n", ret); + return ret; + } + + ref_clk = devm_clk_get(dev, "pll_ref"); + if (IS_ERR(ref_clk)) { + ret = PTR_ERR(ref_clk); + dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n", + ret); + return ret; + } + 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) { + dev_err(dev, "Failed to read clock-output-names: %d\n", ret); + return ret; + } + + 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)) { + ret = PTR_ERR(hdmi_phy->pll); + dev_err(dev, "Failed to register PLL: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,ibias", + &hdmi_phy->ibias); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", + &hdmi_phy->ibias_up); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret); + return ret; + } + + 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)) { + dev_err(dev, "Failed to create HDMI PHY\n"); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, hdmi_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "Failed to register HDMI PHY\n"); + return PTR_ERR(phy_provider); + } + + 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, + }, + {}, +}; + +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..dcf9bb13699b --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi.h @@ -0,0 +1,56 @@ +/* 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/io.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; +}; + +void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits); +void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits); +void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 val, u32 mask); +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/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/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c index febe0aef68d4..ce91ae7f8dbd 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; diff --git a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c index b8ef331e1545..fc7f9df80a7b 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c @@ -48,7 +48,7 @@ static int ipq4019_ss_phy_power_on(struct phy *_phy) return 0; } -static struct phy_ops ipq4019_usb_ss_phy_ops = { +static const struct phy_ops ipq4019_usb_ss_phy_ops = { .power_on = ipq4019_ss_phy_power_on, .power_off = ipq4019_ss_phy_power_off, }; @@ -80,7 +80,7 @@ static int ipq4019_hs_phy_power_on(struct phy *_phy) return 0; } -static struct phy_ops ipq4019_usb_hs_phy_ops = { +static const struct phy_ops ipq4019_usb_hs_phy_ops = { .power_on = ipq4019_hs_phy_power_on, .power_off = ipq4019_hs_phy_power_off, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 6e6f992a9524..5d33ad4d06f2 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -946,6 +946,88 @@ static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = { 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), @@ -1761,6 +1843,16 @@ struct qmp_phy_cfg { const struct qmp_phy_init_tbl *pcs_misc_tbl; int pcs_misc_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; + /* clock ids to be requested */ const char * const *clk_list; int num_clks; @@ -1797,10 +1889,17 @@ struct qmp_phy_cfg { bool no_pcs_sw_reset; }; +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 @@ -1811,9 +1910,12 @@ struct qmp_phy_cfg { * @index: lane index * @qmp: QMP phy to which this lane belongs * @lane_rst: lane's reset controller + * @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; @@ -1824,43 +1926,45 @@ struct qmp_phy { unsigned int index; struct qcom_qmp *qmp; struct reset_control *lane_rst; + 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 - * @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; }; @@ -2203,6 +2307,41 @@ static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sc7180_dpphy_cfg = { + .type = PHY_TYPE_DP, + .nlanes = 1, + + .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), + + .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, + + .has_phy_dp_com_ctrl = true, + .is_dual_lane_phy = true, +}; + +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 qmp_v3_usb3_uniphy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -2479,11 +2618,300 @@ static void qcom_qmp_phy_configure(void __iomem *base, qcom_qmp_phy_configure_lane(base, regs, tbl, num, 0xff); } +static int qcom_qmp_phy_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 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; + int ret; + + qcom_qmp_phy_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num); + + if (cfg->type == PHY_TYPE_DP) { + switch (dp_opts->link_rate) { + case 1620: + qcom_qmp_phy_configure(serdes, cfg->regs, + cfg->serdes_tbl_rbr, + cfg->serdes_tbl_rbr_num); + break; + case 2700: + qcom_qmp_phy_configure(serdes, cfg->regs, + cfg->serdes_tbl_hbr, + cfg->serdes_tbl_hbr_num); + break; + case 5400: + qcom_qmp_phy_configure(serdes, cfg->regs, + cfg->serdes_tbl_hbr2, + cfg->serdes_tbl_hbr2_num); + break; + case 8100: + qcom_qmp_phy_configure(serdes, cfg->regs, + cfg->serdes_tbl_hbr3, + cfg->serdes_tbl_hbr3_num); + break; + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + } + + + 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"); + return ret; + } + } + + return 0; +} + +static void qcom_qmp_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_V3_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_V3_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_V3_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_V3_DP_PHY_AUX_CFG0); + writel(0x13, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1); + writel(0x24, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2); + writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG3); + writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG4); + writel(0x26, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG5); + writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG6); + writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG7); + writel(0xbb, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG8); + writel(0x03, qphy->pcs + QSERDES_V3_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 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 void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy) +{ + const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; + unsigned int v_level = 0, p_level = 0; + u32 bias_en, drvr_en; + 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->lanes == 1) { + bias_en = 0x3e; + drvr_en = 0x13; + } else { + bias_en = 0x3f; + drvr_en = 0x10; + } + + voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr_rbr[v_level][p_level]; + pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr_rbr[v_level][p_level]; + + /* TODO: Move check to config check */ + if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF) + return; + + /* 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 + QSERDES_V3_TX_TX_DRV_LVL); + writel(pre_emphasis_cfg, qphy->tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); + writel(voltage_swing_cfg, qphy->tx2 + QSERDES_V3_TX_TX_DRV_LVL); + writel(pre_emphasis_cfg, qphy->tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); + + 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 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); + + memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts)); + if (qphy->dp_opts.set_voltages) { + qcom_qmp_phy_configure_dp_tx(qphy); + qphy->dp_opts.set_voltages = 0; + } + + return 0; +} + +static int qcom_qmp_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 val, phy_vco_div, status; + unsigned long pixel_freq; + + 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_V3_DP_PHY_PD_CTL); + + writel(0x5c, qphy->pcs + QSERDES_V3_DP_PHY_MODE); + 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_V3_DP_PHY_AUX_CFG2); + writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG); + writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_CFG); + writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG); + writel(0x09, qphy->pcs + QSERDES_V3_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_V3_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_V3_DP_PHY_CFG); + udelay(2000); + writel(0x19, qphy->pcs + QSERDES_V3_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_dp_phy_calibrate(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + 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_V3_DP_PHY_AUX_CFG1); + + return 0; +} + 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; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; void __iomem *pcs = qphy->pcs; void __iomem *dp_com = qmp->dp_com; int ret, i; @@ -2514,7 +2942,7 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) ret = reset_control_deassert(qmp->resets[i]); if (ret) { dev_err(qmp->dev, "%s reset deassert failed\n", - qmp->cfg->reset_list[i]); + qphy->cfg->reset_list[i]); goto err_rst; } } @@ -2533,6 +2961,9 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) 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); @@ -2540,6 +2971,9 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) 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->has_phy_com_ctrl) { @@ -2555,36 +2989,10 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) 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]); @@ -2596,10 +3004,11 @@ err_reg_enable: return ret; } -static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) +static int qcom_qmp_phy_com_exit(struct qmp_phy *qphy) { - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *serdes = qmp->serdes; + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qphy->cfg; + void __iomem *serdes = qphy->serdes; int i = cfg->num_resets; mutex_lock(&qmp->phy_mutex); @@ -2630,20 +3039,12 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) return 0; } -static int qcom_qmp_phy_enable(struct phy *phy) +static int qcom_qmp_phy_init(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 *pcs_misc = qphy->pcs_misc; - void __iomem *dp_com = qmp->dp_com; - void __iomem *status; - unsigned int mask, val, ready; + const struct qmp_phy_cfg *cfg = qphy->cfg; int ret; - dev_vdbg(qmp->dev, "Initializing QMP phy\n"); if (cfg->no_pcs_sw_reset) { @@ -2670,13 +3071,34 @@ static int qcom_qmp_phy_enable(struct phy *phy) ret = reset_control_assert(qmp->ufs_reset); if (ret) - goto err_lane_rst; + return ret; } ret = qcom_qmp_phy_com_init(qphy); if (ret) return ret; + if (cfg->type == PHY_TYPE_DP) + qcom_qmp_phy_dp_aux_init(qphy); + + return 0; +} + +static int qcom_qmp_phy_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; + + qcom_qmp_phy_serdes_init(qphy); + if (cfg->has_lane_rst) { ret = reset_control_deassert(qphy->lane_rst); if (ret) { @@ -2700,13 +3122,23 @@ static int qcom_qmp_phy_enable(struct phy *phy) qcom_qmp_phy_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) + qcom_qmp_phy_configure_dp_tx(qphy); + qcom_qmp_phy_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1); + if (cfg->is_dual_lane_phy) qcom_qmp_phy_configure_lane(qphy->rx2, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 2); - qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + /* Configure link rate, swing, etc. */ + if (cfg->type == PHY_TYPE_DP) + qcom_qmp_phy_configure_dp_phy(qphy); + else + 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; @@ -2724,102 +3156,129 @@ static int qcom_qmp_phy_enable(struct phy *phy) 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; - } + if (cfg->type != PHY_TYPE_DP) { + /* 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); + + 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; + 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) +static int qcom_qmp_phy_power_off(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; + const struct qmp_phy_cfg *cfg = qphy->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); + if (cfg->type == PHY_TYPE_DP) { + /* Assert DP PHY power down */ + writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL); + } else { + /* 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); + /* 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_POWER_DOWN_CONTROL, - cfg->pwrdn_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_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } } + return 0; +} + +static int qcom_qmp_phy_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qphy->cfg; + if (cfg->has_lane_rst) reset_control_assert(qphy->lane_rst); - qcom_qmp_phy_com_exit(qmp); - - qmp->phy_initialized = false; + qcom_qmp_phy_com_exit(qphy); return 0; } +static int qcom_qmp_phy_enable(struct phy *phy) +{ + int ret; + + ret = qcom_qmp_phy_init(phy); + if (ret) + return ret; + + ret = qcom_qmp_phy_power_on(phy); + if (ret) + qcom_qmp_phy_exit(phy); + + return ret; +} + +static int qcom_qmp_phy_disable(struct phy *phy) +{ + int ret; + + ret = qcom_qmp_phy_power_off(phy); + if (ret) + return ret; + return qcom_qmp_phy_exit(phy); +} + 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; + qphy->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; + const struct qmp_phy_cfg *cfg = qphy->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) + 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; @@ -2842,8 +3301,7 @@ static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy) 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; + const struct qmp_phy_cfg *cfg = qphy->cfg; void __iomem *pcs = qphy->pcs; void __iomem *pcs_misc = qphy->pcs_misc; @@ -2863,15 +3321,15 @@ 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; + const struct qmp_phy_cfg *cfg = qphy->cfg; - dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); + dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qphy->mode); - /* Supported only for USB3 PHY */ + /* Supported only for USB3 PHY and luckily USB3 is the first phy */ if (cfg->type != PHY_TYPE_USB3) return 0; - if (!qmp->phy_initialized) { + if (!qmp->init_count) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); return 0; } @@ -2888,16 +3346,16 @@ 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; + const struct qmp_phy_cfg *cfg = qphy->cfg; int ret = 0; - dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); + dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qphy->mode); - /* Supported only for USB3 PHY */ + /* Supported only for USB3 PHY and luckily USB3 is the first phy */ if (cfg->type != PHY_TYPE_USB3) return 0; - if (!qmp->phy_initialized) { + if (!qmp->init_count) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); return 0; } @@ -2920,10 +3378,10 @@ static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev) return 0; } -static int qcom_qmp_phy_vreg_init(struct device *dev) +static int qcom_qmp_phy_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) { struct qcom_qmp *qmp = dev_get_drvdata(dev); - int num = qmp->cfg->num_vregs; + int num = cfg->num_vregs; int i; qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); @@ -2931,24 +3389,24 @@ static int qcom_qmp_phy_vreg_init(struct device *dev) return -ENOMEM; for (i = 0; i < num; i++) - qmp->vregs[i].supply = qmp->cfg->vreg_list[i]; + qmp->vregs[i].supply = cfg->vreg_list[i]; return devm_regulator_bulk_get(dev, num, qmp->vregs); } -static int qcom_qmp_phy_reset_init(struct device *dev) +static int qcom_qmp_phy_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg) { struct qcom_qmp *qmp = dev_get_drvdata(dev); int i; - qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets, + qmp->resets = devm_kcalloc(dev, cfg->num_resets, sizeof(*qmp->resets), GFP_KERNEL); if (!qmp->resets) return -ENOMEM; - for (i = 0; i < qmp->cfg->num_resets; i++) { + for (i = 0; i < cfg->num_resets; i++) { struct reset_control *rst; - const char *name = qmp->cfg->reset_list[i]; + const char *name = cfg->reset_list[i]; rst = devm_reset_control_get(dev, name); if (IS_ERR(rst)) { @@ -2961,10 +3419,10 @@ static int qcom_qmp_phy_reset_init(struct device *dev) return 0; } -static int qcom_qmp_phy_clk_init(struct device *dev) +static int qcom_qmp_phy_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) { struct qcom_qmp *qmp = dev_get_drvdata(dev); - int num = qmp->cfg->num_clks; + int num = cfg->num_clks; int i; qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); @@ -2972,12 +3430,12 @@ static int qcom_qmp_phy_clk_init(struct device *dev) return -ENOMEM; for (i = 0; i < num; i++) - qmp->clks[i].id = qmp->cfg->clk_list[i]; + qmp->clks[i].id = cfg->clk_list[i]; return devm_clk_bulk_get(dev, num, qmp->clks); } -static void phy_pipe_clk_release_provider(void *res) +static void phy_clk_release_provider(void *res) { of_clk_del_provider(res); } @@ -3006,12 +3464,6 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) 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); @@ -3040,9 +3492,202 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) * 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); + ret = devm_add_action(qmp->dev, phy_clk_release_provider, np); + if (ret) + phy_clk_release_provider(np); + + return ret; +} + +/* + * 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; + 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; + + init.ops = &qcom_qmp_dp_link_clk_ops; + init.name = "qmp_dp_phy_pll_link_clk"; + dp_clks->dp_link_hw.init = &init; + ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_link_hw); + if (ret) + return ret; + + init.ops = &qcom_qmp_dp_pixel_clk_ops; + init.name = "qmp_dp_phy_pll_vco_div_clk"; + 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. + */ + ret = devm_add_action(qmp->dev, phy_clk_release_provider, np); if (ret) - phy_pipe_clk_release_provider(np); + phy_clk_release_provider(np); return ret; } @@ -3054,6 +3699,17 @@ static const struct phy_ops qcom_qmp_phy_gen_ops = { .owner = THIS_MODULE, }; +static const struct phy_ops qcom_qmp_phy_dp_ops = { + .init = qcom_qmp_phy_init, + .configure = qcom_qmp_dp_phy_configure, + .power_on = qcom_qmp_phy_power_on, + .calibrate = qcom_qmp_dp_phy_calibrate, + .power_off = qcom_qmp_phy_power_off, + .exit = qcom_qmp_phy_exit, + .set_mode = qcom_qmp_phy_set_mode, + .owner = THIS_MODULE, +}; + static const struct phy_ops qcom_qmp_pcie_ufs_ops = { .power_on = qcom_qmp_phy_enable, .power_off = qcom_qmp_phy_disable, @@ -3062,12 +3718,13 @@ static const struct phy_ops qcom_qmp_pcie_ufs_ops = { }; static -int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) +int qcom_qmp_phy_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 = &qcom_qmp_phy_gen_ops; + const struct phy_ops *ops; char prop_name[MAX_PROP_NAME]; int ret; @@ -3075,6 +3732,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) 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. @@ -3099,7 +3758,7 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) * 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) { + if (cfg->is_dual_lane_phy) { qphy->tx2 = of_iomap(np, 3); qphy->rx2 = of_iomap(np, 4); if (!qphy->tx2 || !qphy->rx2) { @@ -3132,8 +3791,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) 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) { + if (cfg->type == PHY_TYPE_PCIE || + cfg->type == PHY_TYPE_USB3) { ret = PTR_ERR(qphy->pipe_clk); if (ret != -EPROBE_DEFER) dev_err(dev, @@ -3145,7 +3804,7 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) } /* Get lane reset, if any */ - if (qmp->cfg->has_lane_rst) { + if (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)) { @@ -3154,8 +3813,12 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) } } - if (qmp->cfg->type == PHY_TYPE_UFS || qmp->cfg->type == PHY_TYPE_PCIE) + if (cfg->type == PHY_TYPE_UFS || cfg->type == PHY_TYPE_PCIE) ops = &qcom_qmp_pcie_ufs_ops; + else if (cfg->type == PHY_TYPE_DP) + ops = &qcom_qmp_phy_dp_ops; + else + ops = &qcom_qmp_phy_gen_ops; generic_phy = devm_phy_create(dev, np, ops); if (IS_ERR(generic_phy)) { @@ -3199,6 +3862,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,sc7180-qmp-usb3-phy", .data = &sc7180_usb3phy_cfg, }, { + .compatible = "qcom,sc7180-qmp-usb3-dp-phy", + /* It's a combo phy */ + }, { .compatible = "qcom,sdm845-qhp-pcie-phy", .data = &sdm845_qhp_pciephy_cfg, }, { @@ -3239,6 +3905,14 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); +static const struct of_device_id qcom_qmp_combo_phy_of_match_table[] = { + { + .compatible = "qcom,sc7180-qmp-usb3-dp-phy", + .data = &sc7180_usb3dpphy_cfg, + }, + { } +}; + 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) @@ -3248,11 +3922,16 @@ 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; + void __iomem *serdes; + void __iomem *usb_serdes; + void __iomem *dp_serdes; + 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); @@ -3263,40 +3942,57 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) 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; + cfg = of_device_get_match_data(dev); + if (!cfg) { + const struct of_device_id *match; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + match = of_match_device(qcom_qmp_combo_phy_of_match_table, dev); + if (!match) + return -EINVAL; + + combo_cfg = match->data; + 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 */ - qmp->serdes = base; + usb_serdes = 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 (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; + if (combo_cfg || 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); + } + + if (combo_cfg) { + /* 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; + } else { + expected_phys = cfg->nlanes; } mutex_init(&qmp->phy_mutex); - ret = qcom_qmp_phy_clk_init(dev); + ret = qcom_qmp_phy_clk_init(dev, cfg); if (ret) return ret; - ret = qcom_qmp_phy_reset_init(dev); + ret = qcom_qmp_phy_reset_init(dev, cfg); if (ret) return ret; - ret = qcom_qmp_phy_vreg_init(dev); + ret = qcom_qmp_phy_vreg_init(dev, cfg); if (ret) { if (ret != -EPROBE_DEFER) dev_err(dev, "failed to get regulator supplies: %d\n", @@ -3306,14 +4002,13 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) num = of_get_available_child_count(dev->of_node); /* do we have a rogue child node ? */ - if (num > qmp->cfg->nlanes) + if (num > expected_phys) 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); /* @@ -3322,9 +4017,18 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) */ 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; + } else if (of_node_name_eq(child, "usb3-phy")) { + cfg = usb_cfg; + serdes = usb_serdes; + } + /* Create per-lane phy */ - ret = qcom_qmp_phy_create(dev, child, id); + ret = qcom_qmp_phy_create(dev, child, id, serdes, cfg); if (ret) { dev_err(dev, "failed to create lane%d phy, %d\n", id, ret); @@ -3335,11 +4039,20 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) * 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; + if (cfg->type == PHY_TYPE_USB3 || cfg->type == PHY_TYPE_PCIE) { + ret = phy_pipe_clk_register(qmp, child); + if (ret) { + dev_err(qmp->dev, + "failed to register pipe clock source\n"); + goto err_node_put; + } + } else if (cfg->type == PHY_TYPE_DP) { + 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; + } } id++; } diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 904b80ab9009..b7c530088a6c 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -137,6 +137,9 @@ #define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c /* 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 @@ -146,6 +149,13 @@ #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_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 #define QSERDES_V3_COM_CLK_ENABLE1 0x038 #define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c #define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040 @@ -207,12 +217,36 @@ #define QSERDES_V3_COM_CMN_MODE 0x184 /* 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 DP_PHY_TXn_TX_EMP_POST1_LVL_MASK 0x001f +# define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN 0x0020 + +#define QSERDES_V3_TX_TX_DRV_LVL 0x01c +# define DP_PHY_TXn_TX_DRV_LVL_MASK 0x001f +# define DP_PHY_TXn_TX_DRV_LVL_MUX_EN 0x0020 + +#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_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 @@ -315,6 +349,52 @@ #define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c #define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60 +/* Only for QMP V3 PHY - DP PHY registers */ +#define QSERDES_V3_DP_PHY_REVISION_ID0 0x000 +#define QSERDES_V3_DP_PHY_REVISION_ID1 0x004 +#define QSERDES_V3_DP_PHY_REVISION_ID2 0x008 +#define QSERDES_V3_DP_PHY_REVISION_ID3 0x00c +#define QSERDES_V3_DP_PHY_CFG 0x010 +#define QSERDES_V3_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_V3_DP_PHY_MODE 0x01c +#define QSERDES_V3_DP_PHY_AUX_CFG0 0x020 +#define QSERDES_V3_DP_PHY_AUX_CFG1 0x024 +#define QSERDES_V3_DP_PHY_AUX_CFG2 0x028 +#define QSERDES_V3_DP_PHY_AUX_CFG3 0x02c +#define QSERDES_V3_DP_PHY_AUX_CFG4 0x030 +#define QSERDES_V3_DP_PHY_AUX_CFG5 0x034 +#define QSERDES_V3_DP_PHY_AUX_CFG6 0x038 +#define QSERDES_V3_DP_PHY_AUX_CFG7 0x03c +#define QSERDES_V3_DP_PHY_AUX_CFG8 0x040 +#define QSERDES_V3_DP_PHY_AUX_CFG9 0x044 + +#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 - QSERDES COM registers */ #define QSERDES_V4_COM_SSC_EN_CENTER 0x010 #define QSERDES_V4_COM_SSC_PER1 0x01c diff --git a/drivers/phy/ralink/phy-ralink-usb.c b/drivers/phy/ralink/phy-ralink-usb.c index ba3c197fc5b0..95dfa9fd284d 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, diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 0824b9dd5683..c2f22f90736c 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 diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index 9f59a81e4e0d..c3cfc7f0af5c 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -1,5 +1,6 @@ # 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_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.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..4df9476ef2a9 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c @@ -0,0 +1,389 @@ +// 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; + const struct of_device_id *of_id; + 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; + } + + of_id = of_match_device(rk_dphy_dt_ids, dev); + if (!of_id) + return -EINVAL; + + drv_data = of_id->data; + 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/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 0d818b77a0d8..cfa9b8b7e5ac 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -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> @@ -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; diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c index 9832599a0283..dd9ab1519d83 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.c +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -268,7 +268,7 @@ static int samsung_ufs_phy_exit(struct phy *phy) return 0; } -static struct phy_ops samsung_ufs_phy_ops = { +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, diff --git a/drivers/phy/socionext/Kconfig b/drivers/phy/socionext/Kconfig index 8c9d7c37536a..a3970e0f89da 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 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..7427c40bf4ae --- /dev/null +++ b/drivers/phy/socionext/phy-uniphier-ahci.c @@ -0,0 +1,321 @@ +// 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; + struct reset_control *rst, *rst_parent; + 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_ready_high; + bool is_phy_clk; +}; + +/* 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 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); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rst_parent); + if (ret) + goto out_clk_disable; + + 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_clk_disable: + clk_disable_unprepare(priv->clk_parent); + + 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); + clk_disable_unprepare(priv->clk_parent); + + 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); + + 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_pxs2_data = { + .power_on = uniphier_ahciphy_pxs2_power_on, + .power_off = uniphier_ahciphy_pxs2_power_off, + .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_ready_high = true, + .is_phy_clk = true, +}; + +static const struct of_device_id uniphier_ahciphy_match[] = { + { + .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/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c index a174b3c3f010..2ff56ce77b30 100644 --- a/drivers/phy/ti/phy-am654-serdes.c +++ b/drivers/phy/ti/phy-am654-serdes.c @@ -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 @@ -80,27 +103,136 @@ static const struct regmap_config serdes_am654_regmap_config = { .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; @@ -116,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) @@ -129,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; } @@ -187,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) @@ -286,19 +416,37 @@ static int serdes_am654_usb3_init(struct serdes_am654 *phy) static int serdes_am654_pcie_init(struct serdes_am654 *phy) { - int ret; + int ret = 0; - ret = regmap_field_write(phy->config_version, VERSION); - if (ret) - return ret; + 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->cmu_master_cdn_o, 0x1); if (ret) - return ret; - - ret = regmap_field_write(phy->l1_master_cdn_o, 0x1); - if (ret) - return ret; + return -EIO; return 0; } @@ -320,20 +468,19 @@ static int serdes_am654_init(struct phy *x) static int serdes_am654_reset(struct phy *x) { struct serdes_am654 *phy = phy_get_drvdata(x); - int ret; + int ret = 0; serdes_am654_disable_pll(phy); serdes_am654_disable_txrx(phy); - ret = regmap_field_write(phy->por_en, 0x1); - if (ret) - return ret; + 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; } @@ -587,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; @@ -725,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); diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 7edd5c3bc536..5fd2e8a08bfc 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> @@ -22,7 +23,7 @@ #define AM33XX_GMII_SEL_MODE_RGMII 2 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 +42,7 @@ struct phy_gmii_sel_soc_data { u32 num_ports; u32 features; const struct reg_field (*regfields)[PHY_GMII_SEL_LAST]; + bool use_of_data; }; struct phy_gmii_sel_priv { @@ -49,6 +51,8 @@ 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; }; static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) @@ -147,13 +151,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), }, }; @@ -172,16 +172,19 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = { static const struct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = { - { - [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4040, 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(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 = { - .num_ports = 1, + .use_of_data = true, .regfields = phy_gmii_sel_fields_am654, }; @@ -228,7 +231,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); @@ -242,68 +245,97 @@ 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; + const struct reg_field *fields; + struct regmap_field *regfield; + struct reg_field field; + int ret; + + 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); + } + + 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); + } + + 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); + + return 0; +} + +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; - int i, num_ports, ret; + struct device *dev = priv->dev; + int i, ret; - num_ports = priv->soc_data->num_ports; + if (soc_data->use_of_data) { + const __be32 *offset; + u64 size; - if_phys = devm_kcalloc(priv->dev, num_ports, + offset = of_get_address(dev->of_node, 0, &size, NULL); + priv->num_ports = size / sizeof(u32); + if (!priv->num_ports) + return -EINVAL; + priv->reg_offset = __be32_to_cpu(*offset); + } + + 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__, num_ports); - - for (i = 0; i < num_ports; i++) { - const struct reg_field *field; - struct regmap_field *regfield; + dev_dbg(dev, "%s %d\n", __func__, priv->num_ports); - if_phys[i].id = i + 1; - if_phys[i].priv = priv; - - 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); - - 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); + 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; @@ -328,6 +360,7 @@ 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; 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 33c4cf0105a4..c9cfafe89cbf 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -20,7 +20,6 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset-controller.h> -#include <dt-bindings/phy/phy.h> #define WIZ_SERDES_CTRL 0x404 #define WIZ_SERDES_TOP_CTRL 0x408 diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index 507f79d14adb..4fec90d2624f 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -6,23 +6,23 @@ * 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) @@ -89,7 +89,7 @@ static inline void omap_usb_writel(void __iomem *addr, unsigned int offset, } /** - * omap_usb2_set_comparator - links the comparator present in the sytem with + * omap_usb2_set_comparator - links the comparator present in the system with * this phy * @comparator - the companion phy(comparator) for this phy * @@ -142,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) @@ -409,7 +409,7 @@ static int omap_usb2_probe(struct platform_device *pdev) 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"); @@ -438,7 +438,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) @@ -452,10 +451,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"); @@ -504,7 +503,6 @@ static int omap_usb2_probe(struct platform_device *pdev) return PTR_ERR(phy_provider); } - usb_add_phy_dev(&phy->phy); return 0; |