Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 35struct 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
 42static 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
 51static 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
 80static 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
 85static 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
107static u32 get_address_bit(u8 addr[ETH_ALEN], u32 bit)
108{
109	return ((addr[bit / 8]) >> (bit % 8)) & 1;
110}
111
112static 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
128static 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
160static 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
182static 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
201static 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
240static 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
247static 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
255static 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
270static 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
286static 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
301static 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
317static 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
325static 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
388oa_tc6_exit:
389	oa_tc6_exit(priv->tc6);
390free_netdev:
391	free_netdev(priv->netdev);
392	return ret;
393}
394
395static 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
405static const struct spi_device_id spidev_spi_ids[] = {
406	{ .name = "lan8650" },
407	{},
408};
409
410static const struct of_device_id lan865x_dt_ids[] = {
411	{ .compatible = "microchip,lan8650" },
412	{ /* Sentinel */ }
413};
414MODULE_DEVICE_TABLE(of, lan865x_dt_ids);
415
416static 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};
425module_spi_driver(lan865x_driver);
426
427MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver");
428MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>");
429MODULE_LICENSE("GPL");