18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/phy/micrel.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for Micrel PHYs 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: David J. Choi 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2010-2013 Micrel, Inc. 108c2ecf20Sopenharmony_ci * Copyright (c) 2014 Johan Hovold <johan@kernel.org> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Support : Micrel Phys: 138c2ecf20Sopenharmony_ci * Giga phys: ksz9021, ksz9031, ksz9131 148c2ecf20Sopenharmony_ci * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041 158c2ecf20Sopenharmony_ci * ksz8021, ksz8031, ksz8051, 168c2ecf20Sopenharmony_ci * ksz8081, ksz8091, 178c2ecf20Sopenharmony_ci * ksz8061, 188c2ecf20Sopenharmony_ci * Switch : ksz8873, ksz886x 198c2ecf20Sopenharmony_ci * ksz9477 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/phy.h> 268c2ecf20Sopenharmony_ci#include <linux/micrel_phy.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/clk.h> 298c2ecf20Sopenharmony_ci#include <linux/delay.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Operation Mode Strap Override */ 328c2ecf20Sopenharmony_ci#define MII_KSZPHY_OMSO 0x16 338c2ecf20Sopenharmony_ci#define KSZPHY_OMSO_FACTORY_TEST BIT(15) 348c2ecf20Sopenharmony_ci#define KSZPHY_OMSO_B_CAST_OFF BIT(9) 358c2ecf20Sopenharmony_ci#define KSZPHY_OMSO_NAND_TREE_ON BIT(5) 368c2ecf20Sopenharmony_ci#define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) 378c2ecf20Sopenharmony_ci#define KSZPHY_OMSO_MII_OVERRIDE BIT(0) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* general Interrupt control/status reg in vendor specific block. */ 408c2ecf20Sopenharmony_ci#define MII_KSZPHY_INTCS 0x1B 418c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_JABBER BIT(15) 428c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_RECEIVE_ERR BIT(14) 438c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_PAGE_RECEIVE BIT(13) 448c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_PARELLEL BIT(12) 458c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_LINK_PARTNER_ACK BIT(11) 468c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_LINK_DOWN BIT(10) 478c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_REMOTE_FAULT BIT(9) 488c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_LINK_UP BIT(8) 498c2ecf20Sopenharmony_ci#define KSZPHY_INTCS_ALL (KSZPHY_INTCS_LINK_UP |\ 508c2ecf20Sopenharmony_ci KSZPHY_INTCS_LINK_DOWN) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* PHY Control 1 */ 538c2ecf20Sopenharmony_ci#define MII_KSZPHY_CTRL_1 0x1e 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* PHY Control 2 / PHY Control (if no PHY Control 1) */ 568c2ecf20Sopenharmony_ci#define MII_KSZPHY_CTRL_2 0x1f 578c2ecf20Sopenharmony_ci#define MII_KSZPHY_CTRL MII_KSZPHY_CTRL_2 588c2ecf20Sopenharmony_ci/* bitmap of PHY register to set interrupt mode */ 598c2ecf20Sopenharmony_ci#define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9) 608c2ecf20Sopenharmony_ci#define KSZPHY_RMII_REF_CLK_SEL BIT(7) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Write/read to/from extended registers */ 638c2ecf20Sopenharmony_ci#define MII_KSZPHY_EXTREG 0x0b 648c2ecf20Sopenharmony_ci#define KSZPHY_EXTREG_WRITE 0x8000 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define MII_KSZPHY_EXTREG_WRITE 0x0c 678c2ecf20Sopenharmony_ci#define MII_KSZPHY_EXTREG_READ 0x0d 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Extended registers */ 708c2ecf20Sopenharmony_ci#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104 718c2ecf20Sopenharmony_ci#define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105 728c2ecf20Sopenharmony_ci#define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define PS_TO_REG 200 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct kszphy_hw_stat { 778c2ecf20Sopenharmony_ci const char *string; 788c2ecf20Sopenharmony_ci u8 reg; 798c2ecf20Sopenharmony_ci u8 bits; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct kszphy_hw_stat kszphy_hw_stats[] = { 838c2ecf20Sopenharmony_ci { "phy_receive_errors", 21, 16}, 848c2ecf20Sopenharmony_ci { "phy_idle_errors", 10, 8 }, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct kszphy_type { 888c2ecf20Sopenharmony_ci u32 led_mode_reg; 898c2ecf20Sopenharmony_ci u16 interrupt_level_mask; 908c2ecf20Sopenharmony_ci bool has_broadcast_disable; 918c2ecf20Sopenharmony_ci bool has_nand_tree_disable; 928c2ecf20Sopenharmony_ci bool has_rmii_ref_clk_sel; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct kszphy_priv { 968c2ecf20Sopenharmony_ci const struct kszphy_type *type; 978c2ecf20Sopenharmony_ci int led_mode; 988c2ecf20Sopenharmony_ci bool rmii_ref_clk_sel; 998c2ecf20Sopenharmony_ci bool rmii_ref_clk_sel_val; 1008c2ecf20Sopenharmony_ci u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic const struct kszphy_type ksz8021_type = { 1048c2ecf20Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_2, 1058c2ecf20Sopenharmony_ci .has_broadcast_disable = true, 1068c2ecf20Sopenharmony_ci .has_nand_tree_disable = true, 1078c2ecf20Sopenharmony_ci .has_rmii_ref_clk_sel = true, 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const struct kszphy_type ksz8041_type = { 1118c2ecf20Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_1, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct kszphy_type ksz8051_type = { 1158c2ecf20Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_2, 1168c2ecf20Sopenharmony_ci .has_nand_tree_disable = true, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const struct kszphy_type ksz8081_type = { 1208c2ecf20Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_2, 1218c2ecf20Sopenharmony_ci .has_broadcast_disable = true, 1228c2ecf20Sopenharmony_ci .has_nand_tree_disable = true, 1238c2ecf20Sopenharmony_ci .has_rmii_ref_clk_sel = true, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic const struct kszphy_type ks8737_type = { 1278c2ecf20Sopenharmony_ci .interrupt_level_mask = BIT(14), 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct kszphy_type ksz9021_type = { 1318c2ecf20Sopenharmony_ci .interrupt_level_mask = BIT(14), 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int kszphy_extended_write(struct phy_device *phydev, 1358c2ecf20Sopenharmony_ci u32 regnum, u16 val) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum); 1388c2ecf20Sopenharmony_ci return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int kszphy_extended_read(struct phy_device *phydev, 1428c2ecf20Sopenharmony_ci u32 regnum) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci phy_write(phydev, MII_KSZPHY_EXTREG, regnum); 1458c2ecf20Sopenharmony_ci return phy_read(phydev, MII_KSZPHY_EXTREG_READ); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int kszphy_ack_interrupt(struct phy_device *phydev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci /* bit[7..0] int status, which is a read and clear register. */ 1518c2ecf20Sopenharmony_ci int rc; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci rc = phy_read(phydev, MII_KSZPHY_INTCS); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return (rc < 0) ? rc : 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int kszphy_config_intr(struct phy_device *phydev) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 1618c2ecf20Sopenharmony_ci int temp; 1628c2ecf20Sopenharmony_ci u16 mask; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (type && type->interrupt_level_mask) 1658c2ecf20Sopenharmony_ci mask = type->interrupt_level_mask; 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci mask = KSZPHY_CTRL_INT_ACTIVE_HIGH; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* set the interrupt pin active low */ 1708c2ecf20Sopenharmony_ci temp = phy_read(phydev, MII_KSZPHY_CTRL); 1718c2ecf20Sopenharmony_ci if (temp < 0) 1728c2ecf20Sopenharmony_ci return temp; 1738c2ecf20Sopenharmony_ci temp &= ~mask; 1748c2ecf20Sopenharmony_ci phy_write(phydev, MII_KSZPHY_CTRL, temp); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* enable / disable interrupts */ 1778c2ecf20Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 1788c2ecf20Sopenharmony_ci temp = KSZPHY_INTCS_ALL; 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci temp = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return phy_write(phydev, MII_KSZPHY_INTCS, temp); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int ctrl; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ctrl = phy_read(phydev, MII_KSZPHY_CTRL); 1908c2ecf20Sopenharmony_ci if (ctrl < 0) 1918c2ecf20Sopenharmony_ci return ctrl; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (val) 1948c2ecf20Sopenharmony_ci ctrl |= KSZPHY_RMII_REF_CLK_SEL; 1958c2ecf20Sopenharmony_ci else 1968c2ecf20Sopenharmony_ci ctrl &= ~KSZPHY_RMII_REF_CLK_SEL; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return phy_write(phydev, MII_KSZPHY_CTRL, ctrl); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci int rc, temp, shift; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci switch (reg) { 2068c2ecf20Sopenharmony_ci case MII_KSZPHY_CTRL_1: 2078c2ecf20Sopenharmony_ci shift = 14; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case MII_KSZPHY_CTRL_2: 2108c2ecf20Sopenharmony_ci shift = 4; 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci default: 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci temp = phy_read(phydev, reg); 2178c2ecf20Sopenharmony_ci if (temp < 0) { 2188c2ecf20Sopenharmony_ci rc = temp; 2198c2ecf20Sopenharmony_ci goto out; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci temp &= ~(3 << shift); 2238c2ecf20Sopenharmony_ci temp |= val << shift; 2248c2ecf20Sopenharmony_ci rc = phy_write(phydev, reg, temp); 2258c2ecf20Sopenharmony_ciout: 2268c2ecf20Sopenharmony_ci if (rc < 0) 2278c2ecf20Sopenharmony_ci phydev_err(phydev, "failed to set led mode\n"); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return rc; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* Disable PHY address 0 as the broadcast address, so that it can be used as a 2338c2ecf20Sopenharmony_ci * unique (non-broadcast) address on a shared bus. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic int kszphy_broadcast_disable(struct phy_device *phydev) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci int ret; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_OMSO); 2408c2ecf20Sopenharmony_ci if (ret < 0) 2418c2ecf20Sopenharmony_ci goto out; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF); 2448c2ecf20Sopenharmony_ciout: 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci phydev_err(phydev, "failed to disable broadcast address\n"); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int kszphy_nand_tree_disable(struct phy_device *phydev) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int ret; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_OMSO); 2568c2ecf20Sopenharmony_ci if (ret < 0) 2578c2ecf20Sopenharmony_ci goto out; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!(ret & KSZPHY_OMSO_NAND_TREE_ON)) 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = phy_write(phydev, MII_KSZPHY_OMSO, 2638c2ecf20Sopenharmony_ci ret & ~KSZPHY_OMSO_NAND_TREE_ON); 2648c2ecf20Sopenharmony_ciout: 2658c2ecf20Sopenharmony_ci if (ret) 2668c2ecf20Sopenharmony_ci phydev_err(phydev, "failed to disable NAND tree mode\n"); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* Some config bits need to be set again on resume, handle them here. */ 2728c2ecf20Sopenharmony_cistatic int kszphy_config_reset(struct phy_device *phydev) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (priv->rmii_ref_clk_sel) { 2788c2ecf20Sopenharmony_ci ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); 2798c2ecf20Sopenharmony_ci if (ret) { 2808c2ecf20Sopenharmony_ci phydev_err(phydev, 2818c2ecf20Sopenharmony_ci "failed to set rmii reference clock\n"); 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (priv->type && priv->led_mode >= 0) 2878c2ecf20Sopenharmony_ci kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int kszphy_config_init(struct phy_device *phydev) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 2958c2ecf20Sopenharmony_ci const struct kszphy_type *type; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (!priv) 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci type = priv->type; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (type && type->has_broadcast_disable) 3038c2ecf20Sopenharmony_ci kszphy_broadcast_disable(phydev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (type && type->has_nand_tree_disable) 3068c2ecf20Sopenharmony_ci kszphy_nand_tree_disable(phydev); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return kszphy_config_reset(phydev); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int ksz8041_fiber_mode(struct phy_device *phydev) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct device_node *of_node = phydev->mdio.dev.of_node; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return of_property_read_bool(of_node, "micrel,fiber-mode"); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int ksz8041_config_init(struct phy_device *phydev) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Limit supported and advertised modes in fiber mode */ 3238c2ecf20Sopenharmony_ci if (ksz8041_fiber_mode(phydev)) { 3248c2ecf20Sopenharmony_ci phydev->dev_flags |= MICREL_PHY_FXEN; 3258c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask); 3268c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci linkmode_and(phydev->supported, phydev->supported, mask); 3298c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 3308c2ecf20Sopenharmony_ci phydev->supported); 3318c2ecf20Sopenharmony_ci linkmode_and(phydev->advertising, phydev->advertising, mask); 3328c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 3338c2ecf20Sopenharmony_ci phydev->advertising); 3348c2ecf20Sopenharmony_ci phydev->autoneg = AUTONEG_DISABLE; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return kszphy_config_init(phydev); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int ksz8041_config_aneg(struct phy_device *phydev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci /* Skip auto-negotiation in fiber mode */ 3438c2ecf20Sopenharmony_ci if (phydev->dev_flags & MICREL_PHY_FXEN) { 3448c2ecf20Sopenharmony_ci phydev->speed = SPEED_100; 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return genphy_config_aneg(phydev); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev, 3528c2ecf20Sopenharmony_ci const bool ksz_8051) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051) 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ret = phy_read(phydev, MII_BMSR); 3608c2ecf20Sopenharmony_ci if (ret < 0) 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* KSZ8051 PHY and KSZ8794/KSZ8795/KSZ8765 switch share the same 3648c2ecf20Sopenharmony_ci * exact PHY ID. However, they can be told apart by the extended 3658c2ecf20Sopenharmony_ci * capability registers presence. The KSZ8051 PHY has them while 3668c2ecf20Sopenharmony_ci * the switch does not. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci ret &= BMSR_ERCAP; 3698c2ecf20Sopenharmony_ci if (ksz_8051) 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci else 3728c2ecf20Sopenharmony_ci return !ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int ksz8051_match_phy_device(struct phy_device *phydev) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci return ksz8051_ksz8795_match_phy_device(phydev, true); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int ksz8081_config_init(struct phy_device *phydev) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci /* KSZPHY_OMSO_FACTORY_TEST is set at de-assertion of the reset line 3838c2ecf20Sopenharmony_ci * based on the RXER (KSZ8081RNA/RND) or TXC (KSZ8081MNX/RNB) pin. If a 3848c2ecf20Sopenharmony_ci * pull-down is missing, the factory test mode should be cleared by 3858c2ecf20Sopenharmony_ci * manually writing a 0. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci phy_clear_bits(phydev, MII_KSZPHY_OMSO, KSZPHY_OMSO_FACTORY_TEST); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return kszphy_config_init(phydev); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int ksz8061_config_init(struct phy_device *phydev) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); 3978c2ecf20Sopenharmony_ci if (ret) 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return kszphy_config_init(phydev); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int ksz8795_match_phy_device(struct phy_device *phydev) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci return ksz8051_ksz8795_match_phy_device(phydev, false); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int ksz9021_load_values_from_of(struct phy_device *phydev, 4098c2ecf20Sopenharmony_ci const struct device_node *of_node, 4108c2ecf20Sopenharmony_ci u16 reg, 4118c2ecf20Sopenharmony_ci const char *field1, const char *field2, 4128c2ecf20Sopenharmony_ci const char *field3, const char *field4) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci int val1 = -1; 4158c2ecf20Sopenharmony_ci int val2 = -2; 4168c2ecf20Sopenharmony_ci int val3 = -3; 4178c2ecf20Sopenharmony_ci int val4 = -4; 4188c2ecf20Sopenharmony_ci int newval; 4198c2ecf20Sopenharmony_ci int matches = 0; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!of_property_read_u32(of_node, field1, &val1)) 4228c2ecf20Sopenharmony_ci matches++; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (!of_property_read_u32(of_node, field2, &val2)) 4258c2ecf20Sopenharmony_ci matches++; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!of_property_read_u32(of_node, field3, &val3)) 4288c2ecf20Sopenharmony_ci matches++; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (!of_property_read_u32(of_node, field4, &val4)) 4318c2ecf20Sopenharmony_ci matches++; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (!matches) 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (matches < 4) 4378c2ecf20Sopenharmony_ci newval = kszphy_extended_read(phydev, reg); 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci newval = 0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (val1 != -1) 4428c2ecf20Sopenharmony_ci newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (val2 != -2) 4458c2ecf20Sopenharmony_ci newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (val3 != -3) 4488c2ecf20Sopenharmony_ci newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (val4 != -4) 4518c2ecf20Sopenharmony_ci newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return kszphy_extended_write(phydev, reg, newval); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int ksz9021_config_init(struct phy_device *phydev) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci const struct device *dev = &phydev->mdio.dev; 4598c2ecf20Sopenharmony_ci const struct device_node *of_node = dev->of_node; 4608c2ecf20Sopenharmony_ci const struct device *dev_walker; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* The Micrel driver has a deprecated option to place phy OF 4638c2ecf20Sopenharmony_ci * properties in the MAC node. Walk up the tree of devices to 4648c2ecf20Sopenharmony_ci * find a device with an OF node. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci dev_walker = &phydev->mdio.dev; 4678c2ecf20Sopenharmony_ci do { 4688c2ecf20Sopenharmony_ci of_node = dev_walker->of_node; 4698c2ecf20Sopenharmony_ci dev_walker = dev_walker->parent; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci } while (!of_node && dev_walker); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (of_node) { 4748c2ecf20Sopenharmony_ci ksz9021_load_values_from_of(phydev, of_node, 4758c2ecf20Sopenharmony_ci MII_KSZPHY_CLK_CONTROL_PAD_SKEW, 4768c2ecf20Sopenharmony_ci "txen-skew-ps", "txc-skew-ps", 4778c2ecf20Sopenharmony_ci "rxdv-skew-ps", "rxc-skew-ps"); 4788c2ecf20Sopenharmony_ci ksz9021_load_values_from_of(phydev, of_node, 4798c2ecf20Sopenharmony_ci MII_KSZPHY_RX_DATA_PAD_SKEW, 4808c2ecf20Sopenharmony_ci "rxd0-skew-ps", "rxd1-skew-ps", 4818c2ecf20Sopenharmony_ci "rxd2-skew-ps", "rxd3-skew-ps"); 4828c2ecf20Sopenharmony_ci ksz9021_load_values_from_of(phydev, of_node, 4838c2ecf20Sopenharmony_ci MII_KSZPHY_TX_DATA_PAD_SKEW, 4848c2ecf20Sopenharmony_ci "txd0-skew-ps", "txd1-skew-ps", 4858c2ecf20Sopenharmony_ci "txd2-skew-ps", "txd3-skew-ps"); 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci#define KSZ9031_PS_TO_REG 60 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* Extended registers */ 4938c2ecf20Sopenharmony_ci/* MMD Address 0x0 */ 4948c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_FLP_BURST_TX_LO 3 4958c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_FLP_BURST_TX_HI 4 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/* MMD Address 0x2 */ 4988c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 4998c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RX_CTL_M GENMASK(7, 4) 5008c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_TX_CTL_M GENMASK(3, 0) 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 5038c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RXD3 GENMASK(15, 12) 5048c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RXD2 GENMASK(11, 8) 5058c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RXD1 GENMASK(7, 4) 5068c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RXD0 GENMASK(3, 0) 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 5098c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_TXD3 GENMASK(15, 12) 5108c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_TXD2 GENMASK(11, 8) 5118c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_TXD1 GENMASK(7, 4) 5128c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_TXD0 GENMASK(3, 0) 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_CLK_PAD_SKEW 8 5158c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_GTX_CLK GENMASK(9, 5) 5168c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_RX_CLK GENMASK(4, 0) 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To 5198c2ecf20Sopenharmony_ci * provide different RGMII options we need to configure delay offset 5208c2ecf20Sopenharmony_ci * for each pad relative to build in delay. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci/* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of 5238c2ecf20Sopenharmony_ci * 1.80ns 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci#define RX_ID 0x7 5268c2ecf20Sopenharmony_ci#define RX_CLK_ID 0x19 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/* set rx to +0.30ns and rx_clk to -0.90ns to compensate the 5298c2ecf20Sopenharmony_ci * internal 1.2ns delay. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci#define RX_ND 0xc 5328c2ecf20Sopenharmony_ci#define RX_CLK_ND 0x0 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */ 5358c2ecf20Sopenharmony_ci#define TX_ID 0x0 5368c2ecf20Sopenharmony_ci#define TX_CLK_ID 0x1f 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci/* set tx and tx_clk to "No delay adjustment" to keep 0ns 5398c2ecf20Sopenharmony_ci * dealy 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci#define TX_ND 0x7 5428c2ecf20Sopenharmony_ci#define TX_CLK_ND 0xf 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/* MMD Address 0x1C */ 5458c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_EDPD 0x23 5468c2ecf20Sopenharmony_ci#define MII_KSZ9031RN_EDPD_ENABLE BIT(0) 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic int ksz9031_of_load_skew_values(struct phy_device *phydev, 5498c2ecf20Sopenharmony_ci const struct device_node *of_node, 5508c2ecf20Sopenharmony_ci u16 reg, size_t field_sz, 5518c2ecf20Sopenharmony_ci const char *field[], u8 numfields, 5528c2ecf20Sopenharmony_ci bool *update) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci int val[4] = {-1, -2, -3, -4}; 5558c2ecf20Sopenharmony_ci int matches = 0; 5568c2ecf20Sopenharmony_ci u16 mask; 5578c2ecf20Sopenharmony_ci u16 maxval; 5588c2ecf20Sopenharmony_ci u16 newval; 5598c2ecf20Sopenharmony_ci int i; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci for (i = 0; i < numfields; i++) 5628c2ecf20Sopenharmony_ci if (!of_property_read_u32(of_node, field[i], val + i)) 5638c2ecf20Sopenharmony_ci matches++; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (!matches) 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci *update |= true; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (matches < numfields) 5718c2ecf20Sopenharmony_ci newval = phy_read_mmd(phydev, 2, reg); 5728c2ecf20Sopenharmony_ci else 5738c2ecf20Sopenharmony_ci newval = 0; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci maxval = (field_sz == 4) ? 0xf : 0x1f; 5768c2ecf20Sopenharmony_ci for (i = 0; i < numfields; i++) 5778c2ecf20Sopenharmony_ci if (val[i] != -(i + 1)) { 5788c2ecf20Sopenharmony_ci mask = 0xffff; 5798c2ecf20Sopenharmony_ci mask ^= maxval << (field_sz * i); 5808c2ecf20Sopenharmony_ci newval = (newval & mask) | 5818c2ecf20Sopenharmony_ci (((val[i] / KSZ9031_PS_TO_REG) & maxval) 5828c2ecf20Sopenharmony_ci << (field_sz * i)); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return phy_write_mmd(phydev, 2, reg, newval); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/* Center KSZ9031RNX FLP timing at 16ms. */ 5898c2ecf20Sopenharmony_cistatic int ksz9031_center_flp_timing(struct phy_device *phydev) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci int result; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI, 5948c2ecf20Sopenharmony_ci 0x0006); 5958c2ecf20Sopenharmony_ci if (result) 5968c2ecf20Sopenharmony_ci return result; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO, 5998c2ecf20Sopenharmony_ci 0x1A80); 6008c2ecf20Sopenharmony_ci if (result) 6018c2ecf20Sopenharmony_ci return result; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return genphy_restart_aneg(phydev); 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci/* Enable energy-detect power-down mode */ 6078c2ecf20Sopenharmony_cistatic int ksz9031_enable_edpd(struct phy_device *phydev) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci int reg; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci reg = phy_read_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD); 6128c2ecf20Sopenharmony_ci if (reg < 0) 6138c2ecf20Sopenharmony_ci return reg; 6148c2ecf20Sopenharmony_ci return phy_write_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD, 6158c2ecf20Sopenharmony_ci reg | MII_KSZ9031RN_EDPD_ENABLE); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int ksz9031_config_rgmii_delay(struct phy_device *phydev) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci u16 rx, tx, rx_clk, tx_clk; 6218c2ecf20Sopenharmony_ci int ret; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci switch (phydev->interface) { 6248c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 6258c2ecf20Sopenharmony_ci tx = TX_ND; 6268c2ecf20Sopenharmony_ci tx_clk = TX_CLK_ND; 6278c2ecf20Sopenharmony_ci rx = RX_ND; 6288c2ecf20Sopenharmony_ci rx_clk = RX_CLK_ND; 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 6318c2ecf20Sopenharmony_ci tx = TX_ID; 6328c2ecf20Sopenharmony_ci tx_clk = TX_CLK_ID; 6338c2ecf20Sopenharmony_ci rx = RX_ID; 6348c2ecf20Sopenharmony_ci rx_clk = RX_CLK_ID; 6358c2ecf20Sopenharmony_ci break; 6368c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 6378c2ecf20Sopenharmony_ci tx = TX_ND; 6388c2ecf20Sopenharmony_ci tx_clk = TX_CLK_ND; 6398c2ecf20Sopenharmony_ci rx = RX_ID; 6408c2ecf20Sopenharmony_ci rx_clk = RX_CLK_ID; 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 6438c2ecf20Sopenharmony_ci tx = TX_ID; 6448c2ecf20Sopenharmony_ci tx_clk = TX_CLK_ID; 6458c2ecf20Sopenharmony_ci rx = RX_ND; 6468c2ecf20Sopenharmony_ci rx_clk = RX_CLK_ND; 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci default: 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_CONTROL_PAD_SKEW, 6538c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | 6548c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); 6558c2ecf20Sopenharmony_ci if (ret < 0) 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_RX_DATA_PAD_SKEW, 6598c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD3, rx) | 6608c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD2, rx) | 6618c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD1, rx) | 6628c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD0, rx)); 6638c2ecf20Sopenharmony_ci if (ret < 0) 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_TX_DATA_PAD_SKEW, 6678c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD3, tx) | 6688c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD2, tx) | 6698c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD1, tx) | 6708c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD0, tx)); 6718c2ecf20Sopenharmony_ci if (ret < 0) 6728c2ecf20Sopenharmony_ci return ret; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return phy_write_mmd(phydev, 2, MII_KSZ9031RN_CLK_PAD_SKEW, 6758c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | 6768c2ecf20Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int ksz9031_config_init(struct phy_device *phydev) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci const struct device *dev = &phydev->mdio.dev; 6828c2ecf20Sopenharmony_ci const struct device_node *of_node = dev->of_node; 6838c2ecf20Sopenharmony_ci static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; 6848c2ecf20Sopenharmony_ci static const char *rx_data_skews[4] = { 6858c2ecf20Sopenharmony_ci "rxd0-skew-ps", "rxd1-skew-ps", 6868c2ecf20Sopenharmony_ci "rxd2-skew-ps", "rxd3-skew-ps" 6878c2ecf20Sopenharmony_ci }; 6888c2ecf20Sopenharmony_ci static const char *tx_data_skews[4] = { 6898c2ecf20Sopenharmony_ci "txd0-skew-ps", "txd1-skew-ps", 6908c2ecf20Sopenharmony_ci "txd2-skew-ps", "txd3-skew-ps" 6918c2ecf20Sopenharmony_ci }; 6928c2ecf20Sopenharmony_ci static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; 6938c2ecf20Sopenharmony_ci const struct device *dev_walker; 6948c2ecf20Sopenharmony_ci int result; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci result = ksz9031_enable_edpd(phydev); 6978c2ecf20Sopenharmony_ci if (result < 0) 6988c2ecf20Sopenharmony_ci return result; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* The Micrel driver has a deprecated option to place phy OF 7018c2ecf20Sopenharmony_ci * properties in the MAC node. Walk up the tree of devices to 7028c2ecf20Sopenharmony_ci * find a device with an OF node. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci dev_walker = &phydev->mdio.dev; 7058c2ecf20Sopenharmony_ci do { 7068c2ecf20Sopenharmony_ci of_node = dev_walker->of_node; 7078c2ecf20Sopenharmony_ci dev_walker = dev_walker->parent; 7088c2ecf20Sopenharmony_ci } while (!of_node && dev_walker); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (of_node) { 7118c2ecf20Sopenharmony_ci bool update = false; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 7148c2ecf20Sopenharmony_ci result = ksz9031_config_rgmii_delay(phydev); 7158c2ecf20Sopenharmony_ci if (result < 0) 7168c2ecf20Sopenharmony_ci return result; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 7208c2ecf20Sopenharmony_ci MII_KSZ9031RN_CLK_PAD_SKEW, 5, 7218c2ecf20Sopenharmony_ci clk_skews, 2, &update); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 7248c2ecf20Sopenharmony_ci MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, 7258c2ecf20Sopenharmony_ci control_skews, 2, &update); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 7288c2ecf20Sopenharmony_ci MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, 7298c2ecf20Sopenharmony_ci rx_data_skews, 4, &update); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 7328c2ecf20Sopenharmony_ci MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, 7338c2ecf20Sopenharmony_ci tx_data_skews, 4, &update); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (update && !phy_interface_is_rgmii(phydev)) 7368c2ecf20Sopenharmony_ci phydev_warn(phydev, 7378c2ecf20Sopenharmony_ci "*-skew-ps values should be used only with RGMII PHY modes\n"); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Silicon Errata Sheet (DS80000691D or DS80000692D): 7408c2ecf20Sopenharmony_ci * When the device links in the 1000BASE-T slave mode only, 7418c2ecf20Sopenharmony_ci * the optional 125MHz reference output clock (CLK125_NDO) 7428c2ecf20Sopenharmony_ci * has wide duty cycle variation. 7438c2ecf20Sopenharmony_ci * 7448c2ecf20Sopenharmony_ci * The optional CLK125_NDO clock does not meet the RGMII 7458c2ecf20Sopenharmony_ci * 45/55 percent (min/max) duty cycle requirement and therefore 7468c2ecf20Sopenharmony_ci * cannot be used directly by the MAC side for clocking 7478c2ecf20Sopenharmony_ci * applications that have setup/hold time requirements on 7488c2ecf20Sopenharmony_ci * rising and falling clock edges. 7498c2ecf20Sopenharmony_ci * 7508c2ecf20Sopenharmony_ci * Workaround: 7518c2ecf20Sopenharmony_ci * Force the phy to be the master to receive a stable clock 7528c2ecf20Sopenharmony_ci * which meets the duty cycle requirement. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci if (of_property_read_bool(of_node, "micrel,force-master")) { 7558c2ecf20Sopenharmony_ci result = phy_read(phydev, MII_CTRL1000); 7568c2ecf20Sopenharmony_ci if (result < 0) 7578c2ecf20Sopenharmony_ci goto err_force_master; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* enable master mode, config & prefer master */ 7608c2ecf20Sopenharmony_ci result |= CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER; 7618c2ecf20Sopenharmony_ci result = phy_write(phydev, MII_CTRL1000, result); 7628c2ecf20Sopenharmony_ci if (result < 0) 7638c2ecf20Sopenharmony_ci goto err_force_master; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return ksz9031_center_flp_timing(phydev); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cierr_force_master: 7708c2ecf20Sopenharmony_ci phydev_err(phydev, "failed to force the phy to master mode\n"); 7718c2ecf20Sopenharmony_ci return result; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci#define KSZ9131_SKEW_5BIT_MAX 2400 7758c2ecf20Sopenharmony_ci#define KSZ9131_SKEW_4BIT_MAX 800 7768c2ecf20Sopenharmony_ci#define KSZ9131_OFFSET 700 7778c2ecf20Sopenharmony_ci#define KSZ9131_STEP 100 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic int ksz9131_of_load_skew_values(struct phy_device *phydev, 7808c2ecf20Sopenharmony_ci struct device_node *of_node, 7818c2ecf20Sopenharmony_ci u16 reg, size_t field_sz, 7828c2ecf20Sopenharmony_ci char *field[], u8 numfields) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET), 7858c2ecf20Sopenharmony_ci -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)}; 7868c2ecf20Sopenharmony_ci int skewval, skewmax = 0; 7878c2ecf20Sopenharmony_ci int matches = 0; 7888c2ecf20Sopenharmony_ci u16 maxval; 7898c2ecf20Sopenharmony_ci u16 newval; 7908c2ecf20Sopenharmony_ci u16 mask; 7918c2ecf20Sopenharmony_ci int i; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* psec properties in dts should mean x pico seconds */ 7948c2ecf20Sopenharmony_ci if (field_sz == 5) 7958c2ecf20Sopenharmony_ci skewmax = KSZ9131_SKEW_5BIT_MAX; 7968c2ecf20Sopenharmony_ci else 7978c2ecf20Sopenharmony_ci skewmax = KSZ9131_SKEW_4BIT_MAX; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci for (i = 0; i < numfields; i++) 8008c2ecf20Sopenharmony_ci if (!of_property_read_s32(of_node, field[i], &skewval)) { 8018c2ecf20Sopenharmony_ci if (skewval < -KSZ9131_OFFSET) 8028c2ecf20Sopenharmony_ci skewval = -KSZ9131_OFFSET; 8038c2ecf20Sopenharmony_ci else if (skewval > skewmax) 8048c2ecf20Sopenharmony_ci skewval = skewmax; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci val[i] = skewval + KSZ9131_OFFSET; 8078c2ecf20Sopenharmony_ci matches++; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (!matches) 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (matches < numfields) 8148c2ecf20Sopenharmony_ci newval = phy_read_mmd(phydev, 2, reg); 8158c2ecf20Sopenharmony_ci else 8168c2ecf20Sopenharmony_ci newval = 0; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci maxval = (field_sz == 4) ? 0xf : 0x1f; 8198c2ecf20Sopenharmony_ci for (i = 0; i < numfields; i++) 8208c2ecf20Sopenharmony_ci if (val[i] != -(i + 1 + KSZ9131_OFFSET)) { 8218c2ecf20Sopenharmony_ci mask = 0xffff; 8228c2ecf20Sopenharmony_ci mask ^= maxval << (field_sz * i); 8238c2ecf20Sopenharmony_ci newval = (newval & mask) | 8248c2ecf20Sopenharmony_ci (((val[i] / KSZ9131_STEP) & maxval) 8258c2ecf20Sopenharmony_ci << (field_sz * i)); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return phy_write_mmd(phydev, 2, reg, newval); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci#define KSZ9131RN_MMD_COMMON_CTRL_REG 2 8328c2ecf20Sopenharmony_ci#define KSZ9131RN_RXC_DLL_CTRL 76 8338c2ecf20Sopenharmony_ci#define KSZ9131RN_TXC_DLL_CTRL 77 8348c2ecf20Sopenharmony_ci#define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12) 8358c2ecf20Sopenharmony_ci#define KSZ9131RN_DLL_ENABLE_DELAY 0 8368c2ecf20Sopenharmony_ci#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int ksz9131_config_rgmii_delay(struct phy_device *phydev) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci u16 rxcdll_val, txcdll_val; 8418c2ecf20Sopenharmony_ci int ret; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci switch (phydev->interface) { 8448c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 8458c2ecf20Sopenharmony_ci rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 8468c2ecf20Sopenharmony_ci txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 8498c2ecf20Sopenharmony_ci rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 8508c2ecf20Sopenharmony_ci txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 8538c2ecf20Sopenharmony_ci rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 8548c2ecf20Sopenharmony_ci txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 8578c2ecf20Sopenharmony_ci rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; 8588c2ecf20Sopenharmony_ci txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci default: 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 8658c2ecf20Sopenharmony_ci KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, 8668c2ecf20Sopenharmony_ci rxcdll_val); 8678c2ecf20Sopenharmony_ci if (ret < 0) 8688c2ecf20Sopenharmony_ci return ret; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 8718c2ecf20Sopenharmony_ci KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, 8728c2ecf20Sopenharmony_ci txcdll_val); 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int ksz9131_config_init(struct phy_device *phydev) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci const struct device *dev = &phydev->mdio.dev; 8788c2ecf20Sopenharmony_ci struct device_node *of_node = dev->of_node; 8798c2ecf20Sopenharmony_ci char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"}; 8808c2ecf20Sopenharmony_ci char *rx_data_skews[4] = { 8818c2ecf20Sopenharmony_ci "rxd0-skew-psec", "rxd1-skew-psec", 8828c2ecf20Sopenharmony_ci "rxd2-skew-psec", "rxd3-skew-psec" 8838c2ecf20Sopenharmony_ci }; 8848c2ecf20Sopenharmony_ci char *tx_data_skews[4] = { 8858c2ecf20Sopenharmony_ci "txd0-skew-psec", "txd1-skew-psec", 8868c2ecf20Sopenharmony_ci "txd2-skew-psec", "txd3-skew-psec" 8878c2ecf20Sopenharmony_ci }; 8888c2ecf20Sopenharmony_ci char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"}; 8898c2ecf20Sopenharmony_ci const struct device *dev_walker; 8908c2ecf20Sopenharmony_ci int ret; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci dev_walker = &phydev->mdio.dev; 8938c2ecf20Sopenharmony_ci do { 8948c2ecf20Sopenharmony_ci of_node = dev_walker->of_node; 8958c2ecf20Sopenharmony_ci dev_walker = dev_walker->parent; 8968c2ecf20Sopenharmony_ci } while (!of_node && dev_walker); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (!of_node) 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 9028c2ecf20Sopenharmony_ci ret = ksz9131_config_rgmii_delay(phydev); 9038c2ecf20Sopenharmony_ci if (ret < 0) 9048c2ecf20Sopenharmony_ci return ret; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 9088c2ecf20Sopenharmony_ci MII_KSZ9031RN_CLK_PAD_SKEW, 5, 9098c2ecf20Sopenharmony_ci clk_skews, 2); 9108c2ecf20Sopenharmony_ci if (ret < 0) 9118c2ecf20Sopenharmony_ci return ret; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 9148c2ecf20Sopenharmony_ci MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, 9158c2ecf20Sopenharmony_ci control_skews, 2); 9168c2ecf20Sopenharmony_ci if (ret < 0) 9178c2ecf20Sopenharmony_ci return ret; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 9208c2ecf20Sopenharmony_ci MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, 9218c2ecf20Sopenharmony_ci rx_data_skews, 4); 9228c2ecf20Sopenharmony_ci if (ret < 0) 9238c2ecf20Sopenharmony_ci return ret; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 9268c2ecf20Sopenharmony_ci MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, 9278c2ecf20Sopenharmony_ci tx_data_skews, 4); 9288c2ecf20Sopenharmony_ci if (ret < 0) 9298c2ecf20Sopenharmony_ci return ret; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return 0; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 9358c2ecf20Sopenharmony_ci#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) 9368c2ecf20Sopenharmony_ci#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) 9378c2ecf20Sopenharmony_cistatic int ksz8873mll_read_status(struct phy_device *phydev) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci int regval; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* dummy read */ 9428c2ecf20Sopenharmony_ci regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX) 9478c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_HALF; 9488c2ecf20Sopenharmony_ci else 9498c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED) 9528c2ecf20Sopenharmony_ci phydev->speed = SPEED_10; 9538c2ecf20Sopenharmony_ci else 9548c2ecf20Sopenharmony_ci phydev->speed = SPEED_100; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci phydev->link = 1; 9578c2ecf20Sopenharmony_ci phydev->pause = phydev->asym_pause = 0; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return 0; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic int ksz9031_get_features(struct phy_device *phydev) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci int ret; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci ret = genphy_read_abilities(phydev); 9678c2ecf20Sopenharmony_ci if (ret < 0) 9688c2ecf20Sopenharmony_ci return ret; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci /* Silicon Errata Sheet (DS80000691D or DS80000692D): 9718c2ecf20Sopenharmony_ci * Whenever the device's Asymmetric Pause capability is set to 1, 9728c2ecf20Sopenharmony_ci * link-up may fail after a link-up to link-down transition. 9738c2ecf20Sopenharmony_ci * 9748c2ecf20Sopenharmony_ci * The Errata Sheet is for ksz9031, but ksz9021 has the same issue 9758c2ecf20Sopenharmony_ci * 9768c2ecf20Sopenharmony_ci * Workaround: 9778c2ecf20Sopenharmony_ci * Do not enable the Asymmetric Pause capability bit. 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* We force setting the Pause capability as the core will force the 9828c2ecf20Sopenharmony_ci * Asymmetric Pause capability to 1 otherwise. 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int ksz9031_read_status(struct phy_device *phydev) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci int err; 9928c2ecf20Sopenharmony_ci int regval; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci err = genphy_read_status(phydev); 9958c2ecf20Sopenharmony_ci if (err) 9968c2ecf20Sopenharmony_ci return err; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* Make sure the PHY is not broken. Read idle error count, 9998c2ecf20Sopenharmony_ci * and reset the PHY if it is maxed out. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_ci regval = phy_read(phydev, MII_STAT1000); 10028c2ecf20Sopenharmony_ci if ((regval & 0xFF) == 0xFF) { 10038c2ecf20Sopenharmony_ci phy_init_hw(phydev); 10048c2ecf20Sopenharmony_ci phydev->link = 0; 10058c2ecf20Sopenharmony_ci if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev)) 10068c2ecf20Sopenharmony_ci phydev->drv->config_intr(phydev); 10078c2ecf20Sopenharmony_ci return genphy_config_aneg(phydev); 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic int ksz8873mll_config_aneg(struct phy_device *phydev) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic int kszphy_get_sset_count(struct phy_device *phydev) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci return ARRAY_SIZE(kszphy_hw_stats); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic void kszphy_get_strings(struct phy_device *phydev, u8 *data) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci int i; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) { 10288c2ecf20Sopenharmony_ci strlcpy(data + i * ETH_GSTRING_LEN, 10298c2ecf20Sopenharmony_ci kszphy_hw_stats[i].string, ETH_GSTRING_LEN); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic u64 kszphy_get_stat(struct phy_device *phydev, int i) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct kszphy_hw_stat stat = kszphy_hw_stats[i]; 10368c2ecf20Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 10378c2ecf20Sopenharmony_ci int val; 10388c2ecf20Sopenharmony_ci u64 ret; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci val = phy_read(phydev, stat.reg); 10418c2ecf20Sopenharmony_ci if (val < 0) { 10428c2ecf20Sopenharmony_ci ret = U64_MAX; 10438c2ecf20Sopenharmony_ci } else { 10448c2ecf20Sopenharmony_ci val = val & ((1 << stat.bits) - 1); 10458c2ecf20Sopenharmony_ci priv->stats[i] += val; 10468c2ecf20Sopenharmony_ci ret = priv->stats[i]; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci return ret; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic void kszphy_get_stats(struct phy_device *phydev, 10538c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci int i; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) 10588c2ecf20Sopenharmony_ci data[i] = kszphy_get_stat(phydev, i); 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic int kszphy_suspend(struct phy_device *phydev) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci /* Disable PHY Interrupts */ 10648c2ecf20Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) { 10658c2ecf20Sopenharmony_ci phydev->interrupts = PHY_INTERRUPT_DISABLED; 10668c2ecf20Sopenharmony_ci if (phydev->drv->config_intr) 10678c2ecf20Sopenharmony_ci phydev->drv->config_intr(phydev); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return genphy_suspend(phydev); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic int kszphy_resume(struct phy_device *phydev) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci int ret; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci genphy_resume(phydev); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* After switching from power-down to normal mode, an internal global 10808c2ecf20Sopenharmony_ci * reset is automatically generated. Wait a minimum of 1 ms before 10818c2ecf20Sopenharmony_ci * read/write access to the PHY registers. 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci ret = kszphy_config_reset(phydev); 10868c2ecf20Sopenharmony_ci if (ret) 10878c2ecf20Sopenharmony_ci return ret; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Enable PHY Interrupts */ 10908c2ecf20Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) { 10918c2ecf20Sopenharmony_ci phydev->interrupts = PHY_INTERRUPT_ENABLED; 10928c2ecf20Sopenharmony_ci if (phydev->drv->config_intr) 10938c2ecf20Sopenharmony_ci phydev->drv->config_intr(phydev); 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci return 0; 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic int kszphy_probe(struct phy_device *phydev) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 11028c2ecf20Sopenharmony_ci const struct device_node *np = phydev->mdio.dev.of_node; 11038c2ecf20Sopenharmony_ci struct kszphy_priv *priv; 11048c2ecf20Sopenharmony_ci struct clk *clk; 11058c2ecf20Sopenharmony_ci int ret; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 11088c2ecf20Sopenharmony_ci if (!priv) 11098c2ecf20Sopenharmony_ci return -ENOMEM; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci phydev->priv = priv; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci priv->type = type; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (type && type->led_mode_reg) { 11168c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "micrel,led-mode", 11178c2ecf20Sopenharmony_ci &priv->led_mode); 11188c2ecf20Sopenharmony_ci if (ret) 11198c2ecf20Sopenharmony_ci priv->led_mode = -1; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (priv->led_mode > 3) { 11228c2ecf20Sopenharmony_ci phydev_err(phydev, "invalid led mode: 0x%02x\n", 11238c2ecf20Sopenharmony_ci priv->led_mode); 11248c2ecf20Sopenharmony_ci priv->led_mode = -1; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci } else { 11278c2ecf20Sopenharmony_ci priv->led_mode = -1; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref"); 11318c2ecf20Sopenharmony_ci /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ 11328c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(clk)) { 11338c2ecf20Sopenharmony_ci unsigned long rate = clk_get_rate(clk); 11348c2ecf20Sopenharmony_ci bool rmii_ref_clk_sel_25_mhz; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (type) 11378c2ecf20Sopenharmony_ci priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel; 11388c2ecf20Sopenharmony_ci rmii_ref_clk_sel_25_mhz = of_property_read_bool(np, 11398c2ecf20Sopenharmony_ci "micrel,rmii-reference-clock-select-25-mhz"); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (rate > 24500000 && rate < 25500000) { 11428c2ecf20Sopenharmony_ci priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz; 11438c2ecf20Sopenharmony_ci } else if (rate > 49500000 && rate < 50500000) { 11448c2ecf20Sopenharmony_ci priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz; 11458c2ecf20Sopenharmony_ci } else { 11468c2ecf20Sopenharmony_ci phydev_err(phydev, "Clock rate out of range: %ld\n", 11478c2ecf20Sopenharmony_ci rate); 11488c2ecf20Sopenharmony_ci return -EINVAL; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (ksz8041_fiber_mode(phydev)) 11538c2ecf20Sopenharmony_ci phydev->port = PORT_FIBRE; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* Support legacy board-file configuration */ 11568c2ecf20Sopenharmony_ci if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { 11578c2ecf20Sopenharmony_ci priv->rmii_ref_clk_sel = true; 11588c2ecf20Sopenharmony_ci priv->rmii_ref_clk_sel_val = true; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic struct phy_driver ksphy_driver[] = { 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KS8737, 11678c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 11688c2ecf20Sopenharmony_ci .name = "Micrel KS8737", 11698c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 11708c2ecf20Sopenharmony_ci .driver_data = &ks8737_type, 11718c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 11728c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 11738c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 11748c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 11758c2ecf20Sopenharmony_ci .resume = genphy_resume, 11768c2ecf20Sopenharmony_ci}, { 11778c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8021, 11788c2ecf20Sopenharmony_ci .phy_id_mask = 0x00ffffff, 11798c2ecf20Sopenharmony_ci .name = "Micrel KSZ8021 or KSZ8031", 11808c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 11818c2ecf20Sopenharmony_ci .driver_data = &ksz8021_type, 11828c2ecf20Sopenharmony_ci .probe = kszphy_probe, 11838c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 11848c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 11858c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 11868c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 11878c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 11888c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 11898c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 11908c2ecf20Sopenharmony_ci .resume = genphy_resume, 11918c2ecf20Sopenharmony_ci}, { 11928c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8031, 11938c2ecf20Sopenharmony_ci .phy_id_mask = 0x00ffffff, 11948c2ecf20Sopenharmony_ci .name = "Micrel KSZ8031", 11958c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 11968c2ecf20Sopenharmony_ci .driver_data = &ksz8021_type, 11978c2ecf20Sopenharmony_ci .probe = kszphy_probe, 11988c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 11998c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12008c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12018c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 12028c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 12038c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 12048c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 12058c2ecf20Sopenharmony_ci .resume = genphy_resume, 12068c2ecf20Sopenharmony_ci}, { 12078c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8041, 12088c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 12098c2ecf20Sopenharmony_ci .name = "Micrel KSZ8041", 12108c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 12118c2ecf20Sopenharmony_ci .driver_data = &ksz8041_type, 12128c2ecf20Sopenharmony_ci .probe = kszphy_probe, 12138c2ecf20Sopenharmony_ci .config_init = ksz8041_config_init, 12148c2ecf20Sopenharmony_ci .config_aneg = ksz8041_config_aneg, 12158c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12168c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12178c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 12188c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 12198c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 12208c2ecf20Sopenharmony_ci /* No suspend/resume callbacks because of errata DS80000700A, 12218c2ecf20Sopenharmony_ci * receiver error following software power down. 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_ci}, { 12248c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8041RNLI, 12258c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 12268c2ecf20Sopenharmony_ci .name = "Micrel KSZ8041RNLI", 12278c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 12288c2ecf20Sopenharmony_ci .driver_data = &ksz8041_type, 12298c2ecf20Sopenharmony_ci .probe = kszphy_probe, 12308c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 12318c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12328c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12338c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 12348c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 12358c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 12368c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 12378c2ecf20Sopenharmony_ci .resume = genphy_resume, 12388c2ecf20Sopenharmony_ci}, { 12398c2ecf20Sopenharmony_ci .name = "Micrel KSZ8051", 12408c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 12418c2ecf20Sopenharmony_ci .driver_data = &ksz8051_type, 12428c2ecf20Sopenharmony_ci .probe = kszphy_probe, 12438c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 12448c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12458c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12468c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 12478c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 12488c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 12498c2ecf20Sopenharmony_ci .match_phy_device = ksz8051_match_phy_device, 12508c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 12518c2ecf20Sopenharmony_ci .resume = genphy_resume, 12528c2ecf20Sopenharmony_ci}, { 12538c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8001, 12548c2ecf20Sopenharmony_ci .name = "Micrel KSZ8001 or KS8721", 12558c2ecf20Sopenharmony_ci .phy_id_mask = 0x00fffffc, 12568c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 12578c2ecf20Sopenharmony_ci .driver_data = &ksz8041_type, 12588c2ecf20Sopenharmony_ci .probe = kszphy_probe, 12598c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 12608c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12618c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12628c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 12638c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 12648c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 12658c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 12668c2ecf20Sopenharmony_ci .resume = genphy_resume, 12678c2ecf20Sopenharmony_ci}, { 12688c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8081, 12698c2ecf20Sopenharmony_ci .name = "Micrel KSZ8081 or KSZ8091", 12708c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 12718c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 12728c2ecf20Sopenharmony_ci .driver_data = &ksz8081_type, 12738c2ecf20Sopenharmony_ci .probe = kszphy_probe, 12748c2ecf20Sopenharmony_ci .config_init = ksz8081_config_init, 12758c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12768c2ecf20Sopenharmony_ci .soft_reset = genphy_soft_reset, 12778c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12788c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 12798c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 12808c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 12818c2ecf20Sopenharmony_ci .suspend = kszphy_suspend, 12828c2ecf20Sopenharmony_ci .resume = kszphy_resume, 12838c2ecf20Sopenharmony_ci}, { 12848c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8061, 12858c2ecf20Sopenharmony_ci .name = "Micrel KSZ8061", 12868c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 12878c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 12888c2ecf20Sopenharmony_ci .config_init = ksz8061_config_init, 12898c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 12908c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 12918c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 12928c2ecf20Sopenharmony_ci .resume = genphy_resume, 12938c2ecf20Sopenharmony_ci}, { 12948c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ9021, 12958c2ecf20Sopenharmony_ci .phy_id_mask = 0x000ffffe, 12968c2ecf20Sopenharmony_ci .name = "Micrel KSZ9021 Gigabit PHY", 12978c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 12988c2ecf20Sopenharmony_ci .driver_data = &ksz9021_type, 12998c2ecf20Sopenharmony_ci .probe = kszphy_probe, 13008c2ecf20Sopenharmony_ci .get_features = ksz9031_get_features, 13018c2ecf20Sopenharmony_ci .config_init = ksz9021_config_init, 13028c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 13038c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 13048c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 13058c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 13068c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 13078c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13088c2ecf20Sopenharmony_ci .resume = genphy_resume, 13098c2ecf20Sopenharmony_ci .read_mmd = genphy_read_mmd_unsupported, 13108c2ecf20Sopenharmony_ci .write_mmd = genphy_write_mmd_unsupported, 13118c2ecf20Sopenharmony_ci}, { 13128c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ9031, 13138c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 13148c2ecf20Sopenharmony_ci .name = "Micrel KSZ9031 Gigabit PHY", 13158c2ecf20Sopenharmony_ci .driver_data = &ksz9021_type, 13168c2ecf20Sopenharmony_ci .probe = kszphy_probe, 13178c2ecf20Sopenharmony_ci .get_features = ksz9031_get_features, 13188c2ecf20Sopenharmony_ci .config_init = ksz9031_config_init, 13198c2ecf20Sopenharmony_ci .soft_reset = genphy_soft_reset, 13208c2ecf20Sopenharmony_ci .read_status = ksz9031_read_status, 13218c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 13228c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 13238c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 13248c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 13258c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 13268c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13278c2ecf20Sopenharmony_ci .resume = kszphy_resume, 13288c2ecf20Sopenharmony_ci}, { 13298c2ecf20Sopenharmony_ci .phy_id = PHY_ID_LAN8814, 13308c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 13318c2ecf20Sopenharmony_ci .name = "Microchip INDY Gigabit Quad PHY", 13328c2ecf20Sopenharmony_ci .driver_data = &ksz9021_type, 13338c2ecf20Sopenharmony_ci .probe = kszphy_probe, 13348c2ecf20Sopenharmony_ci .soft_reset = genphy_soft_reset, 13358c2ecf20Sopenharmony_ci .read_status = ksz9031_read_status, 13368c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 13378c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 13388c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 13398c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13408c2ecf20Sopenharmony_ci .resume = kszphy_resume, 13418c2ecf20Sopenharmony_ci}, { 13428c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ9131, 13438c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 13448c2ecf20Sopenharmony_ci .name = "Microchip KSZ9131 Gigabit PHY", 13458c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 13468c2ecf20Sopenharmony_ci .driver_data = &ksz9021_type, 13478c2ecf20Sopenharmony_ci .probe = kszphy_probe, 13488c2ecf20Sopenharmony_ci .soft_reset = genphy_soft_reset, 13498c2ecf20Sopenharmony_ci .config_init = ksz9131_config_init, 13508c2ecf20Sopenharmony_ci .read_status = genphy_read_status, 13518c2ecf20Sopenharmony_ci .ack_interrupt = kszphy_ack_interrupt, 13528c2ecf20Sopenharmony_ci .config_intr = kszphy_config_intr, 13538c2ecf20Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 13548c2ecf20Sopenharmony_ci .get_strings = kszphy_get_strings, 13558c2ecf20Sopenharmony_ci .get_stats = kszphy_get_stats, 13568c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13578c2ecf20Sopenharmony_ci .resume = kszphy_resume, 13588c2ecf20Sopenharmony_ci}, { 13598c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ8873MLL, 13608c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 13618c2ecf20Sopenharmony_ci .name = "Micrel KSZ8873MLL Switch", 13628c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 13638c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 13648c2ecf20Sopenharmony_ci .config_aneg = ksz8873mll_config_aneg, 13658c2ecf20Sopenharmony_ci .read_status = ksz8873mll_read_status, 13668c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13678c2ecf20Sopenharmony_ci .resume = genphy_resume, 13688c2ecf20Sopenharmony_ci}, { 13698c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ886X, 13708c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 13718c2ecf20Sopenharmony_ci .name = "Micrel KSZ886X Switch", 13728c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 13738c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 13748c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13758c2ecf20Sopenharmony_ci .resume = genphy_resume, 13768c2ecf20Sopenharmony_ci}, { 13778c2ecf20Sopenharmony_ci .name = "Micrel KSZ87XX Switch", 13788c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 13798c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 13808c2ecf20Sopenharmony_ci .match_phy_device = ksz8795_match_phy_device, 13818c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13828c2ecf20Sopenharmony_ci .resume = genphy_resume, 13838c2ecf20Sopenharmony_ci}, { 13848c2ecf20Sopenharmony_ci .phy_id = PHY_ID_KSZ9477, 13858c2ecf20Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 13868c2ecf20Sopenharmony_ci .name = "Microchip KSZ9477", 13878c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 13888c2ecf20Sopenharmony_ci .config_init = kszphy_config_init, 13898c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 13908c2ecf20Sopenharmony_ci .resume = genphy_resume, 13918c2ecf20Sopenharmony_ci} }; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cimodule_phy_driver(ksphy_driver); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Micrel PHY driver"); 13968c2ecf20Sopenharmony_ciMODULE_AUTHOR("David J. Choi"); 13978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused micrel_tbl[] = { 14008c2ecf20Sopenharmony_ci { PHY_ID_KSZ9021, 0x000ffffe }, 14018c2ecf20Sopenharmony_ci { PHY_ID_KSZ9031, MICREL_PHY_ID_MASK }, 14028c2ecf20Sopenharmony_ci { PHY_ID_KSZ9131, MICREL_PHY_ID_MASK }, 14038c2ecf20Sopenharmony_ci { PHY_ID_KSZ8001, 0x00fffffc }, 14048c2ecf20Sopenharmony_ci { PHY_ID_KS8737, MICREL_PHY_ID_MASK }, 14058c2ecf20Sopenharmony_ci { PHY_ID_KSZ8021, 0x00ffffff }, 14068c2ecf20Sopenharmony_ci { PHY_ID_KSZ8031, 0x00ffffff }, 14078c2ecf20Sopenharmony_ci { PHY_ID_KSZ8041, MICREL_PHY_ID_MASK }, 14088c2ecf20Sopenharmony_ci { PHY_ID_KSZ8051, MICREL_PHY_ID_MASK }, 14098c2ecf20Sopenharmony_ci { PHY_ID_KSZ8061, MICREL_PHY_ID_MASK }, 14108c2ecf20Sopenharmony_ci { PHY_ID_KSZ8081, MICREL_PHY_ID_MASK }, 14118c2ecf20Sopenharmony_ci { PHY_ID_KSZ8873MLL, MICREL_PHY_ID_MASK }, 14128c2ecf20Sopenharmony_ci { PHY_ID_KSZ886X, MICREL_PHY_ID_MASK }, 14138c2ecf20Sopenharmony_ci { PHY_ID_LAN8814, MICREL_PHY_ID_MASK }, 14148c2ecf20Sopenharmony_ci { } 14158c2ecf20Sopenharmony_ci}; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, micrel_tbl); 1418