|
|
|
@ -1,6 +1,6 @@
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
|
/*
|
|
|
|
|
* Motorcomm 8511/8521/8531S PHY driver.
|
|
|
|
|
* Motorcomm 8511/8521/8531/8531S PHY driver.
|
|
|
|
|
*
|
|
|
|
|
* Author: Peter Geis <pgwipeout@gmail.com>
|
|
|
|
|
* Author: Frank <Frank.Sae@motor-comm.com>
|
|
|
|
@ -10,9 +10,11 @@
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/phy.h>
|
|
|
|
|
#include <linux/of.h>
|
|
|
|
|
|
|
|
|
|
#define PHY_ID_YT8511 0x0000010a
|
|
|
|
|
#define PHY_ID_YT8521 0x0000011a
|
|
|
|
|
#define PHY_ID_YT8531 0x4f51e91b
|
|
|
|
|
#define PHY_ID_YT8531S 0x4f51e91a
|
|
|
|
|
|
|
|
|
|
/* YT8521/YT8531S Register Overview
|
|
|
|
@ -161,6 +163,11 @@
|
|
|
|
|
|
|
|
|
|
#define YT8521_CHIP_CONFIG_REG 0xA001
|
|
|
|
|
#define YT8521_CCR_SW_RST BIT(15)
|
|
|
|
|
/* 1b0 disable 1.9ns rxc clock delay *default*
|
|
|
|
|
* 1b1 enable 1.9ns rxc clock delay
|
|
|
|
|
*/
|
|
|
|
|
#define YT8521_CCR_RXC_DLY_EN BIT(8)
|
|
|
|
|
#define YT8521_CCR_RXC_DLY_1_900_NS 1900
|
|
|
|
|
|
|
|
|
|
#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0))
|
|
|
|
|
#define YT8521_CCR_MODE_UTP_TO_RGMII 0
|
|
|
|
@ -178,22 +185,29 @@
|
|
|
|
|
#define YT8521_MODE_POLL 0x3
|
|
|
|
|
|
|
|
|
|
#define YT8521_RGMII_CONFIG1_REG 0xA003
|
|
|
|
|
|
|
|
|
|
/* TX Gig-E Delay is bits 3:0, default 0x1
|
|
|
|
|
* TX Fast-E Delay is bits 7:4, default 0xf
|
|
|
|
|
* RX Delay is bits 13:10, default 0x0
|
|
|
|
|
* Delay = 150ps * N
|
|
|
|
|
* On = 2250ps, off = 0ps
|
|
|
|
|
/* 1b0 use original tx_clk_rgmii *default*
|
|
|
|
|
* 1b1 use inverted tx_clk_rgmii.
|
|
|
|
|
*/
|
|
|
|
|
#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10)
|
|
|
|
|
#define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
|
|
|
|
|
#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
|
|
|
|
|
#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4)
|
|
|
|
|
#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
|
|
|
|
|
#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
|
|
|
|
|
#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0)
|
|
|
|
|
#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
|
|
|
|
|
#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
|
|
|
|
|
#define YT8521_RC1R_TX_CLK_SEL_INVERTED BIT(14)
|
|
|
|
|
#define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10)
|
|
|
|
|
#define YT8521_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4)
|
|
|
|
|
#define YT8521_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0)
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_000_NS 0
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_150_NS 1
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_300_NS 2
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_450_NS 3
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_600_NS 4
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_750_NS 5
|
|
|
|
|
#define YT8521_RC1R_RGMII_0_900_NS 6
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_050_NS 7
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_200_NS 8
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_350_NS 9
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_500_NS 10
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_650_NS 11
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_800_NS 12
|
|
|
|
|
#define YT8521_RC1R_RGMII_1_950_NS 13
|
|
|
|
|
#define YT8521_RC1R_RGMII_2_100_NS 14
|
|
|
|
|
#define YT8521_RC1R_RGMII_2_250_NS 15
|
|
|
|
|
|
|
|
|
|
#define YTPHY_MISC_CONFIG_REG 0xA006
|
|
|
|
|
#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
|
|
|
|
@ -222,11 +236,36 @@
|
|
|
|
|
*/
|
|
|
|
|
#define YTPHY_WCR_TYPE_PULSE BIT(0)
|
|
|
|
|
|
|
|
|
|
#define YT8531S_SYNCE_CFG_REG 0xA012
|
|
|
|
|
#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
|
|
|
|
|
#define YTPHY_SYNCE_CFG_REG 0xA012
|
|
|
|
|
#define YT8521_SCR_SYNCE_ENABLE BIT(5)
|
|
|
|
|
/* 1b0 output 25m clock
|
|
|
|
|
* 1b1 output 125m clock *default*
|
|
|
|
|
*/
|
|
|
|
|
#define YT8521_SCR_CLK_FRE_SEL_125M BIT(3)
|
|
|
|
|
#define YT8521_SCR_CLK_SRC_MASK GENMASK(2, 1)
|
|
|
|
|
#define YT8521_SCR_CLK_SRC_PLL_125M 0
|
|
|
|
|
#define YT8521_SCR_CLK_SRC_UTP_RX 1
|
|
|
|
|
#define YT8521_SCR_CLK_SRC_SDS_RX 2
|
|
|
|
|
#define YT8521_SCR_CLK_SRC_REF_25M 3
|
|
|
|
|
#define YT8531_SCR_SYNCE_ENABLE BIT(6)
|
|
|
|
|
/* 1b0 output 25m clock *default*
|
|
|
|
|
* 1b1 output 125m clock
|
|
|
|
|
*/
|
|
|
|
|
#define YT8531_SCR_CLK_FRE_SEL_125M BIT(4)
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_MASK GENMASK(3, 1)
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_PLL_125M 0
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_UTP_RX 1
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_SDS_RX 2
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_REF_25M 4
|
|
|
|
|
#define YT8531_SCR_CLK_SRC_SSC_25M 5
|
|
|
|
|
|
|
|
|
|
/* Extended Register end */
|
|
|
|
|
|
|
|
|
|
#define YTPHY_DTS_OUTPUT_CLK_DIS 0
|
|
|
|
|
#define YTPHY_DTS_OUTPUT_CLK_25M 25000000
|
|
|
|
|
#define YTPHY_DTS_OUTPUT_CLK_125M 125000000
|
|
|
|
|
|
|
|
|
|
struct yt8521_priv {
|
|
|
|
|
/* combo_advertising is used for case of YT8521 in combo mode,
|
|
|
|
|
* this means that yt8521 may work in utp or fiber mode which depends
|
|
|
|
@ -479,6 +518,61 @@ err_restore_page:
|
|
|
|
|
return phy_restore_page(phydev, old_page, ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int yt8531_set_wol(struct phy_device *phydev,
|
|
|
|
|
struct ethtool_wolinfo *wol)
|
|
|
|
|
{
|
|
|
|
|
const u16 mac_addr_reg[] = {
|
|
|
|
|
YTPHY_WOL_MACADDR2_REG,
|
|
|
|
|
YTPHY_WOL_MACADDR1_REG,
|
|
|
|
|
YTPHY_WOL_MACADDR0_REG,
|
|
|
|
|
};
|
|
|
|
|
const u8 *mac_addr;
|
|
|
|
|
u16 mask, val;
|
|
|
|
|
int ret;
|
|
|
|
|
u8 i;
|
|
|
|
|
|
|
|
|
|
if (wol->wolopts & WAKE_MAGIC) {
|
|
|
|
|
mac_addr = phydev->attached_dev->dev_addr;
|
|
|
|
|
|
|
|
|
|
/* Store the device address for the magic packet */
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
|
ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
|
|
|
|
|
((mac_addr[i * 2] << 8)) |
|
|
|
|
|
(mac_addr[i * 2 + 1]));
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enable WOL feature */
|
|
|
|
|
mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
|
|
|
|
|
val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
|
|
|
|
|
val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
|
|
|
|
|
ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
|
|
|
|
|
mask, val);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Enable WOL interrupt */
|
|
|
|
|
ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
|
|
|
|
|
YTPHY_IER_WOL);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
} else {
|
|
|
|
|
/* Disable WOL feature */
|
|
|
|
|
mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
|
|
|
|
|
ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
|
|
|
|
|
mask, 0);
|
|
|
|
|
|
|
|
|
|
/* Disable WOL interrupt */
|
|
|
|
|
ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
|
|
|
|
|
YTPHY_IER_WOL, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int yt8511_read_page(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
return __phy_read(phydev, YT8511_PAGE_SELECT);
|
|
|
|
@ -593,6 +687,153 @@ static int yt8521_write_page(struct phy_device *phydev, int page)
|
|
|
|
|
return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* struct ytphy_cfg_reg_map - map a config value to a register value
|
|
|
|
|
* @cfg: value in device configuration
|
|
|
|
|
* @reg: value in the register
|
|
|
|
|
*/
|
|
|
|
|
struct ytphy_cfg_reg_map {
|
|
|
|
|
u32 cfg;
|
|
|
|
|
u32 reg;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
|
|
|
|
|
/* for tx delay / rx delay with YT8521_CCR_RXC_DLY_EN is not set. */
|
|
|
|
|
{ 0, YT8521_RC1R_RGMII_0_000_NS },
|
|
|
|
|
{ 150, YT8521_RC1R_RGMII_0_150_NS },
|
|
|
|
|
{ 300, YT8521_RC1R_RGMII_0_300_NS },
|
|
|
|
|
{ 450, YT8521_RC1R_RGMII_0_450_NS },
|
|
|
|
|
{ 600, YT8521_RC1R_RGMII_0_600_NS },
|
|
|
|
|
{ 750, YT8521_RC1R_RGMII_0_750_NS },
|
|
|
|
|
{ 900, YT8521_RC1R_RGMII_0_900_NS },
|
|
|
|
|
{ 1050, YT8521_RC1R_RGMII_1_050_NS },
|
|
|
|
|
{ 1200, YT8521_RC1R_RGMII_1_200_NS },
|
|
|
|
|
{ 1350, YT8521_RC1R_RGMII_1_350_NS },
|
|
|
|
|
{ 1500, YT8521_RC1R_RGMII_1_500_NS },
|
|
|
|
|
{ 1650, YT8521_RC1R_RGMII_1_650_NS },
|
|
|
|
|
{ 1800, YT8521_RC1R_RGMII_1_800_NS },
|
|
|
|
|
{ 1950, YT8521_RC1R_RGMII_1_950_NS }, /* default tx/rx delay */
|
|
|
|
|
{ 2100, YT8521_RC1R_RGMII_2_100_NS },
|
|
|
|
|
{ 2250, YT8521_RC1R_RGMII_2_250_NS },
|
|
|
|
|
|
|
|
|
|
/* only for rx delay with YT8521_CCR_RXC_DLY_EN is set. */
|
|
|
|
|
{ 0 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_000_NS },
|
|
|
|
|
{ 150 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_150_NS },
|
|
|
|
|
{ 300 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_300_NS },
|
|
|
|
|
{ 450 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_450_NS },
|
|
|
|
|
{ 600 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_600_NS },
|
|
|
|
|
{ 750 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_750_NS },
|
|
|
|
|
{ 900 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_900_NS },
|
|
|
|
|
{ 1050 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_050_NS },
|
|
|
|
|
{ 1200 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_200_NS },
|
|
|
|
|
{ 1350 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_350_NS },
|
|
|
|
|
{ 1500 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_500_NS },
|
|
|
|
|
{ 1650 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_650_NS },
|
|
|
|
|
{ 1800 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_800_NS },
|
|
|
|
|
{ 1950 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_950_NS },
|
|
|
|
|
{ 2100 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_100_NS },
|
|
|
|
|
{ 2250 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_250_NS }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
|
|
|
|
|
const char *prop_name,
|
|
|
|
|
const struct ytphy_cfg_reg_map *tbl,
|
|
|
|
|
int tb_size,
|
|
|
|
|
u16 *rxc_dly_en,
|
|
|
|
|
u32 dflt)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
|
|
|
int tb_size_half = tb_size / 2;
|
|
|
|
|
u32 val;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (of_property_read_u32(node, prop_name, &val))
|
|
|
|
|
goto err_dts_val;
|
|
|
|
|
|
|
|
|
|
/* when rxc_dly_en is NULL, it is get the delay for tx, only half of
|
|
|
|
|
* tb_size is valid.
|
|
|
|
|
*/
|
|
|
|
|
if (!rxc_dly_en)
|
|
|
|
|
tb_size = tb_size_half;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tb_size; i++) {
|
|
|
|
|
if (tbl[i].cfg == val) {
|
|
|
|
|
if (rxc_dly_en && i < tb_size_half)
|
|
|
|
|
*rxc_dly_en = 0;
|
|
|
|
|
return tbl[i].reg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
phydev_warn(phydev, "Unsupported value %d for %s using default (%u)\n",
|
|
|
|
|
val, prop_name, dflt);
|
|
|
|
|
|
|
|
|
|
err_dts_val:
|
|
|
|
|
/* when rxc_dly_en is not NULL, it is get the delay for rx.
|
|
|
|
|
* The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
|
|
|
|
|
* so YT8521_CCR_RXC_DLY_EN should not be set.
|
|
|
|
|
*/
|
|
|
|
|
if (rxc_dly_en)
|
|
|
|
|
*rxc_dly_en = 0;
|
|
|
|
|
|
|
|
|
|
return dflt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
|
|
|
|
|
u16 rxc_dly_en = YT8521_CCR_RXC_DLY_EN;
|
|
|
|
|
u32 rx_reg, tx_reg;
|
|
|
|
|
u16 mask, val = 0;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
rx_reg = ytphy_get_delay_reg_value(phydev, "rx-internal-delay-ps",
|
|
|
|
|
ytphy_rgmii_delays, tb_size,
|
|
|
|
|
&rxc_dly_en,
|
|
|
|
|
YT8521_RC1R_RGMII_1_950_NS);
|
|
|
|
|
tx_reg = ytphy_get_delay_reg_value(phydev, "tx-internal-delay-ps",
|
|
|
|
|
ytphy_rgmii_delays, tb_size, NULL,
|
|
|
|
|
YT8521_RC1R_RGMII_1_950_NS);
|
|
|
|
|
|
|
|
|
|
switch (phydev->interface) {
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII:
|
|
|
|
|
rxc_dly_en = 0;
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_RXID:
|
|
|
|
|
val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg);
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_TXID:
|
|
|
|
|
rxc_dly_en = 0;
|
|
|
|
|
val |= FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
|
|
|
val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) |
|
|
|
|
|
FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
|
|
|
|
|
break;
|
|
|
|
|
default: /* do not support other modes */
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
|
|
|
|
|
YT8521_CCR_RXC_DLY_EN, rxc_dly_en);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Generally, it is not necessary to adjust YT8521_RC1R_FE_TX_DELAY */
|
|
|
|
|
mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_GE_TX_DELAY_MASK;
|
|
|
|
|
return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
|
|
|
ret = ytphy_rgmii_clk_delay_config(phydev);
|
|
|
|
|
phy_unlock_mdio_bus(phydev);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* yt8521_probe() - read chip config then set suitable polling_mode
|
|
|
|
|
* @phydev: a pointer to a &struct phy_device
|
|
|
|
@ -601,9 +842,12 @@ static int yt8521_write_page(struct phy_device *phydev, int page)
|
|
|
|
|
*/
|
|
|
|
|
static int yt8521_probe(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
|
|
|
struct device *dev = &phydev->mdio.dev;
|
|
|
|
|
struct yt8521_priv *priv;
|
|
|
|
|
int chip_config;
|
|
|
|
|
u16 mask, val;
|
|
|
|
|
u32 freq;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
|
|
@ -648,27 +892,107 @@ static int yt8521_probe(struct phy_device *phydev)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
|
|
|
|
|
freq = YTPHY_DTS_OUTPUT_CLK_DIS;
|
|
|
|
|
|
|
|
|
|
if (phydev->drv->phy_id == PHY_ID_YT8521) {
|
|
|
|
|
switch (freq) {
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_DIS:
|
|
|
|
|
mask = YT8521_SCR_SYNCE_ENABLE;
|
|
|
|
|
val = 0;
|
|
|
|
|
break;
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_25M:
|
|
|
|
|
mask = YT8521_SCR_SYNCE_ENABLE |
|
|
|
|
|
YT8521_SCR_CLK_SRC_MASK |
|
|
|
|
|
YT8521_SCR_CLK_FRE_SEL_125M;
|
|
|
|
|
val = YT8521_SCR_SYNCE_ENABLE |
|
|
|
|
|
FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
|
|
|
|
|
YT8521_SCR_CLK_SRC_REF_25M);
|
|
|
|
|
break;
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_125M:
|
|
|
|
|
mask = YT8521_SCR_SYNCE_ENABLE |
|
|
|
|
|
YT8521_SCR_CLK_SRC_MASK |
|
|
|
|
|
YT8521_SCR_CLK_FRE_SEL_125M;
|
|
|
|
|
val = YT8521_SCR_SYNCE_ENABLE |
|
|
|
|
|
YT8521_SCR_CLK_FRE_SEL_125M |
|
|
|
|
|
FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
|
|
|
|
|
YT8521_SCR_CLK_SRC_PLL_125M);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
phydev_warn(phydev, "Freq err:%u\n", freq);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
|
|
|
|
|
switch (freq) {
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_DIS:
|
|
|
|
|
mask = YT8531_SCR_SYNCE_ENABLE;
|
|
|
|
|
val = 0;
|
|
|
|
|
break;
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_25M:
|
|
|
|
|
mask = YT8531_SCR_SYNCE_ENABLE |
|
|
|
|
|
YT8531_SCR_CLK_SRC_MASK |
|
|
|
|
|
YT8531_SCR_CLK_FRE_SEL_125M;
|
|
|
|
|
val = YT8531_SCR_SYNCE_ENABLE |
|
|
|
|
|
FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
|
|
|
|
|
YT8531_SCR_CLK_SRC_REF_25M);
|
|
|
|
|
break;
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_125M:
|
|
|
|
|
mask = YT8531_SCR_SYNCE_ENABLE |
|
|
|
|
|
YT8531_SCR_CLK_SRC_MASK |
|
|
|
|
|
YT8531_SCR_CLK_FRE_SEL_125M;
|
|
|
|
|
val = YT8531_SCR_SYNCE_ENABLE |
|
|
|
|
|
YT8531_SCR_CLK_FRE_SEL_125M |
|
|
|
|
|
FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
|
|
|
|
|
YT8531_SCR_CLK_SRC_PLL_125M);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
phydev_warn(phydev, "Freq err:%u\n", freq);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
phydev_warn(phydev, "PHY id err\n");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
|
|
|
|
|
val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* yt8531s_probe() - read chip config then set suitable polling_mode
|
|
|
|
|
* @phydev: a pointer to a &struct phy_device
|
|
|
|
|
*
|
|
|
|
|
* returns 0 or negative errno code
|
|
|
|
|
*/
|
|
|
|
|
static int yt8531s_probe(struct phy_device *phydev)
|
|
|
|
|
static int yt8531_probe(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
|
|
|
u16 mask, val;
|
|
|
|
|
u32 freq;
|
|
|
|
|
|
|
|
|
|
/* Disable SyncE clock output by default */
|
|
|
|
|
ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
|
|
|
|
|
YT8531S_SCR_SYNCE_ENABLE, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
|
|
|
|
|
freq = YTPHY_DTS_OUTPUT_CLK_DIS;
|
|
|
|
|
|
|
|
|
|
/* same as yt8521_probe */
|
|
|
|
|
return yt8521_probe(phydev);
|
|
|
|
|
switch (freq) {
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_DIS:
|
|
|
|
|
mask = YT8531_SCR_SYNCE_ENABLE;
|
|
|
|
|
val = 0;
|
|
|
|
|
break;
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_25M:
|
|
|
|
|
mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
|
|
|
|
|
YT8531_SCR_CLK_FRE_SEL_125M;
|
|
|
|
|
val = YT8531_SCR_SYNCE_ENABLE |
|
|
|
|
|
FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
|
|
|
|
|
YT8531_SCR_CLK_SRC_REF_25M);
|
|
|
|
|
break;
|
|
|
|
|
case YTPHY_DTS_OUTPUT_CLK_125M:
|
|
|
|
|
mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
|
|
|
|
|
YT8531_SCR_CLK_FRE_SEL_125M;
|
|
|
|
|
val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
|
|
|
|
|
FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
|
|
|
|
|
YT8531_SCR_CLK_SRC_PLL_125M);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
phydev_warn(phydev, "Freq err:%u\n", freq);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
|
|
|
|
|
val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -1133,65 +1457,128 @@ static int yt8521_resume(struct phy_device *phydev)
|
|
|
|
|
*/
|
|
|
|
|
static int yt8521_config_init(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
|
|
|
int old_page;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
u16 val;
|
|
|
|
|
|
|
|
|
|
old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
|
|
|
|
|
if (old_page < 0)
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
|
|
|
|
|
switch (phydev->interface) {
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII:
|
|
|
|
|
val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
|
|
|
|
|
val |= YT8521_RC1R_RX_DELAY_DIS;
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_RXID:
|
|
|
|
|
val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
|
|
|
|
|
val |= YT8521_RC1R_RX_DELAY_EN;
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_TXID:
|
|
|
|
|
val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
|
|
|
|
|
val |= YT8521_RC1R_RX_DELAY_DIS;
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
|
|
|
val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
|
|
|
|
|
val |= YT8521_RC1R_RX_DELAY_EN;
|
|
|
|
|
break;
|
|
|
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
|
|
|
break;
|
|
|
|
|
default: /* do not support other modes */
|
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set rgmii delay mode */
|
|
|
|
|
if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
|
|
|
|
|
ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
|
|
|
|
|
(YT8521_RC1R_RX_DELAY_MASK |
|
|
|
|
|
YT8521_RC1R_FE_TX_DELAY_MASK |
|
|
|
|
|
YT8521_RC1R_GE_TX_DELAY_MASK),
|
|
|
|
|
val);
|
|
|
|
|
ret = ytphy_rgmii_clk_delay_config(phydev);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* disable auto sleep */
|
|
|
|
|
ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
|
|
|
|
|
YT8521_ESC1R_SLEEP_SW, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
|
|
|
|
|
/* enable RXC clock when no wire plug */
|
|
|
|
|
ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
|
|
|
|
|
YT8521_CGR_RX_CLK_EN, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
|
|
|
|
|
/* disable auto sleep */
|
|
|
|
|
ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
|
|
|
|
|
YT8521_ESC1R_SLEEP_SW, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
|
|
|
|
|
/* enable RXC clock when no wire plug */
|
|
|
|
|
ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
|
|
|
|
|
YT8521_CGR_RX_CLK_EN, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err_restore_page;
|
|
|
|
|
}
|
|
|
|
|
err_restore_page:
|
|
|
|
|
return phy_restore_page(phydev, old_page, ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int yt8531_config_init(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
|
|
|
|
|
/* disable auto sleep */
|
|
|
|
|
ret = ytphy_modify_ext_with_lock(phydev,
|
|
|
|
|
YT8521_EXTREG_SLEEP_CONTROL1_REG,
|
|
|
|
|
YT8521_ESC1R_SLEEP_SW, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
|
|
|
|
|
/* enable RXC clock when no wire plug */
|
|
|
|
|
ret = ytphy_modify_ext_with_lock(phydev,
|
|
|
|
|
YT8521_CLOCK_GATING_REG,
|
|
|
|
|
YT8521_CGR_RX_CLK_EN, 0);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* yt8531_link_change_notify() - Adjust the tx clock direction according to
|
|
|
|
|
* the current speed and dts config.
|
|
|
|
|
* @phydev: a pointer to a &struct phy_device
|
|
|
|
|
*
|
|
|
|
|
* NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
|
|
|
|
|
* keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
|
|
|
|
|
* JH7110.
|
|
|
|
|
*/
|
|
|
|
|
static void yt8531_link_change_notify(struct phy_device *phydev)
|
|
|
|
|
{
|
|
|
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
|
|
|
bool tx_clk_adj_enabled = false;
|
|
|
|
|
bool tx_clk_1000_inverted;
|
|
|
|
|
bool tx_clk_100_inverted;
|
|
|
|
|
bool tx_clk_10_inverted;
|
|
|
|
|
u16 val = 0;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
|
|
|
|
|
tx_clk_adj_enabled = true;
|
|
|
|
|
|
|
|
|
|
if (!tx_clk_adj_enabled)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
|
|
|
|
|
tx_clk_10_inverted = true;
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
|
|
|
|
|
tx_clk_100_inverted = true;
|
|
|
|
|
if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
|
|
|
|
|
tx_clk_1000_inverted = true;
|
|
|
|
|
|
|
|
|
|
if (phydev->speed < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (phydev->speed) {
|
|
|
|
|
case SPEED_1000:
|
|
|
|
|
if (tx_clk_1000_inverted)
|
|
|
|
|
val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
|
|
|
|
|
break;
|
|
|
|
|
case SPEED_100:
|
|
|
|
|
if (tx_clk_100_inverted)
|
|
|
|
|
val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
|
|
|
|
|
break;
|
|
|
|
|
case SPEED_10:
|
|
|
|
|
if (tx_clk_10_inverted)
|
|
|
|
|
val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
|
|
|
|
|
YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* yt8521_prepare_fiber_features() - A small helper function that setup
|
|
|
|
|
* fiber's features.
|
|
|
|
@ -1774,11 +2161,22 @@ static struct phy_driver motorcomm_phy_drvs[] = {
|
|
|
|
|
.suspend = yt8521_suspend,
|
|
|
|
|
.resume = yt8521_resume,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
|
|
|
|
|
.name = "YT8531 Gigabit Ethernet",
|
|
|
|
|
.probe = yt8531_probe,
|
|
|
|
|
.config_init = yt8531_config_init,
|
|
|
|
|
.suspend = genphy_suspend,
|
|
|
|
|
.resume = genphy_resume,
|
|
|
|
|
.get_wol = ytphy_get_wol,
|
|
|
|
|
.set_wol = yt8531_set_wol,
|
|
|
|
|
.link_change_notify = yt8531_link_change_notify,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
|
|
|
|
|
.name = "YT8531S Gigabit Ethernet",
|
|
|
|
|
.get_features = yt8521_get_features,
|
|
|
|
|
.probe = yt8531s_probe,
|
|
|
|
|
.probe = yt8521_probe,
|
|
|
|
|
.read_page = yt8521_read_page,
|
|
|
|
|
.write_page = yt8521_write_page,
|
|
|
|
|
.get_wol = ytphy_get_wol,
|
|
|
|
@ -1795,7 +2193,7 @@ static struct phy_driver motorcomm_phy_drvs[] = {
|
|
|
|
|
|
|
|
|
|
module_phy_driver(motorcomm_phy_drvs);
|
|
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
|
|
|
|
|
MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
|
|
|
|
|
MODULE_AUTHOR("Peter Geis");
|
|
|
|
|
MODULE_AUTHOR("Frank");
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
@ -1803,6 +2201,7 @@ MODULE_LICENSE("GPL");
|
|
|
|
|
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
|
|
|
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
|
|
|
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
|
|
|
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
|
|
|
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
|
|
|
|
|
{ /* sentinel */ }
|
|
|
|
|
};
|
|
|
|
|