xref: /linux/drivers/net/ethernet/asix/ax88796c_ioctl.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
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 
19 static const char ax88796c_priv_flag_names[][ETH_GSTRING_LEN] = {
20 	"SPICompression",
21 };
22 
23 static void
24 ax88796c_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 
30 static 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 
37 static 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 
44 static void
45 ax88796c_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 
56 static int
57 ax88796c_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 
91 static int ax88796c_get_regs_len(struct net_device *ndev)
92 {
93 	return AX88796C_REGDUMP_LEN + AX88796C_PHY_REGDUMP_LEN;
94 }
95 
96 static void
97 ax88796c_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 
121 static void
122 ax88796c_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 
132 static int
133 ax88796c_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 
148 static 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 
164 static 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 
171 int 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 
192 int
193 ax88796c_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 
214 const 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 
232 int 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 }
240