162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 ASIX Electronics Corporation
462306a36Sopenharmony_ci * Copyright (c) 2020 Samsung Electronics Co., Ltd.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * ASIX AX88796C SPI Fast Ethernet Linux driver
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt)	"ax88796c: " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bitmap.h>
1262306a36Sopenharmony_ci#include <linux/iopoll.h>
1362306a36Sopenharmony_ci#include <linux/phy.h>
1462306a36Sopenharmony_ci#include <linux/netdevice.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "ax88796c_main.h"
1762306a36Sopenharmony_ci#include "ax88796c_ioctl.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic const char ax88796c_priv_flag_names[][ETH_GSTRING_LEN] = {
2062306a36Sopenharmony_ci	"SPICompression",
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void
2462306a36Sopenharmony_ciax88796c_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	/* Inherit standard device info */
2762306a36Sopenharmony_ci	strncpy(info->driver, DRV_NAME, sizeof(info->driver));
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic u32 ax88796c_get_msglevel(struct net_device *ndev)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return ax_local->msg_enable;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void ax88796c_set_msglevel(struct net_device *ndev, u32 level)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ax_local->msg_enable = level;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void
4562306a36Sopenharmony_ciax88796c_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	pause->tx_pause = !!(ax_local->flowctrl & AX_FC_TX);
5062306a36Sopenharmony_ci	pause->rx_pause = !!(ax_local->flowctrl & AX_FC_RX);
5162306a36Sopenharmony_ci	pause->autoneg = (ax_local->flowctrl & AX_FC_ANEG) ?
5262306a36Sopenharmony_ci		AUTONEG_ENABLE :
5362306a36Sopenharmony_ci		AUTONEG_DISABLE;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int
5762306a36Sopenharmony_ciax88796c_set_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
6062306a36Sopenharmony_ci	int fc;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* The following logic comes from phylink_ethtool_set_pauseparam() */
6362306a36Sopenharmony_ci	fc = pause->tx_pause ? AX_FC_TX : 0;
6462306a36Sopenharmony_ci	fc |= pause->rx_pause ? AX_FC_RX : 0;
6562306a36Sopenharmony_ci	fc |= pause->autoneg ? AX_FC_ANEG : 0;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	ax_local->flowctrl = fc;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (pause->autoneg) {
7062306a36Sopenharmony_ci		phy_set_asym_pause(ax_local->phydev, pause->tx_pause,
7162306a36Sopenharmony_ci				   pause->rx_pause);
7262306a36Sopenharmony_ci	} else {
7362306a36Sopenharmony_ci		int maccr = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		phy_set_asym_pause(ax_local->phydev, 0, 0);
7662306a36Sopenharmony_ci		maccr |= (ax_local->flowctrl & AX_FC_RX) ? MACCR_RXFC_ENABLE : 0;
7762306a36Sopenharmony_ci		maccr |= (ax_local->flowctrl & AX_FC_TX) ? MACCR_TXFC_ENABLE : 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		mutex_lock(&ax_local->spi_lock);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		maccr |= AX_READ(&ax_local->ax_spi, P0_MACCR) &
8262306a36Sopenharmony_ci			~(MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE);
8362306a36Sopenharmony_ci		AX_WRITE(&ax_local->ax_spi, maccr, P0_MACCR);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		mutex_unlock(&ax_local->spi_lock);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int ax88796c_get_regs_len(struct net_device *ndev)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	return AX88796C_REGDUMP_LEN + AX88796C_PHY_REGDUMP_LEN;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void
9762306a36Sopenharmony_ciax88796c_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *_p)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
10062306a36Sopenharmony_ci	int offset, i;
10162306a36Sopenharmony_ci	u16 *p = _p;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	memset(p, 0, ax88796c_get_regs_len(ndev));
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	for (offset = 0; offset < AX88796C_REGDUMP_LEN; offset += 2) {
10862306a36Sopenharmony_ci		if (!test_bit(offset / 2, ax88796c_no_regs_mask))
10962306a36Sopenharmony_ci			*p = AX_READ(&ax_local->ax_spi, offset);
11062306a36Sopenharmony_ci		p++;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	for (i = 0; i < AX88796C_PHY_REGDUMP_LEN / 2; i++) {
11662306a36Sopenharmony_ci		*p = phy_read(ax_local->phydev, i);
11762306a36Sopenharmony_ci		p++;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void
12262306a36Sopenharmony_ciax88796c_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	switch (stringset) {
12562306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
12662306a36Sopenharmony_ci		memcpy(data, ax88796c_priv_flag_names,
12762306a36Sopenharmony_ci		       sizeof(ax88796c_priv_flag_names));
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int
13362306a36Sopenharmony_ciax88796c_get_sset_count(struct net_device *ndev, int stringset)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int ret = 0;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	switch (stringset) {
13862306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
13962306a36Sopenharmony_ci		ret = ARRAY_SIZE(ax88796c_priv_flag_names);
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	default:
14262306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return ret;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int ax88796c_set_priv_flags(struct net_device *ndev, u32 flags)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (flags & ~AX_PRIV_FLAGS_MASK)
15362306a36Sopenharmony_ci		return -EOPNOTSUPP;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if ((ax_local->priv_flags ^ flags) & AX_CAP_COMP)
15662306a36Sopenharmony_ci		if (netif_running(ndev))
15762306a36Sopenharmony_ci			return -EBUSY;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ax_local->priv_flags = flags;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic u32 ax88796c_get_priv_flags(struct net_device *ndev)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct ax88796c_device *ax_local = to_ax88796c_device(ndev);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return ax_local->priv_flags;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciint ax88796c_mdio_read(struct mii_bus *mdiobus, int phy_id, int loc)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct ax88796c_device *ax_local = mdiobus->priv;
17462306a36Sopenharmony_ci	int ret;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
17762306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, MDIOCR_RADDR(loc)
17862306a36Sopenharmony_ci			| MDIOCR_FADDR(phy_id) | MDIOCR_READ, P2_MDIOCR);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = read_poll_timeout(AX_READ, ret,
18162306a36Sopenharmony_ci				(ret != 0),
18262306a36Sopenharmony_ci				0, jiffies_to_usecs(HZ / 100), false,
18362306a36Sopenharmony_ci				&ax_local->ax_spi, P2_MDIOCR);
18462306a36Sopenharmony_ci	if (!ret)
18562306a36Sopenharmony_ci		ret = AX_READ(&ax_local->ax_spi, P2_MDIODR);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return ret;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ciint
19362306a36Sopenharmony_ciax88796c_mdio_write(struct mii_bus *mdiobus, int phy_id, int loc, u16 val)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct ax88796c_device *ax_local = mdiobus->priv;
19662306a36Sopenharmony_ci	int ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	mutex_lock(&ax_local->spi_lock);
19962306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi, val, P2_MDIODR);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	AX_WRITE(&ax_local->ax_spi,
20262306a36Sopenharmony_ci		 MDIOCR_RADDR(loc) | MDIOCR_FADDR(phy_id)
20362306a36Sopenharmony_ci		 | MDIOCR_WRITE, P2_MDIOCR);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = read_poll_timeout(AX_READ, ret,
20662306a36Sopenharmony_ci				((ret & MDIOCR_VALID) != 0), 0,
20762306a36Sopenharmony_ci				jiffies_to_usecs(HZ / 100), false,
20862306a36Sopenharmony_ci				&ax_local->ax_spi, P2_MDIOCR);
20962306a36Sopenharmony_ci	mutex_unlock(&ax_local->spi_lock);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return ret;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ciconst struct ethtool_ops ax88796c_ethtool_ops = {
21562306a36Sopenharmony_ci	.get_drvinfo		= ax88796c_get_drvinfo,
21662306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
21762306a36Sopenharmony_ci	.get_msglevel		= ax88796c_get_msglevel,
21862306a36Sopenharmony_ci	.set_msglevel		= ax88796c_set_msglevel,
21962306a36Sopenharmony_ci	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
22062306a36Sopenharmony_ci	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
22162306a36Sopenharmony_ci	.nway_reset		= phy_ethtool_nway_reset,
22262306a36Sopenharmony_ci	.get_pauseparam		= ax88796c_get_pauseparam,
22362306a36Sopenharmony_ci	.set_pauseparam		= ax88796c_set_pauseparam,
22462306a36Sopenharmony_ci	.get_regs_len		= ax88796c_get_regs_len,
22562306a36Sopenharmony_ci	.get_regs		= ax88796c_get_regs,
22662306a36Sopenharmony_ci	.get_strings		= ax88796c_get_strings,
22762306a36Sopenharmony_ci	.get_sset_count		= ax88796c_get_sset_count,
22862306a36Sopenharmony_ci	.get_priv_flags		= ax88796c_get_priv_flags,
22962306a36Sopenharmony_ci	.set_priv_flags		= ax88796c_set_priv_flags,
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciint ax88796c_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	int ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ret = phy_mii_ioctl(ndev->phydev, ifr, cmd);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
240