15f857575SAndrew Lunn // SPDX-License-Identifier: GPL-2.0 22ace13e1SDan Murphy /* Driver for the Texas Instruments DP83822, DP83825 and DP83826 PHYs. 387461f7aSDan Murphy * 487461f7aSDan Murphy * Copyright (C) 2017 Texas Instruments Inc. 587461f7aSDan Murphy */ 687461f7aSDan Murphy 787461f7aSDan Murphy #include <linux/ethtool.h> 887461f7aSDan Murphy #include <linux/etherdevice.h> 987461f7aSDan Murphy #include <linux/kernel.h> 1087461f7aSDan Murphy #include <linux/mii.h> 1187461f7aSDan Murphy #include <linux/module.h> 1287461f7aSDan Murphy #include <linux/of.h> 1387461f7aSDan Murphy #include <linux/phy.h> 1487461f7aSDan Murphy #include <linux/netdevice.h> 1587461f7aSDan Murphy 1687461f7aSDan Murphy #define DP83822_PHY_ID 0x2000a240 172ace13e1SDan Murphy #define DP83825S_PHY_ID 0x2000a140 1806acc17aSDan Murphy #define DP83825I_PHY_ID 0x2000a150 192ace13e1SDan Murphy #define DP83825CM_PHY_ID 0x2000a160 202ace13e1SDan Murphy #define DP83825CS_PHY_ID 0x2000a170 21783da36bSDan Murphy #define DP83826C_PHY_ID 0x2000a130 22783da36bSDan Murphy #define DP83826NC_PHY_ID 0x2000a110 2306acc17aSDan Murphy 2487461f7aSDan Murphy #define DP83822_DEVADDR 0x1f 2587461f7aSDan Murphy 265dc39fd5SDan Murphy #define MII_DP83822_CTRL_2 0x0a 275dc39fd5SDan Murphy #define MII_DP83822_PHYSTS 0x10 2887461f7aSDan Murphy #define MII_DP83822_PHYSCR 0x11 2987461f7aSDan Murphy #define MII_DP83822_MISR1 0x12 3087461f7aSDan Murphy #define MII_DP83822_MISR2 0x13 315dc39fd5SDan Murphy #define MII_DP83822_FCSCR 0x14 3280952952SDan Murphy #define MII_DP83822_RCSR 0x17 3387461f7aSDan Murphy #define MII_DP83822_RESET_CTRL 0x1f 3480952952SDan Murphy #define MII_DP83822_GENCFG 0x465 355dc39fd5SDan Murphy #define MII_DP83822_SOR1 0x467 365dc39fd5SDan Murphy 375dc39fd5SDan Murphy /* GENCFG */ 385dc39fd5SDan Murphy #define DP83822_SIG_DET_LOW BIT(0) 395dc39fd5SDan Murphy 405dc39fd5SDan Murphy /* Control Register 2 bits */ 415dc39fd5SDan Murphy #define DP83822_FX_ENABLE BIT(14) 4287461f7aSDan Murphy 4387461f7aSDan Murphy #define DP83822_HW_RESET BIT(15) 4487461f7aSDan Murphy #define DP83822_SW_RESET BIT(14) 4587461f7aSDan Murphy 465dc39fd5SDan Murphy /* PHY STS bits */ 475dc39fd5SDan Murphy #define DP83822_PHYSTS_DUPLEX BIT(2) 485dc39fd5SDan Murphy #define DP83822_PHYSTS_10 BIT(1) 495dc39fd5SDan Murphy #define DP83822_PHYSTS_LINK BIT(0) 505dc39fd5SDan Murphy 5187461f7aSDan Murphy /* PHYSCR Register Fields */ 5287461f7aSDan Murphy #define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */ 5387461f7aSDan Murphy #define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */ 5487461f7aSDan Murphy 5587461f7aSDan Murphy /* MISR1 bits */ 5687461f7aSDan Murphy #define DP83822_RX_ERR_HF_INT_EN BIT(0) 5787461f7aSDan Murphy #define DP83822_FALSE_CARRIER_HF_INT_EN BIT(1) 5887461f7aSDan Murphy #define DP83822_ANEG_COMPLETE_INT_EN BIT(2) 5987461f7aSDan Murphy #define DP83822_DUP_MODE_CHANGE_INT_EN BIT(3) 6087461f7aSDan Murphy #define DP83822_SPEED_CHANGED_INT_EN BIT(4) 6187461f7aSDan Murphy #define DP83822_LINK_STAT_INT_EN BIT(5) 6287461f7aSDan Murphy #define DP83822_ENERGY_DET_INT_EN BIT(6) 6387461f7aSDan Murphy #define DP83822_LINK_QUAL_INT_EN BIT(7) 6487461f7aSDan Murphy 6587461f7aSDan Murphy /* MISR2 bits */ 6687461f7aSDan Murphy #define DP83822_JABBER_DET_INT_EN BIT(0) 6787461f7aSDan Murphy #define DP83822_WOL_PKT_INT_EN BIT(1) 6887461f7aSDan Murphy #define DP83822_SLEEP_MODE_INT_EN BIT(2) 6987461f7aSDan Murphy #define DP83822_MDI_XOVER_INT_EN BIT(3) 7087461f7aSDan Murphy #define DP83822_LB_FIFO_INT_EN BIT(4) 7187461f7aSDan Murphy #define DP83822_PAGE_RX_INT_EN BIT(5) 7287461f7aSDan Murphy #define DP83822_ANEG_ERR_INT_EN BIT(6) 7387461f7aSDan Murphy #define DP83822_EEE_ERROR_CHANGE_INT_EN BIT(7) 7487461f7aSDan Murphy 7587461f7aSDan Murphy /* INT_STAT1 bits */ 7687461f7aSDan Murphy #define DP83822_WOL_INT_EN BIT(4) 7787461f7aSDan Murphy #define DP83822_WOL_INT_STAT BIT(12) 7887461f7aSDan Murphy 7987461f7aSDan Murphy #define MII_DP83822_RXSOP1 0x04a5 8087461f7aSDan Murphy #define MII_DP83822_RXSOP2 0x04a6 8187461f7aSDan Murphy #define MII_DP83822_RXSOP3 0x04a7 8287461f7aSDan Murphy 8387461f7aSDan Murphy /* WoL Registers */ 8487461f7aSDan Murphy #define MII_DP83822_WOL_CFG 0x04a0 8587461f7aSDan Murphy #define MII_DP83822_WOL_STAT 0x04a1 8687461f7aSDan Murphy #define MII_DP83822_WOL_DA1 0x04a2 8787461f7aSDan Murphy #define MII_DP83822_WOL_DA2 0x04a3 8887461f7aSDan Murphy #define MII_DP83822_WOL_DA3 0x04a4 8987461f7aSDan Murphy 9087461f7aSDan Murphy /* WoL bits */ 9187461f7aSDan Murphy #define DP83822_WOL_MAGIC_EN BIT(0) 9287461f7aSDan Murphy #define DP83822_WOL_SECURE_ON BIT(5) 9387461f7aSDan Murphy #define DP83822_WOL_EN BIT(7) 9487461f7aSDan Murphy #define DP83822_WOL_INDICATION_SEL BIT(8) 9587461f7aSDan Murphy #define DP83822_WOL_CLR_INDICATION BIT(11) 9687461f7aSDan Murphy 97*621427fbSTommaso Merciai /* RCSR bits */ 98*621427fbSTommaso Merciai #define DP83822_RGMII_MODE_EN BIT(9) 9980952952SDan Murphy #define DP83822_RX_CLK_SHIFT BIT(12) 10080952952SDan Murphy #define DP83822_TX_CLK_SHIFT BIT(11) 10180952952SDan Murphy 1025dc39fd5SDan Murphy /* SOR1 mode */ 1035dc39fd5SDan Murphy #define DP83822_STRAP_MODE1 0 1045dc39fd5SDan Murphy #define DP83822_STRAP_MODE2 BIT(0) 1055dc39fd5SDan Murphy #define DP83822_STRAP_MODE3 BIT(1) 1065dc39fd5SDan Murphy #define DP83822_STRAP_MODE4 GENMASK(1, 0) 1075dc39fd5SDan Murphy 1085dc39fd5SDan Murphy #define DP83822_COL_STRAP_MASK GENMASK(11, 10) 1095dc39fd5SDan Murphy #define DP83822_COL_SHIFT 10 1105dc39fd5SDan Murphy #define DP83822_RX_ER_STR_MASK GENMASK(9, 8) 1115dc39fd5SDan Murphy #define DP83822_RX_ER_SHIFT 8 1125dc39fd5SDan Murphy 1135dc39fd5SDan Murphy #define MII_DP83822_FIBER_ADVERTISE (ADVERTISED_TP | ADVERTISED_MII | \ 1149ef7e18bSDan Murphy ADVERTISED_FIBRE | \ 1159ef7e18bSDan Murphy ADVERTISED_Pause | ADVERTISED_Asym_Pause) 1165dc39fd5SDan Murphy 1175dc39fd5SDan Murphy struct dp83822_private { 1185dc39fd5SDan Murphy bool fx_signal_det_low; 1195dc39fd5SDan Murphy int fx_enabled; 1205dc39fd5SDan Murphy u16 fx_sd_enable; 1215dc39fd5SDan Murphy }; 1225dc39fd5SDan Murphy 12387461f7aSDan Murphy static int dp83822_set_wol(struct phy_device *phydev, 12487461f7aSDan Murphy struct ethtool_wolinfo *wol) 12587461f7aSDan Murphy { 12687461f7aSDan Murphy struct net_device *ndev = phydev->attached_dev; 12787461f7aSDan Murphy u16 value; 12887461f7aSDan Murphy const u8 *mac; 12987461f7aSDan Murphy 13087461f7aSDan Murphy if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { 13187461f7aSDan Murphy mac = (const u8 *)ndev->dev_addr; 13287461f7aSDan Murphy 13387461f7aSDan Murphy if (!is_valid_ether_addr(mac)) 13487461f7aSDan Murphy return -EINVAL; 13587461f7aSDan Murphy 13687461f7aSDan Murphy /* MAC addresses start with byte 5, but stored in mac[0]. 13787461f7aSDan Murphy * 822 PHYs store bytes 4|5, 2|3, 0|1 13887461f7aSDan Murphy */ 13987461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1, 14087461f7aSDan Murphy (mac[1] << 8) | mac[0]); 14187461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2, 14287461f7aSDan Murphy (mac[3] << 8) | mac[2]); 14387461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3, 14487461f7aSDan Murphy (mac[5] << 8) | mac[4]); 14587461f7aSDan Murphy 14687461f7aSDan Murphy value = phy_read_mmd(phydev, DP83822_DEVADDR, 14787461f7aSDan Murphy MII_DP83822_WOL_CFG); 14887461f7aSDan Murphy if (wol->wolopts & WAKE_MAGIC) 14987461f7aSDan Murphy value |= DP83822_WOL_MAGIC_EN; 15087461f7aSDan Murphy else 15187461f7aSDan Murphy value &= ~DP83822_WOL_MAGIC_EN; 15287461f7aSDan Murphy 15387461f7aSDan Murphy if (wol->wolopts & WAKE_MAGICSECURE) { 15487461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, 15587461f7aSDan Murphy MII_DP83822_RXSOP1, 15687461f7aSDan Murphy (wol->sopass[1] << 8) | wol->sopass[0]); 15787461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, 15887461f7aSDan Murphy MII_DP83822_RXSOP2, 15987461f7aSDan Murphy (wol->sopass[3] << 8) | wol->sopass[2]); 16087461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, 16187461f7aSDan Murphy MII_DP83822_RXSOP3, 16287461f7aSDan Murphy (wol->sopass[5] << 8) | wol->sopass[4]); 16387461f7aSDan Murphy value |= DP83822_WOL_SECURE_ON; 16487461f7aSDan Murphy } else { 16587461f7aSDan Murphy value &= ~DP83822_WOL_SECURE_ON; 16687461f7aSDan Murphy } 16787461f7aSDan Murphy 168600ac36bSDan Murphy /* Clear any pending WoL interrupt */ 169600ac36bSDan Murphy phy_read(phydev, MII_DP83822_MISR2); 17087461f7aSDan Murphy 171600ac36bSDan Murphy value |= DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL | 172600ac36bSDan Murphy DP83822_WOL_CLR_INDICATION; 173600ac36bSDan Murphy 174600ac36bSDan Murphy return phy_write_mmd(phydev, DP83822_DEVADDR, 175600ac36bSDan Murphy MII_DP83822_WOL_CFG, value); 176600ac36bSDan Murphy } else { 177600ac36bSDan Murphy return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, 178600ac36bSDan Murphy MII_DP83822_WOL_CFG, DP83822_WOL_EN); 179600ac36bSDan Murphy } 18087461f7aSDan Murphy } 18187461f7aSDan Murphy 18287461f7aSDan Murphy static void dp83822_get_wol(struct phy_device *phydev, 18387461f7aSDan Murphy struct ethtool_wolinfo *wol) 18487461f7aSDan Murphy { 18587461f7aSDan Murphy int value; 18687461f7aSDan Murphy u16 sopass_val; 18787461f7aSDan Murphy 18887461f7aSDan Murphy wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); 18987461f7aSDan Murphy wol->wolopts = 0; 19087461f7aSDan Murphy 19187461f7aSDan Murphy value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 19287461f7aSDan Murphy 19387461f7aSDan Murphy if (value & DP83822_WOL_MAGIC_EN) 19487461f7aSDan Murphy wol->wolopts |= WAKE_MAGIC; 19587461f7aSDan Murphy 19687461f7aSDan Murphy if (value & DP83822_WOL_SECURE_ON) { 19787461f7aSDan Murphy sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 19887461f7aSDan Murphy MII_DP83822_RXSOP1); 19987461f7aSDan Murphy wol->sopass[0] = (sopass_val & 0xff); 20087461f7aSDan Murphy wol->sopass[1] = (sopass_val >> 8); 20187461f7aSDan Murphy 20287461f7aSDan Murphy sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 20387461f7aSDan Murphy MII_DP83822_RXSOP2); 20487461f7aSDan Murphy wol->sopass[2] = (sopass_val & 0xff); 20587461f7aSDan Murphy wol->sopass[3] = (sopass_val >> 8); 20687461f7aSDan Murphy 20787461f7aSDan Murphy sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, 20887461f7aSDan Murphy MII_DP83822_RXSOP3); 20987461f7aSDan Murphy wol->sopass[4] = (sopass_val & 0xff); 21087461f7aSDan Murphy wol->sopass[5] = (sopass_val >> 8); 21187461f7aSDan Murphy 21287461f7aSDan Murphy wol->wolopts |= WAKE_MAGICSECURE; 21387461f7aSDan Murphy } 21487461f7aSDan Murphy 21587461f7aSDan Murphy /* WoL is not enabled so set wolopts to 0 */ 21687461f7aSDan Murphy if (!(value & DP83822_WOL_EN)) 21787461f7aSDan Murphy wol->wolopts = 0; 21887461f7aSDan Murphy } 21987461f7aSDan Murphy 22087461f7aSDan Murphy static int dp83822_config_intr(struct phy_device *phydev) 22187461f7aSDan Murphy { 2225dc39fd5SDan Murphy struct dp83822_private *dp83822 = phydev->priv; 22387461f7aSDan Murphy int misr_status; 22487461f7aSDan Murphy int physcr_status; 22587461f7aSDan Murphy int err; 22687461f7aSDan Murphy 22787461f7aSDan Murphy if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 22887461f7aSDan Murphy misr_status = phy_read(phydev, MII_DP83822_MISR1); 22987461f7aSDan Murphy if (misr_status < 0) 23087461f7aSDan Murphy return misr_status; 23187461f7aSDan Murphy 23287461f7aSDan Murphy misr_status |= (DP83822_RX_ERR_HF_INT_EN | 23387461f7aSDan Murphy DP83822_FALSE_CARRIER_HF_INT_EN | 23487461f7aSDan Murphy DP83822_LINK_STAT_INT_EN | 23587461f7aSDan Murphy DP83822_ENERGY_DET_INT_EN | 23687461f7aSDan Murphy DP83822_LINK_QUAL_INT_EN); 23787461f7aSDan Murphy 2385dc39fd5SDan Murphy if (!dp83822->fx_enabled) 2395dc39fd5SDan Murphy misr_status |= DP83822_ANEG_COMPLETE_INT_EN | 2405dc39fd5SDan Murphy DP83822_DUP_MODE_CHANGE_INT_EN | 2415dc39fd5SDan Murphy DP83822_SPEED_CHANGED_INT_EN; 2425dc39fd5SDan Murphy 2435dc39fd5SDan Murphy 24487461f7aSDan Murphy err = phy_write(phydev, MII_DP83822_MISR1, misr_status); 24587461f7aSDan Murphy if (err < 0) 24687461f7aSDan Murphy return err; 24787461f7aSDan Murphy 24887461f7aSDan Murphy misr_status = phy_read(phydev, MII_DP83822_MISR2); 24987461f7aSDan Murphy if (misr_status < 0) 25087461f7aSDan Murphy return misr_status; 25187461f7aSDan Murphy 25287461f7aSDan Murphy misr_status |= (DP83822_JABBER_DET_INT_EN | 25387461f7aSDan Murphy DP83822_SLEEP_MODE_INT_EN | 25487461f7aSDan Murphy DP83822_LB_FIFO_INT_EN | 25587461f7aSDan Murphy DP83822_PAGE_RX_INT_EN | 25687461f7aSDan Murphy DP83822_EEE_ERROR_CHANGE_INT_EN); 25787461f7aSDan Murphy 2585dc39fd5SDan Murphy if (!dp83822->fx_enabled) 2595dc39fd5SDan Murphy misr_status |= DP83822_MDI_XOVER_INT_EN | 2605dc39fd5SDan Murphy DP83822_ANEG_ERR_INT_EN | 2615dc39fd5SDan Murphy DP83822_WOL_PKT_INT_EN; 2625dc39fd5SDan Murphy 26387461f7aSDan Murphy err = phy_write(phydev, MII_DP83822_MISR2, misr_status); 26487461f7aSDan Murphy if (err < 0) 26587461f7aSDan Murphy return err; 26687461f7aSDan Murphy 26787461f7aSDan Murphy physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); 26887461f7aSDan Murphy if (physcr_status < 0) 26987461f7aSDan Murphy return physcr_status; 27087461f7aSDan Murphy 27187461f7aSDan Murphy physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN; 27287461f7aSDan Murphy 27387461f7aSDan Murphy } else { 27487461f7aSDan Murphy err = phy_write(phydev, MII_DP83822_MISR1, 0); 27587461f7aSDan Murphy if (err < 0) 27687461f7aSDan Murphy return err; 27787461f7aSDan Murphy 27837c9d66cSClément Léger err = phy_write(phydev, MII_DP83822_MISR2, 0); 27987461f7aSDan Murphy if (err < 0) 28087461f7aSDan Murphy return err; 28187461f7aSDan Murphy 28287461f7aSDan Murphy physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); 28387461f7aSDan Murphy if (physcr_status < 0) 28487461f7aSDan Murphy return physcr_status; 28587461f7aSDan Murphy 28687461f7aSDan Murphy physcr_status &= ~DP83822_PHYSCR_INTEN; 28787461f7aSDan Murphy } 28887461f7aSDan Murphy 28987461f7aSDan Murphy return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status); 29087461f7aSDan Murphy } 29187461f7aSDan Murphy 2921d1ae3c6SIoana Ciornei static irqreturn_t dp83822_handle_interrupt(struct phy_device *phydev) 2931d1ae3c6SIoana Ciornei { 29473f476aaSIoana Ciornei bool trigger_machine = false; 2951d1ae3c6SIoana Ciornei int irq_status; 2961d1ae3c6SIoana Ciornei 2971d1ae3c6SIoana Ciornei /* The MISR1 and MISR2 registers are holding the interrupt status in 2981d1ae3c6SIoana Ciornei * the upper half (15:8), while the lower half (7:0) is used for 2991d1ae3c6SIoana Ciornei * controlling the interrupt enable state of those individual interrupt 3001d1ae3c6SIoana Ciornei * sources. To determine the possible interrupt sources, just read the 3011d1ae3c6SIoana Ciornei * MISR* register and use it directly to know which interrupts have 3021d1ae3c6SIoana Ciornei * been enabled previously or not. 3031d1ae3c6SIoana Ciornei */ 3041d1ae3c6SIoana Ciornei irq_status = phy_read(phydev, MII_DP83822_MISR1); 3051d1ae3c6SIoana Ciornei if (irq_status < 0) { 3061d1ae3c6SIoana Ciornei phy_error(phydev); 3071d1ae3c6SIoana Ciornei return IRQ_NONE; 3081d1ae3c6SIoana Ciornei } 3091d1ae3c6SIoana Ciornei if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) 31073f476aaSIoana Ciornei trigger_machine = true; 3111d1ae3c6SIoana Ciornei 3121d1ae3c6SIoana Ciornei irq_status = phy_read(phydev, MII_DP83822_MISR2); 3131d1ae3c6SIoana Ciornei if (irq_status < 0) { 3141d1ae3c6SIoana Ciornei phy_error(phydev); 3151d1ae3c6SIoana Ciornei return IRQ_NONE; 3161d1ae3c6SIoana Ciornei } 3171d1ae3c6SIoana Ciornei if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) 31873f476aaSIoana Ciornei trigger_machine = true; 3191d1ae3c6SIoana Ciornei 32073f476aaSIoana Ciornei if (!trigger_machine) 3211d1ae3c6SIoana Ciornei return IRQ_NONE; 3221d1ae3c6SIoana Ciornei 3231d1ae3c6SIoana Ciornei phy_trigger_machine(phydev); 3241d1ae3c6SIoana Ciornei 3251d1ae3c6SIoana Ciornei return IRQ_HANDLED; 3261d1ae3c6SIoana Ciornei } 3271d1ae3c6SIoana Ciornei 32880952952SDan Murphy static int dp8382x_disable_wol(struct phy_device *phydev) 32987461f7aSDan Murphy { 3300d6835ffSMarek Vasut return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, 3310d6835ffSMarek Vasut DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | 3320d6835ffSMarek Vasut DP83822_WOL_SECURE_ON); 33387461f7aSDan Murphy } 33487461f7aSDan Murphy 3355dc39fd5SDan Murphy static int dp83822_read_status(struct phy_device *phydev) 3365dc39fd5SDan Murphy { 3375dc39fd5SDan Murphy struct dp83822_private *dp83822 = phydev->priv; 3385dc39fd5SDan Murphy int status = phy_read(phydev, MII_DP83822_PHYSTS); 3395dc39fd5SDan Murphy int ctrl2; 3405dc39fd5SDan Murphy int ret; 3415dc39fd5SDan Murphy 3425dc39fd5SDan Murphy if (dp83822->fx_enabled) { 3435dc39fd5SDan Murphy if (status & DP83822_PHYSTS_LINK) { 3445dc39fd5SDan Murphy phydev->speed = SPEED_UNKNOWN; 3455dc39fd5SDan Murphy phydev->duplex = DUPLEX_UNKNOWN; 3465dc39fd5SDan Murphy } else { 3475dc39fd5SDan Murphy ctrl2 = phy_read(phydev, MII_DP83822_CTRL_2); 3485dc39fd5SDan Murphy if (ctrl2 < 0) 3495dc39fd5SDan Murphy return ctrl2; 3505dc39fd5SDan Murphy 3515dc39fd5SDan Murphy if (!(ctrl2 & DP83822_FX_ENABLE)) { 3525dc39fd5SDan Murphy ret = phy_write(phydev, MII_DP83822_CTRL_2, 3535dc39fd5SDan Murphy DP83822_FX_ENABLE | ctrl2); 3545dc39fd5SDan Murphy if (ret < 0) 3555dc39fd5SDan Murphy return ret; 3565dc39fd5SDan Murphy } 3575dc39fd5SDan Murphy } 3585dc39fd5SDan Murphy } 3595dc39fd5SDan Murphy 3605dc39fd5SDan Murphy ret = genphy_read_status(phydev); 3615dc39fd5SDan Murphy if (ret) 3625dc39fd5SDan Murphy return ret; 3635dc39fd5SDan Murphy 3645dc39fd5SDan Murphy if (status < 0) 3655dc39fd5SDan Murphy return status; 3665dc39fd5SDan Murphy 3675dc39fd5SDan Murphy if (status & DP83822_PHYSTS_DUPLEX) 3685dc39fd5SDan Murphy phydev->duplex = DUPLEX_FULL; 3695dc39fd5SDan Murphy else 3705dc39fd5SDan Murphy phydev->duplex = DUPLEX_HALF; 3715dc39fd5SDan Murphy 3725dc39fd5SDan Murphy if (status & DP83822_PHYSTS_10) 3735dc39fd5SDan Murphy phydev->speed = SPEED_10; 3745dc39fd5SDan Murphy else 3755dc39fd5SDan Murphy phydev->speed = SPEED_100; 3765dc39fd5SDan Murphy 3775dc39fd5SDan Murphy return 0; 3785dc39fd5SDan Murphy } 3795dc39fd5SDan Murphy 38080952952SDan Murphy static int dp83822_config_init(struct phy_device *phydev) 38180952952SDan Murphy { 3825dc39fd5SDan Murphy struct dp83822_private *dp83822 = phydev->priv; 38380952952SDan Murphy struct device *dev = &phydev->mdio.dev; 38480952952SDan Murphy int rgmii_delay; 38580952952SDan Murphy s32 rx_int_delay; 38680952952SDan Murphy s32 tx_int_delay; 38780952952SDan Murphy int err = 0; 3885dc39fd5SDan Murphy int bmcr; 38980952952SDan Murphy 39080952952SDan Murphy if (phy_interface_is_rgmii(phydev)) { 39180952952SDan Murphy rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, 39280952952SDan Murphy true); 39380952952SDan Murphy 39480952952SDan Murphy if (rx_int_delay <= 0) 39580952952SDan Murphy rgmii_delay = 0; 39680952952SDan Murphy else 39780952952SDan Murphy rgmii_delay = DP83822_RX_CLK_SHIFT; 39880952952SDan Murphy 39980952952SDan Murphy tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0, 40080952952SDan Murphy false); 40180952952SDan Murphy if (tx_int_delay <= 0) 40280952952SDan Murphy rgmii_delay &= ~DP83822_TX_CLK_SHIFT; 40380952952SDan Murphy else 40480952952SDan Murphy rgmii_delay |= DP83822_TX_CLK_SHIFT; 40580952952SDan Murphy 40680952952SDan Murphy if (rgmii_delay) { 40780952952SDan Murphy err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, 40880952952SDan Murphy MII_DP83822_RCSR, rgmii_delay); 40980952952SDan Murphy if (err) 41080952952SDan Murphy return err; 41180952952SDan Murphy } 412*621427fbSTommaso Merciai 413*621427fbSTommaso Merciai phy_set_bits_mmd(phydev, DP83822_DEVADDR, 414*621427fbSTommaso Merciai MII_DP83822_RCSR, DP83822_RGMII_MODE_EN); 415*621427fbSTommaso Merciai } else { 416*621427fbSTommaso Merciai phy_clear_bits_mmd(phydev, DP83822_DEVADDR, 417*621427fbSTommaso Merciai MII_DP83822_RCSR, DP83822_RGMII_MODE_EN); 41880952952SDan Murphy } 41980952952SDan Murphy 4205dc39fd5SDan Murphy if (dp83822->fx_enabled) { 4215dc39fd5SDan Murphy err = phy_modify(phydev, MII_DP83822_CTRL_2, 4225dc39fd5SDan Murphy DP83822_FX_ENABLE, 1); 4235dc39fd5SDan Murphy if (err < 0) 4245dc39fd5SDan Murphy return err; 4255dc39fd5SDan Murphy 4265dc39fd5SDan Murphy /* Only allow advertising what this PHY supports */ 4275dc39fd5SDan Murphy linkmode_and(phydev->advertising, phydev->advertising, 4285dc39fd5SDan Murphy phydev->supported); 4295dc39fd5SDan Murphy 4305dc39fd5SDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 4315dc39fd5SDan Murphy phydev->supported); 4325dc39fd5SDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 4335dc39fd5SDan Murphy phydev->advertising); 4349ef7e18bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, 4359ef7e18bSDan Murphy phydev->supported); 4369ef7e18bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, 4379ef7e18bSDan Murphy phydev->supported); 4389ef7e18bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, 4399ef7e18bSDan Murphy phydev->advertising); 4409ef7e18bSDan Murphy linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, 4419ef7e18bSDan Murphy phydev->advertising); 4425dc39fd5SDan Murphy 4435dc39fd5SDan Murphy /* Auto neg is not supported in fiber mode */ 4445dc39fd5SDan Murphy bmcr = phy_read(phydev, MII_BMCR); 4455dc39fd5SDan Murphy if (bmcr < 0) 4465dc39fd5SDan Murphy return bmcr; 4475dc39fd5SDan Murphy 4485dc39fd5SDan Murphy if (bmcr & BMCR_ANENABLE) { 4495dc39fd5SDan Murphy err = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); 4505dc39fd5SDan Murphy if (err < 0) 4515dc39fd5SDan Murphy return err; 4525dc39fd5SDan Murphy } 4535dc39fd5SDan Murphy phydev->autoneg = AUTONEG_DISABLE; 4545dc39fd5SDan Murphy linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 4555dc39fd5SDan Murphy phydev->supported); 4565dc39fd5SDan Murphy linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 4575dc39fd5SDan Murphy phydev->advertising); 4585dc39fd5SDan Murphy 4595dc39fd5SDan Murphy /* Setup fiber advertisement */ 4605dc39fd5SDan Murphy err = phy_modify_changed(phydev, MII_ADVERTISE, 4615dc39fd5SDan Murphy MII_DP83822_FIBER_ADVERTISE, 4625dc39fd5SDan Murphy MII_DP83822_FIBER_ADVERTISE); 4635dc39fd5SDan Murphy 4645dc39fd5SDan Murphy if (err < 0) 4655dc39fd5SDan Murphy return err; 4665dc39fd5SDan Murphy 4675dc39fd5SDan Murphy if (dp83822->fx_signal_det_low) { 4685dc39fd5SDan Murphy err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, 4695dc39fd5SDan Murphy MII_DP83822_GENCFG, 4705dc39fd5SDan Murphy DP83822_SIG_DET_LOW); 4715dc39fd5SDan Murphy if (err) 4725dc39fd5SDan Murphy return err; 4735dc39fd5SDan Murphy } 4745dc39fd5SDan Murphy } 47580952952SDan Murphy return dp8382x_disable_wol(phydev); 47680952952SDan Murphy } 47780952952SDan Murphy 47880952952SDan Murphy static int dp8382x_config_init(struct phy_device *phydev) 47980952952SDan Murphy { 48080952952SDan Murphy return dp8382x_disable_wol(phydev); 48180952952SDan Murphy } 48280952952SDan Murphy 48387461f7aSDan Murphy static int dp83822_phy_reset(struct phy_device *phydev) 48487461f7aSDan Murphy { 48587461f7aSDan Murphy int err; 48687461f7aSDan Murphy 4875dc39fd5SDan Murphy err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_SW_RESET); 48887461f7aSDan Murphy if (err < 0) 48987461f7aSDan Murphy return err; 49087461f7aSDan Murphy 49180952952SDan Murphy return phydev->drv->config_init(phydev); 49287461f7aSDan Murphy } 49387461f7aSDan Murphy 4945dc39fd5SDan Murphy #ifdef CONFIG_OF_MDIO 4955dc39fd5SDan Murphy static int dp83822_of_init(struct phy_device *phydev) 4965dc39fd5SDan Murphy { 4975dc39fd5SDan Murphy struct dp83822_private *dp83822 = phydev->priv; 4985dc39fd5SDan Murphy struct device *dev = &phydev->mdio.dev; 4995dc39fd5SDan Murphy 5005dc39fd5SDan Murphy /* Signal detection for the PHY is only enabled if the FX_EN and the 5015dc39fd5SDan Murphy * SD_EN pins are strapped. Signal detection can only enabled if FX_EN 5025dc39fd5SDan Murphy * is strapped otherwise signal detection is disabled for the PHY. 5035dc39fd5SDan Murphy */ 5045dc39fd5SDan Murphy if (dp83822->fx_enabled && dp83822->fx_sd_enable) 5055dc39fd5SDan Murphy dp83822->fx_signal_det_low = device_property_present(dev, 5065dc39fd5SDan Murphy "ti,link-loss-low"); 5075dc39fd5SDan Murphy if (!dp83822->fx_enabled) 5085dc39fd5SDan Murphy dp83822->fx_enabled = device_property_present(dev, 5095dc39fd5SDan Murphy "ti,fiber-mode"); 5105dc39fd5SDan Murphy 5115dc39fd5SDan Murphy return 0; 5125dc39fd5SDan Murphy } 5135dc39fd5SDan Murphy #else 5145dc39fd5SDan Murphy static int dp83822_of_init(struct phy_device *phydev) 5155dc39fd5SDan Murphy { 5165dc39fd5SDan Murphy return 0; 5175dc39fd5SDan Murphy } 5185dc39fd5SDan Murphy #endif /* CONFIG_OF_MDIO */ 5195dc39fd5SDan Murphy 5205dc39fd5SDan Murphy static int dp83822_read_straps(struct phy_device *phydev) 5215dc39fd5SDan Murphy { 5225dc39fd5SDan Murphy struct dp83822_private *dp83822 = phydev->priv; 5235dc39fd5SDan Murphy int fx_enabled, fx_sd_enable; 5245dc39fd5SDan Murphy int val; 5255dc39fd5SDan Murphy 5265dc39fd5SDan Murphy val = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_SOR1); 5275dc39fd5SDan Murphy if (val < 0) 5285dc39fd5SDan Murphy return val; 5295dc39fd5SDan Murphy 5305dc39fd5SDan Murphy fx_enabled = (val & DP83822_COL_STRAP_MASK) >> DP83822_COL_SHIFT; 5315dc39fd5SDan Murphy if (fx_enabled == DP83822_STRAP_MODE2 || 5325dc39fd5SDan Murphy fx_enabled == DP83822_STRAP_MODE3) 5335dc39fd5SDan Murphy dp83822->fx_enabled = 1; 5345dc39fd5SDan Murphy 5355dc39fd5SDan Murphy if (dp83822->fx_enabled) { 5365dc39fd5SDan Murphy fx_sd_enable = (val & DP83822_RX_ER_STR_MASK) >> DP83822_RX_ER_SHIFT; 5375dc39fd5SDan Murphy if (fx_sd_enable == DP83822_STRAP_MODE3 || 5385dc39fd5SDan Murphy fx_sd_enable == DP83822_STRAP_MODE4) 5395dc39fd5SDan Murphy dp83822->fx_sd_enable = 1; 5405dc39fd5SDan Murphy } 5415dc39fd5SDan Murphy 5425dc39fd5SDan Murphy return 0; 5435dc39fd5SDan Murphy } 5445dc39fd5SDan Murphy 5455dc39fd5SDan Murphy static int dp83822_probe(struct phy_device *phydev) 5465dc39fd5SDan Murphy { 5475dc39fd5SDan Murphy struct dp83822_private *dp83822; 5485dc39fd5SDan Murphy int ret; 5495dc39fd5SDan Murphy 5505dc39fd5SDan Murphy dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822), 5515dc39fd5SDan Murphy GFP_KERNEL); 5525dc39fd5SDan Murphy if (!dp83822) 5535dc39fd5SDan Murphy return -ENOMEM; 5545dc39fd5SDan Murphy 5555dc39fd5SDan Murphy phydev->priv = dp83822; 5565dc39fd5SDan Murphy 5575dc39fd5SDan Murphy ret = dp83822_read_straps(phydev); 5585dc39fd5SDan Murphy if (ret) 5595dc39fd5SDan Murphy return ret; 5605dc39fd5SDan Murphy 5615dc39fd5SDan Murphy dp83822_of_init(phydev); 5625dc39fd5SDan Murphy 5634217a64eSMichael Walle if (dp83822->fx_enabled) 5644217a64eSMichael Walle phydev->port = PORT_FIBRE; 5654217a64eSMichael Walle 5665dc39fd5SDan Murphy return 0; 5675dc39fd5SDan Murphy } 5685dc39fd5SDan Murphy 56987461f7aSDan Murphy static int dp83822_suspend(struct phy_device *phydev) 57087461f7aSDan Murphy { 57187461f7aSDan Murphy int value; 57287461f7aSDan Murphy 57387461f7aSDan Murphy value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 57487461f7aSDan Murphy 57587461f7aSDan Murphy if (!(value & DP83822_WOL_EN)) 57687461f7aSDan Murphy genphy_suspend(phydev); 57787461f7aSDan Murphy 57887461f7aSDan Murphy return 0; 57987461f7aSDan Murphy } 58087461f7aSDan Murphy 58187461f7aSDan Murphy static int dp83822_resume(struct phy_device *phydev) 58287461f7aSDan Murphy { 58387461f7aSDan Murphy int value; 58487461f7aSDan Murphy 58587461f7aSDan Murphy genphy_resume(phydev); 58687461f7aSDan Murphy 58787461f7aSDan Murphy value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); 58887461f7aSDan Murphy 58987461f7aSDan Murphy phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value | 59087461f7aSDan Murphy DP83822_WOL_CLR_INDICATION); 59187461f7aSDan Murphy 59287461f7aSDan Murphy return 0; 59387461f7aSDan Murphy } 59487461f7aSDan Murphy 59506acc17aSDan Murphy #define DP83822_PHY_DRIVER(_id, _name) \ 59606acc17aSDan Murphy { \ 59706acc17aSDan Murphy PHY_ID_MATCH_MODEL(_id), \ 59806acc17aSDan Murphy .name = (_name), \ 599dcdecdcfSHeiner Kallweit /* PHY_BASIC_FEATURES */ \ 6005dc39fd5SDan Murphy .probe = dp83822_probe, \ 60106acc17aSDan Murphy .soft_reset = dp83822_phy_reset, \ 60206acc17aSDan Murphy .config_init = dp83822_config_init, \ 6035dc39fd5SDan Murphy .read_status = dp83822_read_status, \ 60406acc17aSDan Murphy .get_wol = dp83822_get_wol, \ 60506acc17aSDan Murphy .set_wol = dp83822_set_wol, \ 60606acc17aSDan Murphy .config_intr = dp83822_config_intr, \ 6071d1ae3c6SIoana Ciornei .handle_interrupt = dp83822_handle_interrupt, \ 60806acc17aSDan Murphy .suspend = dp83822_suspend, \ 60906acc17aSDan Murphy .resume = dp83822_resume, \ 61006acc17aSDan Murphy } 61106acc17aSDan Murphy 61280952952SDan Murphy #define DP8382X_PHY_DRIVER(_id, _name) \ 61380952952SDan Murphy { \ 61480952952SDan Murphy PHY_ID_MATCH_MODEL(_id), \ 61580952952SDan Murphy .name = (_name), \ 61680952952SDan Murphy /* PHY_BASIC_FEATURES */ \ 61780952952SDan Murphy .soft_reset = dp83822_phy_reset, \ 61880952952SDan Murphy .config_init = dp8382x_config_init, \ 61980952952SDan Murphy .get_wol = dp83822_get_wol, \ 62080952952SDan Murphy .set_wol = dp83822_set_wol, \ 62180952952SDan Murphy .config_intr = dp83822_config_intr, \ 6221d1ae3c6SIoana Ciornei .handle_interrupt = dp83822_handle_interrupt, \ 62380952952SDan Murphy .suspend = dp83822_suspend, \ 62480952952SDan Murphy .resume = dp83822_resume, \ 62580952952SDan Murphy } 62680952952SDan Murphy 62787461f7aSDan Murphy static struct phy_driver dp83822_driver[] = { 62806acc17aSDan Murphy DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), 62980952952SDan Murphy DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), 63080952952SDan Murphy DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), 63180952952SDan Murphy DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), 63280952952SDan Murphy DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), 63380952952SDan Murphy DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), 63480952952SDan Murphy DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), 63587461f7aSDan Murphy }; 63687461f7aSDan Murphy module_phy_driver(dp83822_driver); 63787461f7aSDan Murphy 63887461f7aSDan Murphy static struct mdio_device_id __maybe_unused dp83822_tbl[] = { 63987461f7aSDan Murphy { DP83822_PHY_ID, 0xfffffff0 }, 64006acc17aSDan Murphy { DP83825I_PHY_ID, 0xfffffff0 }, 641783da36bSDan Murphy { DP83826C_PHY_ID, 0xfffffff0 }, 642783da36bSDan Murphy { DP83826NC_PHY_ID, 0xfffffff0 }, 6432ace13e1SDan Murphy { DP83825S_PHY_ID, 0xfffffff0 }, 6442ace13e1SDan Murphy { DP83825CM_PHY_ID, 0xfffffff0 }, 6452ace13e1SDan Murphy { DP83825CS_PHY_ID, 0xfffffff0 }, 64687461f7aSDan Murphy { }, 64787461f7aSDan Murphy }; 64887461f7aSDan Murphy MODULE_DEVICE_TABLE(mdio, dp83822_tbl); 64987461f7aSDan Murphy 65087461f7aSDan Murphy MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver"); 65187461f7aSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); 6525f857575SAndrew Lunn MODULE_LICENSE("GPL v2"); 653