linux/drivers/net/phy/ax88796b.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/* Driver for Asix PHYs
   3 *
   4 * Author: Michael Schmitz <schmitzmic@gmail.com>
   5 */
   6#include <linux/kernel.h>
   7#include <linux/errno.h>
   8#include <linux/init.h>
   9#include <linux/module.h>
  10#include <linux/mii.h>
  11#include <linux/phy.h>
  12
  13#define PHY_ID_ASIX_AX88772A            0x003b1861
  14#define PHY_ID_ASIX_AX88772C            0x003b1881
  15#define PHY_ID_ASIX_AX88796B            0x003b1841
  16
  17MODULE_DESCRIPTION("Asix PHY driver");
  18MODULE_AUTHOR("Michael Schmitz <schmitzmic@gmail.com>");
  19MODULE_LICENSE("GPL");
  20
  21/**
  22 * asix_soft_reset - software reset the PHY via BMCR_RESET bit
  23 * @phydev: target phy_device struct
  24 *
  25 * Description: Perform a software PHY reset using the standard
  26 * BMCR_RESET bit and poll for the reset bit to be cleared.
  27 * Toggle BMCR_RESET bit off to accommodate broken AX8796B PHY implementation
  28 * such as used on the Individual Computers' X-Surf 100 Zorro card.
  29 *
  30 * Returns: 0 on success, < 0 on failure
  31 */
  32static int asix_soft_reset(struct phy_device *phydev)
  33{
  34        int ret;
  35
  36        /* Asix PHY won't reset unless reset bit toggles */
  37        ret = phy_write(phydev, MII_BMCR, 0);
  38        if (ret < 0)
  39                return ret;
  40
  41        return genphy_soft_reset(phydev);
  42}
  43
  44/* AX88772A is not working properly with some old switches (NETGEAR EN 108TP):
  45 * after autoneg is done and the link status is reported as active, the MII_LPA
  46 * register is 0. This issue is not reproducible on AX88772C.
  47 */
  48static int asix_ax88772a_read_status(struct phy_device *phydev)
  49{
  50        int ret, val;
  51
  52        ret = genphy_update_link(phydev);
  53        if (ret)
  54                return ret;
  55
  56        if (!phydev->link)
  57                return 0;
  58
  59        /* If MII_LPA is 0, phy_resolve_aneg_linkmode() will fail to resolve
  60         * linkmode so use MII_BMCR as default values.
  61         */
  62        val = phy_read(phydev, MII_BMCR);
  63        if (val < 0)
  64                return val;
  65
  66        if (val & BMCR_SPEED100)
  67                phydev->speed = SPEED_100;
  68        else
  69                phydev->speed = SPEED_10;
  70
  71        if (val & BMCR_FULLDPLX)
  72                phydev->duplex = DUPLEX_FULL;
  73        else
  74                phydev->duplex = DUPLEX_HALF;
  75
  76        ret = genphy_read_lpa(phydev);
  77        if (ret < 0)
  78                return ret;
  79
  80        if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
  81                phy_resolve_aneg_linkmode(phydev);
  82
  83        return 0;
  84}
  85
  86static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
  87{
  88        /* Reset PHY, otherwise MII_LPA will provide outdated information.
  89         * This issue is reproducible only with some link partner PHYs
  90         */
  91        if (phydev->state == PHY_NOLINK && phydev->drv->soft_reset)
  92                phydev->drv->soft_reset(phydev);
  93}
  94
  95static struct phy_driver asix_driver[] = {
  96{
  97        PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A),
  98        .name           = "Asix Electronics AX88772A",
  99        .flags          = PHY_IS_INTERNAL,
 100        .read_status    = asix_ax88772a_read_status,
 101        .suspend        = genphy_suspend,
 102        .resume         = genphy_resume,
 103        .soft_reset     = asix_soft_reset,
 104        .link_change_notify     = asix_ax88772a_link_change_notify,
 105}, {
 106        PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C),
 107        .name           = "Asix Electronics AX88772C",
 108        .flags          = PHY_IS_INTERNAL,
 109        .suspend        = genphy_suspend,
 110        .resume         = genphy_resume,
 111        .soft_reset     = asix_soft_reset,
 112}, {
 113        .phy_id         = PHY_ID_ASIX_AX88796B,
 114        .name           = "Asix Electronics AX88796B",
 115        .phy_id_mask    = 0xfffffff0,
 116        /* PHY_BASIC_FEATURES */
 117        .soft_reset     = asix_soft_reset,
 118} };
 119
 120module_phy_driver(asix_driver);
 121
 122static struct mdio_device_id __maybe_unused asix_tbl[] = {
 123        { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) },
 124        { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) },
 125        { PHY_ID_ASIX_AX88796B, 0xfffffff0 },
 126        { }
 127};
 128
 129MODULE_DEVICE_TABLE(mdio, asix_tbl);
 130