android13/kernel-5.10/drivers/net/phy/motorcomm.c

844 lines
20 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_YT8511 0x0000010a
#define PHY_ID_YT8512 0x00000118
#define PHY_ID_YT8512B 0x00000128
#define PHY_ID_YT8531S 0x4f51e91a
#define PHY_ID_YT8531 0x4f51e91b
#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
/* 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
/* 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 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 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 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 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_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,
}, {
/* 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_YT8531S) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
{ /* sentinal */ }
};
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);