M7350/kernel/drivers/net/ethernet/qualcomm/emac/emac_sgmii_v1.c
2024-09-09 08:57:42 +00:00

239 lines
8.2 KiB
C

/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* Qualcomm Technologies, Inc. EMAC SGMII Controller driver.
*/
#include "emac_sgmii.h"
#include "emac_hw.h"
#define PCS_MAX_REG_CNT 10
#define PLL_MAX_REG_CNT 18
static const struct emac_reg_write
physical_coding_sublayer_programming[][PCS_MAX_REG_CNT] = {
/* EMAC_PHY_MAP_DEFAULT */
{
{EMAC_SGMII_PHY_CDR_CTRL0, SGMII_CDR_MAX_CNT},
{EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B},
{EMAC_SGMII_PHY_CMN_PWR_CTRL, BIAS_EN | SYSCLK_EN |
CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN},
{EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN |
L0_TRAN_BIAS_EN},
{EMAC_SGMII_PHY_RX_PWR_CTRL, L0_RX_SIGDET_EN |
(1 << L0_RX_TERM_MODE_SHFT) | L0_RX_I_EN},
{EMAC_SGMII_PHY_CMN_PWR_CTRL, BIAS_EN | PLL_EN | SYSCLK_EN |
CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN},
{EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQ_EN |
L0_RESET_TSYNC_EN | L0_DRV_LVL_BMSK},
{END_MARKER, END_MARKER},
},
/* EMAC_PHY_MAP_MDM9607 */
{
{EMAC_SGMII_PHY_CDR_CTRL0, SGMII_CDR_MAX_CNT},
{EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B},
{EMAC_SGMII_PHY_CMN_PWR_CTRL, BIAS_EN | SYSCLK_EN |
CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN},
{EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN |
L0_TRAN_BIAS_EN},
{EMAC_SGMII_PHY_RX_PWR_CTRL, L0_RX_SIGDET_EN |
(1 << L0_RX_TERM_MODE_SHFT) | L0_RX_I_EN},
{EMAC_SGMII_PHY_CMN_PWR_CTRL, BIAS_EN | PLL_EN | SYSCLK_EN |
CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN},
{EMAC_QSERDES_COM_PLL_VCOTAIL_EN, PLL_VCO_TAIL_MUX |
PLL_VCO_TAIL | PLL_EN_VCOTAIL_EN},
{EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN |
PLL_DIV_ORD},
{EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQ_EN |
L0_RESET_TSYNC_EN | L0_DRV_LVL_BMSK},
{END_MARKER, END_MARKER}
}
};
static const struct emac_reg_write sysclk_refclk_setting[] = {
{EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS},
{EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE},
{END_MARKER, END_MARKER},
};
static const struct emac_reg_write pll_setting[][PLL_MAX_REG_CNT] = {
/* EMAC_PHY_MAP_DEFAULT */
{
{EMAC_QSERDES_COM_PLL_IP_SETI, QSERDES_PLL_IPSETI_DEF},
{EMAC_QSERDES_COM_PLL_CP_SETI, QSERDES_PLL_CP_SETI},
{EMAC_QSERDES_COM_PLL_IP_SETP, QSERDES_PLL_IP_SETP},
{EMAC_QSERDES_COM_PLL_CP_SETP, QSERDES_PLL_CP_SETP},
{EMAC_QSERDES_COM_PLL_CRCTRL, QSERDES_PLL_CRCTRL},
{EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN |
PLL_DIV_ORD},
{EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX |
QSERDES_PLL_DEC},
{EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX |
DEC_START2},
{EMAC_QSERDES_COM_DIV_FRAC_START1, DIV_FRAC_START1_MUX |
QSERDES_PLL_DIV_FRAC_START1},
{EMAC_QSERDES_COM_DIV_FRAC_START2, DIV_FRAC_START2_MUX |
QSERDES_PLL_DIV_FRAC_START2},
{EMAC_QSERDES_COM_DIV_FRAC_START3, DIV_FRAC_START3_MUX |
QSERDES_PLL_DIV_FRAC_START3},
{EMAC_QSERDES_COM_PLLLOCK_CMP1, QSERDES_PLL_LOCK_CMP1},
{EMAC_QSERDES_COM_PLLLOCK_CMP2, QSERDES_PLL_LOCK_CMP2},
{EMAC_QSERDES_COM_PLLLOCK_CMP3, QSERDES_PLL_LOCK_CMP3},
{EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN},
{EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE},
{END_MARKER, END_MARKER}
},
/* EMAC_PHY_MAP_MDM9607 */
{
{EMAC_QSERDES_COM_PLL_IP_SETI, QSERDES_PLL_IPSETI_MDM},
{EMAC_QSERDES_COM_PLL_CP_SETI, QSERDES_PLL_CP_SETI},
{EMAC_QSERDES_COM_PLL_IP_SETP, QSERDES_PLL_IP_SETP},
{EMAC_QSERDES_COM_PLL_CP_SETP, QSERDES_PLL_CP_SETP},
{EMAC_QSERDES_COM_PLL_CRCTRL, QSERDES_PLL_CRCTRL},
{EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX |
QSERDES_PLL_DEC},
{EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX |
DEC_START2},
{EMAC_QSERDES_COM_DIV_FRAC_START1, DIV_FRAC_START1_MUX |
QSERDES_PLL_DIV_FRAC_START1},
{EMAC_QSERDES_COM_DIV_FRAC_START2, DIV_FRAC_START2_MUX |
QSERDES_PLL_DIV_FRAC_START2},
{EMAC_QSERDES_COM_DIV_FRAC_START3, DIV_FRAC_START3_MUX |
QSERDES_PLL_DIV_FRAC_START3},
{EMAC_QSERDES_COM_PLLLOCK_CMP1, QSERDES_PLL_LOCK_CMP1},
{EMAC_QSERDES_COM_PLLLOCK_CMP2, QSERDES_PLL_LOCK_CMP2},
{EMAC_QSERDES_COM_PLLLOCK_CMP3, QSERDES_PLL_LOCK_CMP3},
{EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN},
{EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE},
{EMAC_QSERDES_COM_RES_TRIM_SEARCH, RESTRIM_SEARCH},
{EMAC_QSERDES_COM_BGTC, BGTC},
{END_MARKER, END_MARKER},
}
};
static const struct emac_reg_write cdr_setting[] = {
{EMAC_QSERDES_RX_CDR_CONTROL, SECONDORDERENABLE |
(QSERDES_RX_CDR_CTRL1_THRESH << FIRSTORDER_THRESH_SHFT) |
(QSERDES_RX_CDR_CTRL1_GAIN << SECONDORDERGAIN_SHFT)},
{EMAC_QSERDES_RX_CDR_CONTROL2, SECONDORDERENABLE |
(QSERDES_RX_CDR_CTRL2_THRESH << FIRSTORDER_THRESH_SHFT) |
(QSERDES_RX_CDR_CTRL2_GAIN << SECONDORDERGAIN_SHFT)},
{END_MARKER, END_MARKER},
};
static const struct emac_reg_write tx_rx_setting[] = {
{EMAC_QSERDES_TX_BIST_MODE_LANENO, QSERDES_TX_BIST_MODE_LANENO},
{EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX |
(QSERDES_TX_DRV_LVL << TX_DRV_LVL_SHFT)},
{EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN},
{EMAC_QSERDES_TX_TX_EMP_POST1_LVL, TX_EMP_POST1_LVL_MUX |
(QSERDES_TX_EMP_POST1_LVL << TX_EMP_POST1_LVL_SHFT)},
{EMAC_QSERDES_RX_RX_EQ_GAIN12,
(QSERDES_RX_EQ_GAIN2 << RX_EQ_GAIN2_SHFT) |
(QSERDES_RX_EQ_GAIN1 << RX_EQ_GAIN1_SHFT)},
{EMAC_QSERDES_TX_LANE_MODE,
QSERDES_TX_LANE_MODE},
{END_MARKER, END_MARKER}
};
static int emac_sgmii_v1_init(struct emac_adapter *adpt)
{
int i;
struct emac_phy *phy = &adpt->phy;
struct emac_sgmii *sgmii = phy->private;
emac_sgmii_init_link(adpt, phy->autoneg_advertised, phy->autoneg,
!phy->disable_fc_autoneg);
emac_reg_write_all(
sgmii->base,
(const struct emac_reg_write *)
&physical_coding_sublayer_programming[phy->board_id]);
/* Ensure Rx/Tx lanes power configuration is written to hw before
* configuring the SerDes engine's clocks
*/
wmb();
emac_reg_write_all(sgmii->base, sysclk_refclk_setting);
emac_reg_write_all(
sgmii->base,
(const struct emac_reg_write *)&pll_setting[phy->board_id]);
emac_reg_write_all(sgmii->base, cdr_setting);
emac_reg_write_all(sgmii->base, tx_rx_setting);
/* Ensure SerDes engine configuration is written to hw before powering
* it up
*/
wmb();
writel_relaxed(SERDES_START, sgmii->base + EMAC_SGMII_PHY_SERDES_START);
/* Ensure Rx/Tx SerDes engine power-up command is written to HW */
wmb();
for (i = 0; i < SERDES_START_WAIT_TIMES; i++) {
if (readl_relaxed(sgmii->base + EMAC_QSERDES_COM_RESET_SM) &
QSERDES_READY)
break;
usleep_range(100, 200);
}
if (i == SERDES_START_WAIT_TIMES) {
emac_err(adpt, "serdes failed to start\n");
return -EIO;
}
/* Mask out all the SGMII Interrupt */
writel_relaxed(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
/* Ensure SGMII interrupts are masked out before clearing them */
wmb();
emac_hw_clear_sgmii_intr_status(adpt, SGMII_PHY_INTERRUPT_ERR);
return 0;
}
static void emac_sgmii_v1_reset(struct emac_adapter *adpt)
{
emac_clk_set_rate(adpt, EMAC_CLK_125M, EMC_CLK_RATE_19_2MHZ);
emac_sgmii_reset_prepare(adpt);
emac_sgmii_v1_init(adpt);
emac_clk_set_rate(adpt, EMAC_CLK_125M, EMC_CLK_RATE_125MHZ);
}
int emac_sgmii_v1_link_setup_no_ephy(struct emac_adapter *adpt, u32 speed,
bool autoneg)
{
struct emac_phy *phy = &adpt->phy;
phy->autoneg = autoneg;
phy->autoneg_advertised = speed;
/* The AN_ENABLE and SPEED_CFG can't change on fly. The SGMII_PHY has
* to be re-initialized.
*/
emac_sgmii_reset_prepare(adpt);
return emac_sgmii_v1_init(adpt);
}
struct emac_phy_ops emac_sgmii_v1_ops = {
.config = emac_sgmii_config,
.up = emac_sgmii_up,
.down = emac_sgmii_down,
.init = emac_sgmii_v1_init,
.reset = emac_sgmii_v1_reset,
.init_ephy = emac_sgmii_init_ephy_nop,
.link_setup_no_ephy = emac_sgmii_v1_link_setup_no_ephy,
.link_check_no_ephy = emac_sgmii_link_check_no_ephy,
.tx_clk_set_rate = emac_sgmii_tx_clk_set_rate_nop,
.periodic_task = emac_sgmii_periodic_check,
};