1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+ 2d0507009SDavid J. Choi /* 3d0507009SDavid J. Choi * drivers/net/phy/micrel.c 4d0507009SDavid J. Choi * 5d0507009SDavid J. Choi * Driver for Micrel PHYs 6d0507009SDavid J. Choi * 7d0507009SDavid J. Choi * Author: David J. Choi 8d0507009SDavid J. Choi * 97ab59dc1SDavid J. Choi * Copyright (c) 2010-2013 Micrel, Inc. 10ee0dc2fbSJohan Hovold * Copyright (c) 2014 Johan Hovold <johan@kernel.org> 11d0507009SDavid J. Choi * 127ab59dc1SDavid J. Choi * Support : Micrel Phys: 13bff5b4b3SYuiko Oshino * Giga phys: ksz9021, ksz9031, ksz9131 147ab59dc1SDavid J. Choi * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041 157ab59dc1SDavid J. Choi * ksz8021, ksz8031, ksz8051, 167ab59dc1SDavid J. Choi * ksz8081, ksz8091, 177ab59dc1SDavid J. Choi * ksz8061, 187ab59dc1SDavid J. Choi * Switch : ksz8873, ksz886x 19fc3973a1SWoojung Huh * ksz9477 20d0507009SDavid J. Choi */ 21d0507009SDavid J. Choi 22bcf3440cSOleksij Rempel #include <linux/bitfield.h> 2349011e0cSOleksij Rempel #include <linux/ethtool_netlink.h> 24d0507009SDavid J. Choi #include <linux/kernel.h> 25d0507009SDavid J. Choi #include <linux/module.h> 26d0507009SDavid J. Choi #include <linux/phy.h> 27d606ef3fSBaruch Siach #include <linux/micrel_phy.h> 28954c3967SSean Cross #include <linux/of.h> 291fadee0cSSascha Hauer #include <linux/clk.h> 306110dff7SOleksij Rempel #include <linux/delay.h> 31d0507009SDavid J. Choi 32212ea99aSMarek Vasut /* Operation Mode Strap Override */ 33212ea99aSMarek Vasut #define MII_KSZPHY_OMSO 0x16 347a1d8390SAntoine Tenart #define KSZPHY_OMSO_FACTORY_TEST BIT(15) 3500aee095SJohan Hovold #define KSZPHY_OMSO_B_CAST_OFF BIT(9) 362b0ba96cSSylvain Rochet #define KSZPHY_OMSO_NAND_TREE_ON BIT(5) 3700aee095SJohan Hovold #define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) 3800aee095SJohan Hovold #define KSZPHY_OMSO_MII_OVERRIDE BIT(0) 39212ea99aSMarek Vasut 4051f932c4SChoi, David /* general Interrupt control/status reg in vendor specific block. */ 4151f932c4SChoi, David #define MII_KSZPHY_INTCS 0x1B 4200aee095SJohan Hovold #define KSZPHY_INTCS_JABBER BIT(15) 4300aee095SJohan Hovold #define KSZPHY_INTCS_RECEIVE_ERR BIT(14) 4400aee095SJohan Hovold #define KSZPHY_INTCS_PAGE_RECEIVE BIT(13) 4500aee095SJohan Hovold #define KSZPHY_INTCS_PARELLEL BIT(12) 4600aee095SJohan Hovold #define KSZPHY_INTCS_LINK_PARTNER_ACK BIT(11) 4700aee095SJohan Hovold #define KSZPHY_INTCS_LINK_DOWN BIT(10) 4800aee095SJohan Hovold #define KSZPHY_INTCS_REMOTE_FAULT BIT(9) 4900aee095SJohan Hovold #define KSZPHY_INTCS_LINK_UP BIT(8) 5051f932c4SChoi, David #define KSZPHY_INTCS_ALL (KSZPHY_INTCS_LINK_UP |\ 5151f932c4SChoi, David KSZPHY_INTCS_LINK_DOWN) 5259ca4e58SIoana Ciornei #define KSZPHY_INTCS_LINK_DOWN_STATUS BIT(2) 5359ca4e58SIoana Ciornei #define KSZPHY_INTCS_LINK_UP_STATUS BIT(0) 5459ca4e58SIoana Ciornei #define KSZPHY_INTCS_STATUS (KSZPHY_INTCS_LINK_DOWN_STATUS |\ 5559ca4e58SIoana Ciornei KSZPHY_INTCS_LINK_UP_STATUS) 5651f932c4SChoi, David 5749011e0cSOleksij Rempel /* LinkMD Control/Status */ 5849011e0cSOleksij Rempel #define KSZ8081_LMD 0x1d 5949011e0cSOleksij Rempel #define KSZ8081_LMD_ENABLE_TEST BIT(15) 6049011e0cSOleksij Rempel #define KSZ8081_LMD_STAT_NORMAL 0 6149011e0cSOleksij Rempel #define KSZ8081_LMD_STAT_OPEN 1 6249011e0cSOleksij Rempel #define KSZ8081_LMD_STAT_SHORT 2 6349011e0cSOleksij Rempel #define KSZ8081_LMD_STAT_FAIL 3 6449011e0cSOleksij Rempel #define KSZ8081_LMD_STAT_MASK GENMASK(14, 13) 6549011e0cSOleksij Rempel /* Short cable (<10 meter) has been detected by LinkMD */ 6649011e0cSOleksij Rempel #define KSZ8081_LMD_SHORT_INDICATOR BIT(12) 6749011e0cSOleksij Rempel #define KSZ8081_LMD_DELTA_TIME_MASK GENMASK(8, 0) 6849011e0cSOleksij Rempel 69b3ec7248SDivya Koppera /* Lan8814 general Interrupt control/status reg in GPHY specific block. */ 70b3ec7248SDivya Koppera #define LAN8814_INTC 0x18 71b3ec7248SDivya Koppera #define LAN8814_INTS 0x1B 72b3ec7248SDivya Koppera 73b3ec7248SDivya Koppera #define LAN8814_INT_LINK_DOWN BIT(2) 74b3ec7248SDivya Koppera #define LAN8814_INT_LINK_UP BIT(0) 75b3ec7248SDivya Koppera #define LAN8814_INT_LINK (LAN8814_INT_LINK_UP |\ 76b3ec7248SDivya Koppera LAN8814_INT_LINK_DOWN) 77b3ec7248SDivya Koppera 78b3ec7248SDivya Koppera #define LAN8814_INTR_CTRL_REG 0x34 79b3ec7248SDivya Koppera #define LAN8814_INTR_CTRL_REG_POLARITY BIT(1) 80b3ec7248SDivya Koppera #define LAN8814_INTR_CTRL_REG_INTR_ENABLE BIT(0) 81b3ec7248SDivya Koppera 825a16778eSJohan Hovold /* PHY Control 1 */ 835a16778eSJohan Hovold #define MII_KSZPHY_CTRL_1 0x1e 84f873f112SOleksij Rempel #define KSZ8081_CTRL1_MDIX_STAT BIT(4) 855a16778eSJohan Hovold 865a16778eSJohan Hovold /* PHY Control 2 / PHY Control (if no PHY Control 1) */ 875a16778eSJohan Hovold #define MII_KSZPHY_CTRL_2 0x1f 885a16778eSJohan Hovold #define MII_KSZPHY_CTRL MII_KSZPHY_CTRL_2 8951f932c4SChoi, David /* bitmap of PHY register to set interrupt mode */ 90f873f112SOleksij Rempel #define KSZ8081_CTRL2_HP_MDIX BIT(15) 91f873f112SOleksij Rempel #define KSZ8081_CTRL2_MDI_MDI_X_SELECT BIT(14) 92f873f112SOleksij Rempel #define KSZ8081_CTRL2_DISABLE_AUTO_MDIX BIT(13) 93f873f112SOleksij Rempel #define KSZ8081_CTRL2_FORCE_LINK BIT(11) 94f873f112SOleksij Rempel #define KSZ8081_CTRL2_POWER_SAVING BIT(10) 9500aee095SJohan Hovold #define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9) 9663f44b2bSJohan Hovold #define KSZPHY_RMII_REF_CLK_SEL BIT(7) 9751f932c4SChoi, David 98954c3967SSean Cross /* Write/read to/from extended registers */ 99954c3967SSean Cross #define MII_KSZPHY_EXTREG 0x0b 100954c3967SSean Cross #define KSZPHY_EXTREG_WRITE 0x8000 101954c3967SSean Cross 102954c3967SSean Cross #define MII_KSZPHY_EXTREG_WRITE 0x0c 103954c3967SSean Cross #define MII_KSZPHY_EXTREG_READ 0x0d 104954c3967SSean Cross 105954c3967SSean Cross /* Extended registers */ 106954c3967SSean Cross #define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104 107954c3967SSean Cross #define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105 108954c3967SSean Cross #define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 109954c3967SSean Cross 110954c3967SSean Cross #define PS_TO_REG 200 111954c3967SSean Cross 1122b2427d0SAndrew Lunn struct kszphy_hw_stat { 1132b2427d0SAndrew Lunn const char *string; 1142b2427d0SAndrew Lunn u8 reg; 1152b2427d0SAndrew Lunn u8 bits; 1162b2427d0SAndrew Lunn }; 1172b2427d0SAndrew Lunn 1182b2427d0SAndrew Lunn static struct kszphy_hw_stat kszphy_hw_stats[] = { 1192b2427d0SAndrew Lunn { "phy_receive_errors", 21, 16}, 1202b2427d0SAndrew Lunn { "phy_idle_errors", 10, 8 }, 1212b2427d0SAndrew Lunn }; 1222b2427d0SAndrew Lunn 123e6a423a8SJohan Hovold struct kszphy_type { 124e6a423a8SJohan Hovold u32 led_mode_reg; 125c6f9575cSJohan Hovold u16 interrupt_level_mask; 1260f95903eSJohan Hovold bool has_broadcast_disable; 1272b0ba96cSSylvain Rochet bool has_nand_tree_disable; 12863f44b2bSJohan Hovold bool has_rmii_ref_clk_sel; 129e6a423a8SJohan Hovold }; 130e6a423a8SJohan Hovold 131e6a423a8SJohan Hovold struct kszphy_priv { 132e6a423a8SJohan Hovold const struct kszphy_type *type; 133e7a792e9SJohan Hovold int led_mode; 13463f44b2bSJohan Hovold bool rmii_ref_clk_sel; 13563f44b2bSJohan Hovold bool rmii_ref_clk_sel_val; 1362b2427d0SAndrew Lunn u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; 137e6a423a8SJohan Hovold }; 138e6a423a8SJohan Hovold 139e6a423a8SJohan Hovold static const struct kszphy_type ksz8021_type = { 140e6a423a8SJohan Hovold .led_mode_reg = MII_KSZPHY_CTRL_2, 141d0e1df9cSJohan Hovold .has_broadcast_disable = true, 1422b0ba96cSSylvain Rochet .has_nand_tree_disable = true, 14363f44b2bSJohan Hovold .has_rmii_ref_clk_sel = true, 144e6a423a8SJohan Hovold }; 145e6a423a8SJohan Hovold 146e6a423a8SJohan Hovold static const struct kszphy_type ksz8041_type = { 147e6a423a8SJohan Hovold .led_mode_reg = MII_KSZPHY_CTRL_1, 148e6a423a8SJohan Hovold }; 149e6a423a8SJohan Hovold 150e6a423a8SJohan Hovold static const struct kszphy_type ksz8051_type = { 151e6a423a8SJohan Hovold .led_mode_reg = MII_KSZPHY_CTRL_2, 1522b0ba96cSSylvain Rochet .has_nand_tree_disable = true, 153e6a423a8SJohan Hovold }; 154e6a423a8SJohan Hovold 155e6a423a8SJohan Hovold static const struct kszphy_type ksz8081_type = { 156e6a423a8SJohan Hovold .led_mode_reg = MII_KSZPHY_CTRL_2, 1570f95903eSJohan Hovold .has_broadcast_disable = true, 1582b0ba96cSSylvain Rochet .has_nand_tree_disable = true, 15986dc1342SJohan Hovold .has_rmii_ref_clk_sel = true, 160e6a423a8SJohan Hovold }; 161e6a423a8SJohan Hovold 162c6f9575cSJohan Hovold static const struct kszphy_type ks8737_type = { 163c6f9575cSJohan Hovold .interrupt_level_mask = BIT(14), 164c6f9575cSJohan Hovold }; 165c6f9575cSJohan Hovold 166c6f9575cSJohan Hovold static const struct kszphy_type ksz9021_type = { 167c6f9575cSJohan Hovold .interrupt_level_mask = BIT(14), 168c6f9575cSJohan Hovold }; 169c6f9575cSJohan Hovold 170954c3967SSean Cross static int kszphy_extended_write(struct phy_device *phydev, 171954c3967SSean Cross u32 regnum, u16 val) 172954c3967SSean Cross { 173954c3967SSean Cross phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum); 174954c3967SSean Cross return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val); 175954c3967SSean Cross } 176954c3967SSean Cross 177954c3967SSean Cross static int kszphy_extended_read(struct phy_device *phydev, 178954c3967SSean Cross u32 regnum) 179954c3967SSean Cross { 180954c3967SSean Cross phy_write(phydev, MII_KSZPHY_EXTREG, regnum); 181954c3967SSean Cross return phy_read(phydev, MII_KSZPHY_EXTREG_READ); 182954c3967SSean Cross } 183954c3967SSean Cross 18451f932c4SChoi, David static int kszphy_ack_interrupt(struct phy_device *phydev) 18551f932c4SChoi, David { 18651f932c4SChoi, David /* bit[7..0] int status, which is a read and clear register. */ 18751f932c4SChoi, David int rc; 18851f932c4SChoi, David 18951f932c4SChoi, David rc = phy_read(phydev, MII_KSZPHY_INTCS); 19051f932c4SChoi, David 19151f932c4SChoi, David return (rc < 0) ? rc : 0; 19251f932c4SChoi, David } 19351f932c4SChoi, David 19451f932c4SChoi, David static int kszphy_config_intr(struct phy_device *phydev) 19551f932c4SChoi, David { 196c6f9575cSJohan Hovold const struct kszphy_type *type = phydev->drv->driver_data; 197c0c99d0cSIoana Ciornei int temp, err; 198c6f9575cSJohan Hovold u16 mask; 199c6f9575cSJohan Hovold 200c6f9575cSJohan Hovold if (type && type->interrupt_level_mask) 201c6f9575cSJohan Hovold mask = type->interrupt_level_mask; 202c6f9575cSJohan Hovold else 203c6f9575cSJohan Hovold mask = KSZPHY_CTRL_INT_ACTIVE_HIGH; 20451f932c4SChoi, David 20551f932c4SChoi, David /* set the interrupt pin active low */ 20651f932c4SChoi, David temp = phy_read(phydev, MII_KSZPHY_CTRL); 2075bb8fc0dSJohan Hovold if (temp < 0) 2085bb8fc0dSJohan Hovold return temp; 209c6f9575cSJohan Hovold temp &= ~mask; 21051f932c4SChoi, David phy_write(phydev, MII_KSZPHY_CTRL, temp); 21151f932c4SChoi, David 212c6f9575cSJohan Hovold /* enable / disable interrupts */ 213c0c99d0cSIoana Ciornei if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 214c0c99d0cSIoana Ciornei err = kszphy_ack_interrupt(phydev); 215c0c99d0cSIoana Ciornei if (err) 216c0c99d0cSIoana Ciornei return err; 21751f932c4SChoi, David 218c0c99d0cSIoana Ciornei temp = KSZPHY_INTCS_ALL; 219c0c99d0cSIoana Ciornei err = phy_write(phydev, MII_KSZPHY_INTCS, temp); 220c0c99d0cSIoana Ciornei } else { 221c0c99d0cSIoana Ciornei temp = 0; 222c0c99d0cSIoana Ciornei err = phy_write(phydev, MII_KSZPHY_INTCS, temp); 223c0c99d0cSIoana Ciornei if (err) 224c0c99d0cSIoana Ciornei return err; 225c0c99d0cSIoana Ciornei 226c0c99d0cSIoana Ciornei err = kszphy_ack_interrupt(phydev); 227c0c99d0cSIoana Ciornei } 228c0c99d0cSIoana Ciornei 229c0c99d0cSIoana Ciornei return err; 23051f932c4SChoi, David } 231d0507009SDavid J. Choi 23259ca4e58SIoana Ciornei static irqreturn_t kszphy_handle_interrupt(struct phy_device *phydev) 23359ca4e58SIoana Ciornei { 23459ca4e58SIoana Ciornei int irq_status; 23559ca4e58SIoana Ciornei 23659ca4e58SIoana Ciornei irq_status = phy_read(phydev, MII_KSZPHY_INTCS); 23759ca4e58SIoana Ciornei if (irq_status < 0) { 23859ca4e58SIoana Ciornei phy_error(phydev); 23959ca4e58SIoana Ciornei return IRQ_NONE; 24059ca4e58SIoana Ciornei } 24159ca4e58SIoana Ciornei 242fff4c746SOleksij Rempel if (!(irq_status & KSZPHY_INTCS_STATUS)) 24359ca4e58SIoana Ciornei return IRQ_NONE; 24459ca4e58SIoana Ciornei 24559ca4e58SIoana Ciornei phy_trigger_machine(phydev); 24659ca4e58SIoana Ciornei 24759ca4e58SIoana Ciornei return IRQ_HANDLED; 24859ca4e58SIoana Ciornei } 24959ca4e58SIoana Ciornei 25063f44b2bSJohan Hovold static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) 25163f44b2bSJohan Hovold { 25263f44b2bSJohan Hovold int ctrl; 25363f44b2bSJohan Hovold 25463f44b2bSJohan Hovold ctrl = phy_read(phydev, MII_KSZPHY_CTRL); 25563f44b2bSJohan Hovold if (ctrl < 0) 25663f44b2bSJohan Hovold return ctrl; 25763f44b2bSJohan Hovold 25863f44b2bSJohan Hovold if (val) 25963f44b2bSJohan Hovold ctrl |= KSZPHY_RMII_REF_CLK_SEL; 26063f44b2bSJohan Hovold else 26163f44b2bSJohan Hovold ctrl &= ~KSZPHY_RMII_REF_CLK_SEL; 26263f44b2bSJohan Hovold 26363f44b2bSJohan Hovold return phy_write(phydev, MII_KSZPHY_CTRL, ctrl); 26463f44b2bSJohan Hovold } 26563f44b2bSJohan Hovold 266e7a792e9SJohan Hovold static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) 26720d8435aSBen Dooks { 2685a16778eSJohan Hovold int rc, temp, shift; 2698620546cSJohan Hovold 2705a16778eSJohan Hovold switch (reg) { 2715a16778eSJohan Hovold case MII_KSZPHY_CTRL_1: 2725a16778eSJohan Hovold shift = 14; 2735a16778eSJohan Hovold break; 2745a16778eSJohan Hovold case MII_KSZPHY_CTRL_2: 2755a16778eSJohan Hovold shift = 4; 2765a16778eSJohan Hovold break; 2775a16778eSJohan Hovold default: 2785a16778eSJohan Hovold return -EINVAL; 2795a16778eSJohan Hovold } 2805a16778eSJohan Hovold 28120d8435aSBen Dooks temp = phy_read(phydev, reg); 282b7035860SJohan Hovold if (temp < 0) { 283b7035860SJohan Hovold rc = temp; 284b7035860SJohan Hovold goto out; 285b7035860SJohan Hovold } 28620d8435aSBen Dooks 28728bdc499SSergei Shtylyov temp &= ~(3 << shift); 28820d8435aSBen Dooks temp |= val << shift; 28920d8435aSBen Dooks rc = phy_write(phydev, reg, temp); 290b7035860SJohan Hovold out: 291b7035860SJohan Hovold if (rc < 0) 29272ba48beSAndrew Lunn phydev_err(phydev, "failed to set led mode\n"); 29320d8435aSBen Dooks 294b7035860SJohan Hovold return rc; 29520d8435aSBen Dooks } 29620d8435aSBen Dooks 297bde15129SJohan Hovold /* Disable PHY address 0 as the broadcast address, so that it can be used as a 298bde15129SJohan Hovold * unique (non-broadcast) address on a shared bus. 299bde15129SJohan Hovold */ 300bde15129SJohan Hovold static int kszphy_broadcast_disable(struct phy_device *phydev) 301bde15129SJohan Hovold { 302bde15129SJohan Hovold int ret; 303bde15129SJohan Hovold 304bde15129SJohan Hovold ret = phy_read(phydev, MII_KSZPHY_OMSO); 305bde15129SJohan Hovold if (ret < 0) 306bde15129SJohan Hovold goto out; 307bde15129SJohan Hovold 308bde15129SJohan Hovold ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF); 309bde15129SJohan Hovold out: 310bde15129SJohan Hovold if (ret) 31172ba48beSAndrew Lunn phydev_err(phydev, "failed to disable broadcast address\n"); 312bde15129SJohan Hovold 313bde15129SJohan Hovold return ret; 314bde15129SJohan Hovold } 315bde15129SJohan Hovold 3162b0ba96cSSylvain Rochet static int kszphy_nand_tree_disable(struct phy_device *phydev) 3172b0ba96cSSylvain Rochet { 3182b0ba96cSSylvain Rochet int ret; 3192b0ba96cSSylvain Rochet 3202b0ba96cSSylvain Rochet ret = phy_read(phydev, MII_KSZPHY_OMSO); 3212b0ba96cSSylvain Rochet if (ret < 0) 3222b0ba96cSSylvain Rochet goto out; 3232b0ba96cSSylvain Rochet 3242b0ba96cSSylvain Rochet if (!(ret & KSZPHY_OMSO_NAND_TREE_ON)) 3252b0ba96cSSylvain Rochet return 0; 3262b0ba96cSSylvain Rochet 3272b0ba96cSSylvain Rochet ret = phy_write(phydev, MII_KSZPHY_OMSO, 3282b0ba96cSSylvain Rochet ret & ~KSZPHY_OMSO_NAND_TREE_ON); 3292b0ba96cSSylvain Rochet out: 3302b0ba96cSSylvain Rochet if (ret) 33172ba48beSAndrew Lunn phydev_err(phydev, "failed to disable NAND tree mode\n"); 3322b0ba96cSSylvain Rochet 3332b0ba96cSSylvain Rochet return ret; 3342b0ba96cSSylvain Rochet } 3352b0ba96cSSylvain Rochet 33679e498a9SLeonard Crestez /* Some config bits need to be set again on resume, handle them here. */ 33779e498a9SLeonard Crestez static int kszphy_config_reset(struct phy_device *phydev) 33879e498a9SLeonard Crestez { 33979e498a9SLeonard Crestez struct kszphy_priv *priv = phydev->priv; 34079e498a9SLeonard Crestez int ret; 34179e498a9SLeonard Crestez 34279e498a9SLeonard Crestez if (priv->rmii_ref_clk_sel) { 34379e498a9SLeonard Crestez ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); 34479e498a9SLeonard Crestez if (ret) { 34579e498a9SLeonard Crestez phydev_err(phydev, 34679e498a9SLeonard Crestez "failed to set rmii reference clock\n"); 34779e498a9SLeonard Crestez return ret; 34879e498a9SLeonard Crestez } 34979e498a9SLeonard Crestez } 35079e498a9SLeonard Crestez 35179e498a9SLeonard Crestez if (priv->led_mode >= 0) 35279e498a9SLeonard Crestez kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode); 35379e498a9SLeonard Crestez 35479e498a9SLeonard Crestez return 0; 35579e498a9SLeonard Crestez } 35679e498a9SLeonard Crestez 357d0507009SDavid J. Choi static int kszphy_config_init(struct phy_device *phydev) 358d0507009SDavid J. Choi { 359e6a423a8SJohan Hovold struct kszphy_priv *priv = phydev->priv; 360e6a423a8SJohan Hovold const struct kszphy_type *type; 361d0507009SDavid J. Choi 362e6a423a8SJohan Hovold if (!priv) 363e6a423a8SJohan Hovold return 0; 364e6a423a8SJohan Hovold 365e6a423a8SJohan Hovold type = priv->type; 366e6a423a8SJohan Hovold 3670f95903eSJohan Hovold if (type->has_broadcast_disable) 3680f95903eSJohan Hovold kszphy_broadcast_disable(phydev); 3690f95903eSJohan Hovold 3702b0ba96cSSylvain Rochet if (type->has_nand_tree_disable) 3712b0ba96cSSylvain Rochet kszphy_nand_tree_disable(phydev); 3722b0ba96cSSylvain Rochet 37379e498a9SLeonard Crestez return kszphy_config_reset(phydev); 37420d8435aSBen Dooks } 37520d8435aSBen Dooks 3764217a64eSMichael Walle static int ksz8041_fiber_mode(struct phy_device *phydev) 3774217a64eSMichael Walle { 3784217a64eSMichael Walle struct device_node *of_node = phydev->mdio.dev.of_node; 3794217a64eSMichael Walle 3804217a64eSMichael Walle return of_property_read_bool(of_node, "micrel,fiber-mode"); 3814217a64eSMichael Walle } 3824217a64eSMichael Walle 38377501a79SPhilipp Zabel static int ksz8041_config_init(struct phy_device *phydev) 38477501a79SPhilipp Zabel { 3853c1bcc86SAndrew Lunn __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 3863c1bcc86SAndrew Lunn 38777501a79SPhilipp Zabel /* Limit supported and advertised modes in fiber mode */ 3884217a64eSMichael Walle if (ksz8041_fiber_mode(phydev)) { 38977501a79SPhilipp Zabel phydev->dev_flags |= MICREL_PHY_FXEN; 3903c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask); 3913c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask); 3923c1bcc86SAndrew Lunn 3933c1bcc86SAndrew Lunn linkmode_and(phydev->supported, phydev->supported, mask); 3943c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 3953c1bcc86SAndrew Lunn phydev->supported); 3963c1bcc86SAndrew Lunn linkmode_and(phydev->advertising, phydev->advertising, mask); 3973c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 3983c1bcc86SAndrew Lunn phydev->advertising); 39977501a79SPhilipp Zabel phydev->autoneg = AUTONEG_DISABLE; 40077501a79SPhilipp Zabel } 40177501a79SPhilipp Zabel 40277501a79SPhilipp Zabel return kszphy_config_init(phydev); 40377501a79SPhilipp Zabel } 40477501a79SPhilipp Zabel 40577501a79SPhilipp Zabel static int ksz8041_config_aneg(struct phy_device *phydev) 40677501a79SPhilipp Zabel { 40777501a79SPhilipp Zabel /* Skip auto-negotiation in fiber mode */ 40877501a79SPhilipp Zabel if (phydev->dev_flags & MICREL_PHY_FXEN) { 40977501a79SPhilipp Zabel phydev->speed = SPEED_100; 41077501a79SPhilipp Zabel return 0; 41177501a79SPhilipp Zabel } 41277501a79SPhilipp Zabel 41377501a79SPhilipp Zabel return genphy_config_aneg(phydev); 41477501a79SPhilipp Zabel } 41577501a79SPhilipp Zabel 4168b95599cSMarek Vasut static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev, 417a5e63c7dSSteve Bennett const bool ksz_8051) 4188b95599cSMarek Vasut { 4198b95599cSMarek Vasut int ret; 4208b95599cSMarek Vasut 421a5e63c7dSSteve Bennett if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051) 4228b95599cSMarek Vasut return 0; 4238b95599cSMarek Vasut 4248b95599cSMarek Vasut ret = phy_read(phydev, MII_BMSR); 4258b95599cSMarek Vasut if (ret < 0) 4268b95599cSMarek Vasut return ret; 4278b95599cSMarek Vasut 4288b95599cSMarek Vasut /* KSZ8051 PHY and KSZ8794/KSZ8795/KSZ8765 switch share the same 4298b95599cSMarek Vasut * exact PHY ID. However, they can be told apart by the extended 4308b95599cSMarek Vasut * capability registers presence. The KSZ8051 PHY has them while 4318b95599cSMarek Vasut * the switch does not. 4328b95599cSMarek Vasut */ 4338b95599cSMarek Vasut ret &= BMSR_ERCAP; 434a5e63c7dSSteve Bennett if (ksz_8051) 4358b95599cSMarek Vasut return ret; 4368b95599cSMarek Vasut else 4378b95599cSMarek Vasut return !ret; 4388b95599cSMarek Vasut } 4398b95599cSMarek Vasut 4408b95599cSMarek Vasut static int ksz8051_match_phy_device(struct phy_device *phydev) 4418b95599cSMarek Vasut { 442a5e63c7dSSteve Bennett return ksz8051_ksz8795_match_phy_device(phydev, true); 4438b95599cSMarek Vasut } 4448b95599cSMarek Vasut 4457a1d8390SAntoine Tenart static int ksz8081_config_init(struct phy_device *phydev) 4467a1d8390SAntoine Tenart { 4477a1d8390SAntoine Tenart /* KSZPHY_OMSO_FACTORY_TEST is set at de-assertion of the reset line 4487a1d8390SAntoine Tenart * based on the RXER (KSZ8081RNA/RND) or TXC (KSZ8081MNX/RNB) pin. If a 4497a1d8390SAntoine Tenart * pull-down is missing, the factory test mode should be cleared by 4507a1d8390SAntoine Tenart * manually writing a 0. 4517a1d8390SAntoine Tenart */ 4527a1d8390SAntoine Tenart phy_clear_bits(phydev, MII_KSZPHY_OMSO, KSZPHY_OMSO_FACTORY_TEST); 4537a1d8390SAntoine Tenart 4547a1d8390SAntoine Tenart return kszphy_config_init(phydev); 4557a1d8390SAntoine Tenart } 4567a1d8390SAntoine Tenart 457f873f112SOleksij Rempel static int ksz8081_config_mdix(struct phy_device *phydev, u8 ctrl) 458f873f112SOleksij Rempel { 459f873f112SOleksij Rempel u16 val; 460f873f112SOleksij Rempel 461f873f112SOleksij Rempel switch (ctrl) { 462f873f112SOleksij Rempel case ETH_TP_MDI: 463f873f112SOleksij Rempel val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX; 464f873f112SOleksij Rempel break; 465f873f112SOleksij Rempel case ETH_TP_MDI_X: 466f873f112SOleksij Rempel val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX | 467f873f112SOleksij Rempel KSZ8081_CTRL2_MDI_MDI_X_SELECT; 468f873f112SOleksij Rempel break; 469f873f112SOleksij Rempel case ETH_TP_MDI_AUTO: 470f873f112SOleksij Rempel val = 0; 471f873f112SOleksij Rempel break; 472f873f112SOleksij Rempel default: 473f873f112SOleksij Rempel return 0; 474f873f112SOleksij Rempel } 475f873f112SOleksij Rempel 476f873f112SOleksij Rempel return phy_modify(phydev, MII_KSZPHY_CTRL_2, 477f873f112SOleksij Rempel KSZ8081_CTRL2_HP_MDIX | 478f873f112SOleksij Rempel KSZ8081_CTRL2_MDI_MDI_X_SELECT | 479f873f112SOleksij Rempel KSZ8081_CTRL2_DISABLE_AUTO_MDIX, 480f873f112SOleksij Rempel KSZ8081_CTRL2_HP_MDIX | val); 481f873f112SOleksij Rempel } 482f873f112SOleksij Rempel 483f873f112SOleksij Rempel static int ksz8081_config_aneg(struct phy_device *phydev) 484f873f112SOleksij Rempel { 485f873f112SOleksij Rempel int ret; 486f873f112SOleksij Rempel 487f873f112SOleksij Rempel ret = genphy_config_aneg(phydev); 488f873f112SOleksij Rempel if (ret) 489f873f112SOleksij Rempel return ret; 490f873f112SOleksij Rempel 491f873f112SOleksij Rempel /* The MDI-X configuration is automatically changed by the PHY after 492f873f112SOleksij Rempel * switching from autoneg off to on. So, take MDI-X configuration under 493f873f112SOleksij Rempel * own control and set it after autoneg configuration was done. 494f873f112SOleksij Rempel */ 495f873f112SOleksij Rempel return ksz8081_config_mdix(phydev, phydev->mdix_ctrl); 496f873f112SOleksij Rempel } 497f873f112SOleksij Rempel 498f873f112SOleksij Rempel static int ksz8081_mdix_update(struct phy_device *phydev) 499f873f112SOleksij Rempel { 500f873f112SOleksij Rempel int ret; 501f873f112SOleksij Rempel 502f873f112SOleksij Rempel ret = phy_read(phydev, MII_KSZPHY_CTRL_2); 503f873f112SOleksij Rempel if (ret < 0) 504f873f112SOleksij Rempel return ret; 505f873f112SOleksij Rempel 506f873f112SOleksij Rempel if (ret & KSZ8081_CTRL2_DISABLE_AUTO_MDIX) { 507f873f112SOleksij Rempel if (ret & KSZ8081_CTRL2_MDI_MDI_X_SELECT) 508f873f112SOleksij Rempel phydev->mdix_ctrl = ETH_TP_MDI_X; 509f873f112SOleksij Rempel else 510f873f112SOleksij Rempel phydev->mdix_ctrl = ETH_TP_MDI; 511f873f112SOleksij Rempel } else { 512f873f112SOleksij Rempel phydev->mdix_ctrl = ETH_TP_MDI_AUTO; 513f873f112SOleksij Rempel } 514f873f112SOleksij Rempel 515f873f112SOleksij Rempel ret = phy_read(phydev, MII_KSZPHY_CTRL_1); 516f873f112SOleksij Rempel if (ret < 0) 517f873f112SOleksij Rempel return ret; 518f873f112SOleksij Rempel 519f873f112SOleksij Rempel if (ret & KSZ8081_CTRL1_MDIX_STAT) 520f873f112SOleksij Rempel phydev->mdix = ETH_TP_MDI; 521f873f112SOleksij Rempel else 522f873f112SOleksij Rempel phydev->mdix = ETH_TP_MDI_X; 523f873f112SOleksij Rempel 524f873f112SOleksij Rempel return 0; 525f873f112SOleksij Rempel } 526f873f112SOleksij Rempel 527f873f112SOleksij Rempel static int ksz8081_read_status(struct phy_device *phydev) 528f873f112SOleksij Rempel { 529f873f112SOleksij Rempel int ret; 530f873f112SOleksij Rempel 531f873f112SOleksij Rempel ret = ksz8081_mdix_update(phydev); 532f873f112SOleksij Rempel if (ret < 0) 533f873f112SOleksij Rempel return ret; 534f873f112SOleksij Rempel 535f873f112SOleksij Rempel return genphy_read_status(phydev); 536f873f112SOleksij Rempel } 537f873f112SOleksij Rempel 538232ba3a5SRajasingh Thavamani static int ksz8061_config_init(struct phy_device *phydev) 539232ba3a5SRajasingh Thavamani { 540232ba3a5SRajasingh Thavamani int ret; 541232ba3a5SRajasingh Thavamani 542232ba3a5SRajasingh Thavamani ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); 543232ba3a5SRajasingh Thavamani if (ret) 544232ba3a5SRajasingh Thavamani return ret; 545232ba3a5SRajasingh Thavamani 546232ba3a5SRajasingh Thavamani return kszphy_config_init(phydev); 547232ba3a5SRajasingh Thavamani } 548232ba3a5SRajasingh Thavamani 5498b95599cSMarek Vasut static int ksz8795_match_phy_device(struct phy_device *phydev) 5508b95599cSMarek Vasut { 551a5e63c7dSSteve Bennett return ksz8051_ksz8795_match_phy_device(phydev, false); 5528b95599cSMarek Vasut } 5538b95599cSMarek Vasut 554954c3967SSean Cross static int ksz9021_load_values_from_of(struct phy_device *phydev, 5553c9a9f7fSJaeden Amero const struct device_node *of_node, 5563c9a9f7fSJaeden Amero u16 reg, 5573c9a9f7fSJaeden Amero const char *field1, const char *field2, 5583c9a9f7fSJaeden Amero const char *field3, const char *field4) 559954c3967SSean Cross { 560954c3967SSean Cross int val1 = -1; 561954c3967SSean Cross int val2 = -2; 562954c3967SSean Cross int val3 = -3; 563954c3967SSean Cross int val4 = -4; 564954c3967SSean Cross int newval; 565954c3967SSean Cross int matches = 0; 566954c3967SSean Cross 567954c3967SSean Cross if (!of_property_read_u32(of_node, field1, &val1)) 568954c3967SSean Cross matches++; 569954c3967SSean Cross 570954c3967SSean Cross if (!of_property_read_u32(of_node, field2, &val2)) 571954c3967SSean Cross matches++; 572954c3967SSean Cross 573954c3967SSean Cross if (!of_property_read_u32(of_node, field3, &val3)) 574954c3967SSean Cross matches++; 575954c3967SSean Cross 576954c3967SSean Cross if (!of_property_read_u32(of_node, field4, &val4)) 577954c3967SSean Cross matches++; 578954c3967SSean Cross 579954c3967SSean Cross if (!matches) 580954c3967SSean Cross return 0; 581954c3967SSean Cross 582954c3967SSean Cross if (matches < 4) 583954c3967SSean Cross newval = kszphy_extended_read(phydev, reg); 584954c3967SSean Cross else 585954c3967SSean Cross newval = 0; 586954c3967SSean Cross 587954c3967SSean Cross if (val1 != -1) 588954c3967SSean Cross newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0); 589954c3967SSean Cross 5906a119745SHubert Chaumette if (val2 != -2) 591954c3967SSean Cross newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4); 592954c3967SSean Cross 5936a119745SHubert Chaumette if (val3 != -3) 594954c3967SSean Cross newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8); 595954c3967SSean Cross 5966a119745SHubert Chaumette if (val4 != -4) 597954c3967SSean Cross newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12); 598954c3967SSean Cross 599954c3967SSean Cross return kszphy_extended_write(phydev, reg, newval); 600954c3967SSean Cross } 601954c3967SSean Cross 602954c3967SSean Cross static int ksz9021_config_init(struct phy_device *phydev) 603954c3967SSean Cross { 604ce4f8afdSColin Ian King const struct device_node *of_node; 605651df218SAndrew Lunn const struct device *dev_walker; 606954c3967SSean Cross 607651df218SAndrew Lunn /* The Micrel driver has a deprecated option to place phy OF 608651df218SAndrew Lunn * properties in the MAC node. Walk up the tree of devices to 609651df218SAndrew Lunn * find a device with an OF node. 610651df218SAndrew Lunn */ 611e5a03bfdSAndrew Lunn dev_walker = &phydev->mdio.dev; 612651df218SAndrew Lunn do { 613651df218SAndrew Lunn of_node = dev_walker->of_node; 614651df218SAndrew Lunn dev_walker = dev_walker->parent; 615651df218SAndrew Lunn 616651df218SAndrew Lunn } while (!of_node && dev_walker); 617954c3967SSean Cross 618954c3967SSean Cross if (of_node) { 619954c3967SSean Cross ksz9021_load_values_from_of(phydev, of_node, 620954c3967SSean Cross MII_KSZPHY_CLK_CONTROL_PAD_SKEW, 621954c3967SSean Cross "txen-skew-ps", "txc-skew-ps", 622954c3967SSean Cross "rxdv-skew-ps", "rxc-skew-ps"); 623954c3967SSean Cross ksz9021_load_values_from_of(phydev, of_node, 624954c3967SSean Cross MII_KSZPHY_RX_DATA_PAD_SKEW, 625954c3967SSean Cross "rxd0-skew-ps", "rxd1-skew-ps", 626954c3967SSean Cross "rxd2-skew-ps", "rxd3-skew-ps"); 627954c3967SSean Cross ksz9021_load_values_from_of(phydev, of_node, 628954c3967SSean Cross MII_KSZPHY_TX_DATA_PAD_SKEW, 629954c3967SSean Cross "txd0-skew-ps", "txd1-skew-ps", 630954c3967SSean Cross "txd2-skew-ps", "txd3-skew-ps"); 631954c3967SSean Cross } 632954c3967SSean Cross return 0; 633954c3967SSean Cross } 634954c3967SSean Cross 6356e4b8273SHubert Chaumette #define KSZ9031_PS_TO_REG 60 6366e4b8273SHubert Chaumette 6376e4b8273SHubert Chaumette /* Extended registers */ 6386270e1aeSJaeden Amero /* MMD Address 0x0 */ 6396270e1aeSJaeden Amero #define MII_KSZ9031RN_FLP_BURST_TX_LO 3 6406270e1aeSJaeden Amero #define MII_KSZ9031RN_FLP_BURST_TX_HI 4 6416270e1aeSJaeden Amero 642ae6c97bbSJaeden Amero /* MMD Address 0x2 */ 6436e4b8273SHubert Chaumette #define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 644bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RX_CTL_M GENMASK(7, 4) 645bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TX_CTL_M GENMASK(3, 0) 646bcf3440cSOleksij Rempel 6476e4b8273SHubert Chaumette #define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 648bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD3 GENMASK(15, 12) 649bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD2 GENMASK(11, 8) 650bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD1 GENMASK(7, 4) 651bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD0 GENMASK(3, 0) 652bcf3440cSOleksij Rempel 6536e4b8273SHubert Chaumette #define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 654bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD3 GENMASK(15, 12) 655bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD2 GENMASK(11, 8) 656bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD1 GENMASK(7, 4) 657bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD0 GENMASK(3, 0) 658bcf3440cSOleksij Rempel 6596e4b8273SHubert Chaumette #define MII_KSZ9031RN_CLK_PAD_SKEW 8 660bcf3440cSOleksij Rempel #define MII_KSZ9031RN_GTX_CLK GENMASK(9, 5) 661bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RX_CLK GENMASK(4, 0) 662bcf3440cSOleksij Rempel 663bcf3440cSOleksij Rempel /* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To 664bcf3440cSOleksij Rempel * provide different RGMII options we need to configure delay offset 665bcf3440cSOleksij Rempel * for each pad relative to build in delay. 666bcf3440cSOleksij Rempel */ 667bcf3440cSOleksij Rempel /* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of 668bcf3440cSOleksij Rempel * 1.80ns 669bcf3440cSOleksij Rempel */ 670bcf3440cSOleksij Rempel #define RX_ID 0x7 671bcf3440cSOleksij Rempel #define RX_CLK_ID 0x19 672bcf3440cSOleksij Rempel 673bcf3440cSOleksij Rempel /* set rx to +0.30ns and rx_clk to -0.90ns to compensate the 674bcf3440cSOleksij Rempel * internal 1.2ns delay. 675bcf3440cSOleksij Rempel */ 676bcf3440cSOleksij Rempel #define RX_ND 0xc 677bcf3440cSOleksij Rempel #define RX_CLK_ND 0x0 678bcf3440cSOleksij Rempel 679bcf3440cSOleksij Rempel /* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */ 680bcf3440cSOleksij Rempel #define TX_ID 0x0 681bcf3440cSOleksij Rempel #define TX_CLK_ID 0x1f 682bcf3440cSOleksij Rempel 683bcf3440cSOleksij Rempel /* set tx and tx_clk to "No delay adjustment" to keep 0ns 684bcf3440cSOleksij Rempel * dealy 685bcf3440cSOleksij Rempel */ 686bcf3440cSOleksij Rempel #define TX_ND 0x7 687bcf3440cSOleksij Rempel #define TX_CLK_ND 0xf 6886e4b8273SHubert Chaumette 689af70c1f9SMike Looijmans /* MMD Address 0x1C */ 690af70c1f9SMike Looijmans #define MII_KSZ9031RN_EDPD 0x23 691af70c1f9SMike Looijmans #define MII_KSZ9031RN_EDPD_ENABLE BIT(0) 692af70c1f9SMike Looijmans 6936e4b8273SHubert Chaumette static int ksz9031_of_load_skew_values(struct phy_device *phydev, 6943c9a9f7fSJaeden Amero const struct device_node *of_node, 6956e4b8273SHubert Chaumette u16 reg, size_t field_sz, 696bcf3440cSOleksij Rempel const char *field[], u8 numfields, 697bcf3440cSOleksij Rempel bool *update) 6986e4b8273SHubert Chaumette { 6996e4b8273SHubert Chaumette int val[4] = {-1, -2, -3, -4}; 7006e4b8273SHubert Chaumette int matches = 0; 7016e4b8273SHubert Chaumette u16 mask; 7026e4b8273SHubert Chaumette u16 maxval; 7036e4b8273SHubert Chaumette u16 newval; 7046e4b8273SHubert Chaumette int i; 7056e4b8273SHubert Chaumette 7066e4b8273SHubert Chaumette for (i = 0; i < numfields; i++) 7076e4b8273SHubert Chaumette if (!of_property_read_u32(of_node, field[i], val + i)) 7086e4b8273SHubert Chaumette matches++; 7096e4b8273SHubert Chaumette 7106e4b8273SHubert Chaumette if (!matches) 7116e4b8273SHubert Chaumette return 0; 7126e4b8273SHubert Chaumette 713bcf3440cSOleksij Rempel *update |= true; 714bcf3440cSOleksij Rempel 7156e4b8273SHubert Chaumette if (matches < numfields) 7169b420effSHeiner Kallweit newval = phy_read_mmd(phydev, 2, reg); 7176e4b8273SHubert Chaumette else 7186e4b8273SHubert Chaumette newval = 0; 7196e4b8273SHubert Chaumette 7206e4b8273SHubert Chaumette maxval = (field_sz == 4) ? 0xf : 0x1f; 7216e4b8273SHubert Chaumette for (i = 0; i < numfields; i++) 7226e4b8273SHubert Chaumette if (val[i] != -(i + 1)) { 7236e4b8273SHubert Chaumette mask = 0xffff; 7246e4b8273SHubert Chaumette mask ^= maxval << (field_sz * i); 7256e4b8273SHubert Chaumette newval = (newval & mask) | 7266e4b8273SHubert Chaumette (((val[i] / KSZ9031_PS_TO_REG) & maxval) 7276e4b8273SHubert Chaumette << (field_sz * i)); 7286e4b8273SHubert Chaumette } 7296e4b8273SHubert Chaumette 7309b420effSHeiner Kallweit return phy_write_mmd(phydev, 2, reg, newval); 7316e4b8273SHubert Chaumette } 7326e4b8273SHubert Chaumette 733a0da456bSMax Uvarov /* Center KSZ9031RNX FLP timing at 16ms. */ 7346270e1aeSJaeden Amero static int ksz9031_center_flp_timing(struct phy_device *phydev) 7356270e1aeSJaeden Amero { 7366270e1aeSJaeden Amero int result; 7376270e1aeSJaeden Amero 7389b420effSHeiner Kallweit result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI, 7399b420effSHeiner Kallweit 0x0006); 740a0da456bSMax Uvarov if (result) 741a0da456bSMax Uvarov return result; 742a0da456bSMax Uvarov 7439b420effSHeiner Kallweit result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO, 7449b420effSHeiner Kallweit 0x1A80); 7456270e1aeSJaeden Amero if (result) 7466270e1aeSJaeden Amero return result; 7476270e1aeSJaeden Amero 7486270e1aeSJaeden Amero return genphy_restart_aneg(phydev); 7496270e1aeSJaeden Amero } 7506270e1aeSJaeden Amero 751af70c1f9SMike Looijmans /* Enable energy-detect power-down mode */ 752af70c1f9SMike Looijmans static int ksz9031_enable_edpd(struct phy_device *phydev) 753af70c1f9SMike Looijmans { 754af70c1f9SMike Looijmans int reg; 755af70c1f9SMike Looijmans 7569b420effSHeiner Kallweit reg = phy_read_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD); 757af70c1f9SMike Looijmans if (reg < 0) 758af70c1f9SMike Looijmans return reg; 7599b420effSHeiner Kallweit return phy_write_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD, 760af70c1f9SMike Looijmans reg | MII_KSZ9031RN_EDPD_ENABLE); 761af70c1f9SMike Looijmans } 762af70c1f9SMike Looijmans 763bcf3440cSOleksij Rempel static int ksz9031_config_rgmii_delay(struct phy_device *phydev) 764bcf3440cSOleksij Rempel { 765bcf3440cSOleksij Rempel u16 rx, tx, rx_clk, tx_clk; 766bcf3440cSOleksij Rempel int ret; 767bcf3440cSOleksij Rempel 768bcf3440cSOleksij Rempel switch (phydev->interface) { 769bcf3440cSOleksij Rempel case PHY_INTERFACE_MODE_RGMII: 770bcf3440cSOleksij Rempel tx = TX_ND; 771bcf3440cSOleksij Rempel tx_clk = TX_CLK_ND; 772bcf3440cSOleksij Rempel rx = RX_ND; 773bcf3440cSOleksij Rempel rx_clk = RX_CLK_ND; 774bcf3440cSOleksij Rempel break; 775bcf3440cSOleksij Rempel case PHY_INTERFACE_MODE_RGMII_ID: 776bcf3440cSOleksij Rempel tx = TX_ID; 777bcf3440cSOleksij Rempel tx_clk = TX_CLK_ID; 778bcf3440cSOleksij Rempel rx = RX_ID; 779bcf3440cSOleksij Rempel rx_clk = RX_CLK_ID; 780bcf3440cSOleksij Rempel break; 781bcf3440cSOleksij Rempel case PHY_INTERFACE_MODE_RGMII_RXID: 782bcf3440cSOleksij Rempel tx = TX_ND; 783bcf3440cSOleksij Rempel tx_clk = TX_CLK_ND; 784bcf3440cSOleksij Rempel rx = RX_ID; 785bcf3440cSOleksij Rempel rx_clk = RX_CLK_ID; 786bcf3440cSOleksij Rempel break; 787bcf3440cSOleksij Rempel case PHY_INTERFACE_MODE_RGMII_TXID: 788bcf3440cSOleksij Rempel tx = TX_ID; 789bcf3440cSOleksij Rempel tx_clk = TX_CLK_ID; 790bcf3440cSOleksij Rempel rx = RX_ND; 791bcf3440cSOleksij Rempel rx_clk = RX_CLK_ND; 792bcf3440cSOleksij Rempel break; 793bcf3440cSOleksij Rempel default: 794bcf3440cSOleksij Rempel return 0; 795bcf3440cSOleksij Rempel } 796bcf3440cSOleksij Rempel 797bcf3440cSOleksij Rempel ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_CONTROL_PAD_SKEW, 798bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | 799bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); 800bcf3440cSOleksij Rempel if (ret < 0) 801bcf3440cSOleksij Rempel return ret; 802bcf3440cSOleksij Rempel 803bcf3440cSOleksij Rempel ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_RX_DATA_PAD_SKEW, 804bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_RXD3, rx) | 805bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_RXD2, rx) | 806bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_RXD1, rx) | 807bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_RXD0, rx)); 808bcf3440cSOleksij Rempel if (ret < 0) 809bcf3440cSOleksij Rempel return ret; 810bcf3440cSOleksij Rempel 811bcf3440cSOleksij Rempel ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_TX_DATA_PAD_SKEW, 812bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_TXD3, tx) | 813bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_TXD2, tx) | 814bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_TXD1, tx) | 815bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_TXD0, tx)); 816bcf3440cSOleksij Rempel if (ret < 0) 817bcf3440cSOleksij Rempel return ret; 818bcf3440cSOleksij Rempel 819bcf3440cSOleksij Rempel return phy_write_mmd(phydev, 2, MII_KSZ9031RN_CLK_PAD_SKEW, 820bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | 821bcf3440cSOleksij Rempel FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); 822bcf3440cSOleksij Rempel } 823bcf3440cSOleksij Rempel 8246e4b8273SHubert Chaumette static int ksz9031_config_init(struct phy_device *phydev) 8256e4b8273SHubert Chaumette { 826ce4f8afdSColin Ian King const struct device_node *of_node; 8273c9a9f7fSJaeden Amero static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; 8283c9a9f7fSJaeden Amero static const char *rx_data_skews[4] = { 8296e4b8273SHubert Chaumette "rxd0-skew-ps", "rxd1-skew-ps", 8306e4b8273SHubert Chaumette "rxd2-skew-ps", "rxd3-skew-ps" 8316e4b8273SHubert Chaumette }; 8323c9a9f7fSJaeden Amero static const char *tx_data_skews[4] = { 8336e4b8273SHubert Chaumette "txd0-skew-ps", "txd1-skew-ps", 8346e4b8273SHubert Chaumette "txd2-skew-ps", "txd3-skew-ps" 8356e4b8273SHubert Chaumette }; 8363c9a9f7fSJaeden Amero static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; 837b4c19f71SRoosen Henri const struct device *dev_walker; 838af70c1f9SMike Looijmans int result; 839af70c1f9SMike Looijmans 840af70c1f9SMike Looijmans result = ksz9031_enable_edpd(phydev); 841af70c1f9SMike Looijmans if (result < 0) 842af70c1f9SMike Looijmans return result; 8436e4b8273SHubert Chaumette 844b4c19f71SRoosen Henri /* The Micrel driver has a deprecated option to place phy OF 845b4c19f71SRoosen Henri * properties in the MAC node. Walk up the tree of devices to 846b4c19f71SRoosen Henri * find a device with an OF node. 847b4c19f71SRoosen Henri */ 8489d367eddSDavid S. Miller dev_walker = &phydev->mdio.dev; 849b4c19f71SRoosen Henri do { 850b4c19f71SRoosen Henri of_node = dev_walker->of_node; 851b4c19f71SRoosen Henri dev_walker = dev_walker->parent; 852b4c19f71SRoosen Henri } while (!of_node && dev_walker); 8536e4b8273SHubert Chaumette 8546e4b8273SHubert Chaumette if (of_node) { 855bcf3440cSOleksij Rempel bool update = false; 856bcf3440cSOleksij Rempel 857bcf3440cSOleksij Rempel if (phy_interface_is_rgmii(phydev)) { 858bcf3440cSOleksij Rempel result = ksz9031_config_rgmii_delay(phydev); 859bcf3440cSOleksij Rempel if (result < 0) 860bcf3440cSOleksij Rempel return result; 861bcf3440cSOleksij Rempel } 862bcf3440cSOleksij Rempel 8636e4b8273SHubert Chaumette ksz9031_of_load_skew_values(phydev, of_node, 8646e4b8273SHubert Chaumette MII_KSZ9031RN_CLK_PAD_SKEW, 5, 865bcf3440cSOleksij Rempel clk_skews, 2, &update); 8666e4b8273SHubert Chaumette 8676e4b8273SHubert Chaumette ksz9031_of_load_skew_values(phydev, of_node, 8686e4b8273SHubert Chaumette MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, 869bcf3440cSOleksij Rempel control_skews, 2, &update); 8706e4b8273SHubert Chaumette 8716e4b8273SHubert Chaumette ksz9031_of_load_skew_values(phydev, of_node, 8726e4b8273SHubert Chaumette MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, 873bcf3440cSOleksij Rempel rx_data_skews, 4, &update); 8746e4b8273SHubert Chaumette 8756e4b8273SHubert Chaumette ksz9031_of_load_skew_values(phydev, of_node, 8766e4b8273SHubert Chaumette MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, 877bcf3440cSOleksij Rempel tx_data_skews, 4, &update); 878bcf3440cSOleksij Rempel 87967ca5159SMatthias Schiffer if (update && !phy_interface_is_rgmii(phydev)) 880bcf3440cSOleksij Rempel phydev_warn(phydev, 88167ca5159SMatthias Schiffer "*-skew-ps values should be used only with RGMII PHY modes\n"); 882e1b505a6SMarkus Niebel 883e1b505a6SMarkus Niebel /* Silicon Errata Sheet (DS80000691D or DS80000692D): 884e1b505a6SMarkus Niebel * When the device links in the 1000BASE-T slave mode only, 885e1b505a6SMarkus Niebel * the optional 125MHz reference output clock (CLK125_NDO) 886e1b505a6SMarkus Niebel * has wide duty cycle variation. 887e1b505a6SMarkus Niebel * 888e1b505a6SMarkus Niebel * The optional CLK125_NDO clock does not meet the RGMII 889e1b505a6SMarkus Niebel * 45/55 percent (min/max) duty cycle requirement and therefore 890e1b505a6SMarkus Niebel * cannot be used directly by the MAC side for clocking 891e1b505a6SMarkus Niebel * applications that have setup/hold time requirements on 892e1b505a6SMarkus Niebel * rising and falling clock edges. 893e1b505a6SMarkus Niebel * 894e1b505a6SMarkus Niebel * Workaround: 895e1b505a6SMarkus Niebel * Force the phy to be the master to receive a stable clock 896e1b505a6SMarkus Niebel * which meets the duty cycle requirement. 897e1b505a6SMarkus Niebel */ 898e1b505a6SMarkus Niebel if (of_property_read_bool(of_node, "micrel,force-master")) { 899e1b505a6SMarkus Niebel result = phy_read(phydev, MII_CTRL1000); 900e1b505a6SMarkus Niebel if (result < 0) 901e1b505a6SMarkus Niebel goto err_force_master; 902e1b505a6SMarkus Niebel 903e1b505a6SMarkus Niebel /* enable master mode, config & prefer master */ 904e1b505a6SMarkus Niebel result |= CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER; 905e1b505a6SMarkus Niebel result = phy_write(phydev, MII_CTRL1000, result); 906e1b505a6SMarkus Niebel if (result < 0) 907e1b505a6SMarkus Niebel goto err_force_master; 908e1b505a6SMarkus Niebel } 9096e4b8273SHubert Chaumette } 9106270e1aeSJaeden Amero 9116270e1aeSJaeden Amero return ksz9031_center_flp_timing(phydev); 912e1b505a6SMarkus Niebel 913e1b505a6SMarkus Niebel err_force_master: 914e1b505a6SMarkus Niebel phydev_err(phydev, "failed to force the phy to master mode\n"); 915e1b505a6SMarkus Niebel return result; 9166e4b8273SHubert Chaumette } 9176e4b8273SHubert Chaumette 918bff5b4b3SYuiko Oshino #define KSZ9131_SKEW_5BIT_MAX 2400 919bff5b4b3SYuiko Oshino #define KSZ9131_SKEW_4BIT_MAX 800 920bff5b4b3SYuiko Oshino #define KSZ9131_OFFSET 700 921bff5b4b3SYuiko Oshino #define KSZ9131_STEP 100 922bff5b4b3SYuiko Oshino 923bff5b4b3SYuiko Oshino static int ksz9131_of_load_skew_values(struct phy_device *phydev, 924bff5b4b3SYuiko Oshino struct device_node *of_node, 925bff5b4b3SYuiko Oshino u16 reg, size_t field_sz, 926bff5b4b3SYuiko Oshino char *field[], u8 numfields) 927bff5b4b3SYuiko Oshino { 928bff5b4b3SYuiko Oshino int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET), 929bff5b4b3SYuiko Oshino -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)}; 930bff5b4b3SYuiko Oshino int skewval, skewmax = 0; 931bff5b4b3SYuiko Oshino int matches = 0; 932bff5b4b3SYuiko Oshino u16 maxval; 933bff5b4b3SYuiko Oshino u16 newval; 934bff5b4b3SYuiko Oshino u16 mask; 935bff5b4b3SYuiko Oshino int i; 936bff5b4b3SYuiko Oshino 937bff5b4b3SYuiko Oshino /* psec properties in dts should mean x pico seconds */ 938bff5b4b3SYuiko Oshino if (field_sz == 5) 939bff5b4b3SYuiko Oshino skewmax = KSZ9131_SKEW_5BIT_MAX; 940bff5b4b3SYuiko Oshino else 941bff5b4b3SYuiko Oshino skewmax = KSZ9131_SKEW_4BIT_MAX; 942bff5b4b3SYuiko Oshino 943bff5b4b3SYuiko Oshino for (i = 0; i < numfields; i++) 944bff5b4b3SYuiko Oshino if (!of_property_read_s32(of_node, field[i], &skewval)) { 945bff5b4b3SYuiko Oshino if (skewval < -KSZ9131_OFFSET) 946bff5b4b3SYuiko Oshino skewval = -KSZ9131_OFFSET; 947bff5b4b3SYuiko Oshino else if (skewval > skewmax) 948bff5b4b3SYuiko Oshino skewval = skewmax; 949bff5b4b3SYuiko Oshino 950bff5b4b3SYuiko Oshino val[i] = skewval + KSZ9131_OFFSET; 951bff5b4b3SYuiko Oshino matches++; 952bff5b4b3SYuiko Oshino } 953bff5b4b3SYuiko Oshino 954bff5b4b3SYuiko Oshino if (!matches) 955bff5b4b3SYuiko Oshino return 0; 956bff5b4b3SYuiko Oshino 957bff5b4b3SYuiko Oshino if (matches < numfields) 9589b420effSHeiner Kallweit newval = phy_read_mmd(phydev, 2, reg); 959bff5b4b3SYuiko Oshino else 960bff5b4b3SYuiko Oshino newval = 0; 961bff5b4b3SYuiko Oshino 962bff5b4b3SYuiko Oshino maxval = (field_sz == 4) ? 0xf : 0x1f; 963bff5b4b3SYuiko Oshino for (i = 0; i < numfields; i++) 964bff5b4b3SYuiko Oshino if (val[i] != -(i + 1 + KSZ9131_OFFSET)) { 965bff5b4b3SYuiko Oshino mask = 0xffff; 966bff5b4b3SYuiko Oshino mask ^= maxval << (field_sz * i); 967bff5b4b3SYuiko Oshino newval = (newval & mask) | 968bff5b4b3SYuiko Oshino (((val[i] / KSZ9131_STEP) & maxval) 969bff5b4b3SYuiko Oshino << (field_sz * i)); 970bff5b4b3SYuiko Oshino } 971bff5b4b3SYuiko Oshino 9729b420effSHeiner Kallweit return phy_write_mmd(phydev, 2, reg, newval); 973bff5b4b3SYuiko Oshino } 974bff5b4b3SYuiko Oshino 975bd734a74SPhilippe Schenker #define KSZ9131RN_MMD_COMMON_CTRL_REG 2 976bd734a74SPhilippe Schenker #define KSZ9131RN_RXC_DLL_CTRL 76 977bd734a74SPhilippe Schenker #define KSZ9131RN_TXC_DLL_CTRL 77 978bd734a74SPhilippe Schenker #define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12) 979bd734a74SPhilippe Schenker #define KSZ9131RN_DLL_ENABLE_DELAY 0 980bd734a74SPhilippe Schenker #define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) 981bd734a74SPhilippe Schenker 982bd734a74SPhilippe Schenker static int ksz9131_config_rgmii_delay(struct phy_device *phydev) 983bd734a74SPhilippe Schenker { 984bd734a74SPhilippe Schenker u16 rxcdll_val, txcdll_val; 985bd734a74SPhilippe Schenker int ret; 986bd734a74SPhilippe Schenker 987bd734a74SPhilippe Schenker switch (phydev->interface) { 988bd734a74SPhilippe Schenker case PHY_INTERFACE_MODE_RGMII: 989bd734a74SPhilippe Schenker rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 990bd734a74SPhilippe Schenker txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 991bd734a74SPhilippe Schenker break; 992bd734a74SPhilippe Schenker case PHY_INTERFACE_MODE_RGMII_ID: 993bd734a74SPhilippe Schenker rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 994bd734a74SPhilippe Schenker txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 995bd734a74SPhilippe Schenker break; 996bd734a74SPhilippe Schenker case PHY_INTERFACE_MODE_RGMII_RXID: 997bd734a74SPhilippe Schenker rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 998bd734a74SPhilippe Schenker txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 999bd734a74SPhilippe Schenker break; 1000bd734a74SPhilippe Schenker case PHY_INTERFACE_MODE_RGMII_TXID: 1001bd734a74SPhilippe Schenker rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 1002bd734a74SPhilippe Schenker txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 1003bd734a74SPhilippe Schenker break; 1004bd734a74SPhilippe Schenker default: 1005bd734a74SPhilippe Schenker return 0; 1006bd734a74SPhilippe Schenker } 1007bd734a74SPhilippe Schenker 1008bd734a74SPhilippe Schenker ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 1009bd734a74SPhilippe Schenker KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, 1010bd734a74SPhilippe Schenker rxcdll_val); 1011bd734a74SPhilippe Schenker if (ret < 0) 1012bd734a74SPhilippe Schenker return ret; 1013bd734a74SPhilippe Schenker 1014bd734a74SPhilippe Schenker return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 1015bd734a74SPhilippe Schenker KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, 1016bd734a74SPhilippe Schenker txcdll_val); 1017bd734a74SPhilippe Schenker } 1018bd734a74SPhilippe Schenker 10190316c7e6SFrancesco Dolcini /* Silicon Errata DS80000693B 10200316c7e6SFrancesco Dolcini * 10210316c7e6SFrancesco Dolcini * When LEDs are configured in Individual Mode, LED1 is ON in a no-link 10220316c7e6SFrancesco Dolcini * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves 10230316c7e6SFrancesco Dolcini * according to the datasheet (off if there is no link). 10240316c7e6SFrancesco Dolcini */ 10250316c7e6SFrancesco Dolcini static int ksz9131_led_errata(struct phy_device *phydev) 10260316c7e6SFrancesco Dolcini { 10270316c7e6SFrancesco Dolcini int reg; 10280316c7e6SFrancesco Dolcini 10290316c7e6SFrancesco Dolcini reg = phy_read_mmd(phydev, 2, 0); 10300316c7e6SFrancesco Dolcini if (reg < 0) 10310316c7e6SFrancesco Dolcini return reg; 10320316c7e6SFrancesco Dolcini 10330316c7e6SFrancesco Dolcini if (!(reg & BIT(4))) 10340316c7e6SFrancesco Dolcini return 0; 10350316c7e6SFrancesco Dolcini 10360316c7e6SFrancesco Dolcini return phy_set_bits(phydev, 0x1e, BIT(9)); 10370316c7e6SFrancesco Dolcini } 10380316c7e6SFrancesco Dolcini 1039bff5b4b3SYuiko Oshino static int ksz9131_config_init(struct phy_device *phydev) 1040bff5b4b3SYuiko Oshino { 1041ce4f8afdSColin Ian King struct device_node *of_node; 1042bff5b4b3SYuiko Oshino char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"}; 1043bff5b4b3SYuiko Oshino char *rx_data_skews[4] = { 1044bff5b4b3SYuiko Oshino "rxd0-skew-psec", "rxd1-skew-psec", 1045bff5b4b3SYuiko Oshino "rxd2-skew-psec", "rxd3-skew-psec" 1046bff5b4b3SYuiko Oshino }; 1047bff5b4b3SYuiko Oshino char *tx_data_skews[4] = { 1048bff5b4b3SYuiko Oshino "txd0-skew-psec", "txd1-skew-psec", 1049bff5b4b3SYuiko Oshino "txd2-skew-psec", "txd3-skew-psec" 1050bff5b4b3SYuiko Oshino }; 1051bff5b4b3SYuiko Oshino char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"}; 1052bff5b4b3SYuiko Oshino const struct device *dev_walker; 1053bff5b4b3SYuiko Oshino int ret; 1054bff5b4b3SYuiko Oshino 1055bff5b4b3SYuiko Oshino dev_walker = &phydev->mdio.dev; 1056bff5b4b3SYuiko Oshino do { 1057bff5b4b3SYuiko Oshino of_node = dev_walker->of_node; 1058bff5b4b3SYuiko Oshino dev_walker = dev_walker->parent; 1059bff5b4b3SYuiko Oshino } while (!of_node && dev_walker); 1060bff5b4b3SYuiko Oshino 1061bff5b4b3SYuiko Oshino if (!of_node) 1062bff5b4b3SYuiko Oshino return 0; 1063bff5b4b3SYuiko Oshino 1064bd734a74SPhilippe Schenker if (phy_interface_is_rgmii(phydev)) { 1065bd734a74SPhilippe Schenker ret = ksz9131_config_rgmii_delay(phydev); 1066bd734a74SPhilippe Schenker if (ret < 0) 1067bd734a74SPhilippe Schenker return ret; 1068bd734a74SPhilippe Schenker } 1069bd734a74SPhilippe Schenker 1070bff5b4b3SYuiko Oshino ret = ksz9131_of_load_skew_values(phydev, of_node, 1071bff5b4b3SYuiko Oshino MII_KSZ9031RN_CLK_PAD_SKEW, 5, 1072bff5b4b3SYuiko Oshino clk_skews, 2); 1073bff5b4b3SYuiko Oshino if (ret < 0) 1074bff5b4b3SYuiko Oshino return ret; 1075bff5b4b3SYuiko Oshino 1076bff5b4b3SYuiko Oshino ret = ksz9131_of_load_skew_values(phydev, of_node, 1077bff5b4b3SYuiko Oshino MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, 1078bff5b4b3SYuiko Oshino control_skews, 2); 1079bff5b4b3SYuiko Oshino if (ret < 0) 1080bff5b4b3SYuiko Oshino return ret; 1081bff5b4b3SYuiko Oshino 1082bff5b4b3SYuiko Oshino ret = ksz9131_of_load_skew_values(phydev, of_node, 1083bff5b4b3SYuiko Oshino MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, 1084bff5b4b3SYuiko Oshino rx_data_skews, 4); 1085bff5b4b3SYuiko Oshino if (ret < 0) 1086bff5b4b3SYuiko Oshino return ret; 1087bff5b4b3SYuiko Oshino 1088bff5b4b3SYuiko Oshino ret = ksz9131_of_load_skew_values(phydev, of_node, 1089bff5b4b3SYuiko Oshino MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, 1090bff5b4b3SYuiko Oshino tx_data_skews, 4); 1091bff5b4b3SYuiko Oshino if (ret < 0) 1092bff5b4b3SYuiko Oshino return ret; 1093bff5b4b3SYuiko Oshino 10940316c7e6SFrancesco Dolcini ret = ksz9131_led_errata(phydev); 10950316c7e6SFrancesco Dolcini if (ret < 0) 10960316c7e6SFrancesco Dolcini return ret; 10970316c7e6SFrancesco Dolcini 1098bff5b4b3SYuiko Oshino return 0; 1099bff5b4b3SYuiko Oshino } 1100bff5b4b3SYuiko Oshino 110193272e07SJean-Christophe PLAGNIOL-VILLARD #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 110200aee095SJohan Hovold #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) 110300aee095SJohan Hovold #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) 110432d73b14SJingoo Han static int ksz8873mll_read_status(struct phy_device *phydev) 110593272e07SJean-Christophe PLAGNIOL-VILLARD { 110693272e07SJean-Christophe PLAGNIOL-VILLARD int regval; 110793272e07SJean-Christophe PLAGNIOL-VILLARD 110893272e07SJean-Christophe PLAGNIOL-VILLARD /* dummy read */ 110993272e07SJean-Christophe PLAGNIOL-VILLARD regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 111093272e07SJean-Christophe PLAGNIOL-VILLARD 111193272e07SJean-Christophe PLAGNIOL-VILLARD regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 111293272e07SJean-Christophe PLAGNIOL-VILLARD 111393272e07SJean-Christophe PLAGNIOL-VILLARD if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX) 111493272e07SJean-Christophe PLAGNIOL-VILLARD phydev->duplex = DUPLEX_HALF; 111593272e07SJean-Christophe PLAGNIOL-VILLARD else 111693272e07SJean-Christophe PLAGNIOL-VILLARD phydev->duplex = DUPLEX_FULL; 111793272e07SJean-Christophe PLAGNIOL-VILLARD 111893272e07SJean-Christophe PLAGNIOL-VILLARD if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED) 111993272e07SJean-Christophe PLAGNIOL-VILLARD phydev->speed = SPEED_10; 112093272e07SJean-Christophe PLAGNIOL-VILLARD else 112193272e07SJean-Christophe PLAGNIOL-VILLARD phydev->speed = SPEED_100; 112293272e07SJean-Christophe PLAGNIOL-VILLARD 112393272e07SJean-Christophe PLAGNIOL-VILLARD phydev->link = 1; 112493272e07SJean-Christophe PLAGNIOL-VILLARD phydev->pause = phydev->asym_pause = 0; 112593272e07SJean-Christophe PLAGNIOL-VILLARD 112693272e07SJean-Christophe PLAGNIOL-VILLARD return 0; 112793272e07SJean-Christophe PLAGNIOL-VILLARD } 112893272e07SJean-Christophe PLAGNIOL-VILLARD 11293aed3e2aSAntoine Tenart static int ksz9031_get_features(struct phy_device *phydev) 11303aed3e2aSAntoine Tenart { 11313aed3e2aSAntoine Tenart int ret; 11323aed3e2aSAntoine Tenart 11333aed3e2aSAntoine Tenart ret = genphy_read_abilities(phydev); 11343aed3e2aSAntoine Tenart if (ret < 0) 11353aed3e2aSAntoine Tenart return ret; 11363aed3e2aSAntoine Tenart 11373aed3e2aSAntoine Tenart /* Silicon Errata Sheet (DS80000691D or DS80000692D): 11383aed3e2aSAntoine Tenart * Whenever the device's Asymmetric Pause capability is set to 1, 11393aed3e2aSAntoine Tenart * link-up may fail after a link-up to link-down transition. 11403aed3e2aSAntoine Tenart * 1141407d8098SHans Andersson * The Errata Sheet is for ksz9031, but ksz9021 has the same issue 1142407d8098SHans Andersson * 11433aed3e2aSAntoine Tenart * Workaround: 11443aed3e2aSAntoine Tenart * Do not enable the Asymmetric Pause capability bit. 11453aed3e2aSAntoine Tenart */ 11463aed3e2aSAntoine Tenart linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); 11473aed3e2aSAntoine Tenart 11483aed3e2aSAntoine Tenart /* We force setting the Pause capability as the core will force the 11493aed3e2aSAntoine Tenart * Asymmetric Pause capability to 1 otherwise. 11503aed3e2aSAntoine Tenart */ 11513aed3e2aSAntoine Tenart linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); 11523aed3e2aSAntoine Tenart 11533aed3e2aSAntoine Tenart return 0; 11543aed3e2aSAntoine Tenart } 11553aed3e2aSAntoine Tenart 1156d2fd719bSNathan Sullivan static int ksz9031_read_status(struct phy_device *phydev) 1157d2fd719bSNathan Sullivan { 1158d2fd719bSNathan Sullivan int err; 1159d2fd719bSNathan Sullivan int regval; 1160d2fd719bSNathan Sullivan 1161d2fd719bSNathan Sullivan err = genphy_read_status(phydev); 1162d2fd719bSNathan Sullivan if (err) 1163d2fd719bSNathan Sullivan return err; 1164d2fd719bSNathan Sullivan 1165d2fd719bSNathan Sullivan /* Make sure the PHY is not broken. Read idle error count, 1166d2fd719bSNathan Sullivan * and reset the PHY if it is maxed out. 1167d2fd719bSNathan Sullivan */ 1168d2fd719bSNathan Sullivan regval = phy_read(phydev, MII_STAT1000); 1169d2fd719bSNathan Sullivan if ((regval & 0xFF) == 0xFF) { 1170d2fd719bSNathan Sullivan phy_init_hw(phydev); 1171d2fd719bSNathan Sullivan phydev->link = 0; 1172b866203dSZach Brown if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev)) 1173b866203dSZach Brown phydev->drv->config_intr(phydev); 1174c1a8d0a3SGrygorii Strashko return genphy_config_aneg(phydev); 1175d2fd719bSNathan Sullivan } 1176d2fd719bSNathan Sullivan 1177d2fd719bSNathan Sullivan return 0; 1178d2fd719bSNathan Sullivan } 1179d2fd719bSNathan Sullivan 118093272e07SJean-Christophe PLAGNIOL-VILLARD static int ksz8873mll_config_aneg(struct phy_device *phydev) 118193272e07SJean-Christophe PLAGNIOL-VILLARD { 118293272e07SJean-Christophe PLAGNIOL-VILLARD return 0; 118393272e07SJean-Christophe PLAGNIOL-VILLARD } 118493272e07SJean-Christophe PLAGNIOL-VILLARD 118552939393SOleksij Rempel static int ksz886x_config_mdix(struct phy_device *phydev, u8 ctrl) 118652939393SOleksij Rempel { 118752939393SOleksij Rempel u16 val; 118852939393SOleksij Rempel 118952939393SOleksij Rempel switch (ctrl) { 119052939393SOleksij Rempel case ETH_TP_MDI: 119152939393SOleksij Rempel val = KSZ886X_BMCR_DISABLE_AUTO_MDIX; 119252939393SOleksij Rempel break; 119352939393SOleksij Rempel case ETH_TP_MDI_X: 119452939393SOleksij Rempel /* Note: The naming of the bit KSZ886X_BMCR_FORCE_MDI is bit 119552939393SOleksij Rempel * counter intuitive, the "-X" in "1 = Force MDI" in the data 119652939393SOleksij Rempel * sheet seems to be missing: 119752939393SOleksij Rempel * 1 = Force MDI (sic!) (transmit on RX+/RX- pins) 119852939393SOleksij Rempel * 0 = Normal operation (transmit on TX+/TX- pins) 119952939393SOleksij Rempel */ 120052939393SOleksij Rempel val = KSZ886X_BMCR_DISABLE_AUTO_MDIX | KSZ886X_BMCR_FORCE_MDI; 120152939393SOleksij Rempel break; 120252939393SOleksij Rempel case ETH_TP_MDI_AUTO: 120352939393SOleksij Rempel val = 0; 120452939393SOleksij Rempel break; 120552939393SOleksij Rempel default: 120652939393SOleksij Rempel return 0; 120752939393SOleksij Rempel } 120852939393SOleksij Rempel 120952939393SOleksij Rempel return phy_modify(phydev, MII_BMCR, 121052939393SOleksij Rempel KSZ886X_BMCR_HP_MDIX | KSZ886X_BMCR_FORCE_MDI | 121152939393SOleksij Rempel KSZ886X_BMCR_DISABLE_AUTO_MDIX, 121252939393SOleksij Rempel KSZ886X_BMCR_HP_MDIX | val); 121352939393SOleksij Rempel } 121452939393SOleksij Rempel 121552939393SOleksij Rempel static int ksz886x_config_aneg(struct phy_device *phydev) 121652939393SOleksij Rempel { 121752939393SOleksij Rempel int ret; 121852939393SOleksij Rempel 121952939393SOleksij Rempel ret = genphy_config_aneg(phydev); 122052939393SOleksij Rempel if (ret) 122152939393SOleksij Rempel return ret; 122252939393SOleksij Rempel 122352939393SOleksij Rempel /* The MDI-X configuration is automatically changed by the PHY after 122452939393SOleksij Rempel * switching from autoneg off to on. So, take MDI-X configuration under 122552939393SOleksij Rempel * own control and set it after autoneg configuration was done. 122652939393SOleksij Rempel */ 122752939393SOleksij Rempel return ksz886x_config_mdix(phydev, phydev->mdix_ctrl); 122852939393SOleksij Rempel } 122952939393SOleksij Rempel 123052939393SOleksij Rempel static int ksz886x_mdix_update(struct phy_device *phydev) 123152939393SOleksij Rempel { 123252939393SOleksij Rempel int ret; 123352939393SOleksij Rempel 123452939393SOleksij Rempel ret = phy_read(phydev, MII_BMCR); 123552939393SOleksij Rempel if (ret < 0) 123652939393SOleksij Rempel return ret; 123752939393SOleksij Rempel 123852939393SOleksij Rempel if (ret & KSZ886X_BMCR_DISABLE_AUTO_MDIX) { 123952939393SOleksij Rempel if (ret & KSZ886X_BMCR_FORCE_MDI) 124052939393SOleksij Rempel phydev->mdix_ctrl = ETH_TP_MDI_X; 124152939393SOleksij Rempel else 124252939393SOleksij Rempel phydev->mdix_ctrl = ETH_TP_MDI; 124352939393SOleksij Rempel } else { 124452939393SOleksij Rempel phydev->mdix_ctrl = ETH_TP_MDI_AUTO; 124552939393SOleksij Rempel } 124652939393SOleksij Rempel 124752939393SOleksij Rempel ret = phy_read(phydev, MII_KSZPHY_CTRL); 124852939393SOleksij Rempel if (ret < 0) 124952939393SOleksij Rempel return ret; 125052939393SOleksij Rempel 125152939393SOleksij Rempel /* Same reverse logic as KSZ886X_BMCR_FORCE_MDI */ 125252939393SOleksij Rempel if (ret & KSZ886X_CTRL_MDIX_STAT) 125352939393SOleksij Rempel phydev->mdix = ETH_TP_MDI_X; 125452939393SOleksij Rempel else 125552939393SOleksij Rempel phydev->mdix = ETH_TP_MDI; 125652939393SOleksij Rempel 125752939393SOleksij Rempel return 0; 125852939393SOleksij Rempel } 125952939393SOleksij Rempel 126052939393SOleksij Rempel static int ksz886x_read_status(struct phy_device *phydev) 126152939393SOleksij Rempel { 126252939393SOleksij Rempel int ret; 126352939393SOleksij Rempel 126452939393SOleksij Rempel ret = ksz886x_mdix_update(phydev); 126552939393SOleksij Rempel if (ret < 0) 126652939393SOleksij Rempel return ret; 126752939393SOleksij Rempel 126852939393SOleksij Rempel return genphy_read_status(phydev); 126952939393SOleksij Rempel } 127052939393SOleksij Rempel 12712b2427d0SAndrew Lunn static int kszphy_get_sset_count(struct phy_device *phydev) 12722b2427d0SAndrew Lunn { 12732b2427d0SAndrew Lunn return ARRAY_SIZE(kszphy_hw_stats); 12742b2427d0SAndrew Lunn } 12752b2427d0SAndrew Lunn 12762b2427d0SAndrew Lunn static void kszphy_get_strings(struct phy_device *phydev, u8 *data) 12772b2427d0SAndrew Lunn { 12782b2427d0SAndrew Lunn int i; 12792b2427d0SAndrew Lunn 12802b2427d0SAndrew Lunn for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) { 128155f53567SFlorian Fainelli strlcpy(data + i * ETH_GSTRING_LEN, 12822b2427d0SAndrew Lunn kszphy_hw_stats[i].string, ETH_GSTRING_LEN); 12832b2427d0SAndrew Lunn } 12842b2427d0SAndrew Lunn } 12852b2427d0SAndrew Lunn 12862b2427d0SAndrew Lunn static u64 kszphy_get_stat(struct phy_device *phydev, int i) 12872b2427d0SAndrew Lunn { 12882b2427d0SAndrew Lunn struct kszphy_hw_stat stat = kszphy_hw_stats[i]; 12892b2427d0SAndrew Lunn struct kszphy_priv *priv = phydev->priv; 1290321b4d4bSAndrew Lunn int val; 1291321b4d4bSAndrew Lunn u64 ret; 12922b2427d0SAndrew Lunn 12932b2427d0SAndrew Lunn val = phy_read(phydev, stat.reg); 12942b2427d0SAndrew Lunn if (val < 0) { 12956c3442f5SJisheng Zhang ret = U64_MAX; 12962b2427d0SAndrew Lunn } else { 12972b2427d0SAndrew Lunn val = val & ((1 << stat.bits) - 1); 12982b2427d0SAndrew Lunn priv->stats[i] += val; 1299321b4d4bSAndrew Lunn ret = priv->stats[i]; 13002b2427d0SAndrew Lunn } 13012b2427d0SAndrew Lunn 1302321b4d4bSAndrew Lunn return ret; 13032b2427d0SAndrew Lunn } 13042b2427d0SAndrew Lunn 13052b2427d0SAndrew Lunn static void kszphy_get_stats(struct phy_device *phydev, 13062b2427d0SAndrew Lunn struct ethtool_stats *stats, u64 *data) 13072b2427d0SAndrew Lunn { 13082b2427d0SAndrew Lunn int i; 13092b2427d0SAndrew Lunn 13102b2427d0SAndrew Lunn for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) 13112b2427d0SAndrew Lunn data[i] = kszphy_get_stat(phydev, i); 13122b2427d0SAndrew Lunn } 13132b2427d0SAndrew Lunn 1314836384d2SWenyou Yang static int kszphy_suspend(struct phy_device *phydev) 1315836384d2SWenyou Yang { 1316836384d2SWenyou Yang /* Disable PHY Interrupts */ 1317836384d2SWenyou Yang if (phy_interrupt_is_valid(phydev)) { 1318836384d2SWenyou Yang phydev->interrupts = PHY_INTERRUPT_DISABLED; 1319836384d2SWenyou Yang if (phydev->drv->config_intr) 1320836384d2SWenyou Yang phydev->drv->config_intr(phydev); 1321836384d2SWenyou Yang } 1322836384d2SWenyou Yang 1323836384d2SWenyou Yang return genphy_suspend(phydev); 1324836384d2SWenyou Yang } 1325836384d2SWenyou Yang 1326f5aba91dSAlexandre Belloni static int kszphy_resume(struct phy_device *phydev) 1327f5aba91dSAlexandre Belloni { 132879e498a9SLeonard Crestez int ret; 132979e498a9SLeonard Crestez 1330836384d2SWenyou Yang genphy_resume(phydev); 1331f5aba91dSAlexandre Belloni 13326110dff7SOleksij Rempel /* After switching from power-down to normal mode, an internal global 13336110dff7SOleksij Rempel * reset is automatically generated. Wait a minimum of 1 ms before 13346110dff7SOleksij Rempel * read/write access to the PHY registers. 13356110dff7SOleksij Rempel */ 13366110dff7SOleksij Rempel usleep_range(1000, 2000); 13376110dff7SOleksij Rempel 133879e498a9SLeonard Crestez ret = kszphy_config_reset(phydev); 133979e498a9SLeonard Crestez if (ret) 134079e498a9SLeonard Crestez return ret; 134179e498a9SLeonard Crestez 1342836384d2SWenyou Yang /* Enable PHY Interrupts */ 1343836384d2SWenyou Yang if (phy_interrupt_is_valid(phydev)) { 1344836384d2SWenyou Yang phydev->interrupts = PHY_INTERRUPT_ENABLED; 1345836384d2SWenyou Yang if (phydev->drv->config_intr) 1346836384d2SWenyou Yang phydev->drv->config_intr(phydev); 1347836384d2SWenyou Yang } 1348f5aba91dSAlexandre Belloni 1349f5aba91dSAlexandre Belloni return 0; 1350f5aba91dSAlexandre Belloni } 1351f5aba91dSAlexandre Belloni 1352e6a423a8SJohan Hovold static int kszphy_probe(struct phy_device *phydev) 1353e6a423a8SJohan Hovold { 1354e6a423a8SJohan Hovold const struct kszphy_type *type = phydev->drv->driver_data; 1355e5a03bfdSAndrew Lunn const struct device_node *np = phydev->mdio.dev.of_node; 1356e6a423a8SJohan Hovold struct kszphy_priv *priv; 135763f44b2bSJohan Hovold struct clk *clk; 1358e7a792e9SJohan Hovold int ret; 1359e6a423a8SJohan Hovold 1360e5a03bfdSAndrew Lunn priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 1361e6a423a8SJohan Hovold if (!priv) 1362e6a423a8SJohan Hovold return -ENOMEM; 1363e6a423a8SJohan Hovold 1364e6a423a8SJohan Hovold phydev->priv = priv; 1365e6a423a8SJohan Hovold 1366e6a423a8SJohan Hovold priv->type = type; 1367e6a423a8SJohan Hovold 1368e7a792e9SJohan Hovold if (type->led_mode_reg) { 1369e7a792e9SJohan Hovold ret = of_property_read_u32(np, "micrel,led-mode", 1370e7a792e9SJohan Hovold &priv->led_mode); 1371e7a792e9SJohan Hovold if (ret) 1372e7a792e9SJohan Hovold priv->led_mode = -1; 1373e7a792e9SJohan Hovold 1374e7a792e9SJohan Hovold if (priv->led_mode > 3) { 137572ba48beSAndrew Lunn phydev_err(phydev, "invalid led mode: 0x%02x\n", 1376e7a792e9SJohan Hovold priv->led_mode); 1377e7a792e9SJohan Hovold priv->led_mode = -1; 1378e7a792e9SJohan Hovold } 1379e7a792e9SJohan Hovold } else { 1380e7a792e9SJohan Hovold priv->led_mode = -1; 1381e7a792e9SJohan Hovold } 1382e7a792e9SJohan Hovold 1383e5a03bfdSAndrew Lunn clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref"); 1384bced8701SNiklas Cassel /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ 1385bced8701SNiklas Cassel if (!IS_ERR_OR_NULL(clk)) { 13861fadee0cSSascha Hauer unsigned long rate = clk_get_rate(clk); 138786dc1342SJohan Hovold bool rmii_ref_clk_sel_25_mhz; 13881fadee0cSSascha Hauer 138963f44b2bSJohan Hovold priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel; 139086dc1342SJohan Hovold rmii_ref_clk_sel_25_mhz = of_property_read_bool(np, 139186dc1342SJohan Hovold "micrel,rmii-reference-clock-select-25-mhz"); 139263f44b2bSJohan Hovold 13931fadee0cSSascha Hauer if (rate > 24500000 && rate < 25500000) { 139486dc1342SJohan Hovold priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz; 13951fadee0cSSascha Hauer } else if (rate > 49500000 && rate < 50500000) { 139686dc1342SJohan Hovold priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz; 13971fadee0cSSascha Hauer } else { 139872ba48beSAndrew Lunn phydev_err(phydev, "Clock rate out of range: %ld\n", 139972ba48beSAndrew Lunn rate); 14001fadee0cSSascha Hauer return -EINVAL; 14011fadee0cSSascha Hauer } 14021fadee0cSSascha Hauer } 14031fadee0cSSascha Hauer 14044217a64eSMichael Walle if (ksz8041_fiber_mode(phydev)) 14054217a64eSMichael Walle phydev->port = PORT_FIBRE; 14064217a64eSMichael Walle 140763f44b2bSJohan Hovold /* Support legacy board-file configuration */ 140863f44b2bSJohan Hovold if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { 140963f44b2bSJohan Hovold priv->rmii_ref_clk_sel = true; 141063f44b2bSJohan Hovold priv->rmii_ref_clk_sel_val = true; 141163f44b2bSJohan Hovold } 141263f44b2bSJohan Hovold 141363f44b2bSJohan Hovold return 0; 14141fadee0cSSascha Hauer } 14151fadee0cSSascha Hauer 141649011e0cSOleksij Rempel static int ksz886x_cable_test_start(struct phy_device *phydev) 141749011e0cSOleksij Rempel { 141849011e0cSOleksij Rempel if (phydev->dev_flags & MICREL_KSZ8_P1_ERRATA) 141949011e0cSOleksij Rempel return -EOPNOTSUPP; 142049011e0cSOleksij Rempel 142149011e0cSOleksij Rempel /* If autoneg is enabled, we won't be able to test cross pair 142249011e0cSOleksij Rempel * short. In this case, the PHY will "detect" a link and 142349011e0cSOleksij Rempel * confuse the internal state machine - disable auto neg here. 142449011e0cSOleksij Rempel * If autoneg is disabled, we should set the speed to 10mbit. 142549011e0cSOleksij Rempel */ 142649011e0cSOleksij Rempel return phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_SPEED100); 142749011e0cSOleksij Rempel } 142849011e0cSOleksij Rempel 142949011e0cSOleksij Rempel static int ksz886x_cable_test_result_trans(u16 status) 143049011e0cSOleksij Rempel { 143149011e0cSOleksij Rempel switch (FIELD_GET(KSZ8081_LMD_STAT_MASK, status)) { 143249011e0cSOleksij Rempel case KSZ8081_LMD_STAT_NORMAL: 143349011e0cSOleksij Rempel return ETHTOOL_A_CABLE_RESULT_CODE_OK; 143449011e0cSOleksij Rempel case KSZ8081_LMD_STAT_SHORT: 143549011e0cSOleksij Rempel return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 143649011e0cSOleksij Rempel case KSZ8081_LMD_STAT_OPEN: 143749011e0cSOleksij Rempel return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 143849011e0cSOleksij Rempel case KSZ8081_LMD_STAT_FAIL: 143949011e0cSOleksij Rempel fallthrough; 144049011e0cSOleksij Rempel default: 144149011e0cSOleksij Rempel return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 144249011e0cSOleksij Rempel } 144349011e0cSOleksij Rempel } 144449011e0cSOleksij Rempel 144549011e0cSOleksij Rempel static bool ksz886x_cable_test_failed(u16 status) 144649011e0cSOleksij Rempel { 144749011e0cSOleksij Rempel return FIELD_GET(KSZ8081_LMD_STAT_MASK, status) == 144849011e0cSOleksij Rempel KSZ8081_LMD_STAT_FAIL; 144949011e0cSOleksij Rempel } 145049011e0cSOleksij Rempel 145149011e0cSOleksij Rempel static bool ksz886x_cable_test_fault_length_valid(u16 status) 145249011e0cSOleksij Rempel { 145349011e0cSOleksij Rempel switch (FIELD_GET(KSZ8081_LMD_STAT_MASK, status)) { 145449011e0cSOleksij Rempel case KSZ8081_LMD_STAT_OPEN: 145549011e0cSOleksij Rempel fallthrough; 145649011e0cSOleksij Rempel case KSZ8081_LMD_STAT_SHORT: 145749011e0cSOleksij Rempel return true; 145849011e0cSOleksij Rempel } 145949011e0cSOleksij Rempel return false; 146049011e0cSOleksij Rempel } 146149011e0cSOleksij Rempel 146249011e0cSOleksij Rempel static int ksz886x_cable_test_fault_length(u16 status) 146349011e0cSOleksij Rempel { 146449011e0cSOleksij Rempel int dt; 146549011e0cSOleksij Rempel 146649011e0cSOleksij Rempel /* According to the data sheet the distance to the fault is 146749011e0cSOleksij Rempel * DELTA_TIME * 0.4 meters. 146849011e0cSOleksij Rempel */ 146949011e0cSOleksij Rempel dt = FIELD_GET(KSZ8081_LMD_DELTA_TIME_MASK, status); 147049011e0cSOleksij Rempel 147149011e0cSOleksij Rempel return (dt * 400) / 10; 147249011e0cSOleksij Rempel } 147349011e0cSOleksij Rempel 147449011e0cSOleksij Rempel static int ksz886x_cable_test_wait_for_completion(struct phy_device *phydev) 147549011e0cSOleksij Rempel { 147649011e0cSOleksij Rempel int val, ret; 147749011e0cSOleksij Rempel 147849011e0cSOleksij Rempel ret = phy_read_poll_timeout(phydev, KSZ8081_LMD, val, 147949011e0cSOleksij Rempel !(val & KSZ8081_LMD_ENABLE_TEST), 148049011e0cSOleksij Rempel 30000, 100000, true); 148149011e0cSOleksij Rempel 148249011e0cSOleksij Rempel return ret < 0 ? ret : 0; 148349011e0cSOleksij Rempel } 148449011e0cSOleksij Rempel 148549011e0cSOleksij Rempel static int ksz886x_cable_test_one_pair(struct phy_device *phydev, int pair) 148649011e0cSOleksij Rempel { 148749011e0cSOleksij Rempel static const int ethtool_pair[] = { 148849011e0cSOleksij Rempel ETHTOOL_A_CABLE_PAIR_A, 148949011e0cSOleksij Rempel ETHTOOL_A_CABLE_PAIR_B, 149049011e0cSOleksij Rempel }; 149149011e0cSOleksij Rempel int ret, val, mdix; 149249011e0cSOleksij Rempel 149349011e0cSOleksij Rempel /* There is no way to choice the pair, like we do one ksz9031. 149449011e0cSOleksij Rempel * We can workaround this limitation by using the MDI-X functionality. 149549011e0cSOleksij Rempel */ 149649011e0cSOleksij Rempel if (pair == 0) 149749011e0cSOleksij Rempel mdix = ETH_TP_MDI; 149849011e0cSOleksij Rempel else 149949011e0cSOleksij Rempel mdix = ETH_TP_MDI_X; 150049011e0cSOleksij Rempel 150149011e0cSOleksij Rempel switch (phydev->phy_id & MICREL_PHY_ID_MASK) { 150249011e0cSOleksij Rempel case PHY_ID_KSZ8081: 150349011e0cSOleksij Rempel ret = ksz8081_config_mdix(phydev, mdix); 150449011e0cSOleksij Rempel break; 150549011e0cSOleksij Rempel case PHY_ID_KSZ886X: 150649011e0cSOleksij Rempel ret = ksz886x_config_mdix(phydev, mdix); 150749011e0cSOleksij Rempel break; 150849011e0cSOleksij Rempel default: 150949011e0cSOleksij Rempel ret = -ENODEV; 151049011e0cSOleksij Rempel } 151149011e0cSOleksij Rempel 151249011e0cSOleksij Rempel if (ret) 151349011e0cSOleksij Rempel return ret; 151449011e0cSOleksij Rempel 151549011e0cSOleksij Rempel /* Now we are ready to fire. This command will send a 100ns pulse 151649011e0cSOleksij Rempel * to the pair. 151749011e0cSOleksij Rempel */ 151849011e0cSOleksij Rempel ret = phy_write(phydev, KSZ8081_LMD, KSZ8081_LMD_ENABLE_TEST); 151949011e0cSOleksij Rempel if (ret) 152049011e0cSOleksij Rempel return ret; 152149011e0cSOleksij Rempel 152249011e0cSOleksij Rempel ret = ksz886x_cable_test_wait_for_completion(phydev); 152349011e0cSOleksij Rempel if (ret) 152449011e0cSOleksij Rempel return ret; 152549011e0cSOleksij Rempel 152649011e0cSOleksij Rempel val = phy_read(phydev, KSZ8081_LMD); 152749011e0cSOleksij Rempel if (val < 0) 152849011e0cSOleksij Rempel return val; 152949011e0cSOleksij Rempel 153049011e0cSOleksij Rempel if (ksz886x_cable_test_failed(val)) 153149011e0cSOleksij Rempel return -EAGAIN; 153249011e0cSOleksij Rempel 153349011e0cSOleksij Rempel ret = ethnl_cable_test_result(phydev, ethtool_pair[pair], 153449011e0cSOleksij Rempel ksz886x_cable_test_result_trans(val)); 153549011e0cSOleksij Rempel if (ret) 153649011e0cSOleksij Rempel return ret; 153749011e0cSOleksij Rempel 153849011e0cSOleksij Rempel if (!ksz886x_cable_test_fault_length_valid(val)) 153949011e0cSOleksij Rempel return 0; 154049011e0cSOleksij Rempel 154149011e0cSOleksij Rempel return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], 154249011e0cSOleksij Rempel ksz886x_cable_test_fault_length(val)); 154349011e0cSOleksij Rempel } 154449011e0cSOleksij Rempel 154549011e0cSOleksij Rempel static int ksz886x_cable_test_get_status(struct phy_device *phydev, 154649011e0cSOleksij Rempel bool *finished) 154749011e0cSOleksij Rempel { 154849011e0cSOleksij Rempel unsigned long pair_mask = 0x3; 154949011e0cSOleksij Rempel int retries = 20; 155049011e0cSOleksij Rempel int pair, ret; 155149011e0cSOleksij Rempel 155249011e0cSOleksij Rempel *finished = false; 155349011e0cSOleksij Rempel 155449011e0cSOleksij Rempel /* Try harder if link partner is active */ 155549011e0cSOleksij Rempel while (pair_mask && retries--) { 155649011e0cSOleksij Rempel for_each_set_bit(pair, &pair_mask, 4) { 155749011e0cSOleksij Rempel ret = ksz886x_cable_test_one_pair(phydev, pair); 155849011e0cSOleksij Rempel if (ret == -EAGAIN) 155949011e0cSOleksij Rempel continue; 156049011e0cSOleksij Rempel if (ret < 0) 156149011e0cSOleksij Rempel return ret; 156249011e0cSOleksij Rempel clear_bit(pair, &pair_mask); 156349011e0cSOleksij Rempel } 156449011e0cSOleksij Rempel /* If link partner is in autonegotiation mode it will send 2ms 156549011e0cSOleksij Rempel * of FLPs with at least 6ms of silence. 156649011e0cSOleksij Rempel * Add 2ms sleep to have better chances to hit this silence. 156749011e0cSOleksij Rempel */ 156849011e0cSOleksij Rempel if (pair_mask) 156949011e0cSOleksij Rempel msleep(2); 157049011e0cSOleksij Rempel } 157149011e0cSOleksij Rempel 157249011e0cSOleksij Rempel *finished = true; 157349011e0cSOleksij Rempel 157449011e0cSOleksij Rempel return ret; 157549011e0cSOleksij Rempel } 157649011e0cSOleksij Rempel 15777c2dcfa2SHoratiu Vultur #define LAN_EXT_PAGE_ACCESS_CONTROL 0x16 15787c2dcfa2SHoratiu Vultur #define LAN_EXT_PAGE_ACCESS_ADDRESS_DATA 0x17 15797c2dcfa2SHoratiu Vultur #define LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC 0x4000 15807c2dcfa2SHoratiu Vultur 15817467d716SHoratiu Vultur #define LAN8814_QSGMII_SOFT_RESET 0x43 15827467d716SHoratiu Vultur #define LAN8814_QSGMII_SOFT_RESET_BIT BIT(0) 15837467d716SHoratiu Vultur #define LAN8814_QSGMII_PCS1G_ANEG_CONFIG 0x13 15847467d716SHoratiu Vultur #define LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA BIT(3) 15857467d716SHoratiu Vultur #define LAN8814_ALIGN_SWAP 0x4a 15867467d716SHoratiu Vultur #define LAN8814_ALIGN_TX_A_B_SWAP 0x1 15877467d716SHoratiu Vultur #define LAN8814_ALIGN_TX_A_B_SWAP_MASK GENMASK(2, 0) 15887467d716SHoratiu Vultur 15897c2dcfa2SHoratiu Vultur #define LAN8804_ALIGN_SWAP 0x4a 15907c2dcfa2SHoratiu Vultur #define LAN8804_ALIGN_TX_A_B_SWAP 0x1 15917c2dcfa2SHoratiu Vultur #define LAN8804_ALIGN_TX_A_B_SWAP_MASK GENMASK(2, 0) 15927c2dcfa2SHoratiu Vultur #define LAN8814_CLOCK_MANAGEMENT 0xd 15937c2dcfa2SHoratiu Vultur #define LAN8814_LINK_QUALITY 0x8e 15947c2dcfa2SHoratiu Vultur 15957c2dcfa2SHoratiu Vultur static int lanphy_read_page_reg(struct phy_device *phydev, int page, u32 addr) 15967c2dcfa2SHoratiu Vultur { 15977c2dcfa2SHoratiu Vultur u32 data; 15987c2dcfa2SHoratiu Vultur 1599*4488f6b6SDivya Koppera phy_lock_mdio_bus(phydev); 1600*4488f6b6SDivya Koppera __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); 1601*4488f6b6SDivya Koppera __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); 1602*4488f6b6SDivya Koppera __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, 16037c2dcfa2SHoratiu Vultur (page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC)); 1604*4488f6b6SDivya Koppera data = __phy_read(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA); 1605*4488f6b6SDivya Koppera phy_unlock_mdio_bus(phydev); 16067c2dcfa2SHoratiu Vultur 16077c2dcfa2SHoratiu Vultur return data; 16087c2dcfa2SHoratiu Vultur } 16097c2dcfa2SHoratiu Vultur 16107c2dcfa2SHoratiu Vultur static int lanphy_write_page_reg(struct phy_device *phydev, int page, u16 addr, 16117c2dcfa2SHoratiu Vultur u16 val) 16127c2dcfa2SHoratiu Vultur { 1613*4488f6b6SDivya Koppera phy_lock_mdio_bus(phydev); 1614*4488f6b6SDivya Koppera __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); 1615*4488f6b6SDivya Koppera __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); 1616*4488f6b6SDivya Koppera __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, 1617*4488f6b6SDivya Koppera page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC); 16187c2dcfa2SHoratiu Vultur 1619*4488f6b6SDivya Koppera val = __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, val); 1620*4488f6b6SDivya Koppera if (val != 0) 16217c2dcfa2SHoratiu Vultur phydev_err(phydev, "Error: phy_write has returned error %d\n", 16227c2dcfa2SHoratiu Vultur val); 1623*4488f6b6SDivya Koppera phy_unlock_mdio_bus(phydev); 16247c2dcfa2SHoratiu Vultur return val; 16257c2dcfa2SHoratiu Vultur } 16267c2dcfa2SHoratiu Vultur 16277467d716SHoratiu Vultur static int lan8814_config_init(struct phy_device *phydev) 16287467d716SHoratiu Vultur { 16297467d716SHoratiu Vultur int val; 16307467d716SHoratiu Vultur 16317467d716SHoratiu Vultur /* Reset the PHY */ 16327467d716SHoratiu Vultur val = lanphy_read_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET); 16337467d716SHoratiu Vultur val |= LAN8814_QSGMII_SOFT_RESET_BIT; 16347467d716SHoratiu Vultur lanphy_write_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET, val); 16357467d716SHoratiu Vultur 16367467d716SHoratiu Vultur /* Disable ANEG with QSGMII PCS Host side */ 16377467d716SHoratiu Vultur val = lanphy_read_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG); 16387467d716SHoratiu Vultur val &= ~LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA; 16397467d716SHoratiu Vultur lanphy_write_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG, val); 16407467d716SHoratiu Vultur 16417467d716SHoratiu Vultur /* MDI-X setting for swap A,B transmit */ 16427467d716SHoratiu Vultur val = lanphy_read_page_reg(phydev, 2, LAN8814_ALIGN_SWAP); 16437467d716SHoratiu Vultur val &= ~LAN8814_ALIGN_TX_A_B_SWAP_MASK; 16447467d716SHoratiu Vultur val |= LAN8814_ALIGN_TX_A_B_SWAP; 16457467d716SHoratiu Vultur lanphy_write_page_reg(phydev, 2, LAN8814_ALIGN_SWAP, val); 16467467d716SHoratiu Vultur 16477467d716SHoratiu Vultur return 0; 16487467d716SHoratiu Vultur } 16497467d716SHoratiu Vultur 16507c2dcfa2SHoratiu Vultur static int lan8804_config_init(struct phy_device *phydev) 16517c2dcfa2SHoratiu Vultur { 16527c2dcfa2SHoratiu Vultur int val; 16537c2dcfa2SHoratiu Vultur 16547c2dcfa2SHoratiu Vultur /* MDI-X setting for swap A,B transmit */ 16557c2dcfa2SHoratiu Vultur val = lanphy_read_page_reg(phydev, 2, LAN8804_ALIGN_SWAP); 16567c2dcfa2SHoratiu Vultur val &= ~LAN8804_ALIGN_TX_A_B_SWAP_MASK; 16577c2dcfa2SHoratiu Vultur val |= LAN8804_ALIGN_TX_A_B_SWAP; 16587c2dcfa2SHoratiu Vultur lanphy_write_page_reg(phydev, 2, LAN8804_ALIGN_SWAP, val); 16597c2dcfa2SHoratiu Vultur 16607c2dcfa2SHoratiu Vultur /* Make sure that the PHY will not stop generating the clock when the 16617c2dcfa2SHoratiu Vultur * link partner goes down 16627c2dcfa2SHoratiu Vultur */ 16637c2dcfa2SHoratiu Vultur lanphy_write_page_reg(phydev, 31, LAN8814_CLOCK_MANAGEMENT, 0x27e); 16647c2dcfa2SHoratiu Vultur lanphy_read_page_reg(phydev, 1, LAN8814_LINK_QUALITY); 16657c2dcfa2SHoratiu Vultur 16667c2dcfa2SHoratiu Vultur return 0; 16677c2dcfa2SHoratiu Vultur } 16687c2dcfa2SHoratiu Vultur 1669b3ec7248SDivya Koppera static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev) 1670b3ec7248SDivya Koppera { 1671b3ec7248SDivya Koppera int irq_status; 1672b3ec7248SDivya Koppera 1673b3ec7248SDivya Koppera irq_status = phy_read(phydev, LAN8814_INTS); 1674b3ec7248SDivya Koppera if (irq_status < 0) 1675b3ec7248SDivya Koppera return IRQ_NONE; 1676b3ec7248SDivya Koppera 1677b3ec7248SDivya Koppera if (!(irq_status & LAN8814_INT_LINK)) 1678b3ec7248SDivya Koppera return IRQ_NONE; 1679b3ec7248SDivya Koppera 1680b3ec7248SDivya Koppera phy_trigger_machine(phydev); 1681b3ec7248SDivya Koppera 1682b3ec7248SDivya Koppera return IRQ_HANDLED; 1683b3ec7248SDivya Koppera } 1684b3ec7248SDivya Koppera 1685b3ec7248SDivya Koppera static int lan8814_ack_interrupt(struct phy_device *phydev) 1686b3ec7248SDivya Koppera { 1687b3ec7248SDivya Koppera /* bit[12..0] int status, which is a read and clear register. */ 1688b3ec7248SDivya Koppera int rc; 1689b3ec7248SDivya Koppera 1690b3ec7248SDivya Koppera rc = phy_read(phydev, LAN8814_INTS); 1691b3ec7248SDivya Koppera 1692b3ec7248SDivya Koppera return (rc < 0) ? rc : 0; 1693b3ec7248SDivya Koppera } 1694b3ec7248SDivya Koppera 1695b3ec7248SDivya Koppera static int lan8814_config_intr(struct phy_device *phydev) 1696b3ec7248SDivya Koppera { 1697b3ec7248SDivya Koppera int err; 1698b3ec7248SDivya Koppera 1699b3ec7248SDivya Koppera lanphy_write_page_reg(phydev, 4, LAN8814_INTR_CTRL_REG, 1700b3ec7248SDivya Koppera LAN8814_INTR_CTRL_REG_POLARITY | 1701b3ec7248SDivya Koppera LAN8814_INTR_CTRL_REG_INTR_ENABLE); 1702b3ec7248SDivya Koppera 1703b3ec7248SDivya Koppera /* enable / disable interrupts */ 1704b3ec7248SDivya Koppera if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 1705b3ec7248SDivya Koppera err = lan8814_ack_interrupt(phydev); 1706b3ec7248SDivya Koppera if (err) 1707b3ec7248SDivya Koppera return err; 1708b3ec7248SDivya Koppera 1709b3ec7248SDivya Koppera err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); 1710b3ec7248SDivya Koppera } else { 1711b3ec7248SDivya Koppera err = phy_write(phydev, LAN8814_INTC, 0); 1712b3ec7248SDivya Koppera if (err) 1713b3ec7248SDivya Koppera return err; 1714b3ec7248SDivya Koppera 1715b3ec7248SDivya Koppera err = lan8814_ack_interrupt(phydev); 1716b3ec7248SDivya Koppera } 1717b3ec7248SDivya Koppera 1718b3ec7248SDivya Koppera return err; 1719b3ec7248SDivya Koppera } 1720b3ec7248SDivya Koppera 1721d5bf9071SChristian Hohnstaedt static struct phy_driver ksphy_driver[] = { 1722d5bf9071SChristian Hohnstaedt { 172351f932c4SChoi, David .phy_id = PHY_ID_KS8737, 1724f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 172551f932c4SChoi, David .name = "Micrel KS8737", 1726dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1727c6f9575cSJohan Hovold .driver_data = &ks8737_type, 1728d0507009SDavid J. Choi .config_init = kszphy_config_init, 1729c6f9575cSJohan Hovold .config_intr = kszphy_config_intr, 173059ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 1731f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1732f1131b9cSClaudiu Beznea .resume = kszphy_resume, 1733d5bf9071SChristian Hohnstaedt }, { 1734212ea99aSMarek Vasut .phy_id = PHY_ID_KSZ8021, 1735212ea99aSMarek Vasut .phy_id_mask = 0x00ffffff, 17367ab59dc1SDavid J. Choi .name = "Micrel KSZ8021 or KSZ8031", 1737dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1738e6a423a8SJohan Hovold .driver_data = &ksz8021_type, 173963f44b2bSJohan Hovold .probe = kszphy_probe, 1740d0e1df9cSJohan Hovold .config_init = kszphy_config_init, 1741212ea99aSMarek Vasut .config_intr = kszphy_config_intr, 174259ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 17432b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 17442b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 17452b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1746f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1747f1131b9cSClaudiu Beznea .resume = kszphy_resume, 1748212ea99aSMarek Vasut }, { 1749b818d1a7SHector Palacios .phy_id = PHY_ID_KSZ8031, 1750b818d1a7SHector Palacios .phy_id_mask = 0x00ffffff, 1751b818d1a7SHector Palacios .name = "Micrel KSZ8031", 1752dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1753e6a423a8SJohan Hovold .driver_data = &ksz8021_type, 175463f44b2bSJohan Hovold .probe = kszphy_probe, 1755d0e1df9cSJohan Hovold .config_init = kszphy_config_init, 1756b818d1a7SHector Palacios .config_intr = kszphy_config_intr, 175759ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 17582b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 17592b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 17602b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1761f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1762f1131b9cSClaudiu Beznea .resume = kszphy_resume, 1763b818d1a7SHector Palacios }, { 1764510d573fSMarek Vasut .phy_id = PHY_ID_KSZ8041, 1765f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 1766510d573fSMarek Vasut .name = "Micrel KSZ8041", 1767dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1768e6a423a8SJohan Hovold .driver_data = &ksz8041_type, 1769e6a423a8SJohan Hovold .probe = kszphy_probe, 177077501a79SPhilipp Zabel .config_init = ksz8041_config_init, 177177501a79SPhilipp Zabel .config_aneg = ksz8041_config_aneg, 177251f932c4SChoi, David .config_intr = kszphy_config_intr, 177359ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 17742b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 17752b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 17762b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 17772641b62dSStefan Agner /* No suspend/resume callbacks because of errata DS80000700A, 17782641b62dSStefan Agner * receiver error following software power down. 17792641b62dSStefan Agner */ 1780d5bf9071SChristian Hohnstaedt }, { 17814bd7b512SSergei Shtylyov .phy_id = PHY_ID_KSZ8041RNLI, 1782f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 17834bd7b512SSergei Shtylyov .name = "Micrel KSZ8041RNLI", 1784dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1785e6a423a8SJohan Hovold .driver_data = &ksz8041_type, 1786e6a423a8SJohan Hovold .probe = kszphy_probe, 1787e6a423a8SJohan Hovold .config_init = kszphy_config_init, 17884bd7b512SSergei Shtylyov .config_intr = kszphy_config_intr, 178959ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 17902b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 17912b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 17922b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1793f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1794f1131b9cSClaudiu Beznea .resume = kszphy_resume, 17954bd7b512SSergei Shtylyov }, { 1796510d573fSMarek Vasut .name = "Micrel KSZ8051", 1797dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1798e6a423a8SJohan Hovold .driver_data = &ksz8051_type, 1799e6a423a8SJohan Hovold .probe = kszphy_probe, 180063f44b2bSJohan Hovold .config_init = kszphy_config_init, 180151f932c4SChoi, David .config_intr = kszphy_config_intr, 180259ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 18032b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 18042b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 18052b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 18068b95599cSMarek Vasut .match_phy_device = ksz8051_match_phy_device, 1807f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1808f1131b9cSClaudiu Beznea .resume = kszphy_resume, 1809d5bf9071SChristian Hohnstaedt }, { 1810510d573fSMarek Vasut .phy_id = PHY_ID_KSZ8001, 1811510d573fSMarek Vasut .name = "Micrel KSZ8001 or KS8721", 1812ecd5a323SAlexander Stein .phy_id_mask = 0x00fffffc, 1813dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1814e6a423a8SJohan Hovold .driver_data = &ksz8041_type, 1815e6a423a8SJohan Hovold .probe = kszphy_probe, 1816e6a423a8SJohan Hovold .config_init = kszphy_config_init, 181751f932c4SChoi, David .config_intr = kszphy_config_intr, 181859ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 18192b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 18202b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 18212b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1822f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1823f1131b9cSClaudiu Beznea .resume = kszphy_resume, 1824d5bf9071SChristian Hohnstaedt }, { 18257ab59dc1SDavid J. Choi .phy_id = PHY_ID_KSZ8081, 18267ab59dc1SDavid J. Choi .name = "Micrel KSZ8081 or KSZ8091", 1827f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 182849011e0cSOleksij Rempel .flags = PHY_POLL_CABLE_TEST, 1829dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1830e6a423a8SJohan Hovold .driver_data = &ksz8081_type, 1831e6a423a8SJohan Hovold .probe = kszphy_probe, 18327a1d8390SAntoine Tenart .config_init = ksz8081_config_init, 1833764d31caSChristian Melki .soft_reset = genphy_soft_reset, 1834f873f112SOleksij Rempel .config_aneg = ksz8081_config_aneg, 1835f873f112SOleksij Rempel .read_status = ksz8081_read_status, 18367ab59dc1SDavid J. Choi .config_intr = kszphy_config_intr, 183759ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 18382b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 18392b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 18402b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1841836384d2SWenyou Yang .suspend = kszphy_suspend, 1842f5aba91dSAlexandre Belloni .resume = kszphy_resume, 184349011e0cSOleksij Rempel .cable_test_start = ksz886x_cable_test_start, 184449011e0cSOleksij Rempel .cable_test_get_status = ksz886x_cable_test_get_status, 18457ab59dc1SDavid J. Choi }, { 18467ab59dc1SDavid J. Choi .phy_id = PHY_ID_KSZ8061, 18477ab59dc1SDavid J. Choi .name = "Micrel KSZ8061", 1848f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 1849dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 1850232ba3a5SRajasingh Thavamani .config_init = ksz8061_config_init, 18517ab59dc1SDavid J. Choi .config_intr = kszphy_config_intr, 185259ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 1853f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1854f1131b9cSClaudiu Beznea .resume = kszphy_resume, 18557ab59dc1SDavid J. Choi }, { 1856d0507009SDavid J. Choi .phy_id = PHY_ID_KSZ9021, 185748d7d0adSJason Wang .phy_id_mask = 0x000ffffe, 1858d0507009SDavid J. Choi .name = "Micrel KSZ9021 Gigabit PHY", 1859dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 1860c6f9575cSJohan Hovold .driver_data = &ksz9021_type, 1861bfe72442SGrygorii Strashko .probe = kszphy_probe, 1862407d8098SHans Andersson .get_features = ksz9031_get_features, 1863954c3967SSean Cross .config_init = ksz9021_config_init, 1864c6f9575cSJohan Hovold .config_intr = kszphy_config_intr, 186559ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 18662b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 18672b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 18682b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1869f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1870f1131b9cSClaudiu Beznea .resume = kszphy_resume, 1871c846a2b7SKevin Hao .read_mmd = genphy_read_mmd_unsupported, 1872c846a2b7SKevin Hao .write_mmd = genphy_write_mmd_unsupported, 187393272e07SJean-Christophe PLAGNIOL-VILLARD }, { 18747ab59dc1SDavid J. Choi .phy_id = PHY_ID_KSZ9031, 1875f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 18767ab59dc1SDavid J. Choi .name = "Micrel KSZ9031 Gigabit PHY", 1877c6f9575cSJohan Hovold .driver_data = &ksz9021_type, 1878bfe72442SGrygorii Strashko .probe = kszphy_probe, 18793aed3e2aSAntoine Tenart .get_features = ksz9031_get_features, 18806e4b8273SHubert Chaumette .config_init = ksz9031_config_init, 18811d16073aSHeiner Kallweit .soft_reset = genphy_soft_reset, 1882d2fd719bSNathan Sullivan .read_status = ksz9031_read_status, 1883c6f9575cSJohan Hovold .config_intr = kszphy_config_intr, 188459ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 18852b2427d0SAndrew Lunn .get_sset_count = kszphy_get_sset_count, 18862b2427d0SAndrew Lunn .get_strings = kszphy_get_strings, 18872b2427d0SAndrew Lunn .get_stats = kszphy_get_stats, 1888f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1889f64f1482SXander Huff .resume = kszphy_resume, 18907ab59dc1SDavid J. Choi }, { 18911623ad8eSDivya Koppera .phy_id = PHY_ID_LAN8814, 18921623ad8eSDivya Koppera .phy_id_mask = MICREL_PHY_ID_MASK, 18931623ad8eSDivya Koppera .name = "Microchip INDY Gigabit Quad PHY", 18947467d716SHoratiu Vultur .config_init = lan8814_config_init, 18951623ad8eSDivya Koppera .driver_data = &ksz9021_type, 18961623ad8eSDivya Koppera .probe = kszphy_probe, 18971623ad8eSDivya Koppera .soft_reset = genphy_soft_reset, 18981623ad8eSDivya Koppera .read_status = ksz9031_read_status, 18991623ad8eSDivya Koppera .get_sset_count = kszphy_get_sset_count, 19001623ad8eSDivya Koppera .get_strings = kszphy_get_strings, 19011623ad8eSDivya Koppera .get_stats = kszphy_get_stats, 19021623ad8eSDivya Koppera .suspend = genphy_suspend, 19031623ad8eSDivya Koppera .resume = kszphy_resume, 1904b3ec7248SDivya Koppera .config_intr = lan8814_config_intr, 1905b3ec7248SDivya Koppera .handle_interrupt = lan8814_handle_interrupt, 19061623ad8eSDivya Koppera }, { 19077c2dcfa2SHoratiu Vultur .phy_id = PHY_ID_LAN8804, 19087c2dcfa2SHoratiu Vultur .phy_id_mask = MICREL_PHY_ID_MASK, 19097c2dcfa2SHoratiu Vultur .name = "Microchip LAN966X Gigabit PHY", 19107c2dcfa2SHoratiu Vultur .config_init = lan8804_config_init, 19117c2dcfa2SHoratiu Vultur .driver_data = &ksz9021_type, 19127c2dcfa2SHoratiu Vultur .probe = kszphy_probe, 19137c2dcfa2SHoratiu Vultur .soft_reset = genphy_soft_reset, 19147c2dcfa2SHoratiu Vultur .read_status = ksz9031_read_status, 19157c2dcfa2SHoratiu Vultur .get_sset_count = kszphy_get_sset_count, 19167c2dcfa2SHoratiu Vultur .get_strings = kszphy_get_strings, 19177c2dcfa2SHoratiu Vultur .get_stats = kszphy_get_stats, 19187c2dcfa2SHoratiu Vultur .suspend = genphy_suspend, 19197c2dcfa2SHoratiu Vultur .resume = kszphy_resume, 19207c2dcfa2SHoratiu Vultur }, { 1921bff5b4b3SYuiko Oshino .phy_id = PHY_ID_KSZ9131, 1922bff5b4b3SYuiko Oshino .phy_id_mask = MICREL_PHY_ID_MASK, 1923bff5b4b3SYuiko Oshino .name = "Microchip KSZ9131 Gigabit PHY", 1924dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 1925bff5b4b3SYuiko Oshino .driver_data = &ksz9021_type, 1926bff5b4b3SYuiko Oshino .probe = kszphy_probe, 1927bff5b4b3SYuiko Oshino .config_init = ksz9131_config_init, 1928bff5b4b3SYuiko Oshino .config_intr = kszphy_config_intr, 192959ca4e58SIoana Ciornei .handle_interrupt = kszphy_handle_interrupt, 1930bff5b4b3SYuiko Oshino .get_sset_count = kszphy_get_sset_count, 1931bff5b4b3SYuiko Oshino .get_strings = kszphy_get_strings, 1932bff5b4b3SYuiko Oshino .get_stats = kszphy_get_stats, 1933f1131b9cSClaudiu Beznea .suspend = kszphy_suspend, 1934bff5b4b3SYuiko Oshino .resume = kszphy_resume, 1935bff5b4b3SYuiko Oshino }, { 193693272e07SJean-Christophe PLAGNIOL-VILLARD .phy_id = PHY_ID_KSZ8873MLL, 1937f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 193893272e07SJean-Christophe PLAGNIOL-VILLARD .name = "Micrel KSZ8873MLL Switch", 1939dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 194093272e07SJean-Christophe PLAGNIOL-VILLARD .config_init = kszphy_config_init, 194193272e07SJean-Christophe PLAGNIOL-VILLARD .config_aneg = ksz8873mll_config_aneg, 194293272e07SJean-Christophe PLAGNIOL-VILLARD .read_status = ksz8873mll_read_status, 19431a5465f5SPatrice Vilchez .suspend = genphy_suspend, 19441a5465f5SPatrice Vilchez .resume = genphy_resume, 19457ab59dc1SDavid J. Choi }, { 19467ab59dc1SDavid J. Choi .phy_id = PHY_ID_KSZ886X, 1947f893a99eSFabio Estevam .phy_id_mask = MICREL_PHY_ID_MASK, 1948ab36a3a2SMarek Vasut .name = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch", 1949dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 195049011e0cSOleksij Rempel .flags = PHY_POLL_CABLE_TEST, 19517ab59dc1SDavid J. Choi .config_init = kszphy_config_init, 195252939393SOleksij Rempel .config_aneg = ksz886x_config_aneg, 195352939393SOleksij Rempel .read_status = ksz886x_read_status, 19541a5465f5SPatrice Vilchez .suspend = genphy_suspend, 19551a5465f5SPatrice Vilchez .resume = genphy_resume, 195649011e0cSOleksij Rempel .cable_test_start = ksz886x_cable_test_start, 195749011e0cSOleksij Rempel .cable_test_get_status = ksz886x_cable_test_get_status, 19589d162ed6SSean Nyekjaer }, { 19591d951ba3SMarek Vasut .name = "Micrel KSZ87XX Switch", 1960dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ 19619d162ed6SSean Nyekjaer .config_init = kszphy_config_init, 19628b95599cSMarek Vasut .match_phy_device = ksz8795_match_phy_device, 19639d162ed6SSean Nyekjaer .suspend = genphy_suspend, 19649d162ed6SSean Nyekjaer .resume = genphy_resume, 1965fc3973a1SWoojung Huh }, { 1966fc3973a1SWoojung Huh .phy_id = PHY_ID_KSZ9477, 1967fc3973a1SWoojung Huh .phy_id_mask = MICREL_PHY_ID_MASK, 1968fc3973a1SWoojung Huh .name = "Microchip KSZ9477", 1969dcdecdcfSHeiner Kallweit /* PHY_GBIT_FEATURES */ 1970fc3973a1SWoojung Huh .config_init = kszphy_config_init, 1971fc3973a1SWoojung Huh .suspend = genphy_suspend, 1972fc3973a1SWoojung Huh .resume = genphy_resume, 1973d5bf9071SChristian Hohnstaedt } }; 1974d0507009SDavid J. Choi 197550fd7150SJohan Hovold module_phy_driver(ksphy_driver); 1976d0507009SDavid J. Choi 1977d0507009SDavid J. Choi MODULE_DESCRIPTION("Micrel PHY driver"); 1978d0507009SDavid J. Choi MODULE_AUTHOR("David J. Choi"); 1979d0507009SDavid J. Choi MODULE_LICENSE("GPL"); 198052a60ed2SDavid S. Miller 1981cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused micrel_tbl[] = { 198248d7d0adSJason Wang { PHY_ID_KSZ9021, 0x000ffffe }, 1983f893a99eSFabio Estevam { PHY_ID_KSZ9031, MICREL_PHY_ID_MASK }, 1984bff5b4b3SYuiko Oshino { PHY_ID_KSZ9131, MICREL_PHY_ID_MASK }, 1985ecd5a323SAlexander Stein { PHY_ID_KSZ8001, 0x00fffffc }, 1986f893a99eSFabio Estevam { PHY_ID_KS8737, MICREL_PHY_ID_MASK }, 1987212ea99aSMarek Vasut { PHY_ID_KSZ8021, 0x00ffffff }, 1988b818d1a7SHector Palacios { PHY_ID_KSZ8031, 0x00ffffff }, 1989f893a99eSFabio Estevam { PHY_ID_KSZ8041, MICREL_PHY_ID_MASK }, 1990f893a99eSFabio Estevam { PHY_ID_KSZ8051, MICREL_PHY_ID_MASK }, 1991f893a99eSFabio Estevam { PHY_ID_KSZ8061, MICREL_PHY_ID_MASK }, 1992f893a99eSFabio Estevam { PHY_ID_KSZ8081, MICREL_PHY_ID_MASK }, 1993f893a99eSFabio Estevam { PHY_ID_KSZ8873MLL, MICREL_PHY_ID_MASK }, 1994f893a99eSFabio Estevam { PHY_ID_KSZ886X, MICREL_PHY_ID_MASK }, 19951623ad8eSDivya Koppera { PHY_ID_LAN8814, MICREL_PHY_ID_MASK }, 19967c2dcfa2SHoratiu Vultur { PHY_ID_LAN8804, MICREL_PHY_ID_MASK }, 199752a60ed2SDavid S. Miller { } 199852a60ed2SDavid S. Miller }; 199952a60ed2SDavid S. Miller 200052a60ed2SDavid S. Miller MODULE_DEVICE_TABLE(mdio, micrel_tbl); 2001