1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ASIX AX88172A based USB 2.0 Ethernet Devices |
4 | * Copyright (C) 2012 OMICRON electronics GmbH |
5 | * |
6 | * Supports external PHYs via phylib. Based on the driver for the |
7 | * AX88772. Original copyrights follow: |
8 | * |
9 | * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> |
10 | * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> |
11 | * Copyright (C) 2006 James Painter <jamie.painter@iname.com> |
12 | * Copyright (c) 2002-2003 TiVo Inc. |
13 | */ |
14 | |
15 | #include "asix.h" |
16 | #include <linux/phy.h> |
17 | |
18 | struct ax88172a_private { |
19 | struct mii_bus *mdio; |
20 | struct phy_device *phydev; |
21 | char phy_name[20]; |
22 | u16 phy_addr; |
23 | u16 oldmode; |
24 | int use_embdphy; |
25 | struct asix_rx_fixup_info rx_fixup_info; |
26 | }; |
27 | |
28 | /* set MAC link settings according to information from phylib */ |
29 | static void ax88172a_adjust_link(struct net_device *netdev) |
30 | { |
31 | struct phy_device *phydev = netdev->phydev; |
32 | struct usbnet *dev = netdev_priv(dev: netdev); |
33 | struct ax88172a_private *priv = dev->driver_priv; |
34 | u16 mode = 0; |
35 | |
36 | if (phydev->link) { |
37 | mode = AX88772_MEDIUM_DEFAULT; |
38 | |
39 | if (phydev->duplex == DUPLEX_HALF) |
40 | mode &= ~AX_MEDIUM_FD; |
41 | |
42 | if (phydev->speed != SPEED_100) |
43 | mode &= ~AX_MEDIUM_PS; |
44 | } |
45 | |
46 | if (mode != priv->oldmode) { |
47 | asix_write_medium_mode(dev, mode, in_pm: 0); |
48 | priv->oldmode = mode; |
49 | netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n" , |
50 | phydev->speed, phydev->duplex, mode); |
51 | phy_print_status(phydev); |
52 | } |
53 | } |
54 | |
55 | static void ax88172a_status(struct usbnet *dev, struct urb *urb) |
56 | { |
57 | /* link changes are detected by polling the phy */ |
58 | } |
59 | |
60 | /* use phylib infrastructure */ |
61 | static int ax88172a_init_mdio(struct usbnet *dev) |
62 | { |
63 | struct ax88172a_private *priv = dev->driver_priv; |
64 | int ret; |
65 | |
66 | priv->mdio = mdiobus_alloc(); |
67 | if (!priv->mdio) { |
68 | netdev_err(dev: dev->net, format: "Could not allocate MDIO bus\n" ); |
69 | return -ENOMEM; |
70 | } |
71 | |
72 | priv->mdio->priv = (void *)dev; |
73 | priv->mdio->read = &asix_mdio_bus_read; |
74 | priv->mdio->write = &asix_mdio_bus_write; |
75 | priv->mdio->name = "Asix MDIO Bus" ; |
76 | /* mii bus name is usb-<usb bus number>-<usb device number> */ |
77 | snprintf(buf: priv->mdio->id, MII_BUS_ID_SIZE, fmt: "usb-%03d:%03d" , |
78 | dev->udev->bus->busnum, dev->udev->devnum); |
79 | |
80 | ret = mdiobus_register(priv->mdio); |
81 | if (ret) { |
82 | netdev_err(dev: dev->net, format: "Could not register MDIO bus\n" ); |
83 | goto mfree; |
84 | } |
85 | |
86 | netdev_info(dev: dev->net, format: "registered mdio bus %s\n" , priv->mdio->id); |
87 | return 0; |
88 | |
89 | mfree: |
90 | mdiobus_free(bus: priv->mdio); |
91 | return ret; |
92 | } |
93 | |
94 | static void ax88172a_remove_mdio(struct usbnet *dev) |
95 | { |
96 | struct ax88172a_private *priv = dev->driver_priv; |
97 | |
98 | netdev_info(dev: dev->net, format: "deregistering mdio bus %s\n" , priv->mdio->id); |
99 | mdiobus_unregister(bus: priv->mdio); |
100 | mdiobus_free(bus: priv->mdio); |
101 | } |
102 | |
103 | static const struct net_device_ops ax88172a_netdev_ops = { |
104 | .ndo_open = usbnet_open, |
105 | .ndo_stop = usbnet_stop, |
106 | .ndo_start_xmit = usbnet_start_xmit, |
107 | .ndo_tx_timeout = usbnet_tx_timeout, |
108 | .ndo_change_mtu = usbnet_change_mtu, |
109 | .ndo_get_stats64 = dev_get_tstats64, |
110 | .ndo_set_mac_address = asix_set_mac_address, |
111 | .ndo_validate_addr = eth_validate_addr, |
112 | .ndo_eth_ioctl = phy_do_ioctl_running, |
113 | .ndo_set_rx_mode = asix_set_multicast, |
114 | }; |
115 | |
116 | static const struct ethtool_ops ax88172a_ethtool_ops = { |
117 | .get_drvinfo = asix_get_drvinfo, |
118 | .get_link = usbnet_get_link, |
119 | .get_msglevel = usbnet_get_msglevel, |
120 | .set_msglevel = usbnet_set_msglevel, |
121 | .get_wol = asix_get_wol, |
122 | .set_wol = asix_set_wol, |
123 | .get_eeprom_len = asix_get_eeprom_len, |
124 | .get_eeprom = asix_get_eeprom, |
125 | .set_eeprom = asix_set_eeprom, |
126 | .nway_reset = phy_ethtool_nway_reset, |
127 | .get_link_ksettings = phy_ethtool_get_link_ksettings, |
128 | .set_link_ksettings = phy_ethtool_set_link_ksettings, |
129 | }; |
130 | |
131 | static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy) |
132 | { |
133 | int ret; |
134 | |
135 | ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm: 0); |
136 | if (ret < 0) |
137 | goto err; |
138 | |
139 | msleep(msecs: 150); |
140 | ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm: 0); |
141 | if (ret < 0) |
142 | goto err; |
143 | |
144 | msleep(msecs: 150); |
145 | |
146 | ret = asix_sw_reset(dev, flags: embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD, |
147 | in_pm: 0); |
148 | if (ret < 0) |
149 | goto err; |
150 | |
151 | return 0; |
152 | |
153 | err: |
154 | return ret; |
155 | } |
156 | |
157 | |
158 | static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf) |
159 | { |
160 | int ret; |
161 | u8 buf[ETH_ALEN]; |
162 | struct ax88172a_private *priv; |
163 | |
164 | ret = usbnet_get_endpoints(dev, intf); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | priv = kzalloc(size: sizeof(*priv), GFP_KERNEL); |
169 | if (!priv) |
170 | return -ENOMEM; |
171 | |
172 | dev->driver_priv = priv; |
173 | |
174 | /* Get the MAC address */ |
175 | ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, value: 0, index: 0, ETH_ALEN, data: buf, in_pm: 0); |
176 | if (ret < ETH_ALEN) { |
177 | netdev_err(dev: dev->net, format: "Failed to read MAC address: %d\n" , ret); |
178 | ret = -EIO; |
179 | goto free; |
180 | } |
181 | eth_hw_addr_set(dev: dev->net, addr: buf); |
182 | |
183 | dev->net->netdev_ops = &ax88172a_netdev_ops; |
184 | dev->net->ethtool_ops = &ax88172a_ethtool_ops; |
185 | |
186 | /* are we using the internal or the external phy? */ |
187 | ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, value: 0, index: 0, size: 1, data: buf, in_pm: 0); |
188 | if (ret < 0) { |
189 | netdev_err(dev: dev->net, format: "Failed to read software interface selection register: %d\n" , |
190 | ret); |
191 | goto free; |
192 | } |
193 | |
194 | netdev_dbg(dev->net, "AX_CMD_SW_PHY_STATUS = 0x%02x\n" , buf[0]); |
195 | switch (buf[0] & AX_PHY_SELECT_MASK) { |
196 | case AX_PHY_SELECT_INTERNAL: |
197 | netdev_dbg(dev->net, "use internal phy\n" ); |
198 | priv->use_embdphy = 1; |
199 | break; |
200 | case AX_PHY_SELECT_EXTERNAL: |
201 | netdev_dbg(dev->net, "use external phy\n" ); |
202 | priv->use_embdphy = 0; |
203 | break; |
204 | default: |
205 | netdev_err(dev: dev->net, format: "Interface mode not supported by driver\n" ); |
206 | ret = -ENOTSUPP; |
207 | goto free; |
208 | } |
209 | |
210 | ret = asix_read_phy_addr(dev, internal: priv->use_embdphy); |
211 | if (ret < 0) |
212 | goto free; |
213 | |
214 | priv->phy_addr = ret; |
215 | |
216 | ax88172a_reset_phy(dev, embd_phy: priv->use_embdphy); |
217 | |
218 | /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ |
219 | if (dev->driver_info->flags & FLAG_FRAMING_AX) { |
220 | /* hard_mtu is still the default - the device does not support |
221 | jumbo eth frames */ |
222 | dev->rx_urb_size = 2048; |
223 | } |
224 | |
225 | /* init MDIO bus */ |
226 | ret = ax88172a_init_mdio(dev); |
227 | if (ret) |
228 | goto free; |
229 | |
230 | return 0; |
231 | |
232 | free: |
233 | kfree(objp: priv); |
234 | return ret; |
235 | } |
236 | |
237 | static int ax88172a_stop(struct usbnet *dev) |
238 | { |
239 | struct ax88172a_private *priv = dev->driver_priv; |
240 | |
241 | netdev_dbg(dev->net, "Stopping interface\n" ); |
242 | |
243 | if (priv->phydev) { |
244 | netdev_info(dev: dev->net, format: "Disconnecting from phy %s\n" , |
245 | priv->phy_name); |
246 | phy_stop(phydev: priv->phydev); |
247 | phy_disconnect(phydev: priv->phydev); |
248 | } |
249 | |
250 | return 0; |
251 | } |
252 | |
253 | static void ax88172a_unbind(struct usbnet *dev, struct usb_interface *intf) |
254 | { |
255 | struct ax88172a_private *priv = dev->driver_priv; |
256 | |
257 | ax88172a_remove_mdio(dev); |
258 | kfree(objp: priv); |
259 | } |
260 | |
261 | static int ax88172a_reset(struct usbnet *dev) |
262 | { |
263 | struct asix_data *data = (struct asix_data *)&dev->data; |
264 | struct ax88172a_private *priv = dev->driver_priv; |
265 | int ret; |
266 | u16 rx_ctl; |
267 | |
268 | ax88172a_reset_phy(dev, embd_phy: priv->use_embdphy); |
269 | |
270 | msleep(msecs: 150); |
271 | rx_ctl = asix_read_rx_ctl(dev, in_pm: 0); |
272 | netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n" , rx_ctl); |
273 | ret = asix_write_rx_ctl(dev, mode: 0x0000, in_pm: 0); |
274 | if (ret < 0) |
275 | goto out; |
276 | |
277 | rx_ctl = asix_read_rx_ctl(dev, in_pm: 0); |
278 | netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n" , rx_ctl); |
279 | |
280 | msleep(msecs: 150); |
281 | |
282 | ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, |
283 | AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, |
284 | AX88772_IPG2_DEFAULT, size: 0, NULL, in_pm: 0); |
285 | if (ret < 0) { |
286 | netdev_err(dev: dev->net, format: "Write IPG,IPG1,IPG2 failed: %d\n" , ret); |
287 | goto out; |
288 | } |
289 | |
290 | /* Rewrite MAC address */ |
291 | memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); |
292 | ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, value: 0, index: 0, ETH_ALEN, |
293 | data: data->mac_addr, in_pm: 0); |
294 | if (ret < 0) |
295 | goto out; |
296 | |
297 | /* Set RX_CTL to default values with 2k buffer, and enable cactus */ |
298 | ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm: 0); |
299 | if (ret < 0) |
300 | goto out; |
301 | |
302 | rx_ctl = asix_read_rx_ctl(dev, in_pm: 0); |
303 | netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n" , |
304 | rx_ctl); |
305 | |
306 | rx_ctl = asix_read_medium_status(dev, in_pm: 0); |
307 | netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n" , |
308 | rx_ctl); |
309 | |
310 | /* Connect to PHY */ |
311 | snprintf(buf: priv->phy_name, size: 20, PHY_ID_FMT, |
312 | priv->mdio->id, priv->phy_addr); |
313 | |
314 | priv->phydev = phy_connect(dev: dev->net, bus_id: priv->phy_name, |
315 | handler: &ax88172a_adjust_link, |
316 | interface: PHY_INTERFACE_MODE_MII); |
317 | if (IS_ERR(ptr: priv->phydev)) { |
318 | netdev_err(dev: dev->net, format: "Could not connect to PHY device %s\n" , |
319 | priv->phy_name); |
320 | ret = PTR_ERR(ptr: priv->phydev); |
321 | goto out; |
322 | } |
323 | |
324 | netdev_info(dev: dev->net, format: "Connected to phy %s\n" , priv->phy_name); |
325 | |
326 | /* During power-up, the AX88172A set the power down (BMCR_PDOWN) |
327 | * bit of the PHY. Bring the PHY up again. |
328 | */ |
329 | genphy_resume(phydev: priv->phydev); |
330 | phy_start(phydev: priv->phydev); |
331 | |
332 | return 0; |
333 | |
334 | out: |
335 | return ret; |
336 | |
337 | } |
338 | |
339 | static int ax88172a_rx_fixup(struct usbnet *dev, struct sk_buff *skb) |
340 | { |
341 | struct ax88172a_private *dp = dev->driver_priv; |
342 | struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; |
343 | |
344 | return asix_rx_fixup_internal(dev, skb, rx); |
345 | } |
346 | |
347 | const struct driver_info ax88172a_info = { |
348 | .description = "ASIX AX88172A USB 2.0 Ethernet" , |
349 | .bind = ax88172a_bind, |
350 | .reset = ax88172a_reset, |
351 | .stop = ax88172a_stop, |
352 | .unbind = ax88172a_unbind, |
353 | .status = ax88172a_status, |
354 | .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | |
355 | FLAG_MULTI_PACKET, |
356 | .rx_fixup = ax88172a_rx_fixup, |
357 | .tx_fixup = asix_tx_fixup, |
358 | }; |
359 | |