linux/drivers/net/ethernet/asix/ax88796c_ioctl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2010 ASIX Electronics Corporation
   4 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
   5 *
   6 * ASIX AX88796C SPI Fast Ethernet Linux driver
   7 */
   8
   9#define pr_fmt(fmt)     "ax88796c: " fmt
  10
  11#include <linux/bitmap.h>
  12#include <linux/iopoll.h>
  13#include <linux/phy.h>
  14#include <linux/netdevice.h>
  15
  16#include "ax88796c_main.h"
  17#include "ax88796c_ioctl.h"
  18
  19static const char ax88796c_priv_flag_names[][ETH_GSTRING_LEN] = {
  20        "SPICompression",
  21};
  22
  23static void
  24ax88796c_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
  25{
  26        /* Inherit standard device info */
  27        strncpy(info->driver, DRV_NAME, sizeof(info->driver));
  28}
  29
  30static u32 ax88796c_get_msglevel(struct net_device *ndev)
  31{
  32        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
  33
  34        return ax_local->msg_enable;
  35}
  36
  37static void ax88796c_set_msglevel(struct net_device *ndev, u32 level)
  38{
  39        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
  40
  41        ax_local->msg_enable = level;
  42}
  43
  44static void
  45ax88796c_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
  46{
  47        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
  48
  49        pause->tx_pause = !!(ax_local->flowctrl & AX_FC_TX);
  50        pause->rx_pause = !!(ax_local->flowctrl & AX_FC_RX);
  51        pause->autoneg = (ax_local->flowctrl & AX_FC_ANEG) ?
  52                AUTONEG_ENABLE :
  53                AUTONEG_DISABLE;
  54}
  55
  56static int
  57ax88796c_set_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
  58{
  59        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
  60        int fc;
  61
  62        /* The following logic comes from phylink_ethtool_set_pauseparam() */
  63        fc = pause->tx_pause ? AX_FC_TX : 0;
  64        fc |= pause->rx_pause ? AX_FC_RX : 0;
  65        fc |= pause->autoneg ? AX_FC_ANEG : 0;
  66
  67        ax_local->flowctrl = fc;
  68
  69        if (pause->autoneg) {
  70                phy_set_asym_pause(ax_local->phydev, pause->tx_pause,
  71                                   pause->rx_pause);
  72        } else {
  73                int maccr = 0;
  74
  75                phy_set_asym_pause(ax_local->phydev, 0, 0);
  76                maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
  77                maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
  78
  79                mutex_lock(&ax_local->spi_lock);
  80
  81                maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
  82                        ~(MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
  83                AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
  84
  85                mutex_unlock(&ax_local->spi_lock);
  86        }
  87
  88        return 0;
  89}
  90
  91static int ax88796c_get_regs_len(struct net_device *ndev)
  92{
  93        return AX88796C_REGDUMP_LEN + AX88796C_PHY_REGDUMP_LEN;
  94}
  95
  96static void
  97ax88796c_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *_p)
  98{
  99        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
 100        int offset, i;
 101        u16 *p = _p;
 102
 103        memset(p, 0, ax88796c_get_regs_len(ndev));
 104
 105        mutex_lock(&ax_local->spi_lock);
 106
 107        for (offset = 0; offset < AX88796C_REGDUMP_LEN; offset += 2) {
 108                if (!test_bit(offset / 2, ax88796c_no_regs_mask))
 109                        *p = AX_READ(&ax_local->ax_spi, offset);
 110                p++;
 111        }
 112
 113        mutex_unlock(&ax_local->spi_lock);
 114
 115        for (i = 0; i < AX88796C_PHY_REGDUMP_LEN / 2; i++) {
 116                *p = phy_read(ax_local->phydev, i);
 117                p++;
 118        }
 119}
 120
 121static void
 122ax88796c_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
 123{
 124        switch (stringset) {
 125        case ETH_SS_PRIV_FLAGS:
 126                memcpy(data, ax88796c_priv_flag_names,
 127                       sizeof(ax88796c_priv_flag_names));
 128                break;
 129        }
 130}
 131
 132static int
 133ax88796c_get_sset_count(struct net_device *ndev, int stringset)
 134{
 135        int ret = 0;
 136
 137        switch (stringset) {
 138        case ETH_SS_PRIV_FLAGS:
 139                ret = ARRAY_SIZE(ax88796c_priv_flag_names);
 140                break;
 141        default:
 142                ret = -EOPNOTSUPP;
 143        }
 144
 145        return ret;
 146}
 147
 148static int ax88796c_set_priv_flags(struct net_device *ndev, u32 flags)
 149{
 150        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
 151
 152        if (flags & ~AX_PRIV_FLAGS_MASK)
 153                return -EOPNOTSUPP;
 154
 155        if ((ax_local->priv_flags ^ flags) & AX_CAP_COMP)
 156                if (netif_running(ndev))
 157                        return -EBUSY;
 158
 159        ax_local->priv_flags = flags;
 160
 161        return 0;
 162}
 163
 164static u32 ax88796c_get_priv_flags(struct net_device *ndev)
 165{
 166        struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
 167
 168        return ax_local->priv_flags;
 169}
 170
 171int ax88796c_mdio_read(struct mii_bus *mdiobus, int phy_id, int loc)
 172{
 173        struct ax88796c_device *ax_local = mdiobus->priv;
 174        int ret;
 175
 176        mutex_lock(&ax_local->spi_lock);
 177        AX_WRITE(&ax_local->ax_spi, MDIOCR_RADDR(loc)
 178                        | MDIOCR_FADDR(phy_id) | MDIOCR_READ, P2_MDIOCR);
 179
 180        ret = read_poll_timeout(AX_READ, ret,
 181                                (ret != 0),
 182                                0, jiffies_to_usecs(HZ / 100), false,
 183                                &ax_local->ax_spi, P2_MDIOCR);
 184        if (!ret)
 185                ret = AX_READ(&ax_local->ax_spi, P2_MDIODR);
 186
 187        mutex_unlock(&ax_local->spi_lock);
 188
 189        return ret;
 190}
 191
 192int
 193ax88796c_mdio_write(struct mii_bus *mdiobus, int phy_id, int loc, u16 val)
 194{
 195        struct ax88796c_device *ax_local = mdiobus->priv;
 196        int ret;
 197
 198        mutex_lock(&ax_local->spi_lock);
 199        AX_WRITE(&ax_local->ax_spi, val, P2_MDIODR);
 200
 201        AX_WRITE(&ax_local->ax_spi,
 202                 MDIOCR_RADDR(loc) | MDIOCR_FADDR(phy_id)
 203                 | MDIOCR_WRITE, P2_MDIOCR);
 204
 205        ret = read_poll_timeout(AX_READ, ret,
 206                                ((ret & MDIOCR_VALID) != 0), 0,
 207                                jiffies_to_usecs(HZ / 100), false,
 208                                &ax_local->ax_spi, P2_MDIOCR);
 209        mutex_unlock(&ax_local->spi_lock);
 210
 211        return ret;
 212}
 213
 214const struct ethtool_ops ax88796c_ethtool_ops = {
 215        .get_drvinfo            = ax88796c_get_drvinfo,
 216        .get_link               = ethtool_op_get_link,
 217        .get_msglevel           = ax88796c_get_msglevel,
 218        .set_msglevel           = ax88796c_set_msglevel,
 219        .get_link_ksettings     = phy_ethtool_get_link_ksettings,
 220        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
 221        .nway_reset             = phy_ethtool_nway_reset,
 222        .get_pauseparam         = ax88796c_get_pauseparam,
 223        .set_pauseparam         = ax88796c_set_pauseparam,
 224        .get_regs_len           = ax88796c_get_regs_len,
 225        .get_regs               = ax88796c_get_regs,
 226        .get_strings            = ax88796c_get_strings,
 227        .get_sset_count         = ax88796c_get_sset_count,
 228        .get_priv_flags         = ax88796c_get_priv_flags,
 229        .set_priv_flags         = ax88796c_set_priv_flags,
 230};
 231
 232int ax88796c_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
 233{
 234        int ret;
 235
 236        ret = phy_mii_ioctl(ndev->phydev, ifr, cmd);
 237
 238        return ret;
 239}
 240