123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2010 ASIX Electronics Corporation
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
- *
- * ASIX AX88796C SPI Fast Ethernet Linux driver
- */
- #define pr_fmt(fmt) "ax88796c: " fmt
- #include <linux/bitmap.h>
- #include <linux/iopoll.h>
- #include <linux/phy.h>
- #include <linux/netdevice.h>
- #include "ax88796c_main.h"
- #include "ax88796c_ioctl.h"
- static const char ax88796c_priv_flag_names[][ETH_GSTRING_LEN] = {
- "SPICompression",
- };
- static void
- ax88796c_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
- {
- /* Inherit standard device info */
- strncpy(info->driver, DRV_NAME, sizeof(info->driver));
- }
- static u32 ax88796c_get_msglevel(struct net_device *ndev)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- return ax_local->msg_enable;
- }
- static void ax88796c_set_msglevel(struct net_device *ndev, u32 level)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- ax_local->msg_enable = level;
- }
- static void
- ax88796c_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- pause->tx_pause = !!(ax_local->flowctrl & AX_FC_TX);
- pause->rx_pause = !!(ax_local->flowctrl & AX_FC_RX);
- pause->autoneg = (ax_local->flowctrl & AX_FC_ANEG) ?
- AUTONEG_ENABLE :
- AUTONEG_DISABLE;
- }
- static int
- ax88796c_set_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- int fc;
- /* The following logic comes from phylink_ethtool_set_pauseparam() */
- fc = pause->tx_pause ? AX_FC_TX : 0;
- fc |= pause->rx_pause ? AX_FC_RX : 0;
- fc |= pause->autoneg ? AX_FC_ANEG : 0;
- ax_local->flowctrl = fc;
- if (pause->autoneg) {
- phy_set_asym_pause(ax_local->phydev, pause->tx_pause,
- pause->rx_pause);
- } else {
- int maccr = 0;
- phy_set_asym_pause(ax_local->phydev, 0, 0);
- maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
- maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
- mutex_lock(&ax_local->spi_lock);
- maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
- ~(MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
- AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
- mutex_unlock(&ax_local->spi_lock);
- }
- return 0;
- }
- static int ax88796c_get_regs_len(struct net_device *ndev)
- {
- return AX88796C_REGDUMP_LEN + AX88796C_PHY_REGDUMP_LEN;
- }
- static void
- ax88796c_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *_p)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- int offset, i;
- u16 *p = _p;
- memset(p, 0, ax88796c_get_regs_len(ndev));
- mutex_lock(&ax_local->spi_lock);
- for (offset = 0; offset < AX88796C_REGDUMP_LEN; offset += 2) {
- if (!test_bit(offset / 2, ax88796c_no_regs_mask))
- *p = AX_READ(&ax_local->ax_spi, offset);
- p++;
- }
- mutex_unlock(&ax_local->spi_lock);
- for (i = 0; i < AX88796C_PHY_REGDUMP_LEN / 2; i++) {
- *p = phy_read(ax_local->phydev, i);
- p++;
- }
- }
- static void
- ax88796c_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
- {
- switch (stringset) {
- case ETH_SS_PRIV_FLAGS:
- memcpy(data, ax88796c_priv_flag_names,
- sizeof(ax88796c_priv_flag_names));
- break;
- }
- }
- static int
- ax88796c_get_sset_count(struct net_device *ndev, int stringset)
- {
- int ret = 0;
- switch (stringset) {
- case ETH_SS_PRIV_FLAGS:
- ret = ARRAY_SIZE(ax88796c_priv_flag_names);
- break;
- default:
- ret = -EOPNOTSUPP;
- }
- return ret;
- }
- static int ax88796c_set_priv_flags(struct net_device *ndev, u32 flags)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- if (flags & ~AX_PRIV_FLAGS_MASK)
- return -EOPNOTSUPP;
- if ((ax_local->priv_flags ^ flags) & AX_CAP_COMP)
- if (netif_running(ndev))
- return -EBUSY;
- ax_local->priv_flags = flags;
- return 0;
- }
- static u32 ax88796c_get_priv_flags(struct net_device *ndev)
- {
- struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
- return ax_local->priv_flags;
- }
- int ax88796c_mdio_read(struct mii_bus *mdiobus, int phy_id, int loc)
- {
- struct ax88796c_device *ax_local = mdiobus->priv;
- int ret;
- mutex_lock(&ax_local->spi_lock);
- AX_WRITE(&ax_local->ax_spi, MDIOCR_RADDR(loc)
- | MDIOCR_FADDR(phy_id) | MDIOCR_READ, P2_MDIOCR);
- ret = read_poll_timeout(AX_READ, ret,
- (ret != 0),
- 0, jiffies_to_usecs(HZ / 100), false,
- &ax_local->ax_spi, P2_MDIOCR);
- if (!ret)
- ret = AX_READ(&ax_local->ax_spi, P2_MDIODR);
- mutex_unlock(&ax_local->spi_lock);
- return ret;
- }
- int
- ax88796c_mdio_write(struct mii_bus *mdiobus, int phy_id, int loc, u16 val)
- {
- struct ax88796c_device *ax_local = mdiobus->priv;
- int ret;
- mutex_lock(&ax_local->spi_lock);
- AX_WRITE(&ax_local->ax_spi, val, P2_MDIODR);
- AX_WRITE(&ax_local->ax_spi,
- MDIOCR_RADDR(loc) | MDIOCR_FADDR(phy_id)
- | MDIOCR_WRITE, P2_MDIOCR);
- ret = read_poll_timeout(AX_READ, ret,
- ((ret & MDIOCR_VALID) != 0), 0,
- jiffies_to_usecs(HZ / 100), false,
- &ax_local->ax_spi, P2_MDIOCR);
- mutex_unlock(&ax_local->spi_lock);
- return ret;
- }
- const struct ethtool_ops ax88796c_ethtool_ops = {
- .get_drvinfo = ax88796c_get_drvinfo,
- .get_link = ethtool_op_get_link,
- .get_msglevel = ax88796c_get_msglevel,
- .set_msglevel = ax88796c_set_msglevel,
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
- .nway_reset = phy_ethtool_nway_reset,
- .get_pauseparam = ax88796c_get_pauseparam,
- .set_pauseparam = ax88796c_set_pauseparam,
- .get_regs_len = ax88796c_get_regs_len,
- .get_regs = ax88796c_get_regs,
- .get_strings = ax88796c_get_strings,
- .get_sset_count = ax88796c_get_sset_count,
- .get_priv_flags = ax88796c_get_priv_flags,
- .set_priv_flags = ax88796c_set_priv_flags,
- };
- int ax88796c_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
- {
- int ret;
- ret = phy_mii_ioctl(ndev->phydev, ifr, cmd);
- return ret;
- }
|