1263 lines
30 KiB
C
1263 lines
30 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Driver for Motorcomm PHYs
|
|
*
|
|
* Author: Peter Geis <pgwipeout@gmail.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/phy.h>
|
|
|
|
#define PHY_ID_YT8011 0x4f51eb01
|
|
#define PHY_ID_YT8511 0x0000010a
|
|
#define PHY_ID_YT8512 0x00000118
|
|
#define PHY_ID_YT8512B 0x00000128
|
|
#define PHY_ID_YT8522 0x4f51e928
|
|
#define PHY_ID_YT8531S 0x4f51e91a
|
|
#define PHY_ID_YT8531 0x4f51e91b
|
|
|
|
enum {
|
|
YT8011_RGMII_DVDDIO_1V8 = 1,
|
|
YT8011_RGMII_DVDDIO_2V5,
|
|
YT8011_RGMII_DVDDIO_3V3
|
|
};
|
|
|
|
struct yt8011_priv {
|
|
u8 polling_mode;
|
|
u8 chip_mode;
|
|
u8 vddio;
|
|
};
|
|
|
|
#define YT8011_SPEED_MODE 0xc000
|
|
#define YT8011_DUPLEX 0x2000
|
|
#define YT8011_SPEED_MODE_BIT 14
|
|
#define YT8011_DUPLEX_BIT 13
|
|
#define YT8011_LINK_STATUS_BIT 10
|
|
|
|
#define REG_PHY_SPEC_STATUS 0x11
|
|
#define REG_DEBUG_ADDR_OFFSET 0x1e
|
|
#define REG_DEBUG_DATA 0x1f
|
|
#define REG_MII_MMD_CTRL 0x0D /* MMD access control register */
|
|
#define REG_MII_MMD_DATA 0x0E /* MMD access data register */
|
|
|
|
#define YT8511_PAGE_SELECT 0x1e
|
|
#define YT8511_PAGE 0x1f
|
|
#define YT8511_EXT_CLK_GATE 0x0c
|
|
#define YT8511_EXT_DELAY_DRIVE 0x0d
|
|
#define YT8511_EXT_SLEEP_CTRL 0x27
|
|
|
|
/* 2b00 25m from pll
|
|
* 2b01 25m from xtl *default*
|
|
* 2b10 62.m from pll
|
|
* 2b11 125m from pll
|
|
*/
|
|
#define YT8511_CLK_125M (BIT(2) | BIT(1))
|
|
#define YT8511_PLLON_SLP BIT(14)
|
|
|
|
/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */
|
|
#define YT8511_DELAY_RX BIT(0)
|
|
|
|
/* TX Gig-E Delay is bits 7:4, default 0x5
|
|
* TX Fast-E Delay is bits 15:12, default 0xf
|
|
* Delay = 150ps * N - 250ps
|
|
* On = 2000ps, off = 50ps
|
|
*/
|
|
#define YT8511_DELAY_GE_TX_EN (0xf << 4)
|
|
#define YT8511_DELAY_GE_TX_DIS (0x2 << 4)
|
|
#define YT8511_DELAY_FE_TX_EN (0xf << 12)
|
|
#define YT8511_DELAY_FE_TX_DIS (0x2 << 12)
|
|
|
|
#define YT8512_EXTREG_AFE_PLL 0x50
|
|
#define YT8512_EXTREG_EXTEND_COMBO 0x4000
|
|
#define YT8512_EXTREG_LED0 0x40c0
|
|
#define YT8512_EXTREG_LED1 0x40c3
|
|
|
|
#define YT8512_EXTREG_SLEEP_CONTROL1 0x2027
|
|
|
|
#define YT_SOFTWARE_RESET 0x8000
|
|
|
|
#define YT8512_CONFIG_PLL_REFCLK_SEL_EN 0x0040
|
|
#define YT8512_CONTROL1_RMII_EN 0x0001
|
|
#define YT8512_LED0_ACT_BLK_IND 0x1000
|
|
#define YT8512_LED0_DIS_LED_AN_TRY 0x0001
|
|
#define YT8512_LED0_BT_BLK_EN 0x0002
|
|
#define YT8512_LED0_HT_BLK_EN 0x0004
|
|
#define YT8512_LED0_COL_BLK_EN 0x0008
|
|
#define YT8512_LED0_BT_ON_EN 0x0010
|
|
#define YT8512_LED1_BT_ON_EN 0x0010
|
|
#define YT8512_LED1_TXACT_BLK_EN 0x0100
|
|
#define YT8512_LED1_RXACT_BLK_EN 0x0200
|
|
#define YT8512_SPEED_MODE 0xc000
|
|
#define YT8512_DUPLEX 0x2000
|
|
|
|
#define YT8512_SPEED_MODE_BIT 14
|
|
#define YT8512_DUPLEX_BIT 13
|
|
#define YT8512_EN_SLEEP_SW_BIT 15
|
|
|
|
#define YT8522_TX_CLK_DELAY 0x4210
|
|
#define YT8522_ANAGLOG_IF_CTRL 0x4008
|
|
#define YT8522_DAC_CTRL 0x2057
|
|
#define YT8522_INTERPOLATOR_FILTER_1 0x14
|
|
#define YT8522_INTERPOLATOR_FILTER_2 0x15
|
|
#define YT8522_EXTENDED_COMBO_CTRL_1 0x4000
|
|
|
|
#define YTXXXX_SPEED_MODE 0xc000
|
|
#define YTXXXX_DUPLEX 0x2000
|
|
#define YTXXXX_SPEED_MODE_BIT 14
|
|
#define YTXXXX_DUPLEX_BIT 13
|
|
#define YTXXXX_AUTO_NEGOTIATION_BIT 12
|
|
#define YTXXXX_ASYMMETRIC_PAUSE_BIT 11
|
|
#define YTXXXX_PAUSE_BIT 10
|
|
#define YTXXXX_LINK_STATUS_BIT 10
|
|
|
|
/* if system depends on ethernet packet to restore from sleep,
|
|
* please define this macro to 1 otherwise, define it to 0.
|
|
*/
|
|
#define SYS_WAKEUP_BASED_ON_ETH_PKT 1
|
|
|
|
/* to enable system WOL feature of phy, please define this macro to 1
|
|
* otherwise, define it to 0.
|
|
*/
|
|
#define YTPHY_WOL_FEATURE_ENABLE 0
|
|
|
|
#if (YTPHY_WOL_FEATURE_ENABLE)
|
|
#undef SYS_WAKEUP_BASED_ON_ETH_PKT
|
|
#define SYS_WAKEUP_BASED_ON_ETH_PKT 1
|
|
#endif
|
|
|
|
struct yt8xxx_priv {
|
|
u8 polling_mode;
|
|
u8 chip_mode;
|
|
};
|
|
|
|
/* for YT8531 package A xtal init config */
|
|
#define YTPHY8531A_XTAL_INIT 0
|
|
|
|
#define REG_PHY_SPEC_STATUS 0x11
|
|
#define REG_DEBUG_ADDR_OFFSET 0x1e
|
|
#define REG_DEBUG_DATA 0x1f
|
|
|
|
#define YT8521_EXTREG_SLEEP_CONTROL1 0x27
|
|
#define YT8521_EN_SLEEP_SW_BIT 15
|
|
|
|
#define YT8521_SPEED_MODE 0xc000
|
|
#define YT8521_DUPLEX 0x2000
|
|
#define YT8521_SPEED_MODE_BIT 14
|
|
#define YT8521_DUPLEX_BIT 13
|
|
#define YT8521_LINK_STATUS_BIT 10
|
|
|
|
/* YT8521 polling mode */
|
|
#define YT8521_PHY_MODE_FIBER 1 /* fiber mode only */
|
|
#define YT8521_PHY_MODE_UTP 2 /* utp mode only */
|
|
#define YT8521_PHY_MODE_POLL 3 /* fiber and utp, poll mode */
|
|
|
|
static int yt8521_hw_strap_polling(struct phy_device *phydev);
|
|
#define YT8521_PHY_MODE_CURR yt8521_hw_strap_polling(phydev)
|
|
|
|
static int yt8511_read_page(struct phy_device *phydev)
|
|
{
|
|
return __phy_read(phydev, YT8511_PAGE_SELECT);
|
|
};
|
|
|
|
static int yt8511_write_page(struct phy_device *phydev, int page)
|
|
{
|
|
return __phy_write(phydev, YT8511_PAGE_SELECT, page);
|
|
};
|
|
|
|
static int yt8511_config_init(struct phy_device *phydev)
|
|
{
|
|
int oldpage, ret = 0;
|
|
unsigned int ge, fe;
|
|
|
|
oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE);
|
|
if (oldpage < 0)
|
|
goto err_restore_page;
|
|
|
|
/* set rgmii delay mode */
|
|
switch (phydev->interface) {
|
|
case PHY_INTERFACE_MODE_RGMII:
|
|
ge = YT8511_DELAY_GE_TX_DIS;
|
|
fe = YT8511_DELAY_FE_TX_DIS;
|
|
break;
|
|
case PHY_INTERFACE_MODE_RGMII_RXID:
|
|
ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS;
|
|
fe = YT8511_DELAY_FE_TX_DIS;
|
|
break;
|
|
case PHY_INTERFACE_MODE_RGMII_TXID:
|
|
ge = YT8511_DELAY_GE_TX_EN;
|
|
fe = YT8511_DELAY_FE_TX_EN;
|
|
break;
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN;
|
|
fe = YT8511_DELAY_FE_TX_EN;
|
|
break;
|
|
default: /* do not support other modes */
|
|
ret = -EOPNOTSUPP;
|
|
goto err_restore_page;
|
|
}
|
|
|
|
ret = __phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge);
|
|
if (ret < 0)
|
|
goto err_restore_page;
|
|
|
|
/* set clock mode to 125mhz */
|
|
ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M);
|
|
if (ret < 0)
|
|
goto err_restore_page;
|
|
|
|
/* fast ethernet delay is in a separate page */
|
|
ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE);
|
|
if (ret < 0)
|
|
goto err_restore_page;
|
|
|
|
ret = __phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe);
|
|
if (ret < 0)
|
|
goto err_restore_page;
|
|
|
|
/* leave pll enabled in sleep */
|
|
ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL);
|
|
if (ret < 0)
|
|
goto err_restore_page;
|
|
|
|
ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP);
|
|
if (ret < 0)
|
|
goto err_restore_page;
|
|
|
|
err_restore_page:
|
|
return phy_restore_page(phydev, oldpage, ret);
|
|
}
|
|
|
|
static u32 ytphy_read_ext(struct phy_device *phydev, u32 regnum)
|
|
{
|
|
int ret;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_read(phydev, REG_DEBUG_DATA);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
err_handle:
|
|
phy_unlock_mdio_bus(phydev);
|
|
return ret;
|
|
}
|
|
|
|
static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
|
|
{
|
|
int ret;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
ret = __phy_write(phydev, REG_DEBUG_DATA, val);
|
|
if (ret < 0)
|
|
goto err_handle;
|
|
|
|
err_handle:
|
|
phy_unlock_mdio_bus(phydev);
|
|
return ret;
|
|
}
|
|
|
|
static int __maybe_unused ytphy_write_mmd(struct phy_device *phydev, u16 device, u16 reg, u16 value)
|
|
{
|
|
int ret = 0;
|
|
|
|
phy_lock_mdio_bus(phydev);
|
|
|
|
__phy_write(phydev, REG_MII_MMD_CTRL, device);
|
|
__phy_write(phydev, REG_MII_MMD_DATA, reg);
|
|
__phy_write(phydev, REG_MII_MMD_CTRL, device | 0x4000);
|
|
__phy_write(phydev, REG_MII_MMD_DATA, value);
|
|
|
|
phy_unlock_mdio_bus(phydev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int yt8011_probe(struct phy_device *phydev)
|
|
{
|
|
struct device *dev = &phydev->mdio.dev;
|
|
struct yt8011_priv *priv;
|
|
int chip_config;
|
|
|
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
phydev->priv = priv;
|
|
|
|
/* ext reg 0x9030 bit0
|
|
* 0 = chip works in RGMII mode; 1 = chip works in SGMII mode
|
|
*/
|
|
chip_config = ytphy_read_ext(phydev, 0x9030);
|
|
priv->chip_mode = chip_config & 0x1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8011_config_aneg(struct phy_device *phydev)
|
|
{
|
|
phydev->speed = SPEED_1000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8011_config_vddio(struct phy_device *phydev)
|
|
{
|
|
struct yt8011_priv *priv = phydev->priv;
|
|
|
|
if (!(priv->chip_mode)) { /* rgmii config */
|
|
switch (priv->vddio) {
|
|
case YT8011_RGMII_DVDDIO_2V5:
|
|
dev_info(&phydev->mdio.dev, "config PHY vddio 2v5\n");
|
|
ytphy_write_ext(phydev, 0x9000, 0x8000);
|
|
ytphy_write_ext(phydev, 0x0062, 0x0000);
|
|
ytphy_write_ext(phydev, 0x9000, 0x0000);
|
|
ytphy_write_ext(phydev, 0x9031, 0xb200);
|
|
ytphy_write_ext(phydev, 0x9111, 0x5);
|
|
ytphy_write_ext(phydev, 0x9114, 0x3939);
|
|
ytphy_write_ext(phydev, 0x9112, 0xf);
|
|
ytphy_write_ext(phydev, 0x9110, 0x0);
|
|
ytphy_write_ext(phydev, 0x9113, 0x10);
|
|
ytphy_write_ext(phydev, 0x903d, 0x2);
|
|
break;
|
|
case YT8011_RGMII_DVDDIO_1V8:
|
|
dev_info(&phydev->mdio.dev, "config PHY for 1v8\n");
|
|
ytphy_write_ext(phydev, 0x9000, 0x8000);
|
|
ytphy_write_ext(phydev, 0x0062, 0x0000);
|
|
ytphy_write_ext(phydev, 0x9000, 0x0000);
|
|
ytphy_write_ext(phydev, 0x9031, 0xb200);
|
|
ytphy_write_ext(phydev, 0x9116, 0x6);
|
|
ytphy_write_ext(phydev, 0x9119, 0x3939);
|
|
ytphy_write_ext(phydev, 0x9117, 0xf);
|
|
ytphy_write_ext(phydev, 0x9115, 0x0);
|
|
ytphy_write_ext(phydev, 0x9118, 0x20);
|
|
ytphy_write_ext(phydev, 0x903d, 0x3);
|
|
break;
|
|
case YT8011_RGMII_DVDDIO_3V3:
|
|
default:
|
|
dev_info(&phydev->mdio.dev, "config PHY for 3v3\n");
|
|
ytphy_write_ext(phydev, 0x9000, 0x8000);
|
|
ytphy_write_ext(phydev, 0x0062, 0x0000);
|
|
ytphy_write_ext(phydev, 0x9000, 0x0000);
|
|
ytphy_write_ext(phydev, 0x9031, 0xb200);
|
|
ytphy_write_ext(phydev, 0x903b, 0x0040);
|
|
ytphy_write_ext(phydev, 0x903e, 0x3b3b);
|
|
ytphy_write_ext(phydev, 0x903c, 0xf);
|
|
ytphy_write_ext(phydev, 0x903d, 0x1000);
|
|
ytphy_write_ext(phydev, 0x9038, 0x0000);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static int yt8011_aneg_done(struct phy_device *phydev)
|
|
{
|
|
int link_utp = 0;
|
|
|
|
/* UTP */
|
|
ytphy_write_ext(phydev, 0x9000, 0);
|
|
link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8011_LINK_STATUS_BIT)));
|
|
|
|
return !!(link_utp);
|
|
}
|
|
|
|
static int yt8011_automotive_adjust_status(struct phy_device *phydev, int val)
|
|
{
|
|
int speed_mode;
|
|
int speed = SPEED_UNKNOWN;
|
|
|
|
speed_mode = (val & YT8011_SPEED_MODE) >> YT8011_SPEED_MODE_BIT;
|
|
switch (speed_mode) {
|
|
case 1:
|
|
speed = SPEED_100;
|
|
break;
|
|
case 2:
|
|
speed = SPEED_1000;
|
|
break;
|
|
default:
|
|
speed = SPEED_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
phydev->speed = speed;
|
|
phydev->duplex = DUPLEX_FULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8011_read_status(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
int val;
|
|
int link;
|
|
int link_utp = 0;
|
|
|
|
/* UTP */
|
|
ret = ytphy_write_ext(phydev, 0x9000, 0x0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
link = val & (BIT(YT8011_LINK_STATUS_BIT));
|
|
if (link) {
|
|
link_utp = 1;
|
|
yt8011_automotive_adjust_status(phydev, val);
|
|
} else {
|
|
link_utp = 0;
|
|
}
|
|
|
|
if (link_utp) {
|
|
if (phydev->link == 0)
|
|
phydev->link = 1;
|
|
} else {
|
|
if (phydev->link == 1)
|
|
phydev->link = 0;
|
|
}
|
|
|
|
if (link_utp)
|
|
ytphy_write_ext(phydev, 0x9000, 0x0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ytphy_soft_reset(struct phy_device *phydev)
|
|
{
|
|
int ret = 0, val = 0;
|
|
|
|
val = phy_read(phydev, MII_BMCR);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
ret = phy_write(phydev, MII_BMCR, val | BMCR_RESET);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int yt8011_soft_reset(struct phy_device *phydev)
|
|
{
|
|
struct yt8011_priv *priv = phydev->priv;
|
|
|
|
/* utp */
|
|
ytphy_write_ext(phydev, 0x9000, 0x0);
|
|
ytphy_soft_reset(phydev);
|
|
|
|
if (priv->chip_mode) { /* sgmm */
|
|
ytphy_write_ext(phydev, 0x9000, 0x8000);
|
|
ytphy_soft_reset(phydev);
|
|
|
|
/* restore utp space */
|
|
ytphy_write_ext(phydev, 0x9000, 0x0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8512_clk_init(struct phy_device *phydev)
|
|
{
|
|
struct device_node *node = phydev->mdio.dev.of_node;
|
|
const char *strings = NULL;
|
|
int ret;
|
|
int val;
|
|
|
|
if (node && node->parent && node->parent->parent) {
|
|
ret = of_property_read_string(node->parent->parent,
|
|
"clock_in_out", &strings);
|
|
if (!ret) {
|
|
if (!strcmp(strings, "input"))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
val = ytphy_read_ext(phydev, YT8512_EXTREG_AFE_PLL);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val |= YT8512_CONFIG_PLL_REFCLK_SEL_EN;
|
|
|
|
ret = ytphy_write_ext(phydev, YT8512_EXTREG_AFE_PLL, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
val = ytphy_read_ext(phydev, YT8512_EXTREG_EXTEND_COMBO);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val |= YT8512_CONTROL1_RMII_EN;
|
|
|
|
ret = ytphy_write_ext(phydev, YT8512_EXTREG_EXTEND_COMBO, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
val = phy_read(phydev, MII_BMCR);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val |= YT_SOFTWARE_RESET;
|
|
ret = phy_write(phydev, MII_BMCR, val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int yt8512_led_init(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
int val;
|
|
int mask;
|
|
|
|
val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val |= YT8512_LED0_ACT_BLK_IND;
|
|
|
|
mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN |
|
|
YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN |
|
|
YT8512_LED0_BT_ON_EN;
|
|
val &= ~mask;
|
|
|
|
ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val |= YT8512_LED1_BT_ON_EN;
|
|
|
|
mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN;
|
|
val &= ~mask;
|
|
|
|
ret = ytphy_write_ext(phydev, YT8512_LED1_BT_ON_EN, val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int yt8011_config_init(struct phy_device *phydev)
|
|
{
|
|
struct yt8011_priv *priv = phydev->priv;
|
|
struct device_node *np = phydev->mdio.dev.of_node;
|
|
const char *vddio_conf;
|
|
|
|
phydev->autoneg = AUTONEG_DISABLE;
|
|
|
|
if (!np) {
|
|
dev_err(&phydev->mdio.dev, "Device Tree node is missing\n");
|
|
priv->vddio = YT8011_RGMII_DVDDIO_3V3;
|
|
} else {
|
|
if (of_property_read_string(np, "motorcomm,vddio", &vddio_conf)) {
|
|
dev_err(&phydev->mdio.dev, "Missing 'motorcomm,vddio' property in DTS, using 3v3 default\n");
|
|
priv->vddio = YT8011_RGMII_DVDDIO_3V3;
|
|
} else {
|
|
if (!strcasecmp(vddio_conf, "1v8")) {
|
|
priv->vddio = YT8011_RGMII_DVDDIO_1V8;
|
|
} else if (!strcasecmp(vddio_conf, "2v5")) {
|
|
priv->vddio = YT8011_RGMII_DVDDIO_2V5;
|
|
} else if (!strcasecmp(vddio_conf, "3v3")) {
|
|
priv->vddio = YT8011_RGMII_DVDDIO_3V3;
|
|
} else {
|
|
dev_err(&phydev->mdio.dev, "Invalid 'motorcomm,vddio' value, using 3v3 default\n");
|
|
priv->vddio = YT8011_RGMII_DVDDIO_3V3;
|
|
}
|
|
}
|
|
}
|
|
/* UTP */
|
|
ytphy_write_ext(phydev, 0x9000, 0x0);
|
|
|
|
ytphy_write_ext(phydev, 0x1008, 0x2119);
|
|
ytphy_write_ext(phydev, 0x1092, 0x712);
|
|
ytphy_write_ext(phydev, 0x90bc, 0x6661);
|
|
ytphy_write_ext(phydev, 0x90b9, 0x620b);
|
|
ytphy_write_ext(phydev, 0x2001, 0x6418);
|
|
ytphy_write_ext(phydev, 0x1019, 0x3712);
|
|
ytphy_write_ext(phydev, 0x101a, 0x3713);
|
|
ytphy_write_ext(phydev, 0x2015, 0x1012);
|
|
ytphy_write_ext(phydev, 0x2005, 0x810);
|
|
ytphy_write_ext(phydev, 0x2013, 0xff06);
|
|
ytphy_write_ext(phydev, 0x1053, 0xf);
|
|
ytphy_write_ext(phydev, 0x105e, 0xa46c);
|
|
ytphy_write_ext(phydev, 0x1088, 0x002b);
|
|
ytphy_write_ext(phydev, 0x1088, 0x002b);
|
|
ytphy_write_ext(phydev, 0x1088, 0xb);
|
|
ytphy_write_ext(phydev, 0x3008, 0x143);
|
|
ytphy_write_ext(phydev, 0x3009, 0x1918);
|
|
ytphy_write_ext(phydev, 0x9095, 0x1a1a);
|
|
ytphy_write_ext(phydev, 0x9096, 0x1a10);
|
|
ytphy_write_ext(phydev, 0x9097, 0x101a);
|
|
ytphy_write_ext(phydev, 0x9098, 0x01ff);
|
|
yt8011_config_vddio(phydev);
|
|
|
|
ytphy_soft_reset(phydev);
|
|
|
|
netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n", __func__, phydev->mdio.addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8512_config_init(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
int val;
|
|
|
|
ret = yt8512_clk_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = yt8512_led_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* disable auto sleep */
|
|
val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val &= (~BIT(YT8512_EN_SLEEP_SW_BIT));
|
|
|
|
ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int yt8512_read_status(struct phy_device *phydev)
|
|
{
|
|
int ret;
|
|
int val;
|
|
int speed, speed_mode, duplex;
|
|
|
|
ret = genphy_update_link(phydev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
duplex = (val & YT8512_DUPLEX) >> YT8512_DUPLEX_BIT;
|
|
speed_mode = (val & YT8512_SPEED_MODE) >> YT8512_SPEED_MODE_BIT;
|
|
switch (speed_mode) {
|
|
case 0:
|
|
speed = SPEED_10;
|
|
break;
|
|
case 1:
|
|
speed = SPEED_100;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
default:
|
|
speed = SPEED_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
phydev->speed = speed;
|
|
phydev->duplex = duplex;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8521_soft_reset(struct phy_device *phydev)
|
|
{
|
|
int ret = 0, val;
|
|
|
|
if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_UTP) {
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
ret = ytphy_soft_reset(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_FIBER) {
|
|
ytphy_write_ext(phydev, 0xa000, 2);
|
|
ret = ytphy_soft_reset(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
}
|
|
|
|
if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_POLL) {
|
|
val = ytphy_read_ext(phydev, 0xa001);
|
|
ytphy_write_ext(phydev, 0xa001, (val & ~0x8000));
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
ret = ytphy_soft_reset(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8521_hw_strap_polling(struct phy_device *phydev)
|
|
{
|
|
int val = 0;
|
|
|
|
val = ytphy_read_ext(phydev, 0xa001) & 0x7;
|
|
switch (val) {
|
|
case 1:
|
|
case 4:
|
|
case 5:
|
|
return YT8521_PHY_MODE_FIBER;
|
|
case 2:
|
|
case 6:
|
|
case 7:
|
|
return YT8521_PHY_MODE_POLL;
|
|
case 3:
|
|
case 0:
|
|
default:
|
|
return YT8521_PHY_MODE_UTP;
|
|
}
|
|
}
|
|
|
|
static int yt8521_config_init(struct phy_device *phydev)
|
|
{
|
|
int ret, hw_strap_mode;
|
|
int val;
|
|
|
|
#if (YTPHY_WOL_FEATURE_ENABLE)
|
|
struct ethtool_wolinfo wol;
|
|
|
|
/* set phy wol enable */
|
|
memset(&wol, 0x0, sizeof(struct ethtool_wolinfo));
|
|
wol.wolopts |= WAKE_MAGIC;
|
|
ytphy_wol_feature_set(phydev, &wol);
|
|
#endif
|
|
|
|
phydev->irq = PHY_POLL;
|
|
/* NOTE: this function should not be called more than one for each chip. */
|
|
hw_strap_mode = ytphy_read_ext(phydev, 0xa001) & 0x7;
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
|
|
/* disable auto sleep */
|
|
val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
|
|
ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* enable RXC clock when no wire plug */
|
|
val = ytphy_read_ext(phydev, 0xc);
|
|
if (val < 0)
|
|
return val;
|
|
val &= ~(1 << 12);
|
|
ret = ytphy_write_ext(phydev, 0xc, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
netdev_info(phydev->attached_dev, "%s done, phy addr: %d, strap mode = %d, polling mode = %d\n",
|
|
__func__, phydev->mdio.addr, hw_strap_mode, yt8521_hw_strap_polling(phydev));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* for fiber mode, there is no 10M speed mode and
|
|
* this function is for this purpose.
|
|
*/
|
|
static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp)
|
|
{
|
|
int speed = SPEED_UNKNOWN;
|
|
int speed_mode, duplex;
|
|
|
|
if (is_utp)
|
|
duplex = (val & YT8521_DUPLEX) >> YT8521_DUPLEX_BIT;
|
|
else
|
|
duplex = 1;
|
|
speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT;
|
|
switch (speed_mode) {
|
|
case 0:
|
|
if (is_utp)
|
|
speed = SPEED_10;
|
|
break;
|
|
case 1:
|
|
speed = SPEED_100;
|
|
break;
|
|
case 2:
|
|
speed = SPEED_1000;
|
|
break;
|
|
case 3:
|
|
break;
|
|
default:
|
|
speed = SPEED_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
phydev->speed = speed;
|
|
phydev->duplex = duplex;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* for fiber mode, when speed is 100M, there is no definition for
|
|
* autonegotiation, and this function handles this case and return
|
|
* 1 per linux kernel's polling.
|
|
*/
|
|
static int yt8521_aneg_done(struct phy_device *phydev)
|
|
{
|
|
int link_fiber = 0, link_utp = 0;
|
|
|
|
/* reading Fiber */
|
|
ytphy_write_ext(phydev, 0xa000, 2);
|
|
link_fiber = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8521_LINK_STATUS_BIT)));
|
|
|
|
/* reading UTP */
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
if (!link_fiber)
|
|
link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) & (BIT(YT8521_LINK_STATUS_BIT)));
|
|
|
|
netdev_info(phydev->attached_dev, "%s, phy addr: %d, link_fiber: %d, link_utp: %d\n",
|
|
__func__, phydev->mdio.addr, link_fiber, link_utp);
|
|
return !!(link_fiber | link_utp);
|
|
}
|
|
|
|
static int yt8521_read_status(struct phy_device *phydev)
|
|
{
|
|
int link_utp = 0, link_fiber = 0;
|
|
int yt8521_fiber_latch_val;
|
|
int yt8521_fiber_curr_val;
|
|
int link, ret;
|
|
int val;
|
|
|
|
if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
|
|
/* reading UTP */
|
|
ret = ytphy_write_ext(phydev, 0xa000, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
link = val & (BIT(YT8521_LINK_STATUS_BIT));
|
|
if (link) {
|
|
link_utp = 1;
|
|
yt8521_adjust_status(phydev, val, 1);
|
|
} else {
|
|
link_utp = 0;
|
|
}
|
|
}
|
|
|
|
if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) {
|
|
/* reading Fiber */
|
|
ret = ytphy_write_ext(phydev, 0xa000, 2);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
/* note: below debug information is used to check multiple PHy ports. */
|
|
|
|
/* for fiber, from 1000m to 100m, there is not link down from 0x11,
|
|
* and check reg 1 to identify such case this is important for Linux
|
|
* kernel for that, missing linkdown event will cause problem.
|
|
*/
|
|
yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR);
|
|
yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR);
|
|
link = val & (BIT(YT8521_LINK_STATUS_BIT));
|
|
if (link && yt8521_fiber_latch_val != yt8521_fiber_curr_val) {
|
|
link = 0;
|
|
netdev_info(phydev->attached_dev, "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n",
|
|
__func__, phydev->mdio.addr, yt8521_fiber_latch_val,
|
|
yt8521_fiber_curr_val);
|
|
}
|
|
|
|
if (link) {
|
|
link_fiber = 1;
|
|
yt8521_adjust_status(phydev, val, 0);
|
|
} else {
|
|
link_fiber = 0;
|
|
}
|
|
}
|
|
|
|
if (link_utp || link_fiber) {
|
|
if (phydev->link == 0)
|
|
netdev_info(phydev->attached_dev, "%s, phy addr: %d, link up, media: %s, mii reg 0x11 = 0x%x\n",
|
|
__func__, phydev->mdio.addr,
|
|
(link_utp && link_fiber) ? "UNKNOWN MEDIA" : (link_utp ? "UTP" : "Fiber"),
|
|
(unsigned int)val);
|
|
phydev->link = 1;
|
|
} else {
|
|
if (phydev->link == 1)
|
|
netdev_info(phydev->attached_dev, "%s, phy addr: %d, link down\n",
|
|
__func__, phydev->mdio.addr);
|
|
phydev->link = 0;
|
|
}
|
|
|
|
/* utp or combo */
|
|
if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
|
|
if (link_fiber)
|
|
ytphy_write_ext(phydev, 0xa000, 2);
|
|
if (link_utp)
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8521_suspend(struct phy_device *phydev)
|
|
{
|
|
#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
|
|
int value;
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
value = phy_read(phydev, MII_BMCR);
|
|
phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 2);
|
|
value = phy_read(phydev, MII_BMCR);
|
|
phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8521_resume(struct phy_device *phydev)
|
|
{
|
|
int value, ret;
|
|
|
|
/* disable auto sleep */
|
|
value = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
|
|
if (value < 0)
|
|
return value;
|
|
|
|
value &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
|
|
|
|
ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
|
|
if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
value = phy_read(phydev, MII_BMCR);
|
|
phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
|
|
}
|
|
|
|
if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) {
|
|
ytphy_write_ext(phydev, 0xa000, 2);
|
|
value = phy_read(phydev, MII_BMCR);
|
|
phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
|
|
|
|
ytphy_write_ext(phydev, 0xa000, 0);
|
|
}
|
|
#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8522_read_status(struct phy_device *phydev)
|
|
{
|
|
int speed, speed_mode, duplex, val;
|
|
|
|
genphy_read_status(phydev);
|
|
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
/* link up */
|
|
if ((val & BIT(10)) >> YTXXXX_LINK_STATUS_BIT) {
|
|
duplex = (val & BIT(13)) >> YTXXXX_DUPLEX_BIT;
|
|
speed_mode = (val & (BIT(15) | BIT(14))) >> YTXXXX_SPEED_MODE_BIT;
|
|
switch (speed_mode) {
|
|
case 0:
|
|
speed = SPEED_10;
|
|
break;
|
|
case 1:
|
|
speed = SPEED_100;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
default:
|
|
speed = SPEED_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
phydev->link = 1;
|
|
phydev->speed = speed;
|
|
phydev->duplex = duplex;
|
|
|
|
return 0;
|
|
}
|
|
|
|
phydev->link = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8522_probe(struct phy_device *phydev)
|
|
{
|
|
struct device *dev = &phydev->mdio.dev;
|
|
struct yt8xxx_priv *priv;
|
|
int chip_config;
|
|
|
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
phydev->priv = priv;
|
|
|
|
chip_config = ytphy_read_ext(phydev, YT8522_EXTENDED_COMBO_CTRL_1);
|
|
|
|
priv->chip_mode = ((chip_config & BIT(3)) >> 3);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8522_config_init(struct phy_device *phydev)
|
|
{
|
|
struct yt8xxx_priv *priv = phydev->priv;
|
|
int ret;
|
|
int val;
|
|
|
|
/* UTP */
|
|
if (!priv->chip_mode) {
|
|
val = ytphy_write_ext(phydev, YT8522_TX_CLK_DELAY, 0);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val = ytphy_write_ext(phydev, YT8522_ANAGLOG_IF_CTRL, 0xbf2a);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val = ytphy_write_ext(phydev, YT8522_DAC_CTRL, 0x297f);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val = ytphy_write_ext(phydev, YT8522_INTERPOLATOR_FILTER_1, 0x1FE);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val = ytphy_write_ext(phydev, YT8522_INTERPOLATOR_FILTER_2, 0x1FE);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
/* disable auto sleep */
|
|
val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
val &= (~BIT(YT8512_EN_SLEEP_SW_BIT));
|
|
|
|
ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ytphy_soft_reset(phydev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int yt8531_rxclk_duty_init(struct phy_device *phydev)
|
|
{
|
|
unsigned int value = 0x9696;
|
|
int ret = 0;
|
|
|
|
ret = ytphy_write_ext(phydev, 0xa040, 0xffff);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ytphy_write_ext(phydev, 0xa041, 0xff);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ytphy_write_ext(phydev, 0xa039, 0xbf00);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* nodelay duty = 0x9696 (default)
|
|
* fixed delay duty = 0x4040
|
|
* step delay 0xf duty = 0x4041
|
|
*/
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
|
|
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
|
|
value = 0x4040;
|
|
|
|
ret = ytphy_write_ext(phydev, 0xa03a, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = ytphy_write_ext(phydev, 0xa03b, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = ytphy_write_ext(phydev, 0xa03c, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = ytphy_write_ext(phydev, 0xa03d, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = ytphy_write_ext(phydev, 0xa03e, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = ytphy_write_ext(phydev, 0xa03f, value);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int yt8531S_config_init(struct phy_device *phydev)
|
|
{
|
|
#if (YTPHY8531A_XTAL_INIT)
|
|
int ret = 0;
|
|
|
|
ret = yt8531a_xtal_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
#endif
|
|
|
|
return yt8521_config_init(phydev);
|
|
}
|
|
|
|
static int yt8531_config_init(struct phy_device *phydev)
|
|
{
|
|
int ret = 0, val;
|
|
|
|
#if (YTPHY8531A_XTAL_INIT)
|
|
ret = yt8531a_xtal_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
#endif
|
|
|
|
/* PHY_CLK_OUT 125M enabled (default) */
|
|
ret = ytphy_write_ext(phydev, 0xa012, 0xd0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = yt8531_rxclk_duty_init(phydev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* RXC, PHY_CLK_OUT and RXData Drive strength:
|
|
* Drive strength of RXC = 6, PHY_CLK_OUT = 3, RXD0 = 4 (default 1.8v)
|
|
* If the io voltage is 3.3v, PHY_CLK_OUT = 2, set 0xa010 = 0xdacf
|
|
*/
|
|
ret = ytphy_write_ext(phydev, 0xa010, 0xdbcf);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Change 100M default BGS voltage from 0x294c to 0x274c */
|
|
val = ytphy_read_ext(phydev, 0x57);
|
|
val = (val & ~(0xf << 8)) | (7 << 8);
|
|
ret = ytphy_write_ext(phydev, 0x57, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct phy_driver motorcomm_phy_drvs[] = {
|
|
{
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8011),
|
|
.name = "YT8011 Automotive Gigabit Ethernet",
|
|
.features = PHY_GBIT_FEATURES,
|
|
.flags = PHY_POLL,
|
|
.probe = yt8011_probe,
|
|
.config_aneg = yt8011_config_aneg,
|
|
.aneg_done = yt8011_aneg_done,
|
|
.config_init = yt8011_config_init,
|
|
.read_status = yt8011_read_status,
|
|
.soft_reset = yt8011_soft_reset,
|
|
}, {
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
|
|
.name = "YT8511 Gigabit Ethernet",
|
|
.config_init = yt8511_config_init,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
.read_page = yt8511_read_page,
|
|
.write_page = yt8511_write_page,
|
|
}, {
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8512),
|
|
.name = "YT8512 Ethernet",
|
|
.config_init = yt8512_config_init,
|
|
.read_status = yt8512_read_status,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
}, {
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8512B),
|
|
.name = "YT8512B Ethernet",
|
|
.config_init = yt8512_config_init,
|
|
.read_status = yt8512_read_status,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
}, {
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8522),
|
|
.name = "YT8522 100M Ethernet",
|
|
.features = PHY_BASIC_FEATURES,
|
|
.probe = yt8522_probe,
|
|
.soft_reset = ytphy_soft_reset,
|
|
.config_aneg = genphy_config_aneg,
|
|
.config_init = yt8522_config_init,
|
|
.read_status = yt8522_read_status,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
}, {
|
|
/* same as 8521 */
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
|
|
.name = "YT8531S Gigabit Ethernet",
|
|
.features = PHY_GBIT_FEATURES,
|
|
.soft_reset = yt8521_soft_reset,
|
|
.aneg_done = yt8521_aneg_done,
|
|
.config_init = yt8531S_config_init,
|
|
.read_status = yt8521_read_status,
|
|
.suspend = yt8521_suspend,
|
|
.resume = yt8521_resume,
|
|
#if (YTPHY_WOL_FEATURE_ENABLE)
|
|
.get_wol = &ytphy_wol_feature_get,
|
|
.set_wol = &ytphy_wol_feature_set,
|
|
#endif
|
|
}, {
|
|
/* same as 8511 */
|
|
PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
|
|
.name = "YT8531 Gigabit Ethernet",
|
|
.features = PHY_GBIT_FEATURES,
|
|
.config_init = yt8531_config_init,
|
|
.suspend = genphy_suspend,
|
|
.resume = genphy_resume,
|
|
#if (YTPHY_WOL_FEATURE_ENABLE)
|
|
.get_wol = &ytphy_wol_feature_get,
|
|
.set_wol = &ytphy_wol_feature_set,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
module_phy_driver(motorcomm_phy_drvs);
|
|
|
|
MODULE_DESCRIPTION("Motorcomm PHY driver");
|
|
MODULE_AUTHOR("Peter Geis");
|
|
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_YT8512) },
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8512B) },
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8522) },
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
|
|
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
|
|
{ /* sentinal */ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
|