blob: 77acad02b7f0900e494f80cae10d2d791e0dcc73 [file] [log] [blame]
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @defgroup
* @{
*/
#include "sw.h"
#include "hppe_uniphy_reg.h"
#include "hppe_uniphy.h"
#include "hppe_init.h"
#include "ssdk_init.h"
#include "ssdk_clk.h"
#include "ssdk_dts.h"
#include "adpt.h"
#include "mp_uniphy_reg.h"
#include "mp_uniphy.h"
#include "hsl_phy.h"
static sw_error_t
_adpt_mp_uniphy_calibrate(a_uint32_t dev_id, a_uint32_t uniphy_index)
{
a_uint32_t reg_value = 0;
a_uint32_t retries = 100, calibration_done = 0;
union uniphy_offset_calib_4_u uniphy_offset_calib_4;
memset(&uniphy_offset_calib_4, 0, sizeof(uniphy_offset_calib_4));
ADPT_DEV_ID_CHECK(dev_id);
if(ssdk_is_emulation(dev_id)){
SSDK_INFO("uniphy_index %d on emulation platform\n", uniphy_index);
return SW_OK;
}
/*wait calibration done to uniphy*/
while (calibration_done != UNIPHY_CALIBRATION_DONE) {
mdelay(1);
if (retries-- == 0)
{
SSDK_ERROR("uniphy callibration time out!\n");
return SW_TIMEOUT;
}
reg_value = 0;
hppe_uniphy_offset_calib_4_get(dev_id, uniphy_index, &uniphy_offset_calib_4);
reg_value = uniphy_offset_calib_4.bf.mmd1_reg_calibration_done_reg;
calibration_done = (reg_value & UNIPHY_CALIBRATION_DONE);
}
return SW_OK;
}
sw_error_t
adpt_mp_uniphy_adapter_port_reset(a_uint32_t dev_id,
a_uint32_t port_id)
{
sw_error_t rv = SW_OK;
a_uint32_t uniphy_index = 0;
union uniphy_channel0_input_output_4_u uniphy_channel0_input_output_4;
memset(&uniphy_channel0_input_output_4, 0, sizeof(uniphy_channel0_input_output_4));
if (port_id == SSDK_PHYSICAL_PORT2) {
uniphy_index = SSDK_UNIPHY_INSTANCE0;
} else {
SSDK_ERROR("uniphy adapter reset port_id is %d\n", port_id);
return SW_BAD_VALUE;
}
rv = hppe_uniphy_channel0_input_output_4_get(dev_id, uniphy_index,
&uniphy_channel0_input_output_4);
SW_RTN_ON_ERROR (rv);
uniphy_channel0_input_output_4.bf.newaddedfromhere_ch0_adp_sw_rstn = 0;
rv = hppe_uniphy_channel0_input_output_4_set(dev_id, uniphy_index,
&uniphy_channel0_input_output_4);
SW_RTN_ON_ERROR (rv);
uniphy_channel0_input_output_4.bf.newaddedfromhere_ch0_adp_sw_rstn = 1;
rv = hppe_uniphy_channel0_input_output_4_set(dev_id, uniphy_index,
&uniphy_channel0_input_output_4);
SW_RTN_ON_ERROR (rv);
return rv;
}
sw_error_t
adpt_mp_gcc_uniphy_port_clock_set(a_uint32_t dev_id, a_uint32_t port_id,
a_bool_t enable)
{
sw_error_t rv = SW_OK;
if (port_id == SSDK_PHYSICAL_PORT1) {
qca_gcc_uniphy_port_clock_set(dev_id, SSDK_UNIPHY_INSTANCE0,
port_id, enable);
} else if (port_id == SSDK_PHYSICAL_PORT2) {
qca_gcc_uniphy_port_clock_set(dev_id, SSDK_UNIPHY_INSTANCE1,
port_id, enable);
} else {
return SW_BAD_VALUE;
}
return rv;
}
void
adpt_mp_gcc_uniphy_port_set(a_uint32_t dev_id, a_uint32_t port_id,
a_bool_t enable)
{
enum unphy_rst_type rst_type;
if (port_id == SSDK_PHYSICAL_PORT2) {
rst_type = UNIPHY1_SOFT_RESET_E;
} else {
return;
}
if (enable == A_TRUE) {
ssdk_uniphy_reset(dev_id, rst_type, SSDK_RESET_DEASSERT);
} else {
ssdk_uniphy_reset(dev_id, rst_type, SSDK_RESET_ASSERT);
}
return;
}
void
adpt_mp_gcc_uniphy_port_reset(a_uint32_t dev_id, a_uint32_t port_id)
{
adpt_mp_gcc_uniphy_port_set(dev_id, port_id, A_FALSE);
msleep(100);
adpt_mp_gcc_uniphy_port_set(dev_id, port_id, A_TRUE);
return;
}
static sw_error_t
adpt_mp_uniphy_reset(a_uint32_t dev_id, a_uint32_t uniphy_index)
{
sw_error_t rv = SW_OK;
union pll_power_on_and_reset_u pll_software_reset;
memset(&pll_software_reset, 0, sizeof(pll_software_reset));
ADPT_DEV_ID_CHECK(dev_id);
rv = hppe_uniphy_pll_reset_ctrl_get(dev_id, uniphy_index,
&pll_software_reset);
SW_RTN_ON_ERROR (rv);
pll_software_reset.bf.software_reset_analog_reset = 0;
rv = hppe_uniphy_pll_reset_ctrl_set(dev_id, uniphy_index,
&pll_software_reset);
SW_RTN_ON_ERROR (rv);
msleep(500);
pll_software_reset.bf.software_reset_analog_reset = 1;
rv = hppe_uniphy_pll_reset_ctrl_set(dev_id, uniphy_index,
&pll_software_reset);
SW_RTN_ON_ERROR (rv);
msleep(500);
return SW_OK;
}
static sw_error_t
adpt_mp_uniphy_mode_ctrl_set(a_uint32_t dev_id,
a_uint32_t uniphy_index, a_uint32_t mode)
{
sw_error_t rv = SW_OK;
union uniphy_mode_ctrl_u uniphy_mode_ctrl;
union uniphy_channel0_input_output_4_u uniphy_force_ctrl;
a_bool_t force_port = 0;
memset(&uniphy_mode_ctrl, 0, sizeof(uniphy_mode_ctrl));
memset(&uniphy_force_ctrl, 0, sizeof(uniphy_force_ctrl));
force_port = ssdk_port_feature_get(dev_id, SSDK_PHYSICAL_PORT2,
PHY_F_FORCE);
/* configure uniphy mode ctrl to sgmii/sgmiiplus */
rv = hppe_uniphy_mode_ctrl_get(dev_id, uniphy_index, &uniphy_mode_ctrl);
SW_RTN_ON_ERROR (rv);
if (mode == PORT_WRAPPER_SGMII_CHANNEL0) {
uniphy_mode_ctrl.bf.newaddedfromhere_sg_mode =
UNIPHY_SGMII_MODE_ENABLE;
uniphy_mode_ctrl.bf.newaddedfromhere_sgplus_mode =
UNIPHY_SGMIIPLUS_MODE_DISABLE;
if (force_port == A_TRUE) {
rv = hppe_uniphy_channel0_input_output_4_get(dev_id,
uniphy_index, &uniphy_force_ctrl);
SW_RTN_ON_ERROR (rv);
uniphy_force_ctrl.bf.newaddedfromhere_ch0_force_speed_25m =
UNIPHY_FORCE_SPEED_ENABLE;
rv = hppe_uniphy_channel0_input_output_4_set(dev_id,
uniphy_index, &uniphy_force_ctrl);
SW_RTN_ON_ERROR (rv);
}
} else {
uniphy_mode_ctrl.bf.newaddedfromhere_sg_mode =
UNIPHY_SGMII_MODE_DISABLE;
uniphy_mode_ctrl.bf.newaddedfromhere_sgplus_mode =
UNIPHY_SGMIIPLUS_MODE_ENABLE;
}
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_autoneg_mode =
UNIPHY_ATHEROS_NEGOTIATION;
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_psgmii_qsgmii =
UNIPHY_CH0_QSGMII_SGMII_MODE;
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_qsgmii_sgmii =
UNIPHY_CH0_SGMII_MODE;
uniphy_mode_ctrl.bf.newaddedfromhere_xpcs_mode =
UNIPHY_XPCS_MODE_DISABLE;
rv = hppe_uniphy_mode_ctrl_set(dev_id, uniphy_index, &uniphy_mode_ctrl);
return rv;
}
static sw_error_t
_adpt_mp_uniphy_clk_output_ctrl_set(a_uint32_t dev_id, a_uint32_t index,
a_uint32_t clk_rate)
{
sw_error_t rv = SW_OK;
union uniphy_clock_output_control_u clock_output;
memset(&clock_output, 0, sizeof(union uniphy_clock_output_control_u));
clock_output.bf.ref_clk_output_drv = UNIPHY_CLK_DRV_1;
clock_output.bf.ref_clk_output_en = A_TRUE;
SSDK_INFO("uniphy will output clock as %dHz\n", clk_rate);
if(clk_rate == UNIPHY_CLK_RATE_25M)
{
clock_output.bf.ref_clk_output_div = UNIPHY_CLK_DIV_25M;
}
else if(clk_rate == UNIPHY_CLK_RATE_50M)
{
clock_output.bf.ref_clk_output_div = UNIPHY_CLK_DIV_50M;
}
else
{
return SW_NOT_SUPPORTED;
}
rv = mp_uniphy_clock_output_control_set(dev_id, index,
&clock_output);
return rv;
}
static void
_adpt_mp_uniphy_clk_output_set(a_uint32_t dev_id, a_uint32_t index)
{
a_uint32_t phy_id =0;
a_bool_t force_port = A_FALSE;
a_uint32_t force_speed = 0;
/*when MP connect s17c or qca803x, need to reconfigure reference clock
as 25M for port 2*/
force_port = ssdk_port_feature_get(dev_id, SSDK_PHYSICAL_PORT2, PHY_F_FORCE);
force_speed = ssdk_port_force_speed_get(dev_id, SSDK_PHYSICAL_PORT2);
if ((force_port) && (force_speed == FAL_SPEED_1000))
{
_adpt_mp_uniphy_clk_output_ctrl_set(dev_id, index, UNIPHY_CLK_RATE_25M);
}
phy_id = hsl_port_phyid_get(dev_id, SSDK_PHYSICAL_PORT2);
if (phy_id == QCA8030_PHY || phy_id == QCA8033_PHY || phy_id == QCA8035_PHY)
{
_adpt_mp_uniphy_clk_output_ctrl_set(dev_id, index, UNIPHY_CLK_RATE_25M);
}
return;
}
sw_error_t
adpt_mp_uniphy_mode_configure(a_uint32_t dev_id, a_uint32_t index, a_uint32_t mode)
{
sw_error_t rv = SW_OK;
a_uint32_t clock = UNIPHY_CLK_RATE_125M;
union uniphy_misc2_phy_mode_u uniphy_misc2_phy_mode;
memset(&uniphy_misc2_phy_mode, 0, sizeof(uniphy_misc2_phy_mode));
ADPT_DEV_ID_CHECK(dev_id);
if (index != SSDK_UNIPHY_INSTANCE0) {
SSDK_ERROR("uniphy index is %d\n", index);
return SW_BAD_VALUE;
}
if (mode == PORT_WRAPPER_MAX) {
adpt_mp_gcc_uniphy_port_set(dev_id, SSDK_PHYSICAL_PORT2,
A_FALSE);
return SW_OK;
} else if ((mode == PORT_WRAPPER_SGMII_CHANNEL0) ||
(mode == PORT_WRAPPER_SGMII_PLUS)) {
adpt_mp_gcc_uniphy_port_set(dev_id, SSDK_PHYSICAL_PORT2,
A_TRUE);
} else {
return SW_NOT_SUPPORTED;
}
/*set the PHY mode to SGMII or SGMIIPLUS*/
rv = hppe_uniphy_phy_mode_ctrl_get(dev_id, index,
&uniphy_misc2_phy_mode);
SW_RTN_ON_ERROR (rv);
if (mode == PORT_WRAPPER_SGMII_CHANNEL0) {
uniphy_misc2_phy_mode.bf.phy_mode =
UNIPHY_PHY_SGMII_MODE;
clock = UNIPHY_CLK_RATE_125M;
} else {
uniphy_misc2_phy_mode.bf.phy_mode =
UNIPHY_PHY_SGMIIPLUS_MODE;
clock = UNIPHY_CLK_RATE_312M;
}
rv = hppe_uniphy_phy_mode_ctrl_set(dev_id, index,
&uniphy_misc2_phy_mode);
SW_RTN_ON_ERROR (rv);
/* reset uniphy */
rv = adpt_mp_uniphy_reset(dev_id, index);
SW_RTN_ON_ERROR (rv);
/* disable uniphy port clock */
rv = adpt_mp_gcc_uniphy_port_clock_set(dev_id, SSDK_PHYSICAL_PORT2,
A_FALSE);
SW_RTN_ON_ERROR (rv);
rv = adpt_mp_uniphy_mode_ctrl_set(dev_id, index, mode);
SW_RTN_ON_ERROR (rv);
adpt_mp_gcc_uniphy_port_reset(dev_id, SSDK_PHYSICAL_PORT2);
/* wait uniphy calibration done */
rv = _adpt_mp_uniphy_calibrate(dev_id, index);
SW_RTN_ON_ERROR (rv);
/* enable instance clock */
rv = adpt_mp_gcc_uniphy_port_clock_set(dev_id, SSDK_PHYSICAL_PORT2,
A_TRUE);
SW_RTN_ON_ERROR (rv);
if (SW_OK == rv) {
/* index + 1 point to mp uniphy clock */
ssdk_mp_raw_clock_set(index + 1, UNIPHY_RX, clock);
ssdk_mp_raw_clock_set(index + 1, UNIPHY_TX, clock);
}
if (mode == PORT_WRAPPER_SGMII_CHANNEL0) {
SSDK_DEBUG("mp uniphy %d sgmii configuration is done!\n", index);
} else {
SSDK_DEBUG("mp uniphy %d sgmiiplus configuration is done!\n", index);
}
return rv;
}
sw_error_t
adpt_mp_uniphy_mode_set(a_uint32_t dev_id, a_uint32_t index, a_uint32_t mode)
{
sw_error_t rv = SW_OK;
rv = adpt_mp_uniphy_mode_configure(dev_id, index, mode);
SW_RTN_ON_ERROR(rv);
_adpt_mp_uniphy_clk_output_set(dev_id, index);
/*port2 is connected with PHY, need gpio reset*/
if(!ssdk_port_feature_get(dev_id, SSDK_PHYSICAL_PORT2, PHY_F_FORCE))
{
hsl_port_phy_gpio_reset(dev_id, SSDK_PHYSICAL_PORT2);
msleep(100);
hsl_port_phy_hw_init(dev_id, SSDK_PHYSICAL_PORT2);
}
return rv;
}
sw_error_t adpt_mp_uniphy_init(a_uint32_t dev_id)
{
adpt_api_t *p_adpt_api = NULL;
ADPT_DEV_ID_CHECK(dev_id);
SW_RTN_ON_NULL(p_adpt_api = adpt_api_ptr_get(dev_id));
p_adpt_api->adpt_uniphy_mode_set = adpt_mp_uniphy_mode_set;
return SW_OK;
}
/**
* @}
*/