ax88796c_ioctl.c (5857B)
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}