blob: cf38c00fecf703c632e6745d0598fda65a7c98bc [file] [log] [blame]
/*
* Copyright (c) 2019, 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_global_reg.h"
#include "hppe_global.h"
#include "hppe_uniphy_reg.h"
#include "hppe_uniphy.h"
#include "hppe_init.h"
#include "ssdk_init.h"
#include "ssdk_clk.h"
#include "adpt_hppe.h"
#include "adpt.h"
#include "hppe_reg_access.h"
#include "hsl_phy.h"
#include "adpt_cppe_portctrl.h"
#include "adpt_cppe_uniphy.h"
static sw_error_t
__adpt_cppe_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_cppe_uniphy_port_disable(a_uint32_t dev_id, a_uint32_t uniphy_index,
a_uint32_t port_id)
{
enum unphy_rst_type rst_type = 0;
if (uniphy_index != SSDK_UNIPHY_INSTANCE0) {
return SW_BAD_VALUE;
}
switch (port_id) {
case SSDK_PHYSICAL_PORT1:
rst_type = UNIPHY0_PORT1_DISABLE_E;
break;
case SSDK_PHYSICAL_PORT2:
rst_type = UNIPHY0_PORT2_DISABLE_E;
break;
case SSDK_PHYSICAL_PORT3:
rst_type = UNIPHY0_PORT3_DISABLE_E;
break;
case SSDK_PHYSICAL_PORT4:
rst_type = UNIPHY0_PORT4_DISABLE_E;
break;
case SSDK_PHYSICAL_PORT5:
rst_type = UNIPHY0_PORT5_DISABLE_E;
break;
default:
break;
}
ssdk_uniphy_reset(dev_id, rst_type, SSDK_RESET_ASSERT);
return SW_OK;
}
void
__adpt_cppe_gcc_uniphy_software_reset(a_uint32_t dev_id,
a_uint32_t uniphy_index)
{
a_uint32_t phy_type = 0;
enum unphy_rst_type rst_type = 0;
if (uniphy_index >= SSDK_UNIPHY_INSTANCE2) {
return;
}
if (uniphy_index == SSDK_UNIPHY_INSTANCE0) {
phy_type = hsl_port_phyid_get(dev_id, SSDK_PHYSICAL_PORT4);
if (phy_type == MALIBU2PORT_PHY) {
rst_type = UNIPHY0_PORT_4_5_RESET_E;
} else if (phy_type == QCA8081_PHY_V1_1) {
rst_type = UNIPHY0_PORT_4_RESET_E;
} else {
rst_type = UNIPHY0_SOFT_RESET_E;
}
} else {
rst_type = UNIPHY1_SOFT_RESET_E;
}
ssdk_uniphy_reset(dev_id, rst_type, SSDK_RESET_ASSERT);
msleep(100);
ssdk_uniphy_reset(dev_id, rst_type, SSDK_RESET_DEASSERT);
return;
}
sw_error_t
__adpt_cppe_uniphy_channel_selection_set(a_uint32_t dev_id,
a_uint32_t ch0_selection, a_uint32_t ch4_selection)
{
sw_error_t rv = SW_OK;
union cppe_port_mux_ctrl_u cppe_port_mux_ctrl;
memset(&cppe_port_mux_ctrl, 0, sizeof(cppe_port_mux_ctrl));
ADPT_DEV_ID_CHECK(dev_id);
rv = cppe_port_mux_ctrl_get(dev_id, &cppe_port_mux_ctrl);
SW_RTN_ON_ERROR (rv);
cppe_port_mux_ctrl.bf.pcs0_ch0_sel = ch0_selection;
cppe_port_mux_ctrl.bf.pcs0_ch4_sel = ch4_selection;
rv = cppe_port_mux_ctrl_set(dev_id, &cppe_port_mux_ctrl);
SW_RTN_ON_ERROR (rv);
return rv;
}
static sw_error_t
__adpt_cppe_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;
memset(&uniphy_mode_ctrl, 0, sizeof(uniphy_mode_ctrl));
/* configure uniphy mode ctrl to psgmii/sgmii/sgmiiplus */
rv = hppe_uniphy_mode_ctrl_get(dev_id, uniphy_index, &uniphy_mode_ctrl);
SW_RTN_ON_ERROR (rv);
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_autoneg_mode =
UNIPHY_ATHEROS_NEGOTIATION;
if (mode == PORT_WRAPPER_PSGMII) {
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_psgmii_qsgmii =
UNIPHY_CH0_PSGMII_MODE;
uniphy_mode_ctrl.bf.newaddedfromhere_sg_mode =
UNIPHY_SGMII_MODE_DISABLE;
uniphy_mode_ctrl.bf.newaddedfromhere_sgplus_mode =
UNIPHY_SGMIIPLUS_MODE_DISABLE;
} else if (mode == PORT_WRAPPER_SGMII_CHANNEL0) {
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_psgmii_qsgmii =
UNIPHY_CH0_QSGMII_SGMII_MODE;
uniphy_mode_ctrl.bf.newaddedfromhere_sg_mode =
UNIPHY_SGMII_MODE_ENABLE;
uniphy_mode_ctrl.bf.newaddedfromhere_sgplus_mode =
UNIPHY_SGMIIPLUS_MODE_DISABLE;
} else {
uniphy_mode_ctrl.bf.newaddedfromhere_ch0_psgmii_qsgmii =
UNIPHY_CH0_QSGMII_SGMII_MODE;
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_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;
}
sw_error_t
__adpt_cppe_uniphy_mode_set(a_uint32_t dev_id,
a_uint32_t uniphy_index, a_uint32_t mode)
{
sw_error_t rv = SW_OK;
a_uint32_t i = 0;
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 (uniphy_index != SSDK_UNIPHY_INSTANCE0) {
SSDK_ERROR("uniphy index is %d\n", uniphy_index);
return SW_BAD_VALUE;
}
if ((mode == PORT_WRAPPER_SGMII_CHANNEL0) ||
(mode == PORT_WRAPPER_SGMII_PLUS)) {
/*set the PHY mode to SGMII or SGMIIPLUS*/
rv = hppe_uniphy_phy_mode_ctrl_get(dev_id, uniphy_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;
} else {
uniphy_misc2_phy_mode.bf.phy_mode =
UNIPHY_PHY_SGMIIPLUS_MODE;
}
rv = hppe_uniphy_phy_mode_ctrl_set(dev_id, uniphy_index,
&uniphy_misc2_phy_mode);
SW_RTN_ON_ERROR (rv);
/*reset uniphy*/
rv = __adpt_cppe_uniphy_reset(dev_id, uniphy_index);
SW_RTN_ON_ERROR (rv);
}
/* keep xpcs to reset status */
__adpt_hppe_gcc_uniphy_xpcs_reset(dev_id, uniphy_index, A_TRUE);
SW_RTN_ON_ERROR (rv);
if (mode == PORT_WRAPPER_PSGMII) {
/* disable GCC_UNIPHY0_MISC port 1, 2 and 3*/
for (i = SSDK_PHYSICAL_PORT1; i < SSDK_PHYSICAL_PORT4; i++) {
rv = __adpt_cppe_uniphy_port_disable(dev_id, uniphy_index, i);
SW_RTN_ON_ERROR (rv);
}
/* disable instance0 port 4 and 5 clock */
for (i = SSDK_PHYSICAL_PORT4; i < SSDK_PHYSICAL_PORT6; i++) {
qca_gcc_uniphy_port_clock_set(dev_id, uniphy_index,
i, A_FALSE);
}
rv = __adpt_cppe_uniphy_channel_selection_set(dev_id,
CPPE_PCS0_CHANNEL0_SEL_PSGMII,
CPPE_PCS0_CHANNEL4_SEL_PORT3_CLOCK);
SW_RTN_ON_ERROR (rv);
} else {
/* disable GCC_UNIPHY0_MISC port 2, 3 and 5*/
for (i = SSDK_PHYSICAL_PORT1; i < SSDK_PHYSICAL_PORT6; i++) {
if ((i == SSDK_PHYSICAL_PORT1) || (i == SSDK_PHYSICAL_PORT4)) {
continue;
}
rv = __adpt_cppe_uniphy_port_disable(dev_id, uniphy_index, i);
SW_RTN_ON_ERROR (rv);
}
/* disable instance0 port 4 clock */
qca_gcc_uniphy_port_clock_set(dev_id, uniphy_index,
SSDK_PHYSICAL_PORT4, A_FALSE);
rv = __adpt_cppe_uniphy_channel_selection_set(dev_id,
CPPE_PCS0_CHANNEL0_SEL_SGMIIPLUS,
CPPE_PCS0_CHANNEL4_SEL_PORT5_CLOCK);
SW_RTN_ON_ERROR (rv);
}
rv = __adpt_cppe_uniphy_mode_ctrl_set(dev_id, uniphy_index, mode);
SW_RTN_ON_ERROR (rv);
__adpt_cppe_gcc_uniphy_software_reset(dev_id, uniphy_index);
/* wait uniphy calibration done */
rv = __adpt_hppe_uniphy_calibrate(dev_id, uniphy_index);
SW_RTN_ON_ERROR (rv);
if (mode == PORT_WRAPPER_PSGMII) {
rv = hsl_port_phy_serdes_reset(dev_id);
SW_RTN_ON_ERROR (rv);
/* enable instance0 clock */
for (i = SSDK_PHYSICAL_PORT4; i < SSDK_PHYSICAL_PORT6; i++) {
qca_gcc_uniphy_port_clock_set(dev_id, uniphy_index,
i, A_TRUE);
}
} else {
/* enable instance clock */
qca_gcc_uniphy_port_clock_set(dev_id, uniphy_index,
SSDK_PHYSICAL_PORT4, A_TRUE);
}
if (mode == PORT_WRAPPER_PSGMII) {
SSDK_DEBUG("cypress uniphy %d psgmii configuration is done!\n", uniphy_index);
} else if (mode == PORT_WRAPPER_SGMII_CHANNEL0) {
SSDK_DEBUG("cypress uniphy %d sgmii configuration is done!\n", uniphy_index);
} else {
SSDK_DEBUG("cypress uniphy %d sgmiiplus configuration is done!\n", uniphy_index);
}
return rv;
}
/**
* @}
*/