Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Driver for the Renesas PHY uPD60620.
0004  *
0005  * Copyright (C) 2015 Softing Industrial Automation GmbH
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/phy.h>
0011 
0012 #define UPD60620_PHY_ID    0xb8242824
0013 
0014 /* Extended Registers and values */
0015 /* PHY Special Control/Status    */
0016 #define PHY_PHYSCR         0x1F      /* PHY.31 */
0017 #define PHY_PHYSCR_10MB    0x0004    /* PHY speed = 10mb */
0018 #define PHY_PHYSCR_100MB   0x0008    /* PHY speed = 100mb */
0019 #define PHY_PHYSCR_DUPLEX  0x0010    /* PHY Duplex */
0020 
0021 /* PHY Special Modes */
0022 #define PHY_SPM            0x12      /* PHY.18 */
0023 
0024 /* Init PHY */
0025 
0026 static int upd60620_config_init(struct phy_device *phydev)
0027 {
0028     /* Enable support for passive HUBs (could be a strap option) */
0029     /* PHYMODE: All speeds, HD in parallel detect */
0030     return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr);
0031 }
0032 
0033 /* Get PHY status from common registers */
0034 
0035 static int upd60620_read_status(struct phy_device *phydev)
0036 {
0037     int phy_state;
0038 
0039     /* Read negotiated state */
0040     phy_state = phy_read(phydev, MII_BMSR);
0041     if (phy_state < 0)
0042         return phy_state;
0043 
0044     phydev->link = 0;
0045     linkmode_zero(phydev->lp_advertising);
0046     phydev->pause = 0;
0047     phydev->asym_pause = 0;
0048 
0049     if (phy_state & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) {
0050         phy_state = phy_read(phydev, PHY_PHYSCR);
0051         if (phy_state < 0)
0052             return phy_state;
0053 
0054         if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) {
0055             phydev->link = 1;
0056             phydev->speed = SPEED_10;
0057             phydev->duplex = DUPLEX_HALF;
0058 
0059             if (phy_state & PHY_PHYSCR_100MB)
0060                 phydev->speed = SPEED_100;
0061             if (phy_state & PHY_PHYSCR_DUPLEX)
0062                 phydev->duplex = DUPLEX_FULL;
0063 
0064             phy_state = phy_read(phydev, MII_LPA);
0065             if (phy_state < 0)
0066                 return phy_state;
0067 
0068             mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising,
0069                           phy_state);
0070 
0071             phy_resolve_aneg_pause(phydev);
0072         }
0073     }
0074     return 0;
0075 }
0076 
0077 MODULE_DESCRIPTION("Renesas uPD60620 PHY driver");
0078 MODULE_AUTHOR("Bernd Edlinger <bernd.edlinger@hotmail.de>");
0079 MODULE_LICENSE("GPL");
0080 
0081 static struct phy_driver upd60620_driver[1] = { {
0082     .phy_id         = UPD60620_PHY_ID,
0083     .phy_id_mask    = 0xfffffffe,
0084     .name           = "Renesas uPD60620",
0085     /* PHY_BASIC_FEATURES */
0086     .flags          = 0,
0087     .config_init    = upd60620_config_init,
0088     .read_status    = upd60620_read_status,
0089 } };
0090 
0091 module_phy_driver(upd60620_driver);
0092 
0093 static struct mdio_device_id __maybe_unused upd60620_tbl[] = {
0094     { UPD60620_PHY_ID, 0xfffffffe },
0095     { }
0096 };
0097 
0098 MODULE_DEVICE_TABLE(mdio, upd60620_tbl);