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