phy: cadence-torrent: Implement PHY configure APIs

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 <sjakhade@cadence.com>
Signed-off-by: Yuti Amonkar <yamonkar@cadence.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Swapnil Jakhade 2020-02-06 07:10:57 +01:00 committed by Kishon Vijay Abraham I
parent e4b496a376
commit 572d659256
1 changed files with 431 additions and 5 deletions

View File

@ -36,6 +36,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
@ -119,6 +122,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
@ -128,6 +135,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
@ -139,6 +148,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 */
@ -158,7 +170,8 @@ enum phy_powerstate {
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);
static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy,
u32 num_lanes);
static
int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy);
static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy);
@ -182,9 +195,16 @@ 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 const struct phy_ops cdns_torrent_phy_ops = {
.init = cdns_torrent_dp_init,
.exit = cdns_torrent_dp_exit,
.configure = cdns_torrent_dp_configure,
.owner = THIS_MODULE,
};
@ -196,6 +216,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,
@ -214,6 +244,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)
@ -250,6 +511,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, dp->lanes);
return ret;
}
/* Configure link rate as required. */
static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy,
struct phy_configure_opts_dp *dp)
{
u32 ret;
ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
POWERSTATE_A3);
if (ret)
return ret;
ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false);
if (ret)
return ret;
ndelay(200);
ret = cdns_torrent_dp_configure_rate(cdns_phy, dp);
if (ret)
return ret;
ndelay(200);
ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true);
if (ret)
return ret;
ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
POWERSTATE_A2);
if (ret)
return ret;
ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
POWERSTATE_A0);
if (ret)
return ret;
ndelay(900);
return ret;
}
/* Configure voltage swing and pre-emphasis for all enabled lanes. */
static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy,
struct phy_configure_opts_dp *dp)
{
u8 lane;
u16 val;
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;
@ -321,7 +747,7 @@ static int cdns_torrent_dp_init(struct phy *phy)
if (ret)
return ret;
ret = cdns_torrent_dp_run(cdns_phy);
ret = cdns_torrent_dp_run(cdns_phy, cdns_phy->num_lanes);
return ret;
}
@ -898,7 +1324,7 @@ static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
return ret;
}
static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy)
static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
{
unsigned int read_val;
int ret;
@ -919,12 +1345,12 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy)
ndelay(100);
ret = cdns_torrent_dp_set_power_state(cdns_phy, cdns_phy->num_lanes,
ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
POWERSTATE_A2);
if (ret)
return ret;
ret = cdns_torrent_dp_set_power_state(cdns_phy, cdns_phy->num_lanes,
ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
POWERSTATE_A0);
return ret;