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