162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Aeroflex Gaisler GRETH 10/100/1G Ethernet MAC.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * 2005-2010 (c) Aeroflex Gaisler AB
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This driver supports GRETH 10/100 and GRETH 10/100/1G Ethernet MACs
862306a36Sopenharmony_ci * available in the GRLIB VHDL IP core library.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Full documentation of both cores can be found here:
1162306a36Sopenharmony_ci * https://www.gaisler.com/products/grlib/grip.pdf
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The Gigabit version supports scatter/gather DMA, any alignment of
1462306a36Sopenharmony_ci * buffers and checksum offloading.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Contributors: Kristoffer Glembo
1762306a36Sopenharmony_ci *               Daniel Hellstrom
1862306a36Sopenharmony_ci *               Marko Isomaki
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/uaccess.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/netdevice.h>
2662306a36Sopenharmony_ci#include <linux/etherdevice.h>
2762306a36Sopenharmony_ci#include <linux/ethtool.h>
2862306a36Sopenharmony_ci#include <linux/skbuff.h>
2962306a36Sopenharmony_ci#include <linux/io.h>
3062306a36Sopenharmony_ci#include <linux/crc32.h>
3162306a36Sopenharmony_ci#include <linux/mii.h>
3262306a36Sopenharmony_ci#include <linux/of.h>
3362306a36Sopenharmony_ci#include <linux/of_net.h>
3462306a36Sopenharmony_ci#include <linux/platform_device.h>
3562306a36Sopenharmony_ci#include <linux/slab.h>
3662306a36Sopenharmony_ci#include <asm/cacheflush.h>
3762306a36Sopenharmony_ci#include <asm/byteorder.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#ifdef CONFIG_SPARC
4062306a36Sopenharmony_ci#include <asm/idprom.h>
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include "greth.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define GRETH_DEF_MSG_ENABLE	  \
4662306a36Sopenharmony_ci	(NETIF_MSG_DRV		| \
4762306a36Sopenharmony_ci	 NETIF_MSG_PROBE	| \
4862306a36Sopenharmony_ci	 NETIF_MSG_LINK		| \
4962306a36Sopenharmony_ci	 NETIF_MSG_IFDOWN	| \
5062306a36Sopenharmony_ci	 NETIF_MSG_IFUP		| \
5162306a36Sopenharmony_ci	 NETIF_MSG_RX_ERR	| \
5262306a36Sopenharmony_ci	 NETIF_MSG_TX_ERR)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int greth_debug = -1;	/* -1 == use GRETH_DEF_MSG_ENABLE as value */
5562306a36Sopenharmony_cimodule_param(greth_debug, int, 0);
5662306a36Sopenharmony_ciMODULE_PARM_DESC(greth_debug, "GRETH bitmapped debugging message enable value");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */
5962306a36Sopenharmony_cistatic int macaddr[6];
6062306a36Sopenharmony_cimodule_param_array(macaddr, int, NULL, 0);
6162306a36Sopenharmony_ciMODULE_PARM_DESC(macaddr, "GRETH Ethernet MAC address");
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int greth_edcl = 1;
6462306a36Sopenharmony_cimodule_param(greth_edcl, int, 0);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(greth_edcl, "GRETH EDCL usage indicator. Set to 1 if EDCL is used.");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int greth_open(struct net_device *dev);
6862306a36Sopenharmony_cistatic netdev_tx_t greth_start_xmit(struct sk_buff *skb,
6962306a36Sopenharmony_ci	   struct net_device *dev);
7062306a36Sopenharmony_cistatic netdev_tx_t greth_start_xmit_gbit(struct sk_buff *skb,
7162306a36Sopenharmony_ci	   struct net_device *dev);
7262306a36Sopenharmony_cistatic int greth_rx(struct net_device *dev, int limit);
7362306a36Sopenharmony_cistatic int greth_rx_gbit(struct net_device *dev, int limit);
7462306a36Sopenharmony_cistatic void greth_clean_tx(struct net_device *dev);
7562306a36Sopenharmony_cistatic void greth_clean_tx_gbit(struct net_device *dev);
7662306a36Sopenharmony_cistatic irqreturn_t greth_interrupt(int irq, void *dev_id);
7762306a36Sopenharmony_cistatic int greth_close(struct net_device *dev);
7862306a36Sopenharmony_cistatic int greth_set_mac_add(struct net_device *dev, void *p);
7962306a36Sopenharmony_cistatic void greth_set_multicast_list(struct net_device *dev);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define GRETH_REGLOAD(a)	    (be32_to_cpu(__raw_readl(&(a))))
8262306a36Sopenharmony_ci#define GRETH_REGSAVE(a, v)         (__raw_writel(cpu_to_be32(v), &(a)))
8362306a36Sopenharmony_ci#define GRETH_REGORIN(a, v)         (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) | (v))))
8462306a36Sopenharmony_ci#define GRETH_REGANDIN(a, v)        (GRETH_REGSAVE(a, (GRETH_REGLOAD(a) & (v))))
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define NEXT_TX(N)      (((N) + 1) & GRETH_TXBD_NUM_MASK)
8762306a36Sopenharmony_ci#define SKIP_TX(N, C)   (((N) + C) & GRETH_TXBD_NUM_MASK)
8862306a36Sopenharmony_ci#define NEXT_RX(N)      (((N) + 1) & GRETH_RXBD_NUM_MASK)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void greth_print_rx_packet(void *addr, int len)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	print_hex_dump(KERN_DEBUG, "RX: ", DUMP_PREFIX_OFFSET, 16, 1,
9362306a36Sopenharmony_ci			addr, len, true);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void greth_print_tx_packet(struct sk_buff *skb)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	int i;
9962306a36Sopenharmony_ci	int length;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags == 0)
10262306a36Sopenharmony_ci		length = skb->len;
10362306a36Sopenharmony_ci	else
10462306a36Sopenharmony_ci		length = skb_headlen(skb);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
10762306a36Sopenharmony_ci			skb->data, length, true);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
11262306a36Sopenharmony_ci			       skb_frag_address(&skb_shinfo(skb)->frags[i]),
11362306a36Sopenharmony_ci			       skb_frag_size(&skb_shinfo(skb)->frags[i]), true);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic inline void greth_enable_tx(struct greth_private *greth)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	wmb();
12062306a36Sopenharmony_ci	GRETH_REGORIN(greth->regs->control, GRETH_TXEN);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline void greth_enable_tx_and_irq(struct greth_private *greth)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	wmb(); /* BDs must been written to memory before enabling TX */
12662306a36Sopenharmony_ci	GRETH_REGORIN(greth->regs->control, GRETH_TXEN | GRETH_TXI);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline void greth_disable_tx(struct greth_private *greth)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	GRETH_REGANDIN(greth->regs->control, ~GRETH_TXEN);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic inline void greth_enable_rx(struct greth_private *greth)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	wmb();
13762306a36Sopenharmony_ci	GRETH_REGORIN(greth->regs->control, GRETH_RXEN);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic inline void greth_disable_rx(struct greth_private *greth)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	GRETH_REGANDIN(greth->regs->control, ~GRETH_RXEN);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic inline void greth_enable_irqs(struct greth_private *greth)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	GRETH_REGORIN(greth->regs->control, GRETH_RXI | GRETH_TXI);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic inline void greth_disable_irqs(struct greth_private *greth)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	GRETH_REGANDIN(greth->regs->control, ~(GRETH_RXI|GRETH_TXI));
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic inline void greth_write_bd(u32 *bd, u32 val)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	__raw_writel(cpu_to_be32(val), bd);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic inline u32 greth_read_bd(u32 *bd)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	return be32_to_cpu(__raw_readl(bd));
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void greth_clean_rings(struct greth_private *greth)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	int i;
16862306a36Sopenharmony_ci	struct greth_bd *rx_bdp = greth->rx_bd_base;
16962306a36Sopenharmony_ci	struct greth_bd *tx_bdp = greth->tx_bd_base;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (greth->gbit_mac) {
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		/* Free and unmap RX buffers */
17462306a36Sopenharmony_ci		for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) {
17562306a36Sopenharmony_ci			if (greth->rx_skbuff[i] != NULL) {
17662306a36Sopenharmony_ci				dev_kfree_skb(greth->rx_skbuff[i]);
17762306a36Sopenharmony_ci				dma_unmap_single(greth->dev,
17862306a36Sopenharmony_ci						 greth_read_bd(&rx_bdp->addr),
17962306a36Sopenharmony_ci						 MAX_FRAME_SIZE+NET_IP_ALIGN,
18062306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
18162306a36Sopenharmony_ci			}
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		/* TX buffers */
18562306a36Sopenharmony_ci		while (greth->tx_free < GRETH_TXBD_NUM) {
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci			struct sk_buff *skb = greth->tx_skbuff[greth->tx_last];
18862306a36Sopenharmony_ci			int nr_frags = skb_shinfo(skb)->nr_frags;
18962306a36Sopenharmony_ci			tx_bdp = greth->tx_bd_base + greth->tx_last;
19062306a36Sopenharmony_ci			greth->tx_last = NEXT_TX(greth->tx_last);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			dma_unmap_single(greth->dev,
19362306a36Sopenharmony_ci					 greth_read_bd(&tx_bdp->addr),
19462306a36Sopenharmony_ci					 skb_headlen(skb),
19562306a36Sopenharmony_ci					 DMA_TO_DEVICE);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci			for (i = 0; i < nr_frags; i++) {
19862306a36Sopenharmony_ci				skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
19962306a36Sopenharmony_ci				tx_bdp = greth->tx_bd_base + greth->tx_last;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci				dma_unmap_page(greth->dev,
20262306a36Sopenharmony_ci					       greth_read_bd(&tx_bdp->addr),
20362306a36Sopenharmony_ci					       skb_frag_size(frag),
20462306a36Sopenharmony_ci					       DMA_TO_DEVICE);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci				greth->tx_last = NEXT_TX(greth->tx_last);
20762306a36Sopenharmony_ci			}
20862306a36Sopenharmony_ci			greth->tx_free += nr_frags+1;
20962306a36Sopenharmony_ci			dev_kfree_skb(skb);
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	} else { /* 10/100 Mbps MAC */
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		for (i = 0; i < GRETH_RXBD_NUM; i++, rx_bdp++) {
21662306a36Sopenharmony_ci			kfree(greth->rx_bufs[i]);
21762306a36Sopenharmony_ci			dma_unmap_single(greth->dev,
21862306a36Sopenharmony_ci					 greth_read_bd(&rx_bdp->addr),
21962306a36Sopenharmony_ci					 MAX_FRAME_SIZE,
22062306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		for (i = 0; i < GRETH_TXBD_NUM; i++, tx_bdp++) {
22362306a36Sopenharmony_ci			kfree(greth->tx_bufs[i]);
22462306a36Sopenharmony_ci			dma_unmap_single(greth->dev,
22562306a36Sopenharmony_ci					 greth_read_bd(&tx_bdp->addr),
22662306a36Sopenharmony_ci					 MAX_FRAME_SIZE,
22762306a36Sopenharmony_ci					 DMA_TO_DEVICE);
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int greth_init_rings(struct greth_private *greth)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct sk_buff *skb;
23562306a36Sopenharmony_ci	struct greth_bd *rx_bd, *tx_bd;
23662306a36Sopenharmony_ci	u32 dma_addr;
23762306a36Sopenharmony_ci	int i;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	rx_bd = greth->rx_bd_base;
24062306a36Sopenharmony_ci	tx_bd = greth->tx_bd_base;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Initialize descriptor rings and buffers */
24362306a36Sopenharmony_ci	if (greth->gbit_mac) {
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		for (i = 0; i < GRETH_RXBD_NUM; i++) {
24662306a36Sopenharmony_ci			skb = netdev_alloc_skb(greth->netdev, MAX_FRAME_SIZE+NET_IP_ALIGN);
24762306a36Sopenharmony_ci			if (skb == NULL) {
24862306a36Sopenharmony_ci				if (netif_msg_ifup(greth))
24962306a36Sopenharmony_ci					dev_err(greth->dev, "Error allocating DMA ring.\n");
25062306a36Sopenharmony_ci				goto cleanup;
25162306a36Sopenharmony_ci			}
25262306a36Sopenharmony_ci			skb_reserve(skb, NET_IP_ALIGN);
25362306a36Sopenharmony_ci			dma_addr = dma_map_single(greth->dev,
25462306a36Sopenharmony_ci						  skb->data,
25562306a36Sopenharmony_ci						  MAX_FRAME_SIZE+NET_IP_ALIGN,
25662306a36Sopenharmony_ci						  DMA_FROM_DEVICE);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci			if (dma_mapping_error(greth->dev, dma_addr)) {
25962306a36Sopenharmony_ci				if (netif_msg_ifup(greth))
26062306a36Sopenharmony_ci					dev_err(greth->dev, "Could not create initial DMA mapping\n");
26162306a36Sopenharmony_ci				dev_kfree_skb(skb);
26262306a36Sopenharmony_ci				goto cleanup;
26362306a36Sopenharmony_ci			}
26462306a36Sopenharmony_ci			greth->rx_skbuff[i] = skb;
26562306a36Sopenharmony_ci			greth_write_bd(&rx_bd[i].addr, dma_addr);
26662306a36Sopenharmony_ci			greth_write_bd(&rx_bd[i].stat, GRETH_BD_EN | GRETH_BD_IE);
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	} else {
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		/* 10/100 MAC uses a fixed set of buffers and copy to/from SKBs */
27262306a36Sopenharmony_ci		for (i = 0; i < GRETH_RXBD_NUM; i++) {
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci			greth->rx_bufs[i] = kmalloc(MAX_FRAME_SIZE, GFP_KERNEL);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			if (greth->rx_bufs[i] == NULL) {
27762306a36Sopenharmony_ci				if (netif_msg_ifup(greth))
27862306a36Sopenharmony_ci					dev_err(greth->dev, "Error allocating DMA ring.\n");
27962306a36Sopenharmony_ci				goto cleanup;
28062306a36Sopenharmony_ci			}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci			dma_addr = dma_map_single(greth->dev,
28362306a36Sopenharmony_ci						  greth->rx_bufs[i],
28462306a36Sopenharmony_ci						  MAX_FRAME_SIZE,
28562306a36Sopenharmony_ci						  DMA_FROM_DEVICE);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci			if (dma_mapping_error(greth->dev, dma_addr)) {
28862306a36Sopenharmony_ci				if (netif_msg_ifup(greth))
28962306a36Sopenharmony_ci					dev_err(greth->dev, "Could not create initial DMA mapping\n");
29062306a36Sopenharmony_ci				goto cleanup;
29162306a36Sopenharmony_ci			}
29262306a36Sopenharmony_ci			greth_write_bd(&rx_bd[i].addr, dma_addr);
29362306a36Sopenharmony_ci			greth_write_bd(&rx_bd[i].stat, GRETH_BD_EN | GRETH_BD_IE);
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci		for (i = 0; i < GRETH_TXBD_NUM; i++) {
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			greth->tx_bufs[i] = kmalloc(MAX_FRAME_SIZE, GFP_KERNEL);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			if (greth->tx_bufs[i] == NULL) {
30062306a36Sopenharmony_ci				if (netif_msg_ifup(greth))
30162306a36Sopenharmony_ci					dev_err(greth->dev, "Error allocating DMA ring.\n");
30262306a36Sopenharmony_ci				goto cleanup;
30362306a36Sopenharmony_ci			}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci			dma_addr = dma_map_single(greth->dev,
30662306a36Sopenharmony_ci						  greth->tx_bufs[i],
30762306a36Sopenharmony_ci						  MAX_FRAME_SIZE,
30862306a36Sopenharmony_ci						  DMA_TO_DEVICE);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci			if (dma_mapping_error(greth->dev, dma_addr)) {
31162306a36Sopenharmony_ci				if (netif_msg_ifup(greth))
31262306a36Sopenharmony_ci					dev_err(greth->dev, "Could not create initial DMA mapping\n");
31362306a36Sopenharmony_ci				goto cleanup;
31462306a36Sopenharmony_ci			}
31562306a36Sopenharmony_ci			greth_write_bd(&tx_bd[i].addr, dma_addr);
31662306a36Sopenharmony_ci			greth_write_bd(&tx_bd[i].stat, 0);
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	greth_write_bd(&rx_bd[GRETH_RXBD_NUM - 1].stat,
32062306a36Sopenharmony_ci		       greth_read_bd(&rx_bd[GRETH_RXBD_NUM - 1].stat) | GRETH_BD_WR);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* Initialize pointers. */
32362306a36Sopenharmony_ci	greth->rx_cur = 0;
32462306a36Sopenharmony_ci	greth->tx_next = 0;
32562306a36Sopenharmony_ci	greth->tx_last = 0;
32662306a36Sopenharmony_ci	greth->tx_free = GRETH_TXBD_NUM;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Initialize descriptor base address */
32962306a36Sopenharmony_ci	GRETH_REGSAVE(greth->regs->tx_desc_p, greth->tx_bd_base_phys);
33062306a36Sopenharmony_ci	GRETH_REGSAVE(greth->regs->rx_desc_p, greth->rx_bd_base_phys);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return 0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cicleanup:
33562306a36Sopenharmony_ci	greth_clean_rings(greth);
33662306a36Sopenharmony_ci	return -ENOMEM;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int greth_open(struct net_device *dev)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
34262306a36Sopenharmony_ci	int err;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	err = greth_init_rings(greth);
34562306a36Sopenharmony_ci	if (err) {
34662306a36Sopenharmony_ci		if (netif_msg_ifup(greth))
34762306a36Sopenharmony_ci			dev_err(&dev->dev, "Could not allocate memory for DMA rings\n");
34862306a36Sopenharmony_ci		return err;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	err = request_irq(greth->irq, greth_interrupt, 0, "eth", (void *) dev);
35262306a36Sopenharmony_ci	if (err) {
35362306a36Sopenharmony_ci		if (netif_msg_ifup(greth))
35462306a36Sopenharmony_ci			dev_err(&dev->dev, "Could not allocate interrupt %d\n", dev->irq);
35562306a36Sopenharmony_ci		greth_clean_rings(greth);
35662306a36Sopenharmony_ci		return err;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (netif_msg_ifup(greth))
36062306a36Sopenharmony_ci		dev_dbg(&dev->dev, " starting queue\n");
36162306a36Sopenharmony_ci	netif_start_queue(dev);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	GRETH_REGSAVE(greth->regs->status, 0xFF);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	napi_enable(&greth->napi);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	greth_enable_irqs(greth);
36862306a36Sopenharmony_ci	greth_enable_tx(greth);
36962306a36Sopenharmony_ci	greth_enable_rx(greth);
37062306a36Sopenharmony_ci	return 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic int greth_close(struct net_device *dev)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	napi_disable(&greth->napi);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	greth_disable_irqs(greth);
38162306a36Sopenharmony_ci	greth_disable_tx(greth);
38262306a36Sopenharmony_ci	greth_disable_rx(greth);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	netif_stop_queue(dev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	free_irq(greth->irq, (void *) dev);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	greth_clean_rings(greth);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic netdev_tx_t
39462306a36Sopenharmony_cigreth_start_xmit(struct sk_buff *skb, struct net_device *dev)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
39762306a36Sopenharmony_ci	struct greth_bd *bdp;
39862306a36Sopenharmony_ci	int err = NETDEV_TX_OK;
39962306a36Sopenharmony_ci	u32 status, dma_addr, ctrl;
40062306a36Sopenharmony_ci	unsigned long flags;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Clean TX Ring */
40362306a36Sopenharmony_ci	greth_clean_tx(greth->netdev);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (unlikely(greth->tx_free <= 0)) {
40662306a36Sopenharmony_ci		spin_lock_irqsave(&greth->devlock, flags);/*save from poll/irq*/
40762306a36Sopenharmony_ci		ctrl = GRETH_REGLOAD(greth->regs->control);
40862306a36Sopenharmony_ci		/* Enable TX IRQ only if not already in poll() routine */
40962306a36Sopenharmony_ci		if (ctrl & GRETH_RXI)
41062306a36Sopenharmony_ci			GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_TXI);
41162306a36Sopenharmony_ci		netif_stop_queue(dev);
41262306a36Sopenharmony_ci		spin_unlock_irqrestore(&greth->devlock, flags);
41362306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (netif_msg_pktdata(greth))
41762306a36Sopenharmony_ci		greth_print_tx_packet(skb);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (unlikely(skb->len > MAX_FRAME_SIZE)) {
42162306a36Sopenharmony_ci		dev->stats.tx_errors++;
42262306a36Sopenharmony_ci		goto out;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	bdp = greth->tx_bd_base + greth->tx_next;
42662306a36Sopenharmony_ci	dma_addr = greth_read_bd(&bdp->addr);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	memcpy((unsigned char *) phys_to_virt(dma_addr), skb->data, skb->len);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	dma_sync_single_for_device(greth->dev, dma_addr, skb->len, DMA_TO_DEVICE);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	status = GRETH_BD_EN | GRETH_BD_IE | (skb->len & GRETH_BD_LEN);
43362306a36Sopenharmony_ci	greth->tx_bufs_length[greth->tx_next] = skb->len & GRETH_BD_LEN;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* Wrap around descriptor ring */
43662306a36Sopenharmony_ci	if (greth->tx_next == GRETH_TXBD_NUM_MASK) {
43762306a36Sopenharmony_ci		status |= GRETH_BD_WR;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	greth->tx_next = NEXT_TX(greth->tx_next);
44162306a36Sopenharmony_ci	greth->tx_free--;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Write descriptor control word and enable transmission */
44462306a36Sopenharmony_ci	greth_write_bd(&bdp->stat, status);
44562306a36Sopenharmony_ci	spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
44662306a36Sopenharmony_ci	greth_enable_tx(greth);
44762306a36Sopenharmony_ci	spin_unlock_irqrestore(&greth->devlock, flags);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ciout:
45062306a36Sopenharmony_ci	dev_kfree_skb(skb);
45162306a36Sopenharmony_ci	return err;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic inline u16 greth_num_free_bds(u16 tx_last, u16 tx_next)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	if (tx_next < tx_last)
45762306a36Sopenharmony_ci		return (tx_last - tx_next) - 1;
45862306a36Sopenharmony_ci	else
45962306a36Sopenharmony_ci		return GRETH_TXBD_NUM - (tx_next - tx_last) - 1;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic netdev_tx_t
46362306a36Sopenharmony_cigreth_start_xmit_gbit(struct sk_buff *skb, struct net_device *dev)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
46662306a36Sopenharmony_ci	struct greth_bd *bdp;
46762306a36Sopenharmony_ci	u32 status, dma_addr;
46862306a36Sopenharmony_ci	int curr_tx, nr_frags, i, err = NETDEV_TX_OK;
46962306a36Sopenharmony_ci	unsigned long flags;
47062306a36Sopenharmony_ci	u16 tx_last;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
47362306a36Sopenharmony_ci	tx_last = greth->tx_last;
47462306a36Sopenharmony_ci	rmb(); /* tx_last is updated by the poll task */
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (greth_num_free_bds(tx_last, greth->tx_next) < nr_frags + 1) {
47762306a36Sopenharmony_ci		netif_stop_queue(dev);
47862306a36Sopenharmony_ci		err = NETDEV_TX_BUSY;
47962306a36Sopenharmony_ci		goto out;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (netif_msg_pktdata(greth))
48362306a36Sopenharmony_ci		greth_print_tx_packet(skb);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (unlikely(skb->len > MAX_FRAME_SIZE)) {
48662306a36Sopenharmony_ci		dev->stats.tx_errors++;
48762306a36Sopenharmony_ci		goto out;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* Save skb pointer. */
49162306a36Sopenharmony_ci	greth->tx_skbuff[greth->tx_next] = skb;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Linear buf */
49462306a36Sopenharmony_ci	if (nr_frags != 0)
49562306a36Sopenharmony_ci		status = GRETH_TXBD_MORE;
49662306a36Sopenharmony_ci	else
49762306a36Sopenharmony_ci		status = GRETH_BD_IE;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL)
50062306a36Sopenharmony_ci		status |= GRETH_TXBD_CSALL;
50162306a36Sopenharmony_ci	status |= skb_headlen(skb) & GRETH_BD_LEN;
50262306a36Sopenharmony_ci	if (greth->tx_next == GRETH_TXBD_NUM_MASK)
50362306a36Sopenharmony_ci		status |= GRETH_BD_WR;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	bdp = greth->tx_bd_base + greth->tx_next;
50762306a36Sopenharmony_ci	greth_write_bd(&bdp->stat, status);
50862306a36Sopenharmony_ci	dma_addr = dma_map_single(greth->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(greth->dev, dma_addr)))
51162306a36Sopenharmony_ci		goto map_error;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	greth_write_bd(&bdp->addr, dma_addr);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	curr_tx = NEXT_TX(greth->tx_next);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Frags */
51862306a36Sopenharmony_ci	for (i = 0; i < nr_frags; i++) {
51962306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
52062306a36Sopenharmony_ci		greth->tx_skbuff[curr_tx] = NULL;
52162306a36Sopenharmony_ci		bdp = greth->tx_bd_base + curr_tx;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		status = GRETH_BD_EN;
52462306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
52562306a36Sopenharmony_ci			status |= GRETH_TXBD_CSALL;
52662306a36Sopenharmony_ci		status |= skb_frag_size(frag) & GRETH_BD_LEN;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		/* Wrap around descriptor ring */
52962306a36Sopenharmony_ci		if (curr_tx == GRETH_TXBD_NUM_MASK)
53062306a36Sopenharmony_ci			status |= GRETH_BD_WR;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		/* More fragments left */
53362306a36Sopenharmony_ci		if (i < nr_frags - 1)
53462306a36Sopenharmony_ci			status |= GRETH_TXBD_MORE;
53562306a36Sopenharmony_ci		else
53662306a36Sopenharmony_ci			status |= GRETH_BD_IE; /* enable IRQ on last fragment */
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		greth_write_bd(&bdp->stat, status);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		dma_addr = skb_frag_dma_map(greth->dev, frag, 0, skb_frag_size(frag),
54162306a36Sopenharmony_ci					    DMA_TO_DEVICE);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(greth->dev, dma_addr)))
54462306a36Sopenharmony_ci			goto frag_map_error;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		greth_write_bd(&bdp->addr, dma_addr);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		curr_tx = NEXT_TX(curr_tx);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	wmb();
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* Enable the descriptor chain by enabling the first descriptor */
55462306a36Sopenharmony_ci	bdp = greth->tx_bd_base + greth->tx_next;
55562306a36Sopenharmony_ci	greth_write_bd(&bdp->stat,
55662306a36Sopenharmony_ci		       greth_read_bd(&bdp->stat) | GRETH_BD_EN);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	spin_lock_irqsave(&greth->devlock, flags); /*save from poll/irq*/
55962306a36Sopenharmony_ci	greth->tx_next = curr_tx;
56062306a36Sopenharmony_ci	greth_enable_tx_and_irq(greth);
56162306a36Sopenharmony_ci	spin_unlock_irqrestore(&greth->devlock, flags);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return NETDEV_TX_OK;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cifrag_map_error:
56662306a36Sopenharmony_ci	/* Unmap SKB mappings that succeeded and disable descriptor */
56762306a36Sopenharmony_ci	for (i = 0; greth->tx_next + i != curr_tx; i++) {
56862306a36Sopenharmony_ci		bdp = greth->tx_bd_base + greth->tx_next + i;
56962306a36Sopenharmony_ci		dma_unmap_single(greth->dev,
57062306a36Sopenharmony_ci				 greth_read_bd(&bdp->addr),
57162306a36Sopenharmony_ci				 greth_read_bd(&bdp->stat) & GRETH_BD_LEN,
57262306a36Sopenharmony_ci				 DMA_TO_DEVICE);
57362306a36Sopenharmony_ci		greth_write_bd(&bdp->stat, 0);
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_cimap_error:
57662306a36Sopenharmony_ci	if (net_ratelimit())
57762306a36Sopenharmony_ci		dev_warn(greth->dev, "Could not create TX DMA mapping\n");
57862306a36Sopenharmony_ci	dev_kfree_skb(skb);
57962306a36Sopenharmony_ciout:
58062306a36Sopenharmony_ci	return err;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic irqreturn_t greth_interrupt(int irq, void *dev_id)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct net_device *dev = dev_id;
58662306a36Sopenharmony_ci	struct greth_private *greth;
58762306a36Sopenharmony_ci	u32 status, ctrl;
58862306a36Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	greth = netdev_priv(dev);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	spin_lock(&greth->devlock);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* Get the interrupt events that caused us to be here. */
59562306a36Sopenharmony_ci	status = GRETH_REGLOAD(greth->regs->status);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* Must see if interrupts are enabled also, INT_TX|INT_RX flags may be
59862306a36Sopenharmony_ci	 * set regardless of whether IRQ is enabled or not. Especially
59962306a36Sopenharmony_ci	 * important when shared IRQ.
60062306a36Sopenharmony_ci	 */
60162306a36Sopenharmony_ci	ctrl = GRETH_REGLOAD(greth->regs->control);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* Handle rx and tx interrupts through poll */
60462306a36Sopenharmony_ci	if (((status & (GRETH_INT_RE | GRETH_INT_RX)) && (ctrl & GRETH_RXI)) ||
60562306a36Sopenharmony_ci	    ((status & (GRETH_INT_TE | GRETH_INT_TX)) && (ctrl & GRETH_TXI))) {
60662306a36Sopenharmony_ci		retval = IRQ_HANDLED;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		/* Disable interrupts and schedule poll() */
60962306a36Sopenharmony_ci		greth_disable_irqs(greth);
61062306a36Sopenharmony_ci		napi_schedule(&greth->napi);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	spin_unlock(&greth->devlock);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return retval;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic void greth_clean_tx(struct net_device *dev)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct greth_private *greth;
62162306a36Sopenharmony_ci	struct greth_bd *bdp;
62262306a36Sopenharmony_ci	u32 stat;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	greth = netdev_priv(dev);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	while (1) {
62762306a36Sopenharmony_ci		bdp = greth->tx_bd_base + greth->tx_last;
62862306a36Sopenharmony_ci		GRETH_REGSAVE(greth->regs->status, GRETH_INT_TE | GRETH_INT_TX);
62962306a36Sopenharmony_ci		mb();
63062306a36Sopenharmony_ci		stat = greth_read_bd(&bdp->stat);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		if (unlikely(stat & GRETH_BD_EN))
63362306a36Sopenharmony_ci			break;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		if (greth->tx_free == GRETH_TXBD_NUM)
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		/* Check status for errors */
63962306a36Sopenharmony_ci		if (unlikely(stat & GRETH_TXBD_STATUS)) {
64062306a36Sopenharmony_ci			dev->stats.tx_errors++;
64162306a36Sopenharmony_ci			if (stat & GRETH_TXBD_ERR_AL)
64262306a36Sopenharmony_ci				dev->stats.tx_aborted_errors++;
64362306a36Sopenharmony_ci			if (stat & GRETH_TXBD_ERR_UE)
64462306a36Sopenharmony_ci				dev->stats.tx_fifo_errors++;
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci		dev->stats.tx_packets++;
64762306a36Sopenharmony_ci		dev->stats.tx_bytes += greth->tx_bufs_length[greth->tx_last];
64862306a36Sopenharmony_ci		greth->tx_last = NEXT_TX(greth->tx_last);
64962306a36Sopenharmony_ci		greth->tx_free++;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (greth->tx_free > 0) {
65362306a36Sopenharmony_ci		netif_wake_queue(dev);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic inline void greth_update_tx_stats(struct net_device *dev, u32 stat)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	/* Check status for errors */
66062306a36Sopenharmony_ci	if (unlikely(stat & GRETH_TXBD_STATUS)) {
66162306a36Sopenharmony_ci		dev->stats.tx_errors++;
66262306a36Sopenharmony_ci		if (stat & GRETH_TXBD_ERR_AL)
66362306a36Sopenharmony_ci			dev->stats.tx_aborted_errors++;
66462306a36Sopenharmony_ci		if (stat & GRETH_TXBD_ERR_UE)
66562306a36Sopenharmony_ci			dev->stats.tx_fifo_errors++;
66662306a36Sopenharmony_ci		if (stat & GRETH_TXBD_ERR_LC)
66762306a36Sopenharmony_ci			dev->stats.tx_aborted_errors++;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci	dev->stats.tx_packets++;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic void greth_clean_tx_gbit(struct net_device *dev)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct greth_private *greth;
67562306a36Sopenharmony_ci	struct greth_bd *bdp, *bdp_last_frag;
67662306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
67762306a36Sopenharmony_ci	u32 stat;
67862306a36Sopenharmony_ci	int nr_frags, i;
67962306a36Sopenharmony_ci	u16 tx_last;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	greth = netdev_priv(dev);
68262306a36Sopenharmony_ci	tx_last = greth->tx_last;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	while (tx_last != greth->tx_next) {
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		skb = greth->tx_skbuff[tx_last];
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		nr_frags = skb_shinfo(skb)->nr_frags;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		/* We only clean fully completed SKBs */
69162306a36Sopenharmony_ci		bdp_last_frag = greth->tx_bd_base + SKIP_TX(tx_last, nr_frags);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		GRETH_REGSAVE(greth->regs->status, GRETH_INT_TE | GRETH_INT_TX);
69462306a36Sopenharmony_ci		mb();
69562306a36Sopenharmony_ci		stat = greth_read_bd(&bdp_last_frag->stat);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		if (stat & GRETH_BD_EN)
69862306a36Sopenharmony_ci			break;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		greth->tx_skbuff[tx_last] = NULL;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		greth_update_tx_stats(dev, stat);
70362306a36Sopenharmony_ci		dev->stats.tx_bytes += skb->len;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		bdp = greth->tx_bd_base + tx_last;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		tx_last = NEXT_TX(tx_last);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		dma_unmap_single(greth->dev,
71062306a36Sopenharmony_ci				 greth_read_bd(&bdp->addr),
71162306a36Sopenharmony_ci				 skb_headlen(skb),
71262306a36Sopenharmony_ci				 DMA_TO_DEVICE);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		for (i = 0; i < nr_frags; i++) {
71562306a36Sopenharmony_ci			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
71662306a36Sopenharmony_ci			bdp = greth->tx_bd_base + tx_last;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci			dma_unmap_page(greth->dev,
71962306a36Sopenharmony_ci				       greth_read_bd(&bdp->addr),
72062306a36Sopenharmony_ci				       skb_frag_size(frag),
72162306a36Sopenharmony_ci				       DMA_TO_DEVICE);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci			tx_last = NEXT_TX(tx_last);
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci		dev_kfree_skb(skb);
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci	if (skb) { /* skb is set only if the above while loop was entered */
72862306a36Sopenharmony_ci		wmb();
72962306a36Sopenharmony_ci		greth->tx_last = tx_last;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		if (netif_queue_stopped(dev) &&
73262306a36Sopenharmony_ci		    (greth_num_free_bds(tx_last, greth->tx_next) >
73362306a36Sopenharmony_ci		    (MAX_SKB_FRAGS+1)))
73462306a36Sopenharmony_ci			netif_wake_queue(dev);
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int greth_rx(struct net_device *dev, int limit)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct greth_private *greth;
74162306a36Sopenharmony_ci	struct greth_bd *bdp;
74262306a36Sopenharmony_ci	struct sk_buff *skb;
74362306a36Sopenharmony_ci	int pkt_len;
74462306a36Sopenharmony_ci	int bad, count;
74562306a36Sopenharmony_ci	u32 status, dma_addr;
74662306a36Sopenharmony_ci	unsigned long flags;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	greth = netdev_priv(dev);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	for (count = 0; count < limit; ++count) {
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		bdp = greth->rx_bd_base + greth->rx_cur;
75362306a36Sopenharmony_ci		GRETH_REGSAVE(greth->regs->status, GRETH_INT_RE | GRETH_INT_RX);
75462306a36Sopenharmony_ci		mb();
75562306a36Sopenharmony_ci		status = greth_read_bd(&bdp->stat);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		if (unlikely(status & GRETH_BD_EN)) {
75862306a36Sopenharmony_ci			break;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		dma_addr = greth_read_bd(&bdp->addr);
76262306a36Sopenharmony_ci		bad = 0;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		/* Check status for errors. */
76562306a36Sopenharmony_ci		if (unlikely(status & GRETH_RXBD_STATUS)) {
76662306a36Sopenharmony_ci			if (status & GRETH_RXBD_ERR_FT) {
76762306a36Sopenharmony_ci				dev->stats.rx_length_errors++;
76862306a36Sopenharmony_ci				bad = 1;
76962306a36Sopenharmony_ci			}
77062306a36Sopenharmony_ci			if (status & (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE)) {
77162306a36Sopenharmony_ci				dev->stats.rx_frame_errors++;
77262306a36Sopenharmony_ci				bad = 1;
77362306a36Sopenharmony_ci			}
77462306a36Sopenharmony_ci			if (status & GRETH_RXBD_ERR_CRC) {
77562306a36Sopenharmony_ci				dev->stats.rx_crc_errors++;
77662306a36Sopenharmony_ci				bad = 1;
77762306a36Sopenharmony_ci			}
77862306a36Sopenharmony_ci		}
77962306a36Sopenharmony_ci		if (unlikely(bad)) {
78062306a36Sopenharmony_ci			dev->stats.rx_errors++;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		} else {
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci			pkt_len = status & GRETH_BD_LEN;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci			skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci			if (unlikely(skb == NULL)) {
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci				if (net_ratelimit())
79162306a36Sopenharmony_ci					dev_warn(&dev->dev, "low on memory - " "packet dropped\n");
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci				dev->stats.rx_dropped++;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci			} else {
79662306a36Sopenharmony_ci				skb_reserve(skb, NET_IP_ALIGN);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci				dma_sync_single_for_cpu(greth->dev,
79962306a36Sopenharmony_ci							dma_addr,
80062306a36Sopenharmony_ci							pkt_len,
80162306a36Sopenharmony_ci							DMA_FROM_DEVICE);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci				if (netif_msg_pktdata(greth))
80462306a36Sopenharmony_ci					greth_print_rx_packet(phys_to_virt(dma_addr), pkt_len);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci				skb_put_data(skb, phys_to_virt(dma_addr),
80762306a36Sopenharmony_ci					     pkt_len);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci				skb->protocol = eth_type_trans(skb, dev);
81062306a36Sopenharmony_ci				dev->stats.rx_bytes += pkt_len;
81162306a36Sopenharmony_ci				dev->stats.rx_packets++;
81262306a36Sopenharmony_ci				netif_receive_skb(skb);
81362306a36Sopenharmony_ci			}
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		status = GRETH_BD_EN | GRETH_BD_IE;
81762306a36Sopenharmony_ci		if (greth->rx_cur == GRETH_RXBD_NUM_MASK) {
81862306a36Sopenharmony_ci			status |= GRETH_BD_WR;
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		wmb();
82262306a36Sopenharmony_ci		greth_write_bd(&bdp->stat, status);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		dma_sync_single_for_device(greth->dev, dma_addr, MAX_FRAME_SIZE, DMA_FROM_DEVICE);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		spin_lock_irqsave(&greth->devlock, flags); /* save from XMIT */
82762306a36Sopenharmony_ci		greth_enable_rx(greth);
82862306a36Sopenharmony_ci		spin_unlock_irqrestore(&greth->devlock, flags);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		greth->rx_cur = NEXT_RX(greth->rx_cur);
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return count;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic inline int hw_checksummed(u32 status)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (status & GRETH_RXBD_IP_FRAG)
84062306a36Sopenharmony_ci		return 0;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (status & GRETH_RXBD_IP && status & GRETH_RXBD_IP_CSERR)
84362306a36Sopenharmony_ci		return 0;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (status & GRETH_RXBD_UDP && status & GRETH_RXBD_UDP_CSERR)
84662306a36Sopenharmony_ci		return 0;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (status & GRETH_RXBD_TCP && status & GRETH_RXBD_TCP_CSERR)
84962306a36Sopenharmony_ci		return 0;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return 1;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic int greth_rx_gbit(struct net_device *dev, int limit)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct greth_private *greth;
85762306a36Sopenharmony_ci	struct greth_bd *bdp;
85862306a36Sopenharmony_ci	struct sk_buff *skb, *newskb;
85962306a36Sopenharmony_ci	int pkt_len;
86062306a36Sopenharmony_ci	int bad, count = 0;
86162306a36Sopenharmony_ci	u32 status, dma_addr;
86262306a36Sopenharmony_ci	unsigned long flags;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	greth = netdev_priv(dev);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	for (count = 0; count < limit; ++count) {
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		bdp = greth->rx_bd_base + greth->rx_cur;
86962306a36Sopenharmony_ci		skb = greth->rx_skbuff[greth->rx_cur];
87062306a36Sopenharmony_ci		GRETH_REGSAVE(greth->regs->status, GRETH_INT_RE | GRETH_INT_RX);
87162306a36Sopenharmony_ci		mb();
87262306a36Sopenharmony_ci		status = greth_read_bd(&bdp->stat);
87362306a36Sopenharmony_ci		bad = 0;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		if (status & GRETH_BD_EN)
87662306a36Sopenharmony_ci			break;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		/* Check status for errors. */
87962306a36Sopenharmony_ci		if (unlikely(status & GRETH_RXBD_STATUS)) {
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci			if (status & GRETH_RXBD_ERR_FT) {
88262306a36Sopenharmony_ci				dev->stats.rx_length_errors++;
88362306a36Sopenharmony_ci				bad = 1;
88462306a36Sopenharmony_ci			} else if (status &
88562306a36Sopenharmony_ci				   (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE | GRETH_RXBD_ERR_LE)) {
88662306a36Sopenharmony_ci				dev->stats.rx_frame_errors++;
88762306a36Sopenharmony_ci				bad = 1;
88862306a36Sopenharmony_ci			} else if (status & GRETH_RXBD_ERR_CRC) {
88962306a36Sopenharmony_ci				dev->stats.rx_crc_errors++;
89062306a36Sopenharmony_ci				bad = 1;
89162306a36Sopenharmony_ci			}
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		/* Allocate new skb to replace current, not needed if the
89562306a36Sopenharmony_ci		 * current skb can be reused */
89662306a36Sopenharmony_ci		if (!bad && (newskb=netdev_alloc_skb(dev, MAX_FRAME_SIZE + NET_IP_ALIGN))) {
89762306a36Sopenharmony_ci			skb_reserve(newskb, NET_IP_ALIGN);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci			dma_addr = dma_map_single(greth->dev,
90062306a36Sopenharmony_ci						      newskb->data,
90162306a36Sopenharmony_ci						      MAX_FRAME_SIZE + NET_IP_ALIGN,
90262306a36Sopenharmony_ci						      DMA_FROM_DEVICE);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci			if (!dma_mapping_error(greth->dev, dma_addr)) {
90562306a36Sopenharmony_ci				/* Process the incoming frame. */
90662306a36Sopenharmony_ci				pkt_len = status & GRETH_BD_LEN;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci				dma_unmap_single(greth->dev,
90962306a36Sopenharmony_ci						 greth_read_bd(&bdp->addr),
91062306a36Sopenharmony_ci						 MAX_FRAME_SIZE + NET_IP_ALIGN,
91162306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci				if (netif_msg_pktdata(greth))
91462306a36Sopenharmony_ci					greth_print_rx_packet(phys_to_virt(greth_read_bd(&bdp->addr)), pkt_len);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci				skb_put(skb, pkt_len);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci				if (dev->features & NETIF_F_RXCSUM && hw_checksummed(status))
91962306a36Sopenharmony_ci					skb->ip_summed = CHECKSUM_UNNECESSARY;
92062306a36Sopenharmony_ci				else
92162306a36Sopenharmony_ci					skb_checksum_none_assert(skb);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci				skb->protocol = eth_type_trans(skb, dev);
92462306a36Sopenharmony_ci				dev->stats.rx_packets++;
92562306a36Sopenharmony_ci				dev->stats.rx_bytes += pkt_len;
92662306a36Sopenharmony_ci				netif_receive_skb(skb);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci				greth->rx_skbuff[greth->rx_cur] = newskb;
92962306a36Sopenharmony_ci				greth_write_bd(&bdp->addr, dma_addr);
93062306a36Sopenharmony_ci			} else {
93162306a36Sopenharmony_ci				if (net_ratelimit())
93262306a36Sopenharmony_ci					dev_warn(greth->dev, "Could not create DMA mapping, dropping packet\n");
93362306a36Sopenharmony_ci				dev_kfree_skb(newskb);
93462306a36Sopenharmony_ci				/* reusing current skb, so it is a drop */
93562306a36Sopenharmony_ci				dev->stats.rx_dropped++;
93662306a36Sopenharmony_ci			}
93762306a36Sopenharmony_ci		} else if (bad) {
93862306a36Sopenharmony_ci			/* Bad Frame transfer, the skb is reused */
93962306a36Sopenharmony_ci			dev->stats.rx_dropped++;
94062306a36Sopenharmony_ci		} else {
94162306a36Sopenharmony_ci			/* Failed Allocating a new skb. This is rather stupid
94262306a36Sopenharmony_ci			 * but the current "filled" skb is reused, as if
94362306a36Sopenharmony_ci			 * transfer failure. One could argue that RX descriptor
94462306a36Sopenharmony_ci			 * table handling should be divided into cleaning and
94562306a36Sopenharmony_ci			 * filling as the TX part of the driver
94662306a36Sopenharmony_ci			 */
94762306a36Sopenharmony_ci			if (net_ratelimit())
94862306a36Sopenharmony_ci				dev_warn(greth->dev, "Could not allocate SKB, dropping packet\n");
94962306a36Sopenharmony_ci			/* reusing current skb, so it is a drop */
95062306a36Sopenharmony_ci			dev->stats.rx_dropped++;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		status = GRETH_BD_EN | GRETH_BD_IE;
95462306a36Sopenharmony_ci		if (greth->rx_cur == GRETH_RXBD_NUM_MASK) {
95562306a36Sopenharmony_ci			status |= GRETH_BD_WR;
95662306a36Sopenharmony_ci		}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		wmb();
95962306a36Sopenharmony_ci		greth_write_bd(&bdp->stat, status);
96062306a36Sopenharmony_ci		spin_lock_irqsave(&greth->devlock, flags);
96162306a36Sopenharmony_ci		greth_enable_rx(greth);
96262306a36Sopenharmony_ci		spin_unlock_irqrestore(&greth->devlock, flags);
96362306a36Sopenharmony_ci		greth->rx_cur = NEXT_RX(greth->rx_cur);
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return count;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_cistatic int greth_poll(struct napi_struct *napi, int budget)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	struct greth_private *greth;
97362306a36Sopenharmony_ci	int work_done = 0;
97462306a36Sopenharmony_ci	unsigned long flags;
97562306a36Sopenharmony_ci	u32 mask, ctrl;
97662306a36Sopenharmony_ci	greth = container_of(napi, struct greth_private, napi);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cirestart_txrx_poll:
97962306a36Sopenharmony_ci	if (greth->gbit_mac) {
98062306a36Sopenharmony_ci		greth_clean_tx_gbit(greth->netdev);
98162306a36Sopenharmony_ci		work_done += greth_rx_gbit(greth->netdev, budget - work_done);
98262306a36Sopenharmony_ci	} else {
98362306a36Sopenharmony_ci		if (netif_queue_stopped(greth->netdev))
98462306a36Sopenharmony_ci			greth_clean_tx(greth->netdev);
98562306a36Sopenharmony_ci		work_done += greth_rx(greth->netdev, budget - work_done);
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (work_done < budget) {
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		spin_lock_irqsave(&greth->devlock, flags);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		ctrl = GRETH_REGLOAD(greth->regs->control);
99362306a36Sopenharmony_ci		if ((greth->gbit_mac && (greth->tx_last != greth->tx_next)) ||
99462306a36Sopenharmony_ci		    (!greth->gbit_mac && netif_queue_stopped(greth->netdev))) {
99562306a36Sopenharmony_ci			GRETH_REGSAVE(greth->regs->control,
99662306a36Sopenharmony_ci					ctrl | GRETH_TXI | GRETH_RXI);
99762306a36Sopenharmony_ci			mask = GRETH_INT_RX | GRETH_INT_RE |
99862306a36Sopenharmony_ci			       GRETH_INT_TX | GRETH_INT_TE;
99962306a36Sopenharmony_ci		} else {
100062306a36Sopenharmony_ci			GRETH_REGSAVE(greth->regs->control, ctrl | GRETH_RXI);
100162306a36Sopenharmony_ci			mask = GRETH_INT_RX | GRETH_INT_RE;
100262306a36Sopenharmony_ci		}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		if (GRETH_REGLOAD(greth->regs->status) & mask) {
100562306a36Sopenharmony_ci			GRETH_REGSAVE(greth->regs->control, ctrl);
100662306a36Sopenharmony_ci			spin_unlock_irqrestore(&greth->devlock, flags);
100762306a36Sopenharmony_ci			goto restart_txrx_poll;
100862306a36Sopenharmony_ci		} else {
100962306a36Sopenharmony_ci			napi_complete_done(napi, work_done);
101062306a36Sopenharmony_ci			spin_unlock_irqrestore(&greth->devlock, flags);
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	return work_done;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic int greth_set_mac_add(struct net_device *dev, void *p)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct sockaddr *addr = p;
102062306a36Sopenharmony_ci	struct greth_private *greth;
102162306a36Sopenharmony_ci	struct greth_regs *regs;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	greth = netdev_priv(dev);
102462306a36Sopenharmony_ci	regs = greth->regs;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
102762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
103062306a36Sopenharmony_ci	GRETH_REGSAVE(regs->esa_msb, dev->dev_addr[0] << 8 | dev->dev_addr[1]);
103162306a36Sopenharmony_ci	GRETH_REGSAVE(regs->esa_lsb, dev->dev_addr[2] << 24 | dev->dev_addr[3] << 16 |
103262306a36Sopenharmony_ci		      dev->dev_addr[4] << 8 | dev->dev_addr[5]);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	return 0;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic u32 greth_hash_get_index(__u8 *addr)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	return (ether_crc(6, addr)) & 0x3F;
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic void greth_set_hash_filter(struct net_device *dev)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
104562306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
104662306a36Sopenharmony_ci	struct greth_regs *regs = greth->regs;
104762306a36Sopenharmony_ci	u32 mc_filter[2];
104862306a36Sopenharmony_ci	unsigned int bitnr;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	mc_filter[0] = mc_filter[1] = 0;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
105362306a36Sopenharmony_ci		bitnr = greth_hash_get_index(ha->addr);
105462306a36Sopenharmony_ci		mc_filter[bitnr >> 5] |= 1 << (bitnr & 31);
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	GRETH_REGSAVE(regs->hash_msb, mc_filter[1]);
105862306a36Sopenharmony_ci	GRETH_REGSAVE(regs->hash_lsb, mc_filter[0]);
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic void greth_set_multicast_list(struct net_device *dev)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	int cfg;
106462306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
106562306a36Sopenharmony_ci	struct greth_regs *regs = greth->regs;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	cfg = GRETH_REGLOAD(regs->control);
106862306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
106962306a36Sopenharmony_ci		cfg |= GRETH_CTRL_PR;
107062306a36Sopenharmony_ci	else
107162306a36Sopenharmony_ci		cfg &= ~GRETH_CTRL_PR;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (greth->multicast) {
107462306a36Sopenharmony_ci		if (dev->flags & IFF_ALLMULTI) {
107562306a36Sopenharmony_ci			GRETH_REGSAVE(regs->hash_msb, -1);
107662306a36Sopenharmony_ci			GRETH_REGSAVE(regs->hash_lsb, -1);
107762306a36Sopenharmony_ci			cfg |= GRETH_CTRL_MCEN;
107862306a36Sopenharmony_ci			GRETH_REGSAVE(regs->control, cfg);
107962306a36Sopenharmony_ci			return;
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		if (netdev_mc_empty(dev)) {
108362306a36Sopenharmony_ci			cfg &= ~GRETH_CTRL_MCEN;
108462306a36Sopenharmony_ci			GRETH_REGSAVE(regs->control, cfg);
108562306a36Sopenharmony_ci			return;
108662306a36Sopenharmony_ci		}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		/* Setup multicast filter */
108962306a36Sopenharmony_ci		greth_set_hash_filter(dev);
109062306a36Sopenharmony_ci		cfg |= GRETH_CTRL_MCEN;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci	GRETH_REGSAVE(regs->control, cfg);
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic u32 greth_get_msglevel(struct net_device *dev)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
109862306a36Sopenharmony_ci	return greth->msg_enable;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cistatic void greth_set_msglevel(struct net_device *dev, u32 value)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
110462306a36Sopenharmony_ci	greth->msg_enable = value;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic int greth_get_regs_len(struct net_device *dev)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	return sizeof(struct greth_regs);
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	strscpy(info->driver, dev_driver_string(greth->dev),
111762306a36Sopenharmony_ci		sizeof(info->driver));
111862306a36Sopenharmony_ci	strscpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info));
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	int i;
112462306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
112562306a36Sopenharmony_ci	u32 __iomem *greth_regs = (u32 __iomem *) greth->regs;
112662306a36Sopenharmony_ci	u32 *buff = p;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	for (i = 0; i < sizeof(struct greth_regs) / sizeof(u32); i++)
112962306a36Sopenharmony_ci		buff[i] = greth_read_bd(&greth_regs[i]);
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic const struct ethtool_ops greth_ethtool_ops = {
113362306a36Sopenharmony_ci	.get_msglevel		= greth_get_msglevel,
113462306a36Sopenharmony_ci	.set_msglevel		= greth_set_msglevel,
113562306a36Sopenharmony_ci	.get_drvinfo		= greth_get_drvinfo,
113662306a36Sopenharmony_ci	.get_regs_len           = greth_get_regs_len,
113762306a36Sopenharmony_ci	.get_regs               = greth_get_regs,
113862306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
113962306a36Sopenharmony_ci	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
114062306a36Sopenharmony_ci	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
114162306a36Sopenharmony_ci};
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic struct net_device_ops greth_netdev_ops = {
114462306a36Sopenharmony_ci	.ndo_open		= greth_open,
114562306a36Sopenharmony_ci	.ndo_stop		= greth_close,
114662306a36Sopenharmony_ci	.ndo_start_xmit		= greth_start_xmit,
114762306a36Sopenharmony_ci	.ndo_set_mac_address	= greth_set_mac_add,
114862306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
114962306a36Sopenharmony_ci};
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic inline int wait_for_mdio(struct greth_private *greth)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	unsigned long timeout = jiffies + 4*HZ/100;
115462306a36Sopenharmony_ci	while (GRETH_REGLOAD(greth->regs->mdio) & GRETH_MII_BUSY) {
115562306a36Sopenharmony_ci		if (time_after(jiffies, timeout))
115662306a36Sopenharmony_ci			return 0;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci	return 1;
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic int greth_mdio_read(struct mii_bus *bus, int phy, int reg)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	struct greth_private *greth = bus->priv;
116462306a36Sopenharmony_ci	int data;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	if (!wait_for_mdio(greth))
116762306a36Sopenharmony_ci		return -EBUSY;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	GRETH_REGSAVE(greth->regs->mdio, ((phy & 0x1F) << 11) | ((reg & 0x1F) << 6) | 2);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (!wait_for_mdio(greth))
117262306a36Sopenharmony_ci		return -EBUSY;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (!(GRETH_REGLOAD(greth->regs->mdio) & GRETH_MII_NVALID)) {
117562306a36Sopenharmony_ci		data = (GRETH_REGLOAD(greth->regs->mdio) >> 16) & 0xFFFF;
117662306a36Sopenharmony_ci		return data;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	} else {
117962306a36Sopenharmony_ci		return -1;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int greth_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	struct greth_private *greth = bus->priv;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (!wait_for_mdio(greth))
118862306a36Sopenharmony_ci		return -EBUSY;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	GRETH_REGSAVE(greth->regs->mdio,
119162306a36Sopenharmony_ci		      ((val & 0xFFFF) << 16) | ((phy & 0x1F) << 11) | ((reg & 0x1F) << 6) | 1);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (!wait_for_mdio(greth))
119462306a36Sopenharmony_ci		return -EBUSY;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return 0;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic void greth_link_change(struct net_device *dev)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
120262306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
120362306a36Sopenharmony_ci	unsigned long flags;
120462306a36Sopenharmony_ci	int status_change = 0;
120562306a36Sopenharmony_ci	u32 ctrl;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	spin_lock_irqsave(&greth->devlock, flags);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	if (phydev->link) {
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		if ((greth->speed != phydev->speed) || (greth->duplex != phydev->duplex)) {
121262306a36Sopenharmony_ci			ctrl = GRETH_REGLOAD(greth->regs->control) &
121362306a36Sopenharmony_ci			       ~(GRETH_CTRL_FD | GRETH_CTRL_SP | GRETH_CTRL_GB);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci			if (phydev->duplex)
121662306a36Sopenharmony_ci				ctrl |= GRETH_CTRL_FD;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci			if (phydev->speed == SPEED_100)
121962306a36Sopenharmony_ci				ctrl |= GRETH_CTRL_SP;
122062306a36Sopenharmony_ci			else if (phydev->speed == SPEED_1000)
122162306a36Sopenharmony_ci				ctrl |= GRETH_CTRL_GB;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci			GRETH_REGSAVE(greth->regs->control, ctrl);
122462306a36Sopenharmony_ci			greth->speed = phydev->speed;
122562306a36Sopenharmony_ci			greth->duplex = phydev->duplex;
122662306a36Sopenharmony_ci			status_change = 1;
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (phydev->link != greth->link) {
123162306a36Sopenharmony_ci		if (!phydev->link) {
123262306a36Sopenharmony_ci			greth->speed = 0;
123362306a36Sopenharmony_ci			greth->duplex = -1;
123462306a36Sopenharmony_ci		}
123562306a36Sopenharmony_ci		greth->link = phydev->link;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		status_change = 1;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	spin_unlock_irqrestore(&greth->devlock, flags);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (status_change) {
124362306a36Sopenharmony_ci		if (phydev->link)
124462306a36Sopenharmony_ci			pr_debug("%s: link up (%d/%s)\n",
124562306a36Sopenharmony_ci				dev->name, phydev->speed,
124662306a36Sopenharmony_ci				DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
124762306a36Sopenharmony_ci		else
124862306a36Sopenharmony_ci			pr_debug("%s: link down\n", dev->name);
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic int greth_mdio_probe(struct net_device *dev)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(dev);
125562306a36Sopenharmony_ci	struct phy_device *phy = NULL;
125662306a36Sopenharmony_ci	int ret;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	/* Find the first PHY */
125962306a36Sopenharmony_ci	phy = phy_find_first(greth->mdio);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	if (!phy) {
126262306a36Sopenharmony_ci		if (netif_msg_probe(greth))
126362306a36Sopenharmony_ci			dev_err(&dev->dev, "no PHY found\n");
126462306a36Sopenharmony_ci		return -ENXIO;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	ret = phy_connect_direct(dev, phy, &greth_link_change,
126862306a36Sopenharmony_ci				 greth->gbit_mac ? PHY_INTERFACE_MODE_GMII : PHY_INTERFACE_MODE_MII);
126962306a36Sopenharmony_ci	if (ret) {
127062306a36Sopenharmony_ci		if (netif_msg_ifup(greth))
127162306a36Sopenharmony_ci			dev_err(&dev->dev, "could not attach to PHY\n");
127262306a36Sopenharmony_ci		return ret;
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (greth->gbit_mac)
127662306a36Sopenharmony_ci		phy_set_max_speed(phy, SPEED_1000);
127762306a36Sopenharmony_ci	else
127862306a36Sopenharmony_ci		phy_set_max_speed(phy, SPEED_100);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	linkmode_copy(phy->advertising, phy->supported);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	greth->link = 0;
128362306a36Sopenharmony_ci	greth->speed = 0;
128462306a36Sopenharmony_ci	greth->duplex = -1;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	return 0;
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int greth_mdio_init(struct greth_private *greth)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	int ret;
129262306a36Sopenharmony_ci	unsigned long timeout;
129362306a36Sopenharmony_ci	struct net_device *ndev = greth->netdev;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	greth->mdio = mdiobus_alloc();
129662306a36Sopenharmony_ci	if (!greth->mdio) {
129762306a36Sopenharmony_ci		return -ENOMEM;
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	greth->mdio->name = "greth-mdio";
130162306a36Sopenharmony_ci	snprintf(greth->mdio->id, MII_BUS_ID_SIZE, "%s-%d", greth->mdio->name, greth->irq);
130262306a36Sopenharmony_ci	greth->mdio->read = greth_mdio_read;
130362306a36Sopenharmony_ci	greth->mdio->write = greth_mdio_write;
130462306a36Sopenharmony_ci	greth->mdio->priv = greth;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	ret = mdiobus_register(greth->mdio);
130762306a36Sopenharmony_ci	if (ret) {
130862306a36Sopenharmony_ci		goto error;
130962306a36Sopenharmony_ci	}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	ret = greth_mdio_probe(greth->netdev);
131262306a36Sopenharmony_ci	if (ret) {
131362306a36Sopenharmony_ci		if (netif_msg_probe(greth))
131462306a36Sopenharmony_ci			dev_err(&greth->netdev->dev, "failed to probe MDIO bus\n");
131562306a36Sopenharmony_ci		goto unreg_mdio;
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	phy_start(ndev->phydev);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* If Ethernet debug link is used make autoneg happen right away */
132162306a36Sopenharmony_ci	if (greth->edcl && greth_edcl == 1) {
132262306a36Sopenharmony_ci		phy_start_aneg(ndev->phydev);
132362306a36Sopenharmony_ci		timeout = jiffies + 6*HZ;
132462306a36Sopenharmony_ci		while (!phy_aneg_done(ndev->phydev) &&
132562306a36Sopenharmony_ci		       time_before(jiffies, timeout)) {
132662306a36Sopenharmony_ci		}
132762306a36Sopenharmony_ci		phy_read_status(ndev->phydev);
132862306a36Sopenharmony_ci		greth_link_change(greth->netdev);
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	return 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ciunreg_mdio:
133462306a36Sopenharmony_ci	mdiobus_unregister(greth->mdio);
133562306a36Sopenharmony_cierror:
133662306a36Sopenharmony_ci	mdiobus_free(greth->mdio);
133762306a36Sopenharmony_ci	return ret;
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci/* Initialize the GRETH MAC */
134162306a36Sopenharmony_cistatic int greth_of_probe(struct platform_device *ofdev)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	struct net_device *dev;
134462306a36Sopenharmony_ci	struct greth_private *greth;
134562306a36Sopenharmony_ci	struct greth_regs *regs;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	int i;
134862306a36Sopenharmony_ci	int err;
134962306a36Sopenharmony_ci	int tmp;
135062306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
135162306a36Sopenharmony_ci	unsigned long timeout;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct greth_private));
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (dev == NULL)
135662306a36Sopenharmony_ci		return -ENOMEM;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	greth = netdev_priv(dev);
135962306a36Sopenharmony_ci	greth->netdev = dev;
136062306a36Sopenharmony_ci	greth->dev = &ofdev->dev;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	if (greth_debug > 0)
136362306a36Sopenharmony_ci		greth->msg_enable = greth_debug;
136462306a36Sopenharmony_ci	else
136562306a36Sopenharmony_ci		greth->msg_enable = GRETH_DEF_MSG_ENABLE;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	spin_lock_init(&greth->devlock);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	greth->regs = of_ioremap(&ofdev->resource[0], 0,
137062306a36Sopenharmony_ci				 resource_size(&ofdev->resource[0]),
137162306a36Sopenharmony_ci				 "grlib-greth regs");
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (greth->regs == NULL) {
137462306a36Sopenharmony_ci		if (netif_msg_probe(greth))
137562306a36Sopenharmony_ci			dev_err(greth->dev, "ioremap failure.\n");
137662306a36Sopenharmony_ci		err = -EIO;
137762306a36Sopenharmony_ci		goto error1;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	regs = greth->regs;
138162306a36Sopenharmony_ci	greth->irq = ofdev->archdata.irqs[0];
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	dev_set_drvdata(greth->dev, dev);
138462306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, greth->dev);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (netif_msg_probe(greth))
138762306a36Sopenharmony_ci		dev_dbg(greth->dev, "resetting controller.\n");
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	/* Reset the controller. */
139062306a36Sopenharmony_ci	GRETH_REGSAVE(regs->control, GRETH_RESET);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* Wait for MAC to reset itself */
139362306a36Sopenharmony_ci	timeout = jiffies + HZ/100;
139462306a36Sopenharmony_ci	while (GRETH_REGLOAD(regs->control) & GRETH_RESET) {
139562306a36Sopenharmony_ci		if (time_after(jiffies, timeout)) {
139662306a36Sopenharmony_ci			err = -EIO;
139762306a36Sopenharmony_ci			if (netif_msg_probe(greth))
139862306a36Sopenharmony_ci				dev_err(greth->dev, "timeout when waiting for reset.\n");
139962306a36Sopenharmony_ci			goto error2;
140062306a36Sopenharmony_ci		}
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* Get default PHY address  */
140462306a36Sopenharmony_ci	greth->phyaddr = (GRETH_REGLOAD(regs->mdio) >> 11) & 0x1F;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	/* Check if we have GBIT capable MAC */
140762306a36Sopenharmony_ci	tmp = GRETH_REGLOAD(regs->control);
140862306a36Sopenharmony_ci	greth->gbit_mac = (tmp >> 27) & 1;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	/* Check for multicast capability */
141162306a36Sopenharmony_ci	greth->multicast = (tmp >> 25) & 1;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	greth->edcl = (tmp >> 31) & 1;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	/* If we have EDCL we disable the EDCL speed-duplex FSM so
141662306a36Sopenharmony_ci	 * it doesn't interfere with the software */
141762306a36Sopenharmony_ci	if (greth->edcl != 0)
141862306a36Sopenharmony_ci		GRETH_REGORIN(regs->control, GRETH_CTRL_DISDUPLEX);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/* Check if MAC can handle MDIO interrupts */
142162306a36Sopenharmony_ci	greth->mdio_int_en = (tmp >> 26) & 1;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	err = greth_mdio_init(greth);
142462306a36Sopenharmony_ci	if (err) {
142562306a36Sopenharmony_ci		if (netif_msg_probe(greth))
142662306a36Sopenharmony_ci			dev_err(greth->dev, "failed to register MDIO bus\n");
142762306a36Sopenharmony_ci		goto error2;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	/* Allocate TX descriptor ring in coherent memory */
143162306a36Sopenharmony_ci	greth->tx_bd_base = dma_alloc_coherent(greth->dev, 1024,
143262306a36Sopenharmony_ci					       &greth->tx_bd_base_phys,
143362306a36Sopenharmony_ci					       GFP_KERNEL);
143462306a36Sopenharmony_ci	if (!greth->tx_bd_base) {
143562306a36Sopenharmony_ci		err = -ENOMEM;
143662306a36Sopenharmony_ci		goto error3;
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	/* Allocate RX descriptor ring in coherent memory */
144062306a36Sopenharmony_ci	greth->rx_bd_base = dma_alloc_coherent(greth->dev, 1024,
144162306a36Sopenharmony_ci					       &greth->rx_bd_base_phys,
144262306a36Sopenharmony_ci					       GFP_KERNEL);
144362306a36Sopenharmony_ci	if (!greth->rx_bd_base) {
144462306a36Sopenharmony_ci		err = -ENOMEM;
144562306a36Sopenharmony_ci		goto error4;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	/* Get MAC address from: module param, OF property or ID prom */
144962306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
145062306a36Sopenharmony_ci		if (macaddr[i] != 0)
145162306a36Sopenharmony_ci			break;
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci	if (i == 6) {
145462306a36Sopenharmony_ci		err = of_get_mac_address(ofdev->dev.of_node, addr);
145562306a36Sopenharmony_ci		if (!err) {
145662306a36Sopenharmony_ci			for (i = 0; i < 6; i++)
145762306a36Sopenharmony_ci				macaddr[i] = (unsigned int) addr[i];
145862306a36Sopenharmony_ci		} else {
145962306a36Sopenharmony_ci#ifdef CONFIG_SPARC
146062306a36Sopenharmony_ci			for (i = 0; i < 6; i++)
146162306a36Sopenharmony_ci				macaddr[i] = (unsigned int) idprom->id_ethaddr[i];
146262306a36Sopenharmony_ci#endif
146362306a36Sopenharmony_ci		}
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
146762306a36Sopenharmony_ci		addr[i] = macaddr[i];
146862306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	macaddr[5]++;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (!is_valid_ether_addr(&dev->dev_addr[0])) {
147362306a36Sopenharmony_ci		if (netif_msg_probe(greth))
147462306a36Sopenharmony_ci			dev_err(greth->dev, "no valid ethernet address, aborting.\n");
147562306a36Sopenharmony_ci		err = -EINVAL;
147662306a36Sopenharmony_ci		goto error5;
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	GRETH_REGSAVE(regs->esa_msb, dev->dev_addr[0] << 8 | dev->dev_addr[1]);
148062306a36Sopenharmony_ci	GRETH_REGSAVE(regs->esa_lsb, dev->dev_addr[2] << 24 | dev->dev_addr[3] << 16 |
148162306a36Sopenharmony_ci		      dev->dev_addr[4] << 8 | dev->dev_addr[5]);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	/* Clear all pending interrupts except PHY irq */
148462306a36Sopenharmony_ci	GRETH_REGSAVE(regs->status, 0xFF);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	if (greth->gbit_mac) {
148762306a36Sopenharmony_ci		dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
148862306a36Sopenharmony_ci			NETIF_F_RXCSUM;
148962306a36Sopenharmony_ci		dev->features = dev->hw_features | NETIF_F_HIGHDMA;
149062306a36Sopenharmony_ci		greth_netdev_ops.ndo_start_xmit = greth_start_xmit_gbit;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (greth->multicast) {
149462306a36Sopenharmony_ci		greth_netdev_ops.ndo_set_rx_mode = greth_set_multicast_list;
149562306a36Sopenharmony_ci		dev->flags |= IFF_MULTICAST;
149662306a36Sopenharmony_ci	} else {
149762306a36Sopenharmony_ci		dev->flags &= ~IFF_MULTICAST;
149862306a36Sopenharmony_ci	}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	dev->netdev_ops = &greth_netdev_ops;
150162306a36Sopenharmony_ci	dev->ethtool_ops = &greth_ethtool_ops;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	err = register_netdev(dev);
150462306a36Sopenharmony_ci	if (err) {
150562306a36Sopenharmony_ci		if (netif_msg_probe(greth))
150662306a36Sopenharmony_ci			dev_err(greth->dev, "netdevice registration failed.\n");
150762306a36Sopenharmony_ci		goto error5;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	/* setup NAPI */
151162306a36Sopenharmony_ci	netif_napi_add(dev, &greth->napi, greth_poll);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	return 0;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cierror5:
151662306a36Sopenharmony_ci	dma_free_coherent(greth->dev, 1024, greth->rx_bd_base, greth->rx_bd_base_phys);
151762306a36Sopenharmony_cierror4:
151862306a36Sopenharmony_ci	dma_free_coherent(greth->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys);
151962306a36Sopenharmony_cierror3:
152062306a36Sopenharmony_ci	mdiobus_unregister(greth->mdio);
152162306a36Sopenharmony_cierror2:
152262306a36Sopenharmony_ci	of_iounmap(&ofdev->resource[0], greth->regs, resource_size(&ofdev->resource[0]));
152362306a36Sopenharmony_cierror1:
152462306a36Sopenharmony_ci	free_netdev(dev);
152562306a36Sopenharmony_ci	return err;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_cistatic int greth_of_remove(struct platform_device *of_dev)
152962306a36Sopenharmony_ci{
153062306a36Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(of_dev);
153162306a36Sopenharmony_ci	struct greth_private *greth = netdev_priv(ndev);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	/* Free descriptor areas */
153462306a36Sopenharmony_ci	dma_free_coherent(&of_dev->dev, 1024, greth->rx_bd_base, greth->rx_bd_base_phys);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	dma_free_coherent(&of_dev->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	if (ndev->phydev)
153962306a36Sopenharmony_ci		phy_stop(ndev->phydev);
154062306a36Sopenharmony_ci	mdiobus_unregister(greth->mdio);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	unregister_netdev(ndev);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	of_iounmap(&of_dev->resource[0], greth->regs, resource_size(&of_dev->resource[0]));
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	free_netdev(ndev);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	return 0;
154962306a36Sopenharmony_ci}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_cistatic const struct of_device_id greth_of_match[] = {
155262306a36Sopenharmony_ci	{
155362306a36Sopenharmony_ci	 .name = "GAISLER_ETHMAC",
155462306a36Sopenharmony_ci	 },
155562306a36Sopenharmony_ci	{
155662306a36Sopenharmony_ci	 .name = "01_01d",
155762306a36Sopenharmony_ci	 },
155862306a36Sopenharmony_ci	{},
155962306a36Sopenharmony_ci};
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, greth_of_match);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic struct platform_driver greth_of_driver = {
156462306a36Sopenharmony_ci	.driver = {
156562306a36Sopenharmony_ci		.name = "grlib-greth",
156662306a36Sopenharmony_ci		.of_match_table = greth_of_match,
156762306a36Sopenharmony_ci	},
156862306a36Sopenharmony_ci	.probe = greth_of_probe,
156962306a36Sopenharmony_ci	.remove = greth_of_remove,
157062306a36Sopenharmony_ci};
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_cimodule_platform_driver(greth_of_driver);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ciMODULE_AUTHOR("Aeroflex Gaisler AB.");
157562306a36Sopenharmony_ciMODULE_DESCRIPTION("Aeroflex Gaisler Ethernet MAC driver");
157662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1577