File Index
Files and directories
Sign in
FILE

lan865x.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/microchip/lan865x/lan865x.c C
  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
 39struct 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
 46static 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
 55static 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
 84static 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
 89static 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
111static u32 get_address_bit(u8 addr[ETH_ALEN], u32 bit)
112{
113	return ((addr[bit / 8]) >> (bit % 8)) & 1;
114}
115
116static 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
132static 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
164static 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
186static 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
205static 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
244static 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
251static 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
259static 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
274static 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
290static 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
305static 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
323static 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
333static 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
411oa_tc6_exit:
412	oa_tc6_exit(priv->tc6);
413free_netdev:
414	free_netdev(priv->netdev);
415	return ret;
416}
417
418static 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
428static const struct spi_device_id lan865x_ids[] = {
429	{ .name = "lan8650" },
430	{ .name = "lan8651" },
431	{},
432};
433MODULE_DEVICE_TABLE(spi, lan865x_ids);
434
435static const struct of_device_id lan865x_dt_ids[] = {
436	{ .compatible = "microchip,lan8650" },
437	{ .compatible = "microchip,lan8651" },
438	{ /* Sentinel */ }
439};
440MODULE_DEVICE_TABLE(of, lan865x_dt_ids);
441
442static 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};
451module_spi_driver(lan865x_driver);
452
453MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver");
454MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>");
455MODULE_LICENSE("GPL");
~/ Read-only file preview.