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(phydev: ax_local->phydev, rx: pause->tx_pause, |
71 | tx: pause->rx_pause); |
72 | } else { |
73 | int maccr = 0; |
74 | |
75 | phy_set_asym_pause(phydev: ax_local->phydev, rx: 0, tx: 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_spi: &ax_local->ax_spi, P0_MACCR) & |
82 | ~(MACCR_TXFC_ENABLE | MACCR_RXFC_ENABLE); |
83 | AX_WRITE(ax_spi: &ax_local->ax_spi, value: maccr, P0_MACCR); |
84 | |
85 | mutex_unlock(lock: &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_spi: &ax_local->ax_spi, offset); |
110 | p++; |
111 | } |
112 | |
113 | mutex_unlock(lock: &ax_local->spi_lock); |
114 | |
115 | for (i = 0; i < AX88796C_PHY_REGDUMP_LEN / 2; i++) { |
116 | *p = phy_read(phydev: ax_local->phydev, regnum: 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(dev: 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_spi: &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_spi: &ax_local->ax_spi, P2_MDIODR); |
186 | |
187 | mutex_unlock(lock: &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_spi: &ax_local->ax_spi, value: val, P2_MDIODR); |
200 | |
201 | AX_WRITE(ax_spi: &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(lock: &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(phydev: ndev->phydev, ifr, cmd); |
237 | |
238 | return ret; |
239 | } |
240 | |