// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Motorcomm PHYs * * Author: Peter Geis */ #include #include #include #include #include #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);