summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorYanhong Wang <yanhong.wang@starfivetech.com>2023-06-07 04:22:04 +0300
committerYanhong Wang <yanhong.wang@starfivetech.com>2023-06-09 09:07:13 +0300
commit306c8d2c42fa3005094972d767cb1cce70fdd532 (patch)
treeaa01e64eda96ccc1d372639e3f96b755bf4ea4ee /drivers
parent4da8509260892b3dc1507daad9bea4e3c0cacaa7 (diff)
downloadlinux-306c8d2c42fa3005094972d767cb1cce70fdd532.tar.xz
net: phy: add driver for Motorcomm yt8521 phy
Add driver to support Motorcomm yt8521 phy. This modification is synchronized with Linux 5.15. Signed-off-by: Yanhong Wang <yanhong.wang@starfivetech.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/phy/motorcomm.c2312
1 files changed, 2231 insertions, 81 deletions
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 7e6ac2c5e27e..5d941a615ec0 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -2,136 +2,2286 @@
/*
* Driver for Motorcomm PHYs
*
- * Author: Peter Geis <pgwipeout@gmail.com>
+ * Author: yinghong.zhang<yinghong.zhang@motor-comm.com>
+ *
+ * Copyright (c) 2019 Motorcomm, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Support : Motorcomm Phys:
+ * Giga phys: yt8511, yt8521, yt8531, yt8614, yt8618
+ * 100/10 Phys : yt8512, yt8512b, yt8510
+ * Automotive 100Mb Phys : yt8010
+ * Automotive 100/10 hyper range Phys: yt8510
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+
+/* for wol feature, 20210604 */
+#include <linux/netdevice.h>
+
+#define YT_LINUX_MAJOR 2
+#define YT_LINUX_MINOR 2
+#define YT_LINUX_SUBVERSION 6255
+#define YT_LINUX_VERSIONID "2.2.6255"
-#define PHY_ID_YT8511 0x0000010a
+/********************************************
+ **** configuration section begin ***********/
-#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
+/* 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
-/* 2b00 25m from pll
- * 2b01 25m from xtl *default*
- * 2b10 62.m from pll
- * 2b11 125m from pll
+/* to enable system WOL feature of phy, please define this macro to 1
+ * otherwise, define it to 0.
*/
-#define YT8511_CLK_125M (BIT(2) | BIT(1))
-#define YT8511_PLLON_SLP BIT(14)
+#define YTPHY_WOL_FEATURE_ENABLE 0
-/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */
-#define YT8511_DELAY_RX BIT(0)
+/* some GMAC need clock input from PHY, for eg., 125M,
+ * please enable this macro
+ * by degault, it is set to 0
+ * NOTE: this macro will need macro SYS_WAKEUP_BASED_ON_ETH_PKT to set to 1
+ */
+#define GMAC_CLOCK_INPUT_NEEDED 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
+/* the max number of yt8521 chip on pcb board
+ * the most case is only 1 chip per board, but
+ * by default, we support up to 8.
*/
-#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 YTPHY_BOARD_MAX_NUM_OF_CHIP_8521 8
+#define YTPHY_BOARD_MAX_NUM_OF_CHIP_8614 4
-static int yt8511_read_page(struct phy_device *phydev)
-{
- return __phy_read(phydev, YT8511_PAGE_SELECT);
+/* for YT8531 package A xtal init config */
+#define YTPHY8531A_XTAL_INIT (0)
+
+/**** configuration section end ***********
+ ******************************************/
+
+/* no need to change below */
+#define MOTORCOMM_PHY_ID_MASK 0x00000fff
+#define MOTORCOMM_PHY_ID_8531_MASK 0xffffffff
+#define MOTORCOMM_MPHY_ID_MASK 0x0000ffff
+#define MOTORCOMM_MPHY_ID_MASK_8614 0xffffffff
+#define MOTORCOMM_PHY_ID_MASK_8821 0xffffffff
+
+#define PHY_ID_YT8010 0x00000309
+#define PHY_ID_YT8010AS 0x4f51eb19
+#define PHY_ID_YT8510 0x00000109
+#define PHY_ID_YT8511 0x0000010a
+#define PHY_ID_YT8512 0x00000118
+#define PHY_ID_YT8512B 0x00000128
+#define PHY_ID_YT8521 0x0000011a
+#define PHY_ID_YT8531S 0x4f51e91a
+#define PHY_ID_YT8531 0x4f51e91b
+#define PHY_ID_YT8614 0x4F51E899
+#define PHY_ID_YT8618 0x0000e889
+#define PHY_ID_YT8821 0x4f51ea10
+
+#define REG_PHY_SPEC_STATUS 0x11
+#define REG_DEBUG_ADDR_OFFSET 0x1e
+#define REG_DEBUG_DATA 0x1f
+
+#define YT8512_EXTREG_LED0 0x40c0
+#define YT8512_EXTREG_LED1 0x40c3
+
+#define YT8512_EXTREG_SLEEP_CONTROL1 0x2027
+
+#define YT_SOFTWARE_RESET 0x8000
+
+#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 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
+
+/* based on yt8521 wol feature config register */
+#define YTPHY_UTP_INTR_REG 0x12
+/* WOL Feature Event Interrupt Enable */
+#define YTPHY_WOL_FEATURE_INTR BIT(6)
+
+/* Magic Packet MAC address registers */
+#define YTPHY_WOL_FEATURE_MACADDR2_4_MAGIC_PACKET 0xa007
+#define YTPHY_WOL_FEATURE_MACADDR1_4_MAGIC_PACKET 0xa008
+#define YTPHY_WOL_FEATURE_MACADDR0_4_MAGIC_PACKET 0xa009
+
+#define YTPHY_WOL_FEATURE_REG_CFG 0xa00a
+#define YTPHY_WOL_FEATURE_TYPE_CFG BIT(0) /* WOL TYPE Config */
+#define YTPHY_WOL_FEATURE_ENABLE_CFG BIT(3) /* WOL Enable Config */
+#define YTPHY_WOL_FEATURE_INTR_SEL_CFG BIT(6) /* WOL Event Interrupt Enable Config */
+#define YTPHY_WOL_FEATURE_WIDTH1_CFG BIT(1) /* WOL Pulse Width Config */
+#define YTPHY_WOL_FEATURE_WIDTH2_CFG BIT(2) /* WOL Pulse Width Config */
+
+#define YTPHY_REG_SPACE_UTP 0
+#define YTPHY_REG_SPACE_FIBER 2
+
+#define YTPHY_EXTREG_CHIP_CONFIG 0xa001
+#define YTPHY_EXTREG_RGMII_CONFIG1 0xa003
+#define YTPHY_PAD_DRIVES_STRENGTH_CFG 0xa010
+#define YTPHY_RGMII_SW_DR_MASK GENMASK(5, 4)
+#define YTPHY_RGMII_RXC_DR_MASK GENMASK(15, 13)
+
+enum ytphy_wol_feature_trigger_type_e {
+ YTPHY_WOL_FEATURE_PULSE_TRIGGER,
+ YTPHY_WOL_FEATURE_LEVEL_TRIGGER,
+ YTPHY_WOL_FEATURE_TRIGGER_TYPE_MAX
};
-static int yt8511_write_page(struct phy_device *phydev, int page)
-{
- return __phy_write(phydev, YT8511_PAGE_SELECT, page);
+enum ytphy_wol_feature_pulse_width_e {
+ YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_336MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_168MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_84MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_PULSE_WIDTH_MAX
+};
+
+struct ytphy_wol_feature_cfg {
+ bool enable;
+ int type;
+ int width;
+};
+
+#if (YTPHY_WOL_FEATURE_ENABLE)
+#undef SYS_WAKEUP_BASED_ON_ETH_PKT
+#define SYS_WAKEUP_BASED_ON_ETH_PKT 1
+#endif
+
+/* 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
+
+/* below are for bitmap */
+#define YT_PHY_MODE_FIBER 1 //fiber/sgmii mode only
+#define YT_PHY_MODE_UTP 2 //utp mode only
+#define YT_PHY_MODE_QSGMII 4 //qsgmii mode only
+
+//qsgmii, fiber/sgmii and utp, poll mode
+#define YT_PHY_MODE_POLL (YT_PHY_MODE_FIBER |\
+ YT_PHY_MODE_UTP |\
+ YT_PHY_MODE_QSGMII)
+/* support automatically check polling mode for yt8521
+ * for Fiber only system, please define YT8521_PHY_MODE_CURR 1
+ * for UTP only system, please define YT8521_PHY_MODE_CURR 2
+ * for combo system, please define YT8521_PHY_MODE_CURR 3
+ */
+#define YTPHY_861X_ABC_VER 0
+#if (YTPHY_861X_ABC_VER)
+static int yt8614_get_port_from_phydev(struct phy_device *phydev);
+#endif
+static int yt8521_hw_strap_polling(struct phy_device *phydev);
+static int yt8614_hw_strap_polling(struct phy_device *phydev);
+#define YT8521_PHY_MODE_CURR yt8521_hw_strap_polling(phydev)
+#define YT8614_PHY_MODE_CURR yt8614_hw_strap_polling(phydev)
+
+struct ytphy_reg_field {
+ const char *name;
+ const u8 size; /* Size of the bitfield, in bits */
+ const u8 off; /* Offset from bit 0 */
+ const u8 dflt; /* Default value */
+};
+
+struct ytphy_priv_t {
+ u32 tx_inverted_1000;
+ u32 tx_inverted_100;
+ u32 tx_inverted_10;
+};
+
+static const struct ytphy_reg_field ytphy_dr_grp[] = {
+ { "rgmii_sw_dr", 2, 4, 0x3},
+ { "rgmii_sw_dr_2", 1, 12, 0x0},
+ { "rgmii_sw_dr_rxc", 3, 13, 0x3}
};
+static const struct ytphy_reg_field ytphy_rxtxd_grp[] = {
+ { "rx_delay_sel", 4, 10, 0x0 },
+ { "tx_delay_sel_fe", 4, 4, 0xf },
+ { "tx_delay_sel", 4, 0, 0x1 }
+};
+
+static const struct ytphy_reg_field ytphy_txinver_grp[] = {
+ { "tx_inverted_1000", 1, 14, 0x0 },
+ { "tx_inverted_100", 1, 14, 0x0 },
+ { "tx_inverted_10", 1, 14, 0x0 }
+};
+
+static const struct ytphy_reg_field ytphy_rxden_grp[] = {
+ { "rxc_dly_en", 1, 8, 0x1 }
+};
+
+static uint bitfield_mask(uint shift, uint width)
+{
+ return ((1 << width) - 1) << shift;
+}
+
+static uint bitfield_replace(uint reg_val, uint shift, uint width,
+ uint bitfield_val)
+{
+ uint mask = bitfield_mask(shift, width);
+
+ return (reg_val & ~mask) | ((bitfield_val << shift) & mask);
+}
+
+static int ytphy_config_init(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read(phydev, 3);
+
+ return 0;
+}
+
+static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
+{
+ int ret;
+ int val;
+
+ ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_DEBUG_DATA);
+
+ return val;
+}
+
+static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
+{
+ int ret;
+
+ ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, REG_DEBUG_DATA, val);
+
+ 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;
+}
+
+#if (YTPHY8531A_XTAL_INIT)
+static int yt8531a_xtal_init(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val = 0;
+ bool state = false;
+
+ msleep(50);
+
+ do {
+ ret = ytphy_write_ext(phydev, 0xa012, 0x88);
+ if (ret < 0)
+ return ret;
+
+ msleep(100);
+
+ val = ytphy_read_ext(phydev, 0xa012);
+ if (val < 0)
+ return val;
+
+ usleep_range(10000, 20000);
+ } while (val != 0x88);
+
+ ret = ytphy_write_ext(phydev, 0xa012, 0xc8);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+#endif
+
+int yt8010_soft_reset(struct phy_device *phydev)
+{
+ ytphy_soft_reset(phydev);
+
+ return 0;
+}
+
+int yt8010AS_soft_reset(struct phy_device *phydev)
+{
+ int ret = 0;
+
+ /* sgmii */
+ ytphy_write_ext(phydev, 0xe, 1);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xe, 0);
+ return ret;
+ }
+
+ /* utp */
+ ytphy_write_ext(phydev, 0xe, 0);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int yt8010_aneg_done(struct phy_device *phydev)
+{
+ int val = 0;
+
+ val = phy_read(phydev, 0x1);
+ val = phy_read(phydev, 0x1);
+
+ return (val < 0) ? val : (val & BMSR_LSTATUS);
+}
+
+static int yt8010_config_aneg(struct phy_device *phydev)
+{
+ phydev->speed = SPEED_100;
+ return 0;
+}
+
+static int yt8010_read_status(struct phy_device *phydev)
+{
+ int ret = 0;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ /* for 8010, no definition mii reg 0x04, 0x11, here force 100/full */
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_FULL;
+
+ return 0;
+}
+
+static int yt8010AS_config_init(struct phy_device *phydev)
+{
+ phydev->autoneg = AUTONEG_DISABLE;
+
+ return 0;
+}
+
+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 = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = yt8512_led_init(phydev);
+
+ /* 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;
+}
+
+int yt8521_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_UTP) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ytphy_soft_reset(phydev);
+ }
+
+ if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_FIBER) {
+ ytphy_write_ext(phydev, 0xa000, 2);
+ ytphy_soft_reset(phydev);
+
+ 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);
+ }
+
+ return 0;
+}
+
+#if GMAC_CLOCK_INPUT_NEEDED
+static int ytphy_mii_rd_ext(struct mii_bus *bus, int phy_id, u32 regnum)
+{
+ int ret;
+ int val;
+
+ ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum);
+ if (ret < 0)
+ return ret;
+
+ val = bus->read(bus, phy_id, REG_DEBUG_DATA);
+
+ return val;
+}
+
+static int ytphy_mii_wr_ext(struct mii_bus *bus
+ int phy_id,
+ u32 regnum,
+ u16 val)
+{
+ int ret;
+
+ ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum);
+ if (ret < 0)
+ return ret;
+
+ ret = bus->write(bus, phy_id, REG_DEBUG_DATA, val);
+
+ return ret;
+}
+
+int yt8511_config_dis_txdelay(struct mii_bus *bus, int phy_id)
+{
+ int ret;
+ int val;
+
+ /* disable auto sleep */
+ val = ytphy_mii_rd_ext(bus, phy_id, 0x27);
+ if (val < 0)
+ return val;
+
+ val &= (~BIT(15));
+
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val);
+ if (ret < 0)
+ return ret;
+
+ /* enable RXC clock when no wire plug */
+ val = ytphy_mii_rd_ext(bus, phy_id, 0xc);
+ if (val < 0)
+ return val;
+
+ /* ext reg 0xc b[7:4]
+ * Tx Delay time = 150ps * N - 250ps
+ */
+ val &= ~(0xf << 4);
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val);
+
+ return ret;
+}
+
+int yt8511_config_out_125m(struct mii_bus *bus, int phy_id)
+{
+ int ret;
+ int val;
+
+ /* disable auto sleep */
+ val = ytphy_mii_rd_ext(bus, phy_id, 0x27);
+ if (val < 0)
+ return val;
+
+ val &= (~BIT(15));
+
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val);
+ if (ret < 0)
+ return ret;
+
+ /* enable RXC clock when no wire plug */
+ val = ytphy_mii_rd_ext(bus, phy_id, 0xc);
+ if (val < 0)
+ return val;
+
+ /* ext reg 0xc.b[2:1]
+ * 00-----25M from pll;
+ * 01---- 25M from xtl;(default)
+ * 10-----62.5M from pll;
+ * 11----125M from pll(here set to this value)
+ */
+ val |= (3 << 1);
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val);
+
+#ifdef YT_8511_INIT_TO_MASTER
+ /* for customer, please enable it based on demand.
+ * configure to master
+ */
+
+ /* master/slave config reg*/
+ val = bus->read(bus, phy_id, 0x9);
+ /* to be manual config and force to be master */
+ val |= (0x3 << 11);
+ /* take effect until phy soft reset */
+ ret = bus->write(bus, phy_id, 0x9, val);
+ if (ret < 0)
+ return ret;
+#endif
+
+ return ret;
+}
+
static int yt8511_config_init(struct phy_device *phydev)
{
- int oldpage, ret = 0;
- unsigned int ge, fe;
+ int ret;
+
+ ret = ytphy_config_init(phydev);
+
+ netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n",
+ __func__, phydev->mdio.addr);
+
+ return ret;
+}
+#endif /* GMAC_CLOCK_INPUT_NEEDED */
+
+#if (YTPHY_WOL_FEATURE_ENABLE)
+static int ytphy_switch_reg_space(struct phy_device *phydev, int space)
+{
+ int ret;
+
+ if (space == YTPHY_REG_SPACE_UTP)
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
+ else
+ ret = ytphy_write_ext(phydev, 0xa000, 2);
+
+ return ret;
+}
+
+static int ytphy_wol_feature_enable_cfg(struct phy_device *phydev,
+ struct ytphy_wol_feature_cfg wol_cfg)
+{
+ int ret = 0;
+ int val = 0;
+
+ val = ytphy_read_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG);
+ if (val < 0)
+ return val;
- oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE);
- if (oldpage < 0)
- goto err_restore_page;
+ if (wol_cfg.enable) {
+ val |= YTPHY_WOL_FEATURE_ENABLE_CFG;
- /* set rgmii delay mode */
- switch (phydev->interface) {
- case PHY_INTERFACE_MODE_RGMII:
- ge = YT8511_DELAY_GE_TX_DIS;
- fe = YT8511_DELAY_FE_TX_DIS;
+ if (wol_cfg.type == YTPHY_WOL_FEATURE_LEVEL_TRIGGER) {
+ val &= ~YTPHY_WOL_FEATURE_TYPE_CFG;
+ val &= ~YTPHY_WOL_FEATURE_INTR_SEL_CFG;
+ } else if (wol_cfg.type == YTPHY_WOL_FEATURE_PULSE_TRIGGER) {
+ val |= YTPHY_WOL_FEATURE_TYPE_CFG;
+ val |= YTPHY_WOL_FEATURE_INTR_SEL_CFG;
+
+ if (wol_cfg.width == YTPHY_WOL_FEATURE_84MS_PULSE_WIDTH) {
+ val &= ~YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val &= ~YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ } else if (wol_cfg.width == YTPHY_WOL_FEATURE_168MS_PULSE_WIDTH) {
+ val |= YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val &= ~YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ } else if (wol_cfg.width == YTPHY_WOL_FEATURE_336MS_PULSE_WIDTH) {
+ val &= ~YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val |= YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ } else if (wol_cfg.width == YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH) {
+ val |= YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val |= YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ }
+ }
+ } else {
+ val &= ~YTPHY_WOL_FEATURE_ENABLE_CFG;
+ val &= ~YTPHY_WOL_FEATURE_INTR_SEL_CFG;
+ }
+
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void ytphy_wol_feature_get(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
+{
+ int val = 0;
+
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = 0;
+
+ val = ytphy_read_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG);
+ if (val < 0)
+ return;
+
+ if (val & YTPHY_WOL_FEATURE_ENABLE_CFG)
+ wol->wolopts |= WAKE_MAGIC;
+
+ //return;
+}
+
+static int ytphy_wol_feature_set(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
+{
+ int ret, curr_reg_space, val;
+ struct ytphy_wol_feature_cfg wol_cfg;
+ struct net_device *p_attached_dev = phydev->attached_dev;
+
+ memset(&wol_cfg, 0, sizeof(struct ytphy_wol_feature_cfg));
+ curr_reg_space = ytphy_read_ext(phydev, 0xa000);
+ if (curr_reg_space < 0)
+ return curr_reg_space;
+
+ /* Switch to phy UTP page */
+ ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP);
+ if (ret < 0)
+ return ret;
+
+ if (wol->wolopts & WAKE_MAGIC) {
+ /* Enable the WOL feature interrupt */
+ val = phy_read(phydev, YTPHY_UTP_INTR_REG);
+ val |= YTPHY_WOL_FEATURE_INTR;
+ ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val);
+ if (ret < 0)
+ return ret;
+
+ /* Set the WOL feature config */
+ wol_cfg.enable = true;
+ wol_cfg.type = YTPHY_WOL_FEATURE_PULSE_TRIGGER;
+ wol_cfg.width = YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH;
+ ret = ytphy_wol_feature_enable_cfg(phydev, wol_cfg);
+ if (ret < 0)
+ return ret;
+
+ /* Store the device address for the magic packet */
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR2_4_MAGIC_PACKET,
+ ((p_attached_dev->dev_addr[0] << 8) |
+ p_attached_dev->dev_addr[1]));
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR1_4_MAGIC_PACKET,
+ ((p_attached_dev->dev_addr[2] << 8) |
+ p_attached_dev->dev_addr[3]));
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR0_4_MAGIC_PACKET,
+ ((p_attached_dev->dev_addr[4] << 8) |
+ p_attached_dev->dev_addr[5]));
+ if (ret < 0)
+ return ret;
+ } else {
+ wol_cfg.enable = false;
+ wol_cfg.type = YTPHY_WOL_FEATURE_TRIGGER_TYPE_MAX;
+ wol_cfg.width = YTPHY_WOL_FEATURE_PULSE_WIDTH_MAX;
+ ret = ytphy_wol_feature_enable_cfg(phydev, wol_cfg);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Recover to previous register space page */
+ ret = ytphy_switch_reg_space(phydev, curr_reg_space);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#endif /*(YTPHY_WOL_FEATURE_ENABLE)*/
+
+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 struct device_node *ytphy_get_of_node(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ const struct device_node *of_node;
+ int i = 0;
+
+ do {
+ of_node = dev->of_node;
+ dev = dev->parent;
+ if (i++ > 5) {
+ phydev_err(phydev, "Get of node timeout\n");
+ return NULL;
+ }
+ } while (!of_node && dev);
+
+ return of_node->child;
+}
+
+static void ytphy_link_change_notify(struct phy_device *phydev)
+{
+ u32 val;
+ struct ytphy_priv_t *ytphy_priv = phydev->priv;
+
+ if (phydev->speed < 0)
+ return;
+
+ val = ytphy_read_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1);
+ switch (phydev->speed) {
+ case SPEED_1000:
+ val = bitfield_replace(val, ytphy_txinver_grp[0].off,
+ ytphy_txinver_grp[0].size,
+ ytphy_priv->tx_inverted_1000);
+ break;
+ case SPEED_100:
+ val = bitfield_replace(val, ytphy_txinver_grp[1].off,
+ ytphy_txinver_grp[1].size,
+ ytphy_priv->tx_inverted_100);
+ break;
+ case SPEED_10:
+ val = bitfield_replace(val, ytphy_txinver_grp[2].off,
+ ytphy_txinver_grp[2].size,
+ ytphy_priv->tx_inverted_10);
+ break;
+ default:
+ break;
+ }
+ ytphy_write_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1, val);
+}
+
+static int ytphy_of_config(struct phy_device *phydev)
+{
+ const struct device_node *of_node;
+ const struct device *dev;
+ u32 val;
+ u32 cfg;
+ int ret;
+ int i;
+
+ dev = &phydev->mdio.dev;
+ of_node = ytphy_get_of_node(phydev);
+ if (of_node) {
+ ret = of_property_read_u32(of_node, ytphy_rxden_grp[0].name, &cfg);
+ if (!ret) {
+ val = ytphy_read_ext(phydev, YTPHY_EXTREG_CHIP_CONFIG);
+
+ /*check the cfg overflow or not*/
+ cfg = (cfg > ((1 << ytphy_rxden_grp[0].size) - 1)) ?
+ ((1 << ytphy_rxden_grp[0].size) - 1) : cfg;
+ val = bitfield_replace(val, ytphy_rxden_grp[0].off,
+ ytphy_rxden_grp[0].size, cfg);
+ ytphy_write_ext(phydev, YTPHY_EXTREG_CHIP_CONFIG, val);
+ }
+
+ val = ytphy_read_ext(phydev, YTPHY_PAD_DRIVES_STRENGTH_CFG);
+ for (i = 0; i < ARRAY_SIZE(ytphy_dr_grp); i++) {
+ ret = of_property_read_u32(of_node, ytphy_dr_grp[i].name, &cfg);
+ if (!ret) {
+ cfg = (cfg != -1) ? cfg : ytphy_dr_grp[i].dflt;
+
+ /*check the cfg overflow or not*/
+ cfg = (cfg > ((1 << ytphy_dr_grp[i].size) - 1)) ?
+ ((1 << ytphy_dr_grp[i].size) - 1) : cfg;
+
+ val = bitfield_replace(val, ytphy_dr_grp[i].off,
+ ytphy_dr_grp[i].size, cfg);
+ }
+ }
+ ytphy_write_ext(phydev, YTPHY_PAD_DRIVES_STRENGTH_CFG, val);
+
+ val = ytphy_read_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1);
+ for (i = 0; i < ARRAY_SIZE(ytphy_rxtxd_grp); i++) {
+ ret = of_property_read_u32(of_node, ytphy_rxtxd_grp[i].name, &cfg);
+ if (!ret) {
+ cfg = (cfg != -1) ? cfg : ytphy_rxtxd_grp[i].dflt;
+
+ /*check the cfg overflow or not*/
+ cfg = (cfg > ((1 << ytphy_rxtxd_grp[i].size) - 1)) ?
+ ((1 << ytphy_rxtxd_grp[i].size) - 1) : cfg;
+
+ val = bitfield_replace(val, ytphy_rxtxd_grp[i].off,
+ ytphy_rxtxd_grp[i].size, cfg);
+ }
+ }
+ return ytphy_write_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1, val);
+ }
+ phydev_err(phydev, "Get of node fail\n");
+ return -EINVAL;
+}
+
+static int yt8521_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val, hw_strap_mode;
+
+#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);
+ ret = ytphy_config_init(phydev);
+
+ if (ret < 0)
+ return ret;
+
+ /* 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;
+
+ /*set delay config*/
+ ret = ytphy_of_config(phydev);
+ 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_mode, duplex;
+ int speed = SPEED_UNKNOWN;
+
+ if (is_utp)
+ duplex = (val & YT8512_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 PHY_INTERFACE_MODE_RGMII_RXID:
- ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS;
- fe = YT8511_DELAY_FE_TX_DIS;
+ case 2:
+ speed = SPEED_1000;
break;
- case PHY_INTERFACE_MODE_RGMII_TXID:
- ge = YT8511_DELAY_GE_TX_EN;
- fe = YT8511_DELAY_FE_TX_EN;
+ case 3:
break;
- case PHY_INTERFACE_MODE_RGMII_ID:
- ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN;
- fe = YT8511_DELAY_FE_TX_EN;
+ default:
+ speed = SPEED_UNKNOWN;
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);
+ 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.
+ */
+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 ret;
+ int val;
+ int yt8521_fiber_latch_val;
+ int yt8521_fiber_curr_val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ 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;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER)
+
+ 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;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP)
+
+ 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) ?
+ "UNKONWN 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;
+ }
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) { //utp or combo
+ if (link_fiber)
+ ytphy_write_ext(phydev, 0xa000, 2);
+ if (link_utp)
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+ return 0;
+}
+
+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;
+}
+
+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 yt8531S_config_init(struct phy_device *phydev)
+{
+ int ret = 0;
+
+#if (YTPHY8531A_XTAL_INIT)
+ ret = yt8531a_xtal_init(phydev);
+ if (ret < 0)
+ return ret;
+#endif
+
+ ret = yt8521_config_init(phydev);
+
+ return ret;
+}
+
+static int ytphy_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct ytphy_priv_t *priv;
+ const struct device_node *of_node;
+ u32 val;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ of_node = ytphy_get_of_node(phydev);
+ if (of_node) {
+ ret = of_property_read_u32(of_node, ytphy_txinver_grp[0].name, &val);
+ if (!ret)
+ priv->tx_inverted_1000 = val;
+
+ ret = of_property_read_u32(of_node, ytphy_txinver_grp[1].name, &val);
+ if (!ret)
+ priv->tx_inverted_100 = val;
+
+ ret = of_property_read_u32(of_node, ytphy_txinver_grp[2].name, &val);
+ if (!ret)
+ priv->tx_inverted_10 = val;
+
+ }
+ phydev->priv = priv;
+ return 0;
+}
+
+static int yt8531_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = 0;
+
+ /*set delay config*/
+ ret = ytphy_of_config(phydev);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+int yt8618_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int yt8614_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ /* qsgmii */
+ ytphy_write_ext(phydev, 0xa000, 2);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* sgmii */
+ ytphy_write_ext(phydev, 0xa000, 3);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* utp */
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int yt8618_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val;
+ unsigned int retries = 12;
+#if (YTPHY_861X_ABC_VER)
+ int port = 0;
+#endif
+
+ phydev->irq = PHY_POLL;
+
+#if (YTPHY_861X_ABC_VER)
+ port = yt8614_get_port_from_phydev(phydev);
+#endif
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* for utp to optimize signal */
+ ret = ytphy_write_ext(phydev, 0x41, 0x33);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x42, 0x66);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x43, 0xaa);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x44, 0xd0d);
+ if (ret < 0)
+ return ret;
+
+#if (YTPHY_861X_ABC_VER)
+ if (port == 2 || port == 5) {
+ ret = ytphy_write_ext(phydev, 0x57, 0x2929);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
+ val = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, val | BMCR_RESET);
+ do {
+ msleep(50);
+ ret = phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ return ret;
+ } while ((ret & BMCR_RESET) && --retries);
+ if (ret & BMCR_RESET)
+ return -ETIMEDOUT;
+
+ /* for QSGMII optimization */
+ ytphy_write_ext(phydev, 0xa000, 0x02);
+
+ ret = ytphy_write_ext(phydev, 0x3, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+ ret = ytphy_write_ext(phydev, 0xe, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+
+ netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n",
+ __func__, phydev->mdio.addr);
+ return ret;
+}
+
+static int yt8614_hw_strap_polling(struct phy_device *phydev)
+{
+ int val = 0;
+
+ /* b3:0 are work mode, but b3 is always 1 */
+ val = ytphy_read_ext(phydev, 0xa007) & 0xf;
+ switch (val) {
+ case 8:
+ case 12:
+ case 13:
+ case 15:
+ return (YT_PHY_MODE_FIBER | YT_PHY_MODE_UTP);
+ case 14:
+ case 11:
+ return YT_PHY_MODE_FIBER;
+ case 9:
+ case 10:
+ default:
+ return YT_PHY_MODE_UTP;
+ }
+}
+
+#if (YTPHY_861X_ABC_VER)
+static int yt8614_get_port_from_phydev(struct phy_device *phydev)
+{
+ int tmp = ytphy_read_ext(phydev, 0xa0ff);
+ int phy_addr = 0;
+
+ phy_addr = (unsigned int)phydev->mdio.addr;
+
+ if ((phy_addr - tmp) < 0) {
+ ytphy_write_ext(phydev, 0xa0ff, phy_addr);
+ tmp = phy_addr;
+ }
+
+ return (phy_addr - tmp);
+}
+#endif
+
+static int yt8614_config_init(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val, hw_strap_mode;
+ unsigned int retries = 12;
+#if (YTPHY_861X_ABC_VER)
+ int port = 0;
+#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, 0xa007) & 0xf;
+
+#if (YTPHY_861X_ABC_VER)
+ port = yt8614_get_port_from_phydev(phydev);
+#endif
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* for utp to optimize signal */
+ ret = ytphy_write_ext(phydev, 0x41, 0x33);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x42, 0x66);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x43, 0xaa);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x44, 0xd0d);
+ if (ret < 0)
+ return ret;
+
+#if (YTPHY_861X_ABC_VER)
+ if (port == 2) {
+ ret = ytphy_write_ext(phydev, 0x57, 0x2929);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
+ /* soft reset to take config effect */
+ val = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, val | BMCR_RESET);
+ do {
+ msleep(50);
+ ret = phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ return ret;
+ } while ((ret & BMCR_RESET) && --retries);
+ if (ret & BMCR_RESET)
+ return -ETIMEDOUT;
+
+ /* for QSGMII optimization */
+ ytphy_write_ext(phydev, 0xa000, 0x02);
+ ret = ytphy_write_ext(phydev, 0x3, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+ ret = ytphy_write_ext(phydev, 0xe, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* for SGMII optimization */
+ ytphy_write_ext(phydev, 0xa000, 0x03);
+ ret = ytphy_write_ext(phydev, 0x3, 0x2420);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+ ret = ytphy_write_ext(phydev, 0xe, 0x24a0);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* back up to utp*/
+ ytphy_write_ext(phydev, 0xa000, 0);
+ netdev_info(phydev->attached_dev,
+ "%s done, phy addr: %d, chip mode: %d\n",
+ __func__, phydev->mdio.addr, hw_strap_mode);
+
+ return ret;
+}
+
+int yt8618_aneg_done(struct phy_device *phydev)
+{
+ return genphy_aneg_done(phydev);
+}
+
+int yt8614_aneg_done(struct phy_device *phydev)
+{
+ int link_fiber = 0, link_utp = 0;
+
+ /* reading Fiber */
+ ytphy_write_ext(phydev, 0xa000, 3);
+ 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)));
+ return !!(link_fiber | link_utp);
+}
+
+static int yt8614_read_status(struct phy_device *phydev)
+{
+ int ret;
+ int val, yt8614_fiber_latch_val, yt8614_fiber_curr_val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) {
+ /* switch to utp and reading regs */
+ 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) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up,"
+ "UTP, reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_utp = 1;
+ // here is same as 8521 and re-use the function;
+ yt8521_adjust_status(phydev, val, 1);
+ } else {
+ link_utp = 0;
+ }
+ }
+
+ if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_FIBER) {
+ /* reading Fiber/sgmii */
+ ret = ytphy_write_ext(phydev, 0xa000, 3);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ /* for fiber, from 1000m to 100m, there is not link down from 0x11,
+ * and check reg 1 to identify such case
+ */
+ yt8614_fiber_latch_val = phy_read(phydev, MII_BMSR);
+ yt8614_fiber_curr_val = phy_read(phydev, MII_BMSR);
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link && yt8614_fiber_latch_val != yt8614_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,
+ yt8614_fiber_latch_val,
+ yt8614_fiber_curr_val);
+ }
+
+ if (link) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, Fiber,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ 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\n",
+ __func__, phydev->mdio.addr,
+ (link_utp && link_fiber) ?
+ "both UTP and Fiber" :
+ (link_utp ? "UTP" : "Fiber"));
+ 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;
+ }
+
+ if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) {
+ if (link_utp)
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+ return 0;
+}
+
+static int yt8618_read_status(struct phy_device *phydev)
+{
+ int ret;
+ /* maybe for 8614 yt8521_fiber_latch_val, yt8521_fiber_curr_val; */
+ int val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ /* switch to utp and reading regs */
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
if (ret < 0)
- goto err_restore_page;
+ 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 (link_utp || link_fiber)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+
+ return 0;
+}
+
+int yt8618_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);
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+int yt8618_resume(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);
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+int yt8614_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, 3);
+ 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;
+}
+
+int yt8614_resume(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, 3);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
- /* set clock mode to 125mhz */
- ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M);
+ ytphy_write_ext(phydev, 0xa000, 0);
+
+#endif /* !(SYS_WAKEUP_BASED_ON_ETH_PKT) */
+
+ return 0;
+}
+
+int yt8821_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ val = ytphy_read_ext(phydev, 0xa001);
+ ytphy_write_ext(phydev, 0xa001, (val & ~0x8000));
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_soft_reset(phydev);
+
+ return ret;
+}
+
+static int yt8821_init(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val = 0;
+
+ /* sds pll cfg */
+ ret = ytphy_write_ext(phydev, 0xa050, 0x1000);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0xa000, 0x2);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x23, 0x47a1);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0xbd, 0x3547);
+ if (ret < 0)
+ return ret;
+
+ /* wait 1s */
+ msleep(1000);
+
+ /* calibration dcc */
+ ret = ytphy_write_ext(phydev, 0xbd, 0xa547);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x29, 0x3003);
if (ret < 0)
- goto err_restore_page;
+ return ret;
- /* fast ethernet delay is in a separate page */
- ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE);
+ /* sds driver swing */
+ ret = ytphy_write_ext(phydev, 0x25, 0x788);
if (ret < 0)
- goto err_restore_page;
+ return ret;
- ret = __phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe);
+ /* phy cfg */
+ ret = ytphy_write_ext(phydev, 0xa000, 0x0);
if (ret < 0)
- goto err_restore_page;
+ return ret;
- /* leave pll enabled in sleep */
- ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL);
+ /* phy template cfg */
+ ret = ytphy_write_ext(phydev, 0x471, 0x4545);
if (ret < 0)
- goto err_restore_page;
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x476, 0x4848);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x477, 0x4848);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x478, 0x4848);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x479, 0x4848);
+ if (ret < 0)
+ return ret;
+
+ /* calibrate phy lc pll */
+ ret = ytphy_write_ext(phydev, 0x600, 0x2300);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x8, 0x8041);
+ if (ret < 0)
+ return ret;
+
+ /* prm_small_lng/med */
+ ret = ytphy_write_ext(phydev, 0x388, 0x90);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x387, 0x90);
+ if (ret < 0)
+ return ret;
- ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP);
+ /* echo_delay_cfg */
+ ret = ytphy_write_ext(phydev, 0x3, 0xa026);
if (ret < 0)
- goto err_restore_page;
+ return ret;
-err_restore_page:
- return phy_restore_page(phydev, oldpage, ret);
+ /* pbo setting */
+ ret = ytphy_write_ext(phydev, 0x47e, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x47f, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x480, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x481, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x483, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x484, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x485, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x486, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x488, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x489, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48a, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48b, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48d, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48e, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48f, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x490, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x492, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x493, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x494, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x495, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x497, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x498, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x499, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49a, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49c, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49d, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49e, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49f, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0xa052, 0x7);
+ if (ret < 0)
+ return ret;
+
+ /* fast link down cfg */
+ ret = ytphy_write_ext(phydev, 0x355, 0x7d07);
+ if (ret < 0)
+ return ret;
+
+ /* soft reset */
+ val = phy_read(phydev, MII_BMCR);
+ if (val < 0)
+ return val;
+ ret = phy_write(phydev, MII_BMCR, val | BMCR_RESET);
+
+ return ret;
+}
+
+static int yt8821_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val, hw_strap_mode;
+
+ 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);
+ ret = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = yt8821_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* 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;
+
+ netdev_info(phydev->attached_dev,
+ "%s done, phy addr: %d, strap mode = %d\n",
+ __func__, phydev->mdio.addr, hw_strap_mode);
+
+ return ret;
+}
+
+/* for fiber mode, there is no 10M speed mode and
+ * this function is for this purpose.
+ */
+static int yt8821_adjust_status(struct phy_device *phydev, int val, int is_utp)
+{
+ int speed_mode, duplex;
+ int speed_mode_bit15_14, speed_mode_bit9;
+ int speed = SPEED_UNKNOWN;
+
+ if (is_utp)
+ duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT;
+ else
+ duplex = 1;
+
+ /* Bit9-Bit15-Bit14 speed mode 100---2.5G; 010---1000M; 001---100M; 000---10M */
+ speed_mode_bit15_14 = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT;
+ speed_mode_bit9 = (val & BIT(9)) >> 9;
+ speed_mode = (speed_mode_bit9 << 2) | speed_mode_bit15_14;
+ 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 4:
+ speed = SPEED_2500;
+ break;
+ default:
+ speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ phydev->speed = speed;
+ phydev->duplex = duplex;
+
+ return 0;
+}
+
+static int yt8821_read_status(struct phy_device *phydev)
+{
+ int ret;
+ int val;
+ int yt8521_fiber_latch_val;
+ int yt8521_fiber_curr_val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ 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) {
+ if (link_utp == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, UTP,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_utp = 1;
+ /* speed(2500), duplex */
+ yt8821_adjust_status(phydev, val, 1);
+ } else {
+ link_utp = 0;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER)
+
+ 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) {
+ if (link_fiber == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, Fiber,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_fiber = 1;
+ yt8821_adjust_status(phydev, val, 0);
+ } else {
+ link_fiber = 0;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP)
+
+ if (link_utp || link_fiber) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, media %s,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (link_utp && link_fiber) ?
+ "both UTP and Fiber" :
+ (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;
+ }
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
+ if (link_utp)
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+ return 0;
}
-static struct phy_driver motorcomm_phy_drvs[] = {
+static int yt8821_get_features(struct phy_device *phydev)
+{
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported, 1);
+ return genphy_read_abilities(phydev);
+}
+
+static struct phy_driver ytphy_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 = PHY_ID_YT8010,
+ .name = "YT8010 Automotive Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8010_soft_reset,
+ .config_aneg = yt8010_config_aneg,
+ .aneg_done = yt8010_aneg_done,
+ .config_init = ytphy_config_init,
+ .read_status = yt8010_read_status,
+ }, {
+ .phy_id = PHY_ID_YT8010AS,
+ .name = "YT8010AS Automotive Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8010AS_soft_reset,
+ .aneg_done = yt8010_aneg_done,
+ .config_init = yt8010AS_config_init,
+ .read_status = yt8010_read_status,
+ }, {
+ .phy_id = PHY_ID_YT8510,
+ .name = "YT8510 100/10Mb Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+ .config_init = ytphy_config_init,
+ .read_status = genphy_read_status,
+ }, {
+ .phy_id = PHY_ID_YT8511,
+ .name = "YT8511 Gigabit Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+#if GMAC_CLOCK_INPUT_NEEDED
+ .config_init = yt8511_config_init,
+#else
+ .config_init = ytphy_config_init,
+#endif
+ .read_status = genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_YT8512,
+ .name = "YT8512 Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+ .config_init = yt8512_config_init,
+ .read_status = yt8512_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_YT8512B,
+ .name = "YT8512B Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+ .config_init = yt8512_config_init,
+ .read_status = yt8512_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_YT8521,
+ .name = "YT8521 Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .probe = ytphy_probe,
+ .soft_reset = yt8521_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .config_init = yt8521_config_init,
+ .read_status = yt8521_read_status,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+ .link_change_notify = ytphy_link_change_notify,
+#if (YTPHY_WOL_FEATURE_ENABLE)
+ .get_wol = &ytphy_wol_feature_get,
+ .set_wol = &ytphy_wol_feature_set,
+#endif
+ }, {
+ /* same as 8521 */
+ .phy_id = PHY_ID_YT8531S,
+ .name = "YT8531S Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8521_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .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 = PHY_ID_YT8531,
+ .name = "YT8531 Gigabit Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .probe = ytphy_probe,
+ .config_aneg = genphy_config_aneg,
+ .config_init = yt8531_config_init,
+ .read_status = genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .link_change_notify = ytphy_link_change_notify,
+#if (YTPHY_WOL_FEATURE_ENABLE)
+ .get_wol = &ytphy_wol_feature_get,
+ .set_wol = &ytphy_wol_feature_set,
+#endif
+ }, {
+ .phy_id = PHY_ID_YT8618,
+ .name = "YT8618 Ethernet",
+ .phy_id_mask = MOTORCOMM_MPHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8618_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8618_aneg_done,
+ .config_init = yt8618_config_init,
+ .read_status = yt8618_read_status,
+ .suspend = yt8618_suspend,
+ .resume = yt8618_resume,
+ },
+ {
+ .phy_id = PHY_ID_YT8614,
+ .name = "YT8614 Ethernet",
+ .phy_id_mask = MOTORCOMM_MPHY_ID_MASK_8614,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8614_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8614_aneg_done,
+ .config_init = yt8614_config_init,
+ .read_status = yt8614_read_status,
+ .suspend = yt8614_suspend,
+ .resume = yt8614_resume,
+ },
+ {
+ .phy_id = PHY_ID_YT8821,
+ .name = "YT8821 2.5Gb Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK_8821,
+ .flags = PHY_POLL,
+ .soft_reset = yt8821_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .get_features = yt8821_get_features,
+ .config_init = yt8821_config_init,
+ .read_status = yt8821_read_status,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
},
};
-module_phy_driver(motorcomm_phy_drvs);
+/* for linux 4.x */
+module_phy_driver(ytphy_drvs);
MODULE_DESCRIPTION("Motorcomm PHY driver");
-MODULE_AUTHOR("Peter Geis");
+MODULE_AUTHOR("Leilei Zhao");
MODULE_LICENSE("GPL");
-static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
- { /* sentinal */ }
+static struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8531S, MOTORCOMM_PHY_ID_8531_MASK },
+ { PHY_ID_YT8531, MOTORCOMM_PHY_ID_8531_MASK },
+ { PHY_ID_YT8618, MOTORCOMM_MPHY_ID_MASK },
+ { PHY_ID_YT8614, MOTORCOMM_MPHY_ID_MASK_8614 },
+ { PHY_ID_YT8821, MOTORCOMM_PHY_ID_MASK_8821 },
+ { }
};
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
+