xref: /linux/drivers/net/ethernet/microchip/lan865x/lan865x.c (revision c1ead4b4dfe0f643cfc66571ca7d2fa332eddd35)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Microchip's LAN865x 10BASE-T1S MAC-PHY driver
4  *
5  * Author: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
6  */
7 
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/phy.h>
11 #include <linux/oa_tc6.h>
12 
13 #define DRV_NAME			"lan8650"
14 
15 /* MAC Network Control Register */
16 #define LAN865X_REG_MAC_NET_CTL		0x00010000
17 #define MAC_NET_CTL_TXEN		BIT(3) /* Transmit Enable */
18 #define MAC_NET_CTL_RXEN		BIT(2) /* Receive Enable */
19 
20 /* MAC Network Configuration Reg */
21 #define LAN865X_REG_MAC_NET_CFG		0x00010001
22 #define MAC_NET_CFG_PROMISCUOUS_MODE	BIT(4)
23 #define MAC_NET_CFG_MULTICAST_MODE	BIT(6)
24 #define MAC_NET_CFG_UNICAST_MODE	BIT(7)
25 
26 /* MAC Hash Register Bottom */
27 #define LAN865X_REG_MAC_L_HASH		0x00010020
28 /* MAC Hash Register Top */
29 #define LAN865X_REG_MAC_H_HASH		0x00010021
30 /* MAC Specific Addr 1 Bottom Reg */
31 #define LAN865X_REG_MAC_L_SADDR1	0x00010022
32 /* MAC Specific Addr 1 Top Reg */
33 #define LAN865X_REG_MAC_H_SADDR1	0x00010023
34 
35 /* MAC TSU Timer Increment Register */
36 #define LAN865X_REG_MAC_TSU_TIMER_INCR		0x00010077
37 #define MAC_TSU_TIMER_INCR_COUNT_NANOSECONDS	0x0028
38 
39 struct lan865x_priv {
40 	struct work_struct multicast_work;
41 	struct net_device *netdev;
42 	struct spi_device *spi;
43 	struct oa_tc6 *tc6;
44 };
45 
lan865x_set_hw_macaddr_low_bytes(struct oa_tc6 * tc6,const u8 * mac)46 static int lan865x_set_hw_macaddr_low_bytes(struct oa_tc6 *tc6, const u8 *mac)
47 {
48 	u32 regval;
49 
50 	regval = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0];
51 
52 	return oa_tc6_write_register(tc6, LAN865X_REG_MAC_L_SADDR1, regval);
53 }
54 
lan865x_set_hw_macaddr(struct lan865x_priv * priv,const u8 * mac)55 static int lan865x_set_hw_macaddr(struct lan865x_priv *priv, const u8 *mac)
56 {
57 	int restore_ret;
58 	u32 regval;
59 	int ret;
60 
61 	/* Configure MAC address low bytes */
62 	ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6, mac);
63 	if (ret)
64 		return ret;
65 
66 	/* Prepare and configure MAC address high bytes */
67 	regval = (mac[5] << 8) | mac[4];
68 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_SADDR1,
69 				    regval);
70 	if (!ret)
71 		return 0;
72 
73 	/* Restore the old MAC address low bytes from netdev if the new MAC
74 	 * address high bytes setting failed.
75 	 */
76 	restore_ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6,
77 						       priv->netdev->dev_addr);
78 	if (restore_ret)
79 		return restore_ret;
80 
81 	return ret;
82 }
83 
84 static const struct ethtool_ops lan865x_ethtool_ops = {
85 	.get_link_ksettings = phy_ethtool_get_link_ksettings,
86 	.set_link_ksettings = phy_ethtool_set_link_ksettings,
87 };
88 
lan865x_set_mac_address(struct net_device * netdev,void * addr)89 static int lan865x_set_mac_address(struct net_device *netdev, void *addr)
90 {
91 	struct lan865x_priv *priv = netdev_priv(netdev);
92 	struct sockaddr *address = addr;
93 	int ret;
94 
95 	ret = eth_prepare_mac_addr_change(netdev, addr);
96 	if (ret < 0)
97 		return ret;
98 
99 	if (ether_addr_equal(address->sa_data, netdev->dev_addr))
100 		return 0;
101 
102 	ret = lan865x_set_hw_macaddr(priv, address->sa_data);
103 	if (ret)
104 		return ret;
105 
106 	eth_commit_mac_addr_change(netdev, addr);
107 
108 	return 0;
109 }
110 
get_address_bit(u8 addr[ETH_ALEN],u32 bit)111 static u32 get_address_bit(u8 addr[ETH_ALEN], u32 bit)
112 {
113 	return ((addr[bit / 8]) >> (bit % 8)) & 1;
114 }
115 
lan865x_hash(u8 addr[ETH_ALEN])116 static u32 lan865x_hash(u8 addr[ETH_ALEN])
117 {
118 	u32 hash_index = 0;
119 
120 	for (int i = 0; i < 6; i++) {
121 		u32 hash = 0;
122 
123 		for (int j = 0; j < 8; j++)
124 			hash ^= get_address_bit(addr, (j * 6) + i);
125 
126 		hash_index |= (hash << i);
127 	}
128 
129 	return hash_index;
130 }
131 
lan865x_set_specific_multicast_addr(struct lan865x_priv * priv)132 static int lan865x_set_specific_multicast_addr(struct lan865x_priv *priv)
133 {
134 	struct netdev_hw_addr *ha;
135 	u32 hash_lo = 0;
136 	u32 hash_hi = 0;
137 	int ret;
138 
139 	netdev_for_each_mc_addr(ha, priv->netdev) {
140 		u32 bit_num = lan865x_hash(ha->addr);
141 
142 		if (bit_num >= BIT(5))
143 			hash_hi |= (1 << (bit_num - BIT(5)));
144 		else
145 			hash_lo |= (1 << bit_num);
146 	}
147 
148 	/* Enabling specific multicast addresses */
149 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, hash_hi);
150 	if (ret) {
151 		netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n",
152 			   ret);
153 		return ret;
154 	}
155 
156 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, hash_lo);
157 	if (ret)
158 		netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n",
159 			   ret);
160 
161 	return ret;
162 }
163 
lan865x_set_all_multicast_addr(struct lan865x_priv * priv)164 static int lan865x_set_all_multicast_addr(struct lan865x_priv *priv)
165 {
166 	int ret;
167 
168 	/* Enabling all multicast addresses */
169 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH,
170 				    0xffffffff);
171 	if (ret) {
172 		netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n",
173 			   ret);
174 		return ret;
175 	}
176 
177 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH,
178 				    0xffffffff);
179 	if (ret)
180 		netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n",
181 			   ret);
182 
183 	return ret;
184 }
185 
lan865x_clear_all_multicast_addr(struct lan865x_priv * priv)186 static int lan865x_clear_all_multicast_addr(struct lan865x_priv *priv)
187 {
188 	int ret;
189 
190 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, 0);
191 	if (ret) {
192 		netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n",
193 			   ret);
194 		return ret;
195 	}
196 
197 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, 0);
198 	if (ret)
199 		netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n",
200 			   ret);
201 
202 	return ret;
203 }
204 
lan865x_multicast_work_handler(struct work_struct * work)205 static void lan865x_multicast_work_handler(struct work_struct *work)
206 {
207 	struct lan865x_priv *priv = container_of(work, struct lan865x_priv,
208 						 multicast_work);
209 	u32 regval = 0;
210 	int ret;
211 
212 	if (priv->netdev->flags & IFF_PROMISC) {
213 		/* Enabling promiscuous mode */
214 		regval |= MAC_NET_CFG_PROMISCUOUS_MODE;
215 		regval &= (~MAC_NET_CFG_MULTICAST_MODE);
216 		regval &= (~MAC_NET_CFG_UNICAST_MODE);
217 	} else if (priv->netdev->flags & IFF_ALLMULTI) {
218 		/* Enabling all multicast mode */
219 		if (lan865x_set_all_multicast_addr(priv))
220 			return;
221 
222 		regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE);
223 		regval |= MAC_NET_CFG_MULTICAST_MODE;
224 		regval &= (~MAC_NET_CFG_UNICAST_MODE);
225 	} else if (!netdev_mc_empty(priv->netdev)) {
226 		/* Enabling specific multicast mode */
227 		if (lan865x_set_specific_multicast_addr(priv))
228 			return;
229 
230 		regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE);
231 		regval |= MAC_NET_CFG_MULTICAST_MODE;
232 		regval &= (~MAC_NET_CFG_UNICAST_MODE);
233 	} else {
234 		/* Enabling local mac address only */
235 		if (lan865x_clear_all_multicast_addr(priv))
236 			return;
237 	}
238 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CFG, regval);
239 	if (ret)
240 		netdev_err(priv->netdev, "Failed to enable promiscuous/multicast/normal mode: %d\n",
241 			   ret);
242 }
243 
lan865x_set_multicast_list(struct net_device * netdev)244 static void lan865x_set_multicast_list(struct net_device *netdev)
245 {
246 	struct lan865x_priv *priv = netdev_priv(netdev);
247 
248 	schedule_work(&priv->multicast_work);
249 }
250 
lan865x_send_packet(struct sk_buff * skb,struct net_device * netdev)251 static netdev_tx_t lan865x_send_packet(struct sk_buff *skb,
252 				       struct net_device *netdev)
253 {
254 	struct lan865x_priv *priv = netdev_priv(netdev);
255 
256 	return oa_tc6_start_xmit(priv->tc6, skb);
257 }
258 
lan865x_hw_disable(struct lan865x_priv * priv)259 static int lan865x_hw_disable(struct lan865x_priv *priv)
260 {
261 	u32 regval;
262 
263 	if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, &regval))
264 		return -ENODEV;
265 
266 	regval &= ~(MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN);
267 
268 	if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval))
269 		return -ENODEV;
270 
271 	return 0;
272 }
273 
lan865x_net_close(struct net_device * netdev)274 static int lan865x_net_close(struct net_device *netdev)
275 {
276 	struct lan865x_priv *priv = netdev_priv(netdev);
277 	int ret;
278 
279 	netif_stop_queue(netdev);
280 	phy_stop(netdev->phydev);
281 	ret = lan865x_hw_disable(priv);
282 	if (ret) {
283 		netdev_err(netdev, "Failed to disable the hardware: %d\n", ret);
284 		return ret;
285 	}
286 
287 	return 0;
288 }
289 
lan865x_hw_enable(struct lan865x_priv * priv)290 static int lan865x_hw_enable(struct lan865x_priv *priv)
291 {
292 	u32 regval;
293 
294 	if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, &regval))
295 		return -ENODEV;
296 
297 	regval |= MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN;
298 
299 	if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval))
300 		return -ENODEV;
301 
302 	return 0;
303 }
304 
lan865x_net_open(struct net_device * netdev)305 static int lan865x_net_open(struct net_device *netdev)
306 {
307 	struct lan865x_priv *priv = netdev_priv(netdev);
308 	int ret;
309 
310 	ret = lan865x_hw_enable(priv);
311 	if (ret) {
312 		netdev_err(netdev, "Failed to enable hardware: %d\n", ret);
313 		return ret;
314 	}
315 
316 	phy_start(netdev->phydev);
317 
318 	netif_start_queue(netdev);
319 
320 	return 0;
321 }
322 
323 static const struct net_device_ops lan865x_netdev_ops = {
324 	.ndo_open		= lan865x_net_open,
325 	.ndo_stop		= lan865x_net_close,
326 	.ndo_start_xmit		= lan865x_send_packet,
327 	.ndo_set_rx_mode	= lan865x_set_multicast_list,
328 	.ndo_set_mac_address	= lan865x_set_mac_address,
329 	.ndo_validate_addr	= eth_validate_addr,
330 	.ndo_eth_ioctl          = phy_do_ioctl_running,
331 };
332 
lan865x_probe(struct spi_device * spi)333 static int lan865x_probe(struct spi_device *spi)
334 {
335 	struct net_device *netdev;
336 	struct lan865x_priv *priv;
337 	int ret;
338 
339 	netdev = alloc_etherdev(sizeof(struct lan865x_priv));
340 	if (!netdev)
341 		return -ENOMEM;
342 
343 	priv = netdev_priv(netdev);
344 	priv->netdev = netdev;
345 	priv->spi = spi;
346 	spi_set_drvdata(spi, priv);
347 	INIT_WORK(&priv->multicast_work, lan865x_multicast_work_handler);
348 
349 	priv->tc6 = oa_tc6_init(spi, netdev);
350 	if (!priv->tc6) {
351 		ret = -ENODEV;
352 		goto free_netdev;
353 	}
354 
355 	/* LAN865x Rev.B0/B1 configuration parameters from AN1760
356 	 * As per the Configuration Application Note AN1760 published in the
357 	 * link, https://www.microchip.com/en-us/application-notes/an1760
358 	 * Revision F (DS60001760G - June 2024), configure the MAC to set time
359 	 * stamping at the end of the Start of Frame Delimiter (SFD) and set the
360 	 * Timer Increment reg to 40 ns to be used as a 25 MHz internal clock.
361 	 */
362 	ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_TSU_TIMER_INCR,
363 				    MAC_TSU_TIMER_INCR_COUNT_NANOSECONDS);
364 	if (ret) {
365 		dev_err(&spi->dev, "Failed to config TSU Timer Incr reg: %d\n",
366 			ret);
367 		goto oa_tc6_exit;
368 	}
369 
370 	/* As per the point s3 in the below errata, SPI receive Ethernet frame
371 	 * transfer may halt when starting the next frame in the same data block
372 	 * (chunk) as the end of a previous frame. The RFA field should be
373 	 * configured to 01b or 10b for proper operation. In these modes, only
374 	 * one receive Ethernet frame will be placed in a single data block.
375 	 * When the RFA field is written to 01b, received frames will be forced
376 	 * to only start in the first word of the data block payload (SWO=0). As
377 	 * recommended, enable zero align receive frame feature for proper
378 	 * operation.
379 	 *
380 	 * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/Errata/LAN8650-1-Errata-80001075.pdf
381 	 */
382 	ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6);
383 	if (ret) {
384 		dev_err(&spi->dev, "Failed to set ZARFE: %d\n", ret);
385 		goto oa_tc6_exit;
386 	}
387 
388 	/* Get the MAC address from the SPI device tree node */
389 	if (device_get_ethdev_address(&spi->dev, netdev))
390 		eth_hw_addr_random(netdev);
391 
392 	ret = lan865x_set_hw_macaddr(priv, netdev->dev_addr);
393 	if (ret) {
394 		dev_err(&spi->dev, "Failed to configure MAC: %d\n", ret);
395 		goto oa_tc6_exit;
396 	}
397 
398 	netdev->if_port = IF_PORT_10BASET;
399 	netdev->irq = spi->irq;
400 	netdev->netdev_ops = &lan865x_netdev_ops;
401 	netdev->ethtool_ops = &lan865x_ethtool_ops;
402 
403 	ret = register_netdev(netdev);
404 	if (ret) {
405 		dev_err(&spi->dev, "Register netdev failed (ret = %d)", ret);
406 		goto oa_tc6_exit;
407 	}
408 
409 	return 0;
410 
411 oa_tc6_exit:
412 	oa_tc6_exit(priv->tc6);
413 free_netdev:
414 	free_netdev(priv->netdev);
415 	return ret;
416 }
417 
lan865x_remove(struct spi_device * spi)418 static void lan865x_remove(struct spi_device *spi)
419 {
420 	struct lan865x_priv *priv = spi_get_drvdata(spi);
421 
422 	cancel_work_sync(&priv->multicast_work);
423 	unregister_netdev(priv->netdev);
424 	oa_tc6_exit(priv->tc6);
425 	free_netdev(priv->netdev);
426 }
427 
428 static const struct spi_device_id lan865x_ids[] = {
429 	{ .name = "lan8650" },
430 	{ .name = "lan8651" },
431 	{},
432 };
433 MODULE_DEVICE_TABLE(spi, lan865x_ids);
434 
435 static const struct of_device_id lan865x_dt_ids[] = {
436 	{ .compatible = "microchip,lan8650" },
437 	{ .compatible = "microchip,lan8651" },
438 	{ /* Sentinel */ }
439 };
440 MODULE_DEVICE_TABLE(of, lan865x_dt_ids);
441 
442 static struct spi_driver lan865x_driver = {
443 	.driver = {
444 		.name = DRV_NAME,
445 		.of_match_table = lan865x_dt_ids,
446 	 },
447 	.probe = lan865x_probe,
448 	.remove = lan865x_remove,
449 	.id_table = lan865x_ids,
450 };
451 module_spi_driver(lan865x_driver);
452 
453 MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver");
454 MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>");
455 MODULE_LICENSE("GPL");
456