blob: 7daeec098e9acf6afefcf9bf52b7a945b4f590b6 [file] [log] [blame]
/*
* Copyright (c) 2017-2020, 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.
*
*/
#include <common.h>
#include <net.h>
#include <asm-generic/errno.h>
#include <asm/io.h>
#include <malloc.h>
#include <phy.h>
#include <net.h>
#include <miiphy.h>
#include "ipq5018_uniphy.h"
#include "ipq_phy.h"
static uint32_t cur_mode;
static int ppe_uniphy_calibration(void)
{
int retries = 100, calibration_done = 0;
uint32_t reg_value = 0;
while(calibration_done != UNIPHY_CALIBRATION_DONE) {
mdelay(1);
if (retries-- == 0) {
printf("uniphy callibration time out!\n");
return -1;
}
reg_value = readl(PPE_UNIPHY_BASE + PPE_UNIPHY_OFFSET_CALIB_4);
calibration_done = (reg_value >> 0x7) & 0x1;
}
return 0;
}
static void ppe_gcc_uniphy_soft_reset(void)
{
uint32_t reg_value;
reg_value = readl(GCC_UNIPHY0_MISC);
reg_value |= GCC_UNIPHY_SGMII_SOFT_RESET;
writel(reg_value, GCC_UNIPHY0_MISC);
udelay(500);
reg_value &= ~GCC_UNIPHY_SGMII_SOFT_RESET;
writel(reg_value, GCC_UNIPHY0_MISC);
}
static void ppe_uniphy_sgmii_mode_set(uint32_t mode)
{
uint32_t phy_mode = 0x70;
writel(UNIPHY_MISC2_REG_SGMII_MODE,
PPE_UNIPHY_BASE + UNIPHY_MISC2_REG_OFFSET);
writel(UNIPHY_PLL_RESET_REG_VALUE, PPE_UNIPHY_BASE +
UNIPHY_PLL_RESET_REG_OFFSET);
mdelay(10);
writel(UNIPHY_PLL_RESET_REG_DEFAULT_VALUE, PPE_UNIPHY_BASE +
UNIPHY_PLL_RESET_REG_OFFSET);
mdelay(10);
writel(0x0, GCC_UNIPHY_RX_CBCR);
udelay(500);
writel(0x0, GCC_UNIPHY_TX_CBCR);
udelay(500);
writel(0x0, GCC_GMAC1_RX_CBCR);
udelay(500);
writel(0x0, GCC_GMAC1_TX_CBCR);
udelay(500);
switch (mode) {
case PORT_WRAPPER_SGMII_FIBER:
writel(UNIPHY_SG_MODE, PPE_UNIPHY_BASE + PPE_UNIPHY_MODE_CONTROL);
break;
case PORT_WRAPPER_SGMII0_RGMII4:
case PORT_WRAPPER_SGMII1_RGMII4:
case PORT_WRAPPER_SGMII4_RGMII4:
writel((UNIPHY_SG_MODE | UNIPHY_PSGMII_MAC_MODE),
PPE_UNIPHY_BASE + PPE_UNIPHY_MODE_CONTROL);
break;
case PORT_WRAPPER_SGMII_PLUS:
writel((UNIPHY_SG_PLUS_MODE | UNIPHY_PSGMII_MAC_MODE),
PPE_UNIPHY_BASE + PPE_UNIPHY_MODE_CONTROL);
phy_mode = 0x30;
break;
default:
printf("SGMII Config. wrongly");
break;
}
if ((cur_mode == PORT_WRAPPER_SGMII_PLUS) ||
(mode == PORT_WRAPPER_SGMII_PLUS)){
cur_mode = mode;
ppe_gcc_uniphy_soft_reset();
}
writel(phy_mode, PPE_UNIPHY_BASE + PPE_UNIPHY_ALLREG_DEC_MISC2);
writel(0x1, GCC_UNIPHY_RX_CBCR);
udelay(500);
writel(0x1, GCC_UNIPHY_TX_CBCR);
udelay(500);
writel(0x1, GCC_GMAC1_RX_CBCR);
udelay(500);
writel(0x1, GCC_GMAC1_TX_CBCR);
udelay(500);
ppe_uniphy_calibration();
}
void ppe_uniphy_mode_set(uint32_t mode)
{
/*
* SGMII and SHMII plus confugure in same function
*/
ppe_uniphy_sgmii_mode_set(mode);
}
void ppe_uniphy_set_forceMode(void)
{
uint32_t reg_value;
reg_value = readl(PPE_UNIPHY_BASE + UNIPHY_DEC_CHANNEL_0_INPUT_OUTPUT_4);
reg_value |= UNIPHY_FORCE_SPEED_25M;
writel(reg_value, PPE_UNIPHY_BASE + UNIPHY_DEC_CHANNEL_0_INPUT_OUTPUT_4);
}
void ppe_uniphy_refclk_set(void)
{
/*
* This function drive the uniphy ref clock
* DEC_REFCLKOUTPUTCONTROLREGISTERS
* Its is configured as 25 MHZ
*/
u32 reg_val = readl(PPE_UNIPHY_BASE | UNIPHY_REF_CLK_CTRL_REG);
reg_val |= 0x2;
writel(reg_val, PPE_UNIPHY_BASE | UNIPHY_REF_CLK_CTRL_REG);
mdelay(200);
}