From: Swapnil Jakhade <sjakh...@cadence.com>

Add support for PHY configuration APIs. These will mainly reconfigure
link rate, number of lanes, voltage swing and pre-emphasis values.

Signed-off-by: Swapnil Jakhade <sjakh...@cadence.com>
---
 drivers/phy/cadence/phy-cadence-torrent.c | 424 ++++++++++++++++++++++++++++++
 1 file changed, 424 insertions(+)

diff --git a/drivers/phy/cadence/phy-cadence-torrent.c 
b/drivers/phy/cadence/phy-cadence-torrent.c
index ebc3b68..006e786 100644
--- a/drivers/phy/cadence/phy-cadence-torrent.c
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -37,6 +37,9 @@
 #define PHY_AUX_CONFIG                 0x00
 #define PHY_AUX_CTRL                   0x04
 #define PHY_RESET                      0x20
+#define PMA_TX_ELEC_IDLE_MASK          0xF0U
+#define PMA_TX_ELEC_IDLE_SHIFT         4
+#define PHY_L00_RESET_N_MASK           0x01U
 #define PHY_PMA_XCVR_PLLCLK_EN         0x24
 #define PHY_PMA_XCVR_PLLCLK_EN_ACK     0x28
 #define PHY_PMA_XCVR_POWER_STATE_REQ   0x2c
@@ -120,6 +123,10 @@
 #define CMN_PDIAG_PLL1_CP_IADJ_M0      0x00714
 #define CMN_PDIAG_PLL1_FILT_PADJ_M0    0x00718
 
+#define TX_TXCC_CTRL                   0x10100
+#define TX_TXCC_CPOST_MULT_00          0x10130
+#define TX_TXCC_MGNFS_MULT_000         0x10140
+#define DRV_DIAG_TX_DRV                        0x10318
 #define XCVR_DIAG_PLLDRC_CTRL          0x10394
 #define XCVR_DIAG_HSCLK_SEL            0x10398
 #define XCVR_DIAG_HSCLK_DIV            0x1039c
@@ -129,6 +136,8 @@
 #define TX_PSC_A2                      0x10408
 #define TX_PSC_A3                      0x1040c
 #define TX_RCVDET_ST_TMR               0x1048c
+#define TX_DIAG_ACYA                   0x1079c
+#define TX_DIAG_ACYA_HBDC_MASK         0x0001U
 #define RX_PSC_A0                      0x20000
 #define RX_PSC_A1                      0x20004
 #define RX_PSC_A2                      0x20008
@@ -140,6 +149,9 @@
 
 #define PHY_PLL_CFG                    0x30038
 
+#define PHY_PMA_CMN_CTRL2              0x38004
+#define PHY_PMA_PLL_RAW_CTRL           0x3800c
+
 struct cdns_torrent_phy {
        void __iomem *base;     /* DPTX registers base */
        void __iomem *sd_base; /* SD0801 registers base */
@@ -184,12 +196,18 @@ static void cdns_dp_phy_write_field(struct 
cdns_torrent_phy *cdns_phy,
                                    unsigned char num_bits,
                                    unsigned int val);
 
+static int cdns_torrent_dp_configure(struct phy *phy,
+                                    union phy_configure_opts *opts);
+static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
+                                          u32 num_lanes,
+                                          enum phy_powerstate powerstate);
 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,
+       .configure      = cdns_torrent_dp_configure,
        .power_on       = cdns_torrent_phy_on,
        .power_off      = cdns_torrent_phy_off,
        .owner          = THIS_MODULE,
@@ -203,6 +221,16 @@ static void cdns_torrent_phy_write(struct cdns_torrent_phy 
*cdns_phy,
        writel(val, cdns_phy->sd_base + offset);
 }
 
+static u32 cdns_torrent_phy_read(struct cdns_torrent_phy *cdns_phy, u32 offset)
+{
+       return readl(cdns_phy->sd_base + offset);
+}
+
+#define cdns_torrent_phy_read_poll_timeout(cdns_phy, offset, val, cond, \
+                                          delay_us, timeout_us) \
+       readl_poll_timeout((cdns_phy)->sd_base + (offset), \
+                          val, cond, delay_us, timeout_us)
+
 /* DPTX mmr access functions */
 
 static void cdns_torrent_dp_write(struct cdns_torrent_phy *cdns_phy,
@@ -221,6 +249,237 @@ static u32 cdns_torrent_dp_read(struct cdns_torrent_phy 
*cdns_phy, u32 offset)
        readl_poll_timeout((cdns_phy)->base + (offset), \
                           val, cond, delay_us, timeout_us)
 
+/*
+ * Structure used to store values of PHY registers for voltage-related
+ * coefficients, for particular voltage swing and pre-emphasis level. Values
+ * are shared across all physical lanes.
+ */
+struct coefficients {
+       /* Value of DRV_DIAG_TX_DRV register to use */
+       u16 diag_tx_drv;
+       /* Value of TX_TXCC_MGNFS_MULT_000 register to use */
+       u16 mgnfs_mult;
+       /* Value of TX_TXCC_CPOST_MULT_00 register to use */
+       u16 cpost_mult;
+};
+
+/*
+ * Array consists of values of voltage-related registers for sd0801 PHY. A 
value
+ * of 0xFFFF is a placeholder for invalid combination, and will never be used.
+ */
+static const struct coefficients vltg_coeff[4][4] = {
+       /* voltage swing 0, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x002A,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F,
+                .cpost_mult = 0x0014},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0012,
+                .cpost_mult = 0x0020},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x002A}
+       },
+
+       /* voltage swing 1, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013,
+                .cpost_mult = 0x0012},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x001F},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF}
+       },
+
+       /* voltage swing 2, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x0013},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF}
+       },
+
+       /* voltage swing 3, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF}
+       }
+};
+
+/*
+ * Enable or disable PLL for selected lanes.
+ */
+static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy,
+                                     struct phy_configure_opts_dp *dp,
+                                     bool enable)
+{
+       u32 rd_val;
+       u32 ret;
+       /*
+        * Used to determine, which bits to check for or enable in
+        * PHY_PMA_XCVR_PLLCLK_EN register.
+        */
+       u32 pll_bits;
+       /* Used to enable or disable lanes. */
+       u32 pll_val;
+
+       /* Select values of registers and mask, depending on enabled lane
+        * count.
+        */
+       switch (dp->lanes) {
+       /* lane 0 */
+       case (1):
+               pll_bits = 0x00000001;
+               break;
+       /* lanes 0-1 */
+       case (2):
+               pll_bits = 0x00000003;
+               break;
+       /* lanes 0-3, all */
+       default:
+               pll_bits = 0x0000000F;
+               break;
+       }
+
+       if (enable)
+               pll_val = pll_bits;
+       else
+               pll_val = 0x00000000;
+
+       cdns_torrent_dp_write(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN, pll_val);
+
+       /* Wait for acknowledgment from PHY. */
+       ret = cdns_torrent_dp_read_poll_timeout(cdns_phy,
+                                               PHY_PMA_XCVR_PLLCLK_EN_ACK,
+                                               rd_val,
+                                               (rd_val & pll_bits) == pll_val,
+                                               0, POLL_TIMEOUT_US);
+       ndelay(100);
+       return ret;
+}
+
+/*
+ * Perform register operations related to setting link rate, once powerstate is
+ * set and PLL disable request was processed.
+ */
+static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy,
+                                         struct phy_configure_opts_dp *dp)
+{
+       u32 ret;
+       u32 read_val;
+
+       /* Disable the cmn_pll0_en before re-programming the new data rate. */
+       cdns_torrent_phy_write(cdns_phy, PHY_PMA_PLL_RAW_CTRL, 0);
+
+       /*
+        * Wait for PLL ready de-assertion.
+        * For PLL0 - PHY_PMA_CMN_CTRL2[2] == 1
+        */
+       ret = cdns_torrent_phy_read_poll_timeout(cdns_phy, PHY_PMA_CMN_CTRL2,
+                                                read_val,
+                                                ((read_val >> 2) & 0x01) != 0,
+                                                0, POLL_TIMEOUT_US);
+       if (ret)
+               return ret;
+       ndelay(200);
+
+       /* DP Rate Change - VCO Output settings. */
+       if (cdns_phy->ref_clk_rate == REF_CLK_19_2MHz) {
+               /* PMA common configuration 19.2MHz */
+               cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy, dp->link_rate,
+                                                       dp->ssc);
+               cdns_torrent_dp_pma_cmn_cfg_19_2mhz(cdns_phy);
+       } else if (cdns_phy->ref_clk_rate == REF_CLK_25MHz) {
+               /* PMA common configuration 25MHz */
+               cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy, dp->link_rate,
+                                                     dp->ssc);
+               cdns_torrent_dp_pma_cmn_cfg_25mhz(cdns_phy);
+       }
+       cdns_torrent_dp_pma_cmn_rate(cdns_phy, dp->link_rate, dp->lanes);
+
+       /* Enable the cmn_pll0_en. */
+       cdns_torrent_phy_write(cdns_phy, PHY_PMA_PLL_RAW_CTRL, 0x3);
+
+       /*
+        * Wait for PLL ready assertion.
+        * For PLL0 - PHY_PMA_CMN_CTRL2[0] == 1
+        */
+       ret = cdns_torrent_phy_read_poll_timeout(cdns_phy, PHY_PMA_CMN_CTRL2,
+                                                read_val,
+                                                (read_val & 0x01) != 0,
+                                                0, POLL_TIMEOUT_US);
+       return ret;
+}
+
+/*
+ * Verify, that parameters to configure PHY with are correct.
+ */
+static int cdns_torrent_dp_verify_config(struct cdns_torrent_phy *cdns_phy,
+                                        struct phy_configure_opts_dp *dp)
+{
+       u8 i;
+
+       /* If changing link rate was required, verify it's supported. */
+       if (dp->set_rate) {
+               switch (dp->link_rate) {
+               case 1620:
+               case 2160:
+               case 2430:
+               case 2700:
+               case 3240:
+               case 4320:
+               case 5400:
+               case 8100:
+                       /* valid bit rate */
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       /* Verify lane count. */
+       switch (dp->lanes) {
+       case 1:
+       case 2:
+       case 4:
+               /* valid lane count. */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Check against actual number of PHY's lanes. */
+       if (dp->lanes > cdns_phy->num_lanes)
+               return -EINVAL;
+
+       /*
+        * If changing voltages is required, check swing and pre-emphasis
+        * levels, per-lane.
+        */
+       if (dp->set_voltages) {
+               /* Lane count verified previously. */
+               for (i = 0; i < dp->lanes; i++) {
+                       if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+                               return -EINVAL;
+
+                       /* Sum of voltage swing and pre-emphasis levels cannot
+                        * exceed 3.
+                        */
+                       if (dp->voltage[i] + dp->pre[i] > 3)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 /* Set power state A0 and PLL clock enable to 0 on enabled lanes. */
 static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy,
                                       u32 num_lanes)
@@ -257,6 +516,171 @@ static void cdns_torrent_dp_set_a0_pll(struct 
cdns_torrent_phy *cdns_phy,
        cdns_torrent_dp_write(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN, pll_clk_en);
 }
 
+/* Configure lane count as required. */
+static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy,
+                                    struct phy_configure_opts_dp *dp)
+{
+       u32 value;
+       u32 ret;
+       u8 lane_mask = (1 << dp->lanes) - 1;
+
+       value = cdns_torrent_dp_read(cdns_phy, PHY_RESET);
+       /* clear pma_tx_elec_idle_ln_* bits. */
+       value &= ~PMA_TX_ELEC_IDLE_MASK;
+       /* Assert pma_tx_elec_idle_ln_* for disabled lanes. */
+       value |= ((~lane_mask) << PMA_TX_ELEC_IDLE_SHIFT) &
+                PMA_TX_ELEC_IDLE_MASK;
+       cdns_torrent_dp_write(cdns_phy, PHY_RESET, value);
+
+       /* reset the link by asserting phy_l00_reset_n low */
+       cdns_torrent_dp_write(cdns_phy, PHY_RESET,
+                             value & (~PHY_L00_RESET_N_MASK));
+
+       /*
+        * Assert lane reset on unused lanes and lane 0 so they remain in reset
+        * and powered down when re-enabling the link
+        */
+       value = (value & 0x0000FFF0) | (0x0000000E & lane_mask);
+       cdns_torrent_dp_write(cdns_phy, PHY_RESET, value);
+
+       cdns_torrent_dp_set_a0_pll(cdns_phy, dp->lanes);
+
+       /* release phy_l0*_reset_n based on used laneCount */
+       value = (value & 0x0000FFF0) | (0x0000000F & lane_mask);
+       cdns_torrent_dp_write(cdns_phy, PHY_RESET, value);
+
+       /* Wait, until PHY gets ready after releasing PHY reset signal. */
+       ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
+       if (ret)
+               return ret;
+
+       ndelay(100);
+
+       /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
+       cdns_torrent_dp_write(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+
+       ret = cdns_torrent_dp_run(cdns_phy);
+
+       return ret;
+}
+
+/* Configure link rate as required. */
+static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy,
+                                   struct phy_configure_opts_dp *dp)
+{
+       u32 ret;
+
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+                                             POWERSTATE_A3);
+       if (ret)
+               return ret;
+       ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false);
+       if (ret)
+               return ret;
+       ndelay(200);
+
+       ret = cdns_torrent_dp_configure_rate(cdns_phy, dp);
+       if (ret)
+               return ret;
+       ndelay(200);
+
+       ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true);
+       if (ret)
+               return ret;
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+                                             POWERSTATE_A2);
+       if (ret)
+               return ret;
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+                                             POWERSTATE_A0);
+       if (ret)
+               return ret;
+       ndelay(900);
+
+       return ret;
+}
+
+/* Configure voltage swing and pre-emphasis for all enabled lanes. */
+static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy,
+                                        struct phy_configure_opts_dp *dp)
+{
+       u8 lane;
+       u16 val;
+       unsigned int lane_bits;
+
+       for (lane = 0; lane < dp->lanes; lane++) {
+               lane_bits = (lane & LANE_MASK) << 11;
+
+               val = cdns_torrent_phy_read(cdns_phy,
+                                           (TX_DIAG_ACYA | lane_bits));
+               /*
+                * Write 1 to register bit TX_DIAG_ACYA[0] to freeze the
+                * current state of the analog TX driver.
+                */
+               val |= TX_DIAG_ACYA_HBDC_MASK;
+               cdns_torrent_phy_write(cdns_phy,
+                                      (TX_DIAG_ACYA | lane_bits), val);
+
+               cdns_torrent_phy_write(cdns_phy,
+                                      (TX_TXCC_CTRL | lane_bits), 0x08A4);
+               val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].diag_tx_drv;
+               cdns_torrent_phy_write(cdns_phy,
+                                      (DRV_DIAG_TX_DRV | lane_bits), val);
+               val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].mgnfs_mult;
+               cdns_torrent_phy_write(cdns_phy,
+                                      (TX_TXCC_MGNFS_MULT_000 | lane_bits),
+                                      val);
+               val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].cpost_mult;
+               cdns_torrent_phy_write(cdns_phy,
+                                      (TX_TXCC_CPOST_MULT_00 | lane_bits),
+                                      val);
+
+               val = cdns_torrent_phy_read(cdns_phy,
+                                           (TX_DIAG_ACYA | lane_bits));
+               /*
+                * Write 0 to register bit TX_DIAG_ACYA[0] to allow the state of
+                * analog TX driver to reflect the new programmed one.
+                */
+               val &= ~TX_DIAG_ACYA_HBDC_MASK;
+               cdns_torrent_phy_write(cdns_phy,
+                                      (TX_DIAG_ACYA | lane_bits), val);
+       }
+};
+
+static int cdns_torrent_dp_configure(struct phy *phy,
+                                    union phy_configure_opts *opts)
+{
+       struct cdns_torrent_phy *cdns_phy = phy_get_drvdata(phy);
+       int ret;
+
+       ret = cdns_torrent_dp_verify_config(cdns_phy, &opts->dp);
+       if (ret) {
+               dev_err(&phy->dev, "invalid params for phy configure\n");
+               return ret;
+       }
+
+       if (opts->dp.set_lanes) {
+               ret = cdns_torrent_dp_set_lanes(cdns_phy, &opts->dp);
+               if (ret) {
+                       dev_err(&phy->dev, "cdns_torrent_dp_set_lanes 
failed\n");
+                       return ret;
+               }
+       }
+
+       if (opts->dp.set_rate) {
+               ret = cdns_torrent_dp_set_rate(cdns_phy, &opts->dp);
+               if (ret) {
+                       dev_err(&phy->dev, "cdns_torrent_dp_set_rate failed\n");
+                       return ret;
+               }
+       }
+
+       if (opts->dp.set_voltages)
+               cdns_torrent_dp_set_voltages(cdns_phy, &opts->dp);
+
+       return ret;
+}
+
 static int cdns_torrent_dp_init(struct phy *phy)
 {
        unsigned char lane_bits;
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to