linux/drivers/net/phy/at803x.c
<<
>>
Prefs
   1/*
   2 * drivers/net/phy/at803x.c
   3 *
   4 * Driver for Atheros 803x PHY
   5 *
   6 * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/phy.h>
  15#include <linux/module.h>
  16#include <linux/string.h>
  17#include <linux/netdevice.h>
  18#include <linux/etherdevice.h>
  19
  20#define AT803X_INTR_ENABLE                      0x12
  21#define AT803X_INTR_STATUS                      0x13
  22#define AT803X_WOL_ENABLE                       0x01
  23#define AT803X_DEVICE_ADDR                      0x03
  24#define AT803X_LOC_MAC_ADDR_0_15_OFFSET         0x804C
  25#define AT803X_LOC_MAC_ADDR_16_31_OFFSET        0x804B
  26#define AT803X_LOC_MAC_ADDR_32_47_OFFSET        0x804A
  27#define AT803X_MMD_ACCESS_CONTROL               0x0D
  28#define AT803X_MMD_ACCESS_CONTROL_DATA          0x0E
  29#define AT803X_FUNC_DATA                        0x4003
  30#define AT803X_DEBUG_ADDR                       0x1D
  31#define AT803X_DEBUG_DATA                       0x1E
  32#define AT803X_DEBUG_SYSTEM_MODE_CTRL           0x05
  33#define AT803X_DEBUG_RGMII_TX_CLK_DLY           BIT(8)
  34
  35MODULE_DESCRIPTION("Atheros 803x PHY driver");
  36MODULE_AUTHOR("Matus Ujhelyi");
  37MODULE_LICENSE("GPL");
  38
  39static int at803x_set_wol(struct phy_device *phydev,
  40                          struct ethtool_wolinfo *wol)
  41{
  42        struct net_device *ndev = phydev->attached_dev;
  43        const u8 *mac;
  44        int ret;
  45        u32 value;
  46        unsigned int i, offsets[] = {
  47                AT803X_LOC_MAC_ADDR_32_47_OFFSET,
  48                AT803X_LOC_MAC_ADDR_16_31_OFFSET,
  49                AT803X_LOC_MAC_ADDR_0_15_OFFSET,
  50        };
  51
  52        if (!ndev)
  53                return -ENODEV;
  54
  55        if (wol->wolopts & WAKE_MAGIC) {
  56                mac = (const u8 *) ndev->dev_addr;
  57
  58                if (!is_valid_ether_addr(mac))
  59                        return -EFAULT;
  60
  61                for (i = 0; i < 3; i++) {
  62                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
  63                                  AT803X_DEVICE_ADDR);
  64                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
  65                                  offsets[i]);
  66                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
  67                                  AT803X_FUNC_DATA);
  68                        phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
  69                                  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
  70                }
  71
  72                value = phy_read(phydev, AT803X_INTR_ENABLE);
  73                value |= AT803X_WOL_ENABLE;
  74                ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
  75                if (ret)
  76                        return ret;
  77                value = phy_read(phydev, AT803X_INTR_STATUS);
  78        } else {
  79                value = phy_read(phydev, AT803X_INTR_ENABLE);
  80                value &= (~AT803X_WOL_ENABLE);
  81                ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
  82                if (ret)
  83                        return ret;
  84                value = phy_read(phydev, AT803X_INTR_STATUS);
  85        }
  86
  87        return ret;
  88}
  89
  90static void at803x_get_wol(struct phy_device *phydev,
  91                           struct ethtool_wolinfo *wol)
  92{
  93        u32 value;
  94
  95        wol->supported = WAKE_MAGIC;
  96        wol->wolopts = 0;
  97
  98        value = phy_read(phydev, AT803X_INTR_ENABLE);
  99        if (value & AT803X_WOL_ENABLE)
 100                wol->wolopts |= WAKE_MAGIC;
 101}
 102
 103static int at803x_config_init(struct phy_device *phydev)
 104{
 105        int val;
 106        int ret;
 107        u32 features;
 108
 109        features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
 110                   SUPPORTED_FIBRE | SUPPORTED_BNC;
 111
 112        val = phy_read(phydev, MII_BMSR);
 113        if (val < 0)
 114                return val;
 115
 116        if (val & BMSR_ANEGCAPABLE)
 117                features |= SUPPORTED_Autoneg;
 118        if (val & BMSR_100FULL)
 119                features |= SUPPORTED_100baseT_Full;
 120        if (val & BMSR_100HALF)
 121                features |= SUPPORTED_100baseT_Half;
 122        if (val & BMSR_10FULL)
 123                features |= SUPPORTED_10baseT_Full;
 124        if (val & BMSR_10HALF)
 125                features |= SUPPORTED_10baseT_Half;
 126
 127        if (val & BMSR_ESTATEN) {
 128                val = phy_read(phydev, MII_ESTATUS);
 129                if (val < 0)
 130                        return val;
 131
 132                if (val & ESTATUS_1000_TFULL)
 133                        features |= SUPPORTED_1000baseT_Full;
 134                if (val & ESTATUS_1000_THALF)
 135                        features |= SUPPORTED_1000baseT_Half;
 136        }
 137
 138        phydev->supported = features;
 139        phydev->advertising = features;
 140
 141        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
 142                ret = phy_write(phydev, AT803X_DEBUG_ADDR,
 143                                AT803X_DEBUG_SYSTEM_MODE_CTRL);
 144                if (ret)
 145                        return ret;
 146                ret = phy_write(phydev, AT803X_DEBUG_DATA,
 147                                AT803X_DEBUG_RGMII_TX_CLK_DLY);
 148                if (ret)
 149                        return ret;
 150        }
 151
 152        return 0;
 153}
 154
 155static struct phy_driver at803x_driver[] = {
 156{
 157        /* ATHEROS 8035 */
 158        .phy_id         = 0x004dd072,
 159        .name           = "Atheros 8035 ethernet",
 160        .phy_id_mask    = 0xffffffef,
 161        .config_init    = at803x_config_init,
 162        .set_wol        = at803x_set_wol,
 163        .get_wol        = at803x_get_wol,
 164        .features       = PHY_GBIT_FEATURES,
 165        .flags          = PHY_HAS_INTERRUPT,
 166        .config_aneg    = &genphy_config_aneg,
 167        .read_status    = &genphy_read_status,
 168        .driver         = {
 169                .owner = THIS_MODULE,
 170        },
 171}, {
 172        /* ATHEROS 8030 */
 173        .phy_id         = 0x004dd076,
 174        .name           = "Atheros 8030 ethernet",
 175        .phy_id_mask    = 0xffffffef,
 176        .config_init    = at803x_config_init,
 177        .set_wol        = at803x_set_wol,
 178        .get_wol        = at803x_get_wol,
 179        .features       = PHY_GBIT_FEATURES,
 180        .flags          = PHY_HAS_INTERRUPT,
 181        .config_aneg    = &genphy_config_aneg,
 182        .read_status    = &genphy_read_status,
 183        .driver         = {
 184                .owner = THIS_MODULE,
 185        },
 186}, {
 187        /* ATHEROS 8031 */
 188        .phy_id         = 0x004dd074,
 189        .name           = "Atheros 8031 ethernet",
 190        .phy_id_mask    = 0xffffffef,
 191        .config_init    = at803x_config_init,
 192        .set_wol        = at803x_set_wol,
 193        .get_wol        = at803x_get_wol,
 194        .features       = PHY_GBIT_FEATURES,
 195        .flags          = PHY_HAS_INTERRUPT,
 196        .config_aneg    = &genphy_config_aneg,
 197        .read_status    = &genphy_read_status,
 198        .driver         = {
 199                .owner = THIS_MODULE,
 200        },
 201} };
 202
 203static int __init atheros_init(void)
 204{
 205        return phy_drivers_register(at803x_driver,
 206                                    ARRAY_SIZE(at803x_driver));
 207}
 208
 209static void __exit atheros_exit(void)
 210{
 211        return phy_drivers_unregister(at803x_driver,
 212                                      ARRAY_SIZE(at803x_driver));
 213}
 214
 215module_init(atheros_init);
 216module_exit(atheros_exit);
 217
 218static struct mdio_device_id __maybe_unused atheros_tbl[] = {
 219        { 0x004dd076, 0xffffffef },
 220        { 0x004dd074, 0xffffffef },
 221        { 0x004dd072, 0xffffffef },
 222        { }
 223};
 224
 225MODULE_DEVICE_TABLE(mdio, atheros_tbl);
 226