162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Faraday FTMAC100 10/100 Ethernet
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright 2009-2011 Faraday Technology
662306a36Sopenharmony_ci * Po-Yu Chuang <ratbert@faraday-tech.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/etherdevice.h>
1362306a36Sopenharmony_ci#include <linux/ethtool.h>
1462306a36Sopenharmony_ci#include <linux/if_ether.h>
1562306a36Sopenharmony_ci#include <linux/if_vlan.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/mii.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
2262306a36Sopenharmony_ci#include <linux/netdevice.h>
2362306a36Sopenharmony_ci#include <linux/platform_device.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "ftmac100.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DRV_NAME	"ftmac100"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define RX_QUEUE_ENTRIES	128	/* must be power of 2 */
3062306a36Sopenharmony_ci#define TX_QUEUE_ENTRIES	16	/* must be power of 2 */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define RX_BUF_SIZE		2044	/* must be smaller than 0x7ff */
3362306a36Sopenharmony_ci#define MAX_PKT_SIZE		RX_BUF_SIZE /* multi-segment not supported */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#if MAX_PKT_SIZE > 0x7ff
3662306a36Sopenharmony_ci#error invalid MAX_PKT_SIZE
3762306a36Sopenharmony_ci#endif
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#if RX_BUF_SIZE > 0x7ff || RX_BUF_SIZE > PAGE_SIZE
4062306a36Sopenharmony_ci#error invalid RX_BUF_SIZE
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/******************************************************************************
4462306a36Sopenharmony_ci * private data
4562306a36Sopenharmony_ci *****************************************************************************/
4662306a36Sopenharmony_cistruct ftmac100_descs {
4762306a36Sopenharmony_ci	struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
4862306a36Sopenharmony_ci	struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES];
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct ftmac100 {
5262306a36Sopenharmony_ci	struct resource *res;
5362306a36Sopenharmony_ci	void __iomem *base;
5462306a36Sopenharmony_ci	int irq;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	struct ftmac100_descs *descs;
5762306a36Sopenharmony_ci	dma_addr_t descs_dma_addr;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	unsigned int rx_pointer;
6062306a36Sopenharmony_ci	unsigned int tx_clean_pointer;
6162306a36Sopenharmony_ci	unsigned int tx_pointer;
6262306a36Sopenharmony_ci	unsigned int tx_pending;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	spinlock_t tx_lock;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	struct net_device *netdev;
6762306a36Sopenharmony_ci	struct device *dev;
6862306a36Sopenharmony_ci	struct napi_struct napi;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	struct mii_if_info mii;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int ftmac100_alloc_rx_page(struct ftmac100 *priv,
7462306a36Sopenharmony_ci				  struct ftmac100_rxdes *rxdes, gfp_t gfp);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/******************************************************************************
7762306a36Sopenharmony_ci * internal functions (hardware register access)
7862306a36Sopenharmony_ci *****************************************************************************/
7962306a36Sopenharmony_ci#define INT_MASK_ALL_ENABLED	(FTMAC100_INT_RPKT_FINISH	| \
8062306a36Sopenharmony_ci				 FTMAC100_INT_NORXBUF		| \
8162306a36Sopenharmony_ci				 FTMAC100_INT_XPKT_OK		| \
8262306a36Sopenharmony_ci				 FTMAC100_INT_XPKT_LOST		| \
8362306a36Sopenharmony_ci				 FTMAC100_INT_RPKT_LOST		| \
8462306a36Sopenharmony_ci				 FTMAC100_INT_AHB_ERR		| \
8562306a36Sopenharmony_ci				 FTMAC100_INT_PHYSTS_CHG)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define INT_MASK_ALL_DISABLED	0
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void ftmac100_enable_all_int(struct ftmac100 *priv)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void ftmac100_disable_all_int(struct ftmac100 *priv)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	iowrite32(INT_MASK_ALL_DISABLED, priv->base + FTMAC100_OFFSET_IMR);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void ftmac100_txdma_start_polling(struct ftmac100 *priv)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int ftmac100_reset(struct ftmac100 *priv)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
11762306a36Sopenharmony_ci	int i;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* NOTE: reset clears all registers */
12062306a36Sopenharmony_ci	iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
12362306a36Sopenharmony_ci		unsigned int maccr;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
12662306a36Sopenharmony_ci		if (!(maccr & FTMAC100_MACCR_SW_RST)) {
12762306a36Sopenharmony_ci			/*
12862306a36Sopenharmony_ci			 * FTMAC100_MACCR_SW_RST cleared does not indicate
12962306a36Sopenharmony_ci			 * that hardware reset completed (what the f*ck).
13062306a36Sopenharmony_ci			 * We still need to wait for a while.
13162306a36Sopenharmony_ci			 */
13262306a36Sopenharmony_ci			udelay(500);
13362306a36Sopenharmony_ci			return 0;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		udelay(1000);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	netdev_err(netdev, "software reset failed\n");
14062306a36Sopenharmony_ci	return -EIO;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	unsigned int maddr = mac[0] << 8 | mac[1];
14662306a36Sopenharmony_ci	unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR);
14962306a36Sopenharmony_ci	iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void ftmac100_setup_mc_ht(struct ftmac100 *priv)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
15562306a36Sopenharmony_ci	u64 maht = 0; /* Multicast Address Hash Table */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, priv->netdev) {
15862306a36Sopenharmony_ci		u32 hash = ether_crc(ETH_ALEN, ha->addr) >> 26;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		maht |= BIT_ULL(hash);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	iowrite32(lower_32_bits(maht), priv->base + FTMAC100_OFFSET_MAHT0);
16462306a36Sopenharmony_ci	iowrite32(upper_32_bits(maht), priv->base + FTMAC100_OFFSET_MAHT1);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void ftmac100_set_rx_bits(struct ftmac100 *priv, unsigned int *maccr)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* Clear all */
17262306a36Sopenharmony_ci	*maccr &= ~(FTMAC100_MACCR_RCV_ALL | FTMAC100_MACCR_RX_MULTIPKT |
17362306a36Sopenharmony_ci		   FTMAC100_MACCR_HT_MULTI_EN);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Set the requested bits */
17662306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC)
17762306a36Sopenharmony_ci		*maccr |= FTMAC100_MACCR_RCV_ALL;
17862306a36Sopenharmony_ci	if (netdev->flags & IFF_ALLMULTI)
17962306a36Sopenharmony_ci		*maccr |= FTMAC100_MACCR_RX_MULTIPKT;
18062306a36Sopenharmony_ci	else if (netdev_mc_count(netdev)) {
18162306a36Sopenharmony_ci		*maccr |= FTMAC100_MACCR_HT_MULTI_EN;
18262306a36Sopenharmony_ci		ftmac100_setup_mc_ht(priv);
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci#define MACCR_ENABLE_ALL	(FTMAC100_MACCR_XMT_EN	| \
18762306a36Sopenharmony_ci				 FTMAC100_MACCR_RCV_EN	| \
18862306a36Sopenharmony_ci				 FTMAC100_MACCR_XDMA_EN	| \
18962306a36Sopenharmony_ci				 FTMAC100_MACCR_RDMA_EN	| \
19062306a36Sopenharmony_ci				 FTMAC100_MACCR_CRC_APD	| \
19162306a36Sopenharmony_ci				 FTMAC100_MACCR_FULLDUP	| \
19262306a36Sopenharmony_ci				 FTMAC100_MACCR_RX_RUNT	| \
19362306a36Sopenharmony_ci				 FTMAC100_MACCR_RX_BROADPKT)
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int ftmac100_start_hw(struct ftmac100 *priv)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
19862306a36Sopenharmony_ci	unsigned int maccr = MACCR_ENABLE_ALL;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (ftmac100_reset(priv))
20162306a36Sopenharmony_ci		return -EIO;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* setup ring buffer base registers */
20462306a36Sopenharmony_ci	ftmac100_set_rx_ring_base(priv,
20562306a36Sopenharmony_ci				  priv->descs_dma_addr +
20662306a36Sopenharmony_ci				  offsetof(struct ftmac100_descs, rxdes));
20762306a36Sopenharmony_ci	ftmac100_set_tx_ring_base(priv,
20862306a36Sopenharmony_ci				  priv->descs_dma_addr +
20962306a36Sopenharmony_ci				  offsetof(struct ftmac100_descs, txdes));
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	iowrite32(FTMAC100_APTC_RXPOLL_CNT(1), priv->base + FTMAC100_OFFSET_APTC);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	ftmac100_set_mac(priv, netdev->dev_addr);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	 /* See ftmac100_change_mtu() */
21662306a36Sopenharmony_ci	if (netdev->mtu > ETH_DATA_LEN)
21762306a36Sopenharmony_ci		maccr |= FTMAC100_MACCR_RX_FTL;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ftmac100_set_rx_bits(priv, &maccr);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR);
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void ftmac100_stop_hw(struct ftmac100 *priv)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/******************************************************************************
23162306a36Sopenharmony_ci * internal functions (receive descriptor)
23262306a36Sopenharmony_ci *****************************************************************************/
23362306a36Sopenharmony_cistatic bool ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FRS);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_LRS);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	/* clear status bits */
25162306a36Sopenharmony_ci	rxdes->rxdes0 = cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic bool ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ERR);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ODD_NB);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic unsigned int ftmac100_rxdes_frame_length(struct ftmac100_rxdes *rxdes)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RFL;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic bool ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_MULTICAST);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes,
28562306a36Sopenharmony_ci					   unsigned int size)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	rxdes->rxdes1 &= cpu_to_le32(FTMAC100_RXDES1_EDORR);
28862306a36Sopenharmony_ci	rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_RXBUF_SIZE(size));
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_EDORR);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
29762306a36Sopenharmony_ci					dma_addr_t addr)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	rxdes->rxdes2 = cpu_to_le32(addr);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	return le32_to_cpu(rxdes->rxdes2);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci/*
30862306a36Sopenharmony_ci * rxdes3 is not used by hardware. We use it to keep track of page.
30962306a36Sopenharmony_ci * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_cistatic void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	rxdes->rxdes3 = (unsigned int)page;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	return (struct page *)rxdes->rxdes3;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/******************************************************************************
32262306a36Sopenharmony_ci * internal functions (receive)
32362306a36Sopenharmony_ci *****************************************************************************/
32462306a36Sopenharmony_cistatic int ftmac100_next_rx_pointer(int pointer)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	return &priv->descs->rxdes[priv->rx_pointer];
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic struct ftmac100_rxdes *
34062306a36Sopenharmony_ciftmac100_rx_locate_first_segment(struct ftmac100 *priv)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	while (!ftmac100_rxdes_owned_by_dma(rxdes)) {
34562306a36Sopenharmony_ci		if (ftmac100_rxdes_first_segment(rxdes))
34662306a36Sopenharmony_ci			return rxdes;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		ftmac100_rxdes_set_dma_own(rxdes);
34962306a36Sopenharmony_ci		ftmac100_rx_pointer_advance(priv);
35062306a36Sopenharmony_ci		rxdes = ftmac100_current_rxdes(priv);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return NULL;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic bool ftmac100_rx_packet_error(struct ftmac100 *priv,
35762306a36Sopenharmony_ci				     struct ftmac100_rxdes *rxdes)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
36062306a36Sopenharmony_ci	bool error = false;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (unlikely(ftmac100_rxdes_rx_error(rxdes))) {
36362306a36Sopenharmony_ci		if (net_ratelimit())
36462306a36Sopenharmony_ci			netdev_info(netdev, "rx err\n");
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		netdev->stats.rx_errors++;
36762306a36Sopenharmony_ci		error = true;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (unlikely(ftmac100_rxdes_crc_error(rxdes))) {
37162306a36Sopenharmony_ci		if (net_ratelimit())
37262306a36Sopenharmony_ci			netdev_info(netdev, "rx crc err\n");
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		netdev->stats.rx_crc_errors++;
37562306a36Sopenharmony_ci		error = true;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (unlikely(ftmac100_rxdes_runt(rxdes))) {
37962306a36Sopenharmony_ci		if (net_ratelimit())
38062306a36Sopenharmony_ci			netdev_info(netdev, "rx runt\n");
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		netdev->stats.rx_length_errors++;
38362306a36Sopenharmony_ci		error = true;
38462306a36Sopenharmony_ci	} else if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) {
38562306a36Sopenharmony_ci		if (net_ratelimit())
38662306a36Sopenharmony_ci			netdev_info(netdev, "rx odd nibble\n");
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		netdev->stats.rx_length_errors++;
38962306a36Sopenharmony_ci		error = true;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	/*
39262306a36Sopenharmony_ci	 * FTMAC100_RXDES0_FTL is not an error, it just indicates that the
39362306a36Sopenharmony_ci	 * frame is longer than 1518 octets. Receiving these is possible when
39462306a36Sopenharmony_ci	 * we told the hardware not to drop them, via FTMAC100_MACCR_RX_FTL.
39562306a36Sopenharmony_ci	 */
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return error;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void ftmac100_rx_drop_packet(struct ftmac100 *priv)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
40362306a36Sopenharmony_ci	struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
40462306a36Sopenharmony_ci	bool done = false;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (net_ratelimit())
40762306a36Sopenharmony_ci		netdev_dbg(netdev, "drop packet %p\n", rxdes);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	do {
41062306a36Sopenharmony_ci		if (ftmac100_rxdes_last_segment(rxdes))
41162306a36Sopenharmony_ci			done = true;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		ftmac100_rxdes_set_dma_own(rxdes);
41462306a36Sopenharmony_ci		ftmac100_rx_pointer_advance(priv);
41562306a36Sopenharmony_ci		rxdes = ftmac100_current_rxdes(priv);
41662306a36Sopenharmony_ci	} while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	netdev->stats.rx_dropped++;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
42462306a36Sopenharmony_ci	struct ftmac100_rxdes *rxdes;
42562306a36Sopenharmony_ci	struct sk_buff *skb;
42662306a36Sopenharmony_ci	struct page *page;
42762306a36Sopenharmony_ci	dma_addr_t map;
42862306a36Sopenharmony_ci	int length;
42962306a36Sopenharmony_ci	bool ret;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	rxdes = ftmac100_rx_locate_first_segment(priv);
43262306a36Sopenharmony_ci	if (!rxdes)
43362306a36Sopenharmony_ci		return false;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
43662306a36Sopenharmony_ci		ftmac100_rx_drop_packet(priv);
43762306a36Sopenharmony_ci		return true;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* We don't support multi-segment packets for now, so drop them. */
44162306a36Sopenharmony_ci	ret = ftmac100_rxdes_last_segment(rxdes);
44262306a36Sopenharmony_ci	if (unlikely(!ret)) {
44362306a36Sopenharmony_ci		netdev->stats.rx_length_errors++;
44462306a36Sopenharmony_ci		ftmac100_rx_drop_packet(priv);
44562306a36Sopenharmony_ci		return true;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* start processing */
44962306a36Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(netdev, 128);
45062306a36Sopenharmony_ci	if (unlikely(!skb)) {
45162306a36Sopenharmony_ci		if (net_ratelimit())
45262306a36Sopenharmony_ci			netdev_err(netdev, "rx skb alloc failed\n");
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		ftmac100_rx_drop_packet(priv);
45562306a36Sopenharmony_ci		return true;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (unlikely(ftmac100_rxdes_multicast(rxdes)))
45962306a36Sopenharmony_ci		netdev->stats.multicast++;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	map = ftmac100_rxdes_get_dma_addr(rxdes);
46262306a36Sopenharmony_ci	dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	length = ftmac100_rxdes_frame_length(rxdes);
46562306a36Sopenharmony_ci	page = ftmac100_rxdes_get_page(rxdes);
46662306a36Sopenharmony_ci	skb_fill_page_desc(skb, 0, page, 0, length);
46762306a36Sopenharmony_ci	skb->len += length;
46862306a36Sopenharmony_ci	skb->data_len += length;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (length > 128) {
47162306a36Sopenharmony_ci		skb->truesize += PAGE_SIZE;
47262306a36Sopenharmony_ci		/* We pull the minimum amount into linear part */
47362306a36Sopenharmony_ci		__pskb_pull_tail(skb, ETH_HLEN);
47462306a36Sopenharmony_ci	} else {
47562306a36Sopenharmony_ci		/* Small frames are copied into linear part to free one page */
47662306a36Sopenharmony_ci		__pskb_pull_tail(skb, length);
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci	ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ftmac100_rx_pointer_advance(priv);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	netdev->stats.rx_packets++;
48562306a36Sopenharmony_ci	netdev->stats.rx_bytes += skb->len;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* push packet to protocol stack */
48862306a36Sopenharmony_ci	netif_receive_skb(skb);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	(*processed)++;
49162306a36Sopenharmony_ci	return true;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/******************************************************************************
49562306a36Sopenharmony_ci * internal functions (transmit descriptor)
49662306a36Sopenharmony_ci *****************************************************************************/
49762306a36Sopenharmony_cistatic void ftmac100_txdes_reset(struct ftmac100_txdes *txdes)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	/* clear all except end of ring bit */
50062306a36Sopenharmony_ci	txdes->txdes0 = 0;
50162306a36Sopenharmony_ci	txdes->txdes1 &= cpu_to_le32(FTMAC100_TXDES1_EDOTR);
50262306a36Sopenharmony_ci	txdes->txdes2 = 0;
50362306a36Sopenharmony_ci	txdes->txdes3 = 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	/*
51462306a36Sopenharmony_ci	 * Make sure dma own bit will not be set before any other
51562306a36Sopenharmony_ci	 * descriptor fields.
51662306a36Sopenharmony_ci	 */
51762306a36Sopenharmony_ci	wmb();
51862306a36Sopenharmony_ci	txdes->txdes0 |= cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_EXSCOL);
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_LATECOL);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_EDOTR);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_FTS);
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_LTS);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXIC);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes,
55262306a36Sopenharmony_ci					   unsigned int len)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXBUF_SIZE(len));
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
55862306a36Sopenharmony_ci					dma_addr_t addr)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	txdes->txdes2 = cpu_to_le32(addr);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	return le32_to_cpu(txdes->txdes2);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/*
56962306a36Sopenharmony_ci * txdes3 is not used by hardware. We use it to keep track of socket buffer.
57062306a36Sopenharmony_ci * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
57162306a36Sopenharmony_ci */
57262306a36Sopenharmony_cistatic void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	txdes->txdes3 = (unsigned int)skb;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	return (struct sk_buff *)txdes->txdes3;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/******************************************************************************
58362306a36Sopenharmony_ci * internal functions (transmit)
58462306a36Sopenharmony_ci *****************************************************************************/
58562306a36Sopenharmony_cistatic int ftmac100_next_tx_pointer(int pointer)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic void ftmac100_tx_pointer_advance(struct ftmac100 *priv)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer);
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	return &priv->descs->txdes[priv->tx_pointer];
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic struct ftmac100_txdes *ftmac100_current_clean_txdes(struct ftmac100 *priv)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	return &priv->descs->txdes[priv->tx_clean_pointer];
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic bool ftmac100_tx_complete_packet(struct ftmac100 *priv)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
61362306a36Sopenharmony_ci	struct ftmac100_txdes *txdes;
61462306a36Sopenharmony_ci	struct sk_buff *skb;
61562306a36Sopenharmony_ci	dma_addr_t map;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (priv->tx_pending == 0)
61862306a36Sopenharmony_ci		return false;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	txdes = ftmac100_current_clean_txdes(priv);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (ftmac100_txdes_owned_by_dma(txdes))
62362306a36Sopenharmony_ci		return false;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	skb = ftmac100_txdes_get_skb(txdes);
62662306a36Sopenharmony_ci	map = ftmac100_txdes_get_dma_addr(txdes);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (unlikely(ftmac100_txdes_excessive_collision(txdes) ||
62962306a36Sopenharmony_ci		     ftmac100_txdes_late_collision(txdes))) {
63062306a36Sopenharmony_ci		/*
63162306a36Sopenharmony_ci		 * packet transmitted to ethernet lost due to late collision
63262306a36Sopenharmony_ci		 * or excessive collision
63362306a36Sopenharmony_ci		 */
63462306a36Sopenharmony_ci		netdev->stats.tx_aborted_errors++;
63562306a36Sopenharmony_ci	} else {
63662306a36Sopenharmony_ci		netdev->stats.tx_packets++;
63762306a36Sopenharmony_ci		netdev->stats.tx_bytes += skb->len;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
64162306a36Sopenharmony_ci	dev_kfree_skb(skb);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	ftmac100_txdes_reset(txdes);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ftmac100_tx_clean_pointer_advance(priv);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	spin_lock(&priv->tx_lock);
64862306a36Sopenharmony_ci	priv->tx_pending--;
64962306a36Sopenharmony_ci	spin_unlock(&priv->tx_lock);
65062306a36Sopenharmony_ci	netif_wake_queue(netdev);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return true;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void ftmac100_tx_complete(struct ftmac100 *priv)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	while (ftmac100_tx_complete_packet(priv))
65862306a36Sopenharmony_ci		;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
66262306a36Sopenharmony_ci				 dma_addr_t map)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
66562306a36Sopenharmony_ci	struct ftmac100_txdes *txdes;
66662306a36Sopenharmony_ci	unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	txdes = ftmac100_current_txdes(priv);
66962306a36Sopenharmony_ci	ftmac100_tx_pointer_advance(priv);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* setup TX descriptor */
67262306a36Sopenharmony_ci	ftmac100_txdes_set_skb(txdes, skb);
67362306a36Sopenharmony_ci	ftmac100_txdes_set_dma_addr(txdes, map);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	ftmac100_txdes_set_first_segment(txdes);
67662306a36Sopenharmony_ci	ftmac100_txdes_set_last_segment(txdes);
67762306a36Sopenharmony_ci	ftmac100_txdes_set_txint(txdes);
67862306a36Sopenharmony_ci	ftmac100_txdes_set_buffer_size(txdes, len);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	spin_lock(&priv->tx_lock);
68162306a36Sopenharmony_ci	priv->tx_pending++;
68262306a36Sopenharmony_ci	if (priv->tx_pending == TX_QUEUE_ENTRIES)
68362306a36Sopenharmony_ci		netif_stop_queue(netdev);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* start transmit */
68662306a36Sopenharmony_ci	ftmac100_txdes_set_dma_own(txdes);
68762306a36Sopenharmony_ci	spin_unlock(&priv->tx_lock);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	ftmac100_txdma_start_polling(priv);
69062306a36Sopenharmony_ci	return NETDEV_TX_OK;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/******************************************************************************
69462306a36Sopenharmony_ci * internal functions (buffer)
69562306a36Sopenharmony_ci *****************************************************************************/
69662306a36Sopenharmony_cistatic int ftmac100_alloc_rx_page(struct ftmac100 *priv,
69762306a36Sopenharmony_ci				  struct ftmac100_rxdes *rxdes, gfp_t gfp)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
70062306a36Sopenharmony_ci	struct page *page;
70162306a36Sopenharmony_ci	dma_addr_t map;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	page = alloc_page(gfp);
70462306a36Sopenharmony_ci	if (!page) {
70562306a36Sopenharmony_ci		if (net_ratelimit())
70662306a36Sopenharmony_ci			netdev_err(netdev, "failed to allocate rx page\n");
70762306a36Sopenharmony_ci		return -ENOMEM;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE);
71162306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(priv->dev, map))) {
71262306a36Sopenharmony_ci		if (net_ratelimit())
71362306a36Sopenharmony_ci			netdev_err(netdev, "failed to map rx page\n");
71462306a36Sopenharmony_ci		__free_page(page);
71562306a36Sopenharmony_ci		return -ENOMEM;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	ftmac100_rxdes_set_page(rxdes, page);
71962306a36Sopenharmony_ci	ftmac100_rxdes_set_dma_addr(rxdes, map);
72062306a36Sopenharmony_ci	ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE);
72162306a36Sopenharmony_ci	ftmac100_rxdes_set_dma_own(rxdes);
72262306a36Sopenharmony_ci	return 0;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic void ftmac100_free_buffers(struct ftmac100 *priv)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	int i;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
73062306a36Sopenharmony_ci		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
73162306a36Sopenharmony_ci		struct page *page = ftmac100_rxdes_get_page(rxdes);
73262306a36Sopenharmony_ci		dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		if (!page)
73562306a36Sopenharmony_ci			continue;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
73862306a36Sopenharmony_ci		__free_page(page);
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
74262306a36Sopenharmony_ci		struct ftmac100_txdes *txdes = &priv->descs->txdes[i];
74362306a36Sopenharmony_ci		struct sk_buff *skb = ftmac100_txdes_get_skb(txdes);
74462306a36Sopenharmony_ci		dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		if (!skb)
74762306a36Sopenharmony_ci			continue;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
75062306a36Sopenharmony_ci		dev_kfree_skb(skb);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs),
75462306a36Sopenharmony_ci			  priv->descs, priv->descs_dma_addr);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int ftmac100_alloc_buffers(struct ftmac100 *priv)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	int i;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	priv->descs = dma_alloc_coherent(priv->dev,
76262306a36Sopenharmony_ci					 sizeof(struct ftmac100_descs),
76362306a36Sopenharmony_ci					 &priv->descs_dma_addr, GFP_KERNEL);
76462306a36Sopenharmony_ci	if (!priv->descs)
76562306a36Sopenharmony_ci		return -ENOMEM;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* initialize RX ring */
76862306a36Sopenharmony_ci	ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
77162306a36Sopenharmony_ci		struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
77462306a36Sopenharmony_ci			goto err;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* initialize TX ring */
77862306a36Sopenharmony_ci	ftmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
77962306a36Sopenharmony_ci	return 0;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cierr:
78262306a36Sopenharmony_ci	ftmac100_free_buffers(priv);
78362306a36Sopenharmony_ci	return -ENOMEM;
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci/******************************************************************************
78762306a36Sopenharmony_ci * struct mii_if_info functions
78862306a36Sopenharmony_ci *****************************************************************************/
78962306a36Sopenharmony_cistatic int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
79262306a36Sopenharmony_ci	unsigned int phycr;
79362306a36Sopenharmony_ci	int i;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
79662306a36Sopenharmony_ci		FTMAC100_PHYCR_REGAD(reg) |
79762306a36Sopenharmony_ci		FTMAC100_PHYCR_MIIRD;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
80262306a36Sopenharmony_ci		phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		if ((phycr & FTMAC100_PHYCR_MIIRD) == 0)
80562306a36Sopenharmony_ci			return phycr & FTMAC100_PHYCR_MIIRDATA;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		udelay(100);
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	netdev_err(netdev, "mdio read timed out\n");
81162306a36Sopenharmony_ci	return 0;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg,
81562306a36Sopenharmony_ci				int data)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
81862306a36Sopenharmony_ci	unsigned int phycr;
81962306a36Sopenharmony_ci	int i;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
82262306a36Sopenharmony_ci		FTMAC100_PHYCR_REGAD(reg) |
82362306a36Sopenharmony_ci		FTMAC100_PHYCR_MIIWR;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	data = FTMAC100_PHYWDATA_MIIWDATA(data);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA);
82862306a36Sopenharmony_ci	iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
83162306a36Sopenharmony_ci		phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
83462306a36Sopenharmony_ci			return;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci		udelay(100);
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	netdev_err(netdev, "mdio write timed out\n");
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/******************************************************************************
84362306a36Sopenharmony_ci * struct ethtool_ops functions
84462306a36Sopenharmony_ci *****************************************************************************/
84562306a36Sopenharmony_cistatic void ftmac100_get_drvinfo(struct net_device *netdev,
84662306a36Sopenharmony_ci				 struct ethtool_drvinfo *info)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
84962306a36Sopenharmony_ci	strscpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info));
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic int ftmac100_get_link_ksettings(struct net_device *netdev,
85362306a36Sopenharmony_ci				       struct ethtool_link_ksettings *cmd)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&priv->mii, cmd);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return 0;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int ftmac100_set_link_ksettings(struct net_device *netdev,
86362306a36Sopenharmony_ci				       const struct ethtool_link_ksettings *cmd)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
86662306a36Sopenharmony_ci	return mii_ethtool_set_link_ksettings(&priv->mii, cmd);
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int ftmac100_nway_reset(struct net_device *netdev)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
87262306a36Sopenharmony_ci	return mii_nway_restart(&priv->mii);
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic u32 ftmac100_get_link(struct net_device *netdev)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
87862306a36Sopenharmony_ci	return mii_link_ok(&priv->mii);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic const struct ethtool_ops ftmac100_ethtool_ops = {
88262306a36Sopenharmony_ci	.get_drvinfo		= ftmac100_get_drvinfo,
88362306a36Sopenharmony_ci	.nway_reset		= ftmac100_nway_reset,
88462306a36Sopenharmony_ci	.get_link		= ftmac100_get_link,
88562306a36Sopenharmony_ci	.get_link_ksettings	= ftmac100_get_link_ksettings,
88662306a36Sopenharmony_ci	.set_link_ksettings	= ftmac100_set_link_ksettings,
88762306a36Sopenharmony_ci};
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci/******************************************************************************
89062306a36Sopenharmony_ci * interrupt handler
89162306a36Sopenharmony_ci *****************************************************************************/
89262306a36Sopenharmony_cistatic irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct net_device *netdev = dev_id;
89562306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* Disable interrupts for polling */
89862306a36Sopenharmony_ci	ftmac100_disable_all_int(priv);
89962306a36Sopenharmony_ci	if (likely(netif_running(netdev)))
90062306a36Sopenharmony_ci		napi_schedule(&priv->napi);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return IRQ_HANDLED;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci/******************************************************************************
90662306a36Sopenharmony_ci * struct napi_struct functions
90762306a36Sopenharmony_ci *****************************************************************************/
90862306a36Sopenharmony_cistatic int ftmac100_poll(struct napi_struct *napi, int budget)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct ftmac100 *priv = container_of(napi, struct ftmac100, napi);
91162306a36Sopenharmony_ci	struct net_device *netdev = priv->netdev;
91262306a36Sopenharmony_ci	unsigned int status;
91362306a36Sopenharmony_ci	bool completed = true;
91462306a36Sopenharmony_ci	int rx = 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) {
91962306a36Sopenharmony_ci		/*
92062306a36Sopenharmony_ci		 * FTMAC100_INT_RPKT_FINISH:
92162306a36Sopenharmony_ci		 *	RX DMA has received packets into RX buffer successfully
92262306a36Sopenharmony_ci		 *
92362306a36Sopenharmony_ci		 * FTMAC100_INT_NORXBUF:
92462306a36Sopenharmony_ci		 *	RX buffer unavailable
92562306a36Sopenharmony_ci		 */
92662306a36Sopenharmony_ci		bool retry;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		do {
92962306a36Sopenharmony_ci			retry = ftmac100_rx_packet(priv, &rx);
93062306a36Sopenharmony_ci		} while (retry && rx < budget);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci		if (retry && rx == budget)
93362306a36Sopenharmony_ci			completed = false;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) {
93762306a36Sopenharmony_ci		/*
93862306a36Sopenharmony_ci		 * FTMAC100_INT_XPKT_OK:
93962306a36Sopenharmony_ci		 *	packet transmitted to ethernet successfully
94062306a36Sopenharmony_ci		 *
94162306a36Sopenharmony_ci		 * FTMAC100_INT_XPKT_LOST:
94262306a36Sopenharmony_ci		 *	packet transmitted to ethernet lost due to late
94362306a36Sopenharmony_ci		 *	collision or excessive collision
94462306a36Sopenharmony_ci		 */
94562306a36Sopenharmony_ci		ftmac100_tx_complete(priv);
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (status & (FTMAC100_INT_NORXBUF | FTMAC100_INT_RPKT_LOST |
94962306a36Sopenharmony_ci		      FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) {
95062306a36Sopenharmony_ci		if (net_ratelimit())
95162306a36Sopenharmony_ci			netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
95262306a36Sopenharmony_ci				    status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "",
95362306a36Sopenharmony_ci				    status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
95462306a36Sopenharmony_ci				    status & FTMAC100_INT_AHB_ERR ? "AHB_ERR " : "",
95562306a36Sopenharmony_ci				    status & FTMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : "");
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		if (status & FTMAC100_INT_NORXBUF) {
95862306a36Sopenharmony_ci			/* RX buffer unavailable */
95962306a36Sopenharmony_ci			netdev->stats.rx_over_errors++;
96062306a36Sopenharmony_ci		}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		if (status & FTMAC100_INT_RPKT_LOST) {
96362306a36Sopenharmony_ci			/* received packet lost due to RX FIFO full */
96462306a36Sopenharmony_ci			netdev->stats.rx_fifo_errors++;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		if (status & FTMAC100_INT_PHYSTS_CHG) {
96862306a36Sopenharmony_ci			/* PHY link status change */
96962306a36Sopenharmony_ci			mii_check_link(&priv->mii);
97062306a36Sopenharmony_ci		}
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (completed) {
97462306a36Sopenharmony_ci		/* stop polling */
97562306a36Sopenharmony_ci		napi_complete(napi);
97662306a36Sopenharmony_ci		ftmac100_enable_all_int(priv);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	return rx;
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci/******************************************************************************
98362306a36Sopenharmony_ci * struct net_device_ops functions
98462306a36Sopenharmony_ci *****************************************************************************/
98562306a36Sopenharmony_cistatic int ftmac100_open(struct net_device *netdev)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
98862306a36Sopenharmony_ci	int err;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	err = ftmac100_alloc_buffers(priv);
99162306a36Sopenharmony_ci	if (err) {
99262306a36Sopenharmony_ci		netdev_err(netdev, "failed to allocate buffers\n");
99362306a36Sopenharmony_ci		goto err_alloc;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name, netdev);
99762306a36Sopenharmony_ci	if (err) {
99862306a36Sopenharmony_ci		netdev_err(netdev, "failed to request irq %d\n", priv->irq);
99962306a36Sopenharmony_ci		goto err_irq;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	priv->rx_pointer = 0;
100362306a36Sopenharmony_ci	priv->tx_clean_pointer = 0;
100462306a36Sopenharmony_ci	priv->tx_pointer = 0;
100562306a36Sopenharmony_ci	priv->tx_pending = 0;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	err = ftmac100_start_hw(priv);
100862306a36Sopenharmony_ci	if (err)
100962306a36Sopenharmony_ci		goto err_hw;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	napi_enable(&priv->napi);
101262306a36Sopenharmony_ci	netif_start_queue(netdev);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	ftmac100_enable_all_int(priv);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	return 0;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cierr_hw:
101962306a36Sopenharmony_ci	free_irq(priv->irq, netdev);
102062306a36Sopenharmony_cierr_irq:
102162306a36Sopenharmony_ci	ftmac100_free_buffers(priv);
102262306a36Sopenharmony_cierr_alloc:
102362306a36Sopenharmony_ci	return err;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic int ftmac100_stop(struct net_device *netdev)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	ftmac100_disable_all_int(priv);
103162306a36Sopenharmony_ci	netif_stop_queue(netdev);
103262306a36Sopenharmony_ci	napi_disable(&priv->napi);
103362306a36Sopenharmony_ci	ftmac100_stop_hw(priv);
103462306a36Sopenharmony_ci	free_irq(priv->irq, netdev);
103562306a36Sopenharmony_ci	ftmac100_free_buffers(priv);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return 0;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic netdev_tx_t
104162306a36Sopenharmony_ciftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
104462306a36Sopenharmony_ci	dma_addr_t map;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (unlikely(skb->len > MAX_PKT_SIZE)) {
104762306a36Sopenharmony_ci		if (net_ratelimit())
104862306a36Sopenharmony_ci			netdev_dbg(netdev, "tx packet too big\n");
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
105162306a36Sopenharmony_ci		dev_kfree_skb(skb);
105262306a36Sopenharmony_ci		return NETDEV_TX_OK;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
105662306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(priv->dev, map))) {
105762306a36Sopenharmony_ci		/* drop packet */
105862306a36Sopenharmony_ci		if (net_ratelimit())
105962306a36Sopenharmony_ci			netdev_err(netdev, "map socket buffer failed\n");
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		netdev->stats.tx_dropped++;
106262306a36Sopenharmony_ci		dev_kfree_skb(skb);
106362306a36Sopenharmony_ci		return NETDEV_TX_OK;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return ftmac100_xmit(priv, skb, map);
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci/* optional */
107062306a36Sopenharmony_cistatic int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
107362306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(ifr);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	return generic_mii_ioctl(&priv->mii, data, cmd, NULL);
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cistatic int ftmac100_change_mtu(struct net_device *netdev, int mtu)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
108162306a36Sopenharmony_ci	unsigned int maccr;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
108462306a36Sopenharmony_ci	if (mtu > ETH_DATA_LEN) {
108562306a36Sopenharmony_ci		/* process long packets in the driver */
108662306a36Sopenharmony_ci		maccr |= FTMAC100_MACCR_RX_FTL;
108762306a36Sopenharmony_ci	} else {
108862306a36Sopenharmony_ci		/* Let the controller drop incoming packets greater
108962306a36Sopenharmony_ci		 * than 1518 (that is 1500 + 14 Ethernet + 4 FCS).
109062306a36Sopenharmony_ci		 */
109162306a36Sopenharmony_ci		maccr &= ~FTMAC100_MACCR_RX_FTL;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci	iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	netdev->mtu = mtu;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return 0;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic void ftmac100_set_rx_mode(struct net_device *netdev)
110162306a36Sopenharmony_ci{
110262306a36Sopenharmony_ci	struct ftmac100 *priv = netdev_priv(netdev);
110362306a36Sopenharmony_ci	unsigned int maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	ftmac100_set_rx_bits(priv, &maccr);
110662306a36Sopenharmony_ci	iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic const struct net_device_ops ftmac100_netdev_ops = {
111062306a36Sopenharmony_ci	.ndo_open		= ftmac100_open,
111162306a36Sopenharmony_ci	.ndo_stop		= ftmac100_stop,
111262306a36Sopenharmony_ci	.ndo_start_xmit		= ftmac100_hard_start_xmit,
111362306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
111462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
111562306a36Sopenharmony_ci	.ndo_eth_ioctl		= ftmac100_do_ioctl,
111662306a36Sopenharmony_ci	.ndo_change_mtu		= ftmac100_change_mtu,
111762306a36Sopenharmony_ci	.ndo_set_rx_mode	= ftmac100_set_rx_mode,
111862306a36Sopenharmony_ci};
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci/******************************************************************************
112162306a36Sopenharmony_ci * struct platform_driver functions
112262306a36Sopenharmony_ci *****************************************************************************/
112362306a36Sopenharmony_cistatic int ftmac100_probe(struct platform_device *pdev)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	struct resource *res;
112662306a36Sopenharmony_ci	int irq;
112762306a36Sopenharmony_ci	struct net_device *netdev;
112862306a36Sopenharmony_ci	struct ftmac100 *priv;
112962306a36Sopenharmony_ci	int err;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
113262306a36Sopenharmony_ci	if (!res)
113362306a36Sopenharmony_ci		return -ENXIO;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
113662306a36Sopenharmony_ci	if (irq < 0)
113762306a36Sopenharmony_ci		return irq;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* setup net_device */
114062306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(*priv));
114162306a36Sopenharmony_ci	if (!netdev) {
114262306a36Sopenharmony_ci		err = -ENOMEM;
114362306a36Sopenharmony_ci		goto err_alloc_etherdev;
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
114762306a36Sopenharmony_ci	netdev->ethtool_ops = &ftmac100_ethtool_ops;
114862306a36Sopenharmony_ci	netdev->netdev_ops = &ftmac100_netdev_ops;
114962306a36Sopenharmony_ci	netdev->max_mtu = MAX_PKT_SIZE - VLAN_ETH_HLEN;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	err = platform_get_ethdev_address(&pdev->dev, netdev);
115262306a36Sopenharmony_ci	if (err == -EPROBE_DEFER)
115362306a36Sopenharmony_ci		goto defer_get_mac;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	platform_set_drvdata(pdev, netdev);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* setup private data */
115862306a36Sopenharmony_ci	priv = netdev_priv(netdev);
115962306a36Sopenharmony_ci	priv->netdev = netdev;
116062306a36Sopenharmony_ci	priv->dev = &pdev->dev;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	spin_lock_init(&priv->tx_lock);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	/* initialize NAPI */
116562306a36Sopenharmony_ci	netif_napi_add(netdev, &priv->napi, ftmac100_poll);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	/* map io memory */
116862306a36Sopenharmony_ci	priv->res = request_mem_region(res->start, resource_size(res),
116962306a36Sopenharmony_ci				       dev_name(&pdev->dev));
117062306a36Sopenharmony_ci	if (!priv->res) {
117162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Could not reserve memory region\n");
117262306a36Sopenharmony_ci		err = -ENOMEM;
117362306a36Sopenharmony_ci		goto err_req_mem;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	priv->base = ioremap(res->start, resource_size(res));
117762306a36Sopenharmony_ci	if (!priv->base) {
117862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
117962306a36Sopenharmony_ci		err = -EIO;
118062306a36Sopenharmony_ci		goto err_ioremap;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	priv->irq = irq;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	/* initialize struct mii_if_info */
118662306a36Sopenharmony_ci	priv->mii.phy_id	= 0;
118762306a36Sopenharmony_ci	priv->mii.phy_id_mask	= 0x1f;
118862306a36Sopenharmony_ci	priv->mii.reg_num_mask	= 0x1f;
118962306a36Sopenharmony_ci	priv->mii.dev		= netdev;
119062306a36Sopenharmony_ci	priv->mii.mdio_read	= ftmac100_mdio_read;
119162306a36Sopenharmony_ci	priv->mii.mdio_write	= ftmac100_mdio_write;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/* register network device */
119462306a36Sopenharmony_ci	err = register_netdev(netdev);
119562306a36Sopenharmony_ci	if (err) {
119662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register netdev\n");
119762306a36Sopenharmony_ci		goto err_register_netdev;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (!is_valid_ether_addr(netdev->dev_addr)) {
120362306a36Sopenharmony_ci		eth_hw_addr_random(netdev);
120462306a36Sopenharmony_ci		netdev_info(netdev, "generated random MAC address %pM\n",
120562306a36Sopenharmony_ci			    netdev->dev_addr);
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	return 0;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cierr_register_netdev:
121162306a36Sopenharmony_ci	iounmap(priv->base);
121262306a36Sopenharmony_cierr_ioremap:
121362306a36Sopenharmony_ci	release_resource(priv->res);
121462306a36Sopenharmony_cierr_req_mem:
121562306a36Sopenharmony_ci	netif_napi_del(&priv->napi);
121662306a36Sopenharmony_cidefer_get_mac:
121762306a36Sopenharmony_ci	free_netdev(netdev);
121862306a36Sopenharmony_cierr_alloc_etherdev:
121962306a36Sopenharmony_ci	return err;
122062306a36Sopenharmony_ci}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_cistatic int ftmac100_remove(struct platform_device *pdev)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	struct net_device *netdev;
122562306a36Sopenharmony_ci	struct ftmac100 *priv;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	netdev = platform_get_drvdata(pdev);
122862306a36Sopenharmony_ci	priv = netdev_priv(netdev);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	unregister_netdev(netdev);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	iounmap(priv->base);
123362306a36Sopenharmony_ci	release_resource(priv->res);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	netif_napi_del(&priv->napi);
123662306a36Sopenharmony_ci	free_netdev(netdev);
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic const struct of_device_id ftmac100_of_ids[] = {
124162306a36Sopenharmony_ci	{ .compatible = "andestech,atmac100" },
124262306a36Sopenharmony_ci	{ }
124362306a36Sopenharmony_ci};
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic struct platform_driver ftmac100_driver = {
124662306a36Sopenharmony_ci	.probe		= ftmac100_probe,
124762306a36Sopenharmony_ci	.remove		= ftmac100_remove,
124862306a36Sopenharmony_ci	.driver		= {
124962306a36Sopenharmony_ci		.name	= DRV_NAME,
125062306a36Sopenharmony_ci		.of_match_table = ftmac100_of_ids
125162306a36Sopenharmony_ci	},
125262306a36Sopenharmony_ci};
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci/******************************************************************************
125562306a36Sopenharmony_ci * initialization / finalization
125662306a36Sopenharmony_ci *****************************************************************************/
125762306a36Sopenharmony_cimodule_platform_driver(ftmac100_driver);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ciMODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
126062306a36Sopenharmony_ciMODULE_DESCRIPTION("FTMAC100 driver");
126162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
126262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ftmac100_of_ids);
1263