M7350/kernel/drivers/net/ethernet/qualcomm/emac/emac_rgmii.c

194 lines
5.5 KiB
C
Raw Permalink Normal View History

2024-09-09 08:57:42 +00:00
/* Copyright (c) 2015, 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 RGMII Controller driver.
*/
#include "emac.h"
#include "emac_hw.h"
/* RGMII specific macros */
#define EMAC_RGMII_PLL_LOCK_TIMEOUT (HZ / 1000) /* 1ms */
#define EMAC_RGMII_CORE_IE_C 0x2001
#define EMAC_RGMII_PLL_L_VAL 0x14
#define EMAC_RGMII_PHY_MODE 0
static int emac_rgmii_config(struct platform_device *pdev,
struct emac_adapter *adpt)
{
/* For rgmii phy, the mdio lines are dedicated pins */
adpt->phy.uses_gpios = false;
return 0;
}
static int emac_rgmii_init(struct emac_adapter *adpt)
{
u32 val;
unsigned long timeout;
struct emac_hw *hw = &adpt->hw;
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1, 0, FREQ_MODE);
emac_reg_w32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR18,
EMAC_RGMII_CORE_IE_C);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
RGMII_PHY_MODE_BMSK,
(EMAC_RGMII_PHY_MODE << RGMII_PHY_MODE_SHFT));
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, PHY_RESET, 0);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3,
PLL_L_VAL_5_0_BMSK,
(EMAC_RGMII_PLL_L_VAL << PLL_L_VAL_5_0_SHFT));
/* Reset PHY PLL */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3, 0, PLL_RESET);
/* Ensure PLL is in reset */
wmb();
usleep_range(10, 15);
/* power down analog sections of PLL and ensure the same */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3, 0, BYPASSNL);
/* Ensure power down is complete before setting configuration */
wmb();
usleep_range(10, 15);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, CKEDGE_SEL);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
TX_ID_EN_L, RX_ID_EN_L);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2,
HDRIVE_BMSK, (0x0 << HDRIVE_SHFT));
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, WOL_EN, 0);
/* Reset PHY */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, PHY_RESET);
/* Ensure reset is complete before pulling out of reset */
wmb();
usleep_range(10, 15);
/* Pull PHY out of reset */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, PHY_RESET, 0);
/* Ensure that pulling PHY out of reset is complete before enabling the
* enabling
*/
wmb();
usleep_range(1000, 1500);
/* Pull PHY PLL out of reset */
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR3, PLL_RESET, 0);
/* Ensure PLL is enabled before enabling the AHB clock*/
wmb();
usleep_range(10, 15);
emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR5,
0, RMII_125_CLK_EN);
/* Ensure AHB clock enable is written to HW before the loop waiting for
* it to complete
*/
wmb();
/* wait for PLL to lock */
timeout = jiffies + EMAC_RGMII_PLL_LOCK_TIMEOUT;
do {
val = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_STATUS);
if (val & PLL_LOCK_DET)
break;
usleep_range(100, 150);
} while (time_after_eq(timeout, jiffies));
if (time_after(jiffies, timeout)) {
emac_err(adpt, "PHY PLL lock failed\n");
return -EIO;
}
return 0;
}
static void emac_rgmii_reset_nop(struct emac_adapter *adpt)
{
}
static int emac_rgmii_init_ephy(struct emac_adapter *adpt)
{
u16 val;
struct emac_phy *phy = &adpt->phy;
/* disable hibernation in case of rgmii phy */
int retval = emac_phy_write(adpt, phy->addr, MII_DBG_ADDR,
HIBERNATE_CTRL_REG);
if (retval)
return retval;
retval = emac_phy_read(adpt, phy->addr, MII_DBG_DATA, &val);
if (retval)
return retval;
val &= ~HIBERNATE_EN;
retval = emac_phy_write(adpt, phy->addr, MII_DBG_DATA, val);
return retval;
}
static int emac_rgmii_link_setup_no_ephy(struct emac_adapter *adpt, u32 speed,
bool autoneg)
{
emac_err(adpt, "error rgmii can't setup phy link without ephy\n");
return -ENOTSUPP;
}
static int emac_rgmii_link_check_no_ephy(struct emac_adapter *adpt, u32 *speed,
bool *link_up)
{
emac_err(adpt, "error rgmii can't check phy link without ephy\n");
return -ENOTSUPP;
}
static int emac_rgmii_up_nop(struct emac_adapter *adpt)
{
return 0;
}
static void emac_rgmii_down_nop(struct emac_adapter *adpt)
{
}
static void emac_rgmii_tx_clk_set_rate(struct emac_adapter *adpt)
{
switch (adpt->phy.link_speed) {
case EMAC_LINK_SPEED_1GB_FULL:
clk_set_rate(adpt->clk[EMAC_CLK_TX].clk, EMC_CLK_RATE_125MHZ);
break;
case EMAC_LINK_SPEED_100_FULL:
case EMAC_LINK_SPEED_100_HALF:
clk_set_rate(adpt->clk[EMAC_CLK_TX].clk, EMC_CLK_RATE_25MHZ);
break;
case EMAC_LINK_SPEED_10_FULL:
case EMAC_LINK_SPEED_10_HALF:
clk_set_rate(adpt->clk[EMAC_CLK_TX].clk, EMC_CLK_RATE_2_5MHZ);
break;
}
}
static void emac_rgmii_periodic_nop(struct emac_adapter *adpt)
{
}
struct emac_phy_ops emac_rgmii_ops = {
.config = emac_rgmii_config,
.up = emac_rgmii_up_nop,
.down = emac_rgmii_down_nop,
.init = emac_rgmii_init,
.reset = emac_rgmii_reset_nop,
.init_ephy = emac_rgmii_init_ephy,
.link_setup_no_ephy = emac_rgmii_link_setup_no_ephy,
.link_check_no_ephy = emac_rgmii_link_check_no_ephy,
.tx_clk_set_rate = emac_rgmii_tx_clk_set_rate,
.periodic_task = emac_rgmii_periodic_nop,
};