Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  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	strscpy(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}