162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#define pr_fmt(fmt)			"bcmasp_intf: " fmt
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <asm/byteorder.h>
562306a36Sopenharmony_ci#include <linux/brcmphy.h>
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/etherdevice.h>
962306a36Sopenharmony_ci#include <linux/netdevice.h>
1062306a36Sopenharmony_ci#include <linux/of_net.h>
1162306a36Sopenharmony_ci#include <linux/of_mdio.h>
1262306a36Sopenharmony_ci#include <linux/phy.h>
1362306a36Sopenharmony_ci#include <linux/phy_fixed.h>
1462306a36Sopenharmony_ci#include <linux/ptp_classify.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <net/ip.h>
1762306a36Sopenharmony_ci#include <net/ipv6.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "bcmasp.h"
2062306a36Sopenharmony_ci#include "bcmasp_intf_defs.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int incr_ring(int index, int ring_count)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	index++;
2562306a36Sopenharmony_ci	if (index == ring_count)
2662306a36Sopenharmony_ci		return 0;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return index;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Points to last byte of descriptor */
3262306a36Sopenharmony_cistatic dma_addr_t incr_last_byte(dma_addr_t addr, dma_addr_t beg,
3362306a36Sopenharmony_ci				 int ring_count)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	dma_addr_t end = beg + (ring_count * DESC_SIZE);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	addr += DESC_SIZE;
3862306a36Sopenharmony_ci	if (addr > end)
3962306a36Sopenharmony_ci		return beg + DESC_SIZE - 1;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return addr;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Points to first byte of descriptor */
4562306a36Sopenharmony_cistatic dma_addr_t incr_first_byte(dma_addr_t addr, dma_addr_t beg,
4662306a36Sopenharmony_ci				  int ring_count)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	dma_addr_t end = beg + (ring_count * DESC_SIZE);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	addr += DESC_SIZE;
5162306a36Sopenharmony_ci	if (addr >= end)
5262306a36Sopenharmony_ci		return beg;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return addr;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void bcmasp_enable_tx(struct bcmasp_intf *intf, int en)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	if (en) {
6062306a36Sopenharmony_ci		tx_spb_ctrl_wl(intf, TX_SPB_CTRL_ENABLE_EN, TX_SPB_CTRL_ENABLE);
6162306a36Sopenharmony_ci		tx_epkt_core_wl(intf, (TX_EPKT_C_CFG_MISC_EN |
6262306a36Sopenharmony_ci				TX_EPKT_C_CFG_MISC_PT |
6362306a36Sopenharmony_ci				(intf->port << TX_EPKT_C_CFG_MISC_PS_SHIFT)),
6462306a36Sopenharmony_ci				TX_EPKT_C_CFG_MISC);
6562306a36Sopenharmony_ci	} else {
6662306a36Sopenharmony_ci		tx_spb_ctrl_wl(intf, 0x0, TX_SPB_CTRL_ENABLE);
6762306a36Sopenharmony_ci		tx_epkt_core_wl(intf, 0x0, TX_EPKT_C_CFG_MISC);
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void bcmasp_enable_rx(struct bcmasp_intf *intf, int en)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (en)
7462306a36Sopenharmony_ci		rx_edpkt_cfg_wl(intf, RX_EDPKT_CFG_ENABLE_EN,
7562306a36Sopenharmony_ci				RX_EDPKT_CFG_ENABLE);
7662306a36Sopenharmony_ci	else
7762306a36Sopenharmony_ci		rx_edpkt_cfg_wl(intf, 0x0, RX_EDPKT_CFG_ENABLE);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void bcmasp_set_rx_mode(struct net_device *dev)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	unsigned char mask[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
8362306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
8462306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
8562306a36Sopenharmony_ci	int ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	spin_lock_bh(&intf->parent->mda_lock);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	bcmasp_disable_all_filters(intf);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
9262306a36Sopenharmony_ci		goto set_promisc;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	bcmasp_set_promisc(intf, 0);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	bcmasp_set_broad(intf, 1);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	bcmasp_set_oaddr(intf, dev->dev_addr, 1);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
10162306a36Sopenharmony_ci		bcmasp_set_allmulti(intf, 1);
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		bcmasp_set_allmulti(intf, 0);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
10662306a36Sopenharmony_ci			ret = bcmasp_set_en_mda_filter(intf, ha->addr, mask);
10762306a36Sopenharmony_ci			if (ret) {
10862306a36Sopenharmony_ci				intf->mib.mc_filters_full_cnt++;
10962306a36Sopenharmony_ci				goto set_promisc;
11062306a36Sopenharmony_ci			}
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	netdev_for_each_uc_addr(ha, dev) {
11562306a36Sopenharmony_ci		ret = bcmasp_set_en_mda_filter(intf, ha->addr, mask);
11662306a36Sopenharmony_ci		if (ret) {
11762306a36Sopenharmony_ci			intf->mib.uc_filters_full_cnt++;
11862306a36Sopenharmony_ci			goto set_promisc;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	spin_unlock_bh(&intf->parent->mda_lock);
12362306a36Sopenharmony_ci	return;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciset_promisc:
12662306a36Sopenharmony_ci	bcmasp_set_promisc(intf, 1);
12762306a36Sopenharmony_ci	intf->mib.promisc_filters_cnt++;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* disable all filters used by this port */
13062306a36Sopenharmony_ci	bcmasp_disable_all_filters(intf);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	spin_unlock_bh(&intf->parent->mda_lock);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void bcmasp_clean_txcb(struct bcmasp_intf *intf, int index)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct bcmasp_tx_cb *txcb = &intf->tx_cbs[index];
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	txcb->skb = NULL;
14062306a36Sopenharmony_ci	dma_unmap_addr_set(txcb, dma_addr, 0);
14162306a36Sopenharmony_ci	dma_unmap_len_set(txcb, dma_len, 0);
14262306a36Sopenharmony_ci	txcb->last = false;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int tx_spb_ring_full(struct bcmasp_intf *intf, int cnt)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int next_index, i;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Check if we have enough room for cnt descriptors */
15062306a36Sopenharmony_ci	for (i = 0; i < cnt; i++) {
15162306a36Sopenharmony_ci		next_index = incr_ring(intf->tx_spb_index, DESC_RING_COUNT);
15262306a36Sopenharmony_ci		if (next_index == intf->tx_spb_clean_index)
15362306a36Sopenharmony_ci			return 1;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic struct sk_buff *bcmasp_csum_offload(struct net_device *dev,
16062306a36Sopenharmony_ci					   struct sk_buff *skb,
16162306a36Sopenharmony_ci					   bool *csum_hw)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
16462306a36Sopenharmony_ci	u32 header = 0, header2 = 0, epkt = 0;
16562306a36Sopenharmony_ci	struct bcmasp_pkt_offload *offload;
16662306a36Sopenharmony_ci	unsigned int header_cnt = 0;
16762306a36Sopenharmony_ci	u8 ip_proto;
16862306a36Sopenharmony_ci	int ret;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (skb->ip_summed != CHECKSUM_PARTIAL)
17162306a36Sopenharmony_ci		return skb;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = skb_cow_head(skb, sizeof(*offload));
17462306a36Sopenharmony_ci	if (ret < 0) {
17562306a36Sopenharmony_ci		intf->mib.tx_realloc_offload_failed++;
17662306a36Sopenharmony_ci		goto help;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	switch (skb->protocol) {
18062306a36Sopenharmony_ci	case htons(ETH_P_IP):
18162306a36Sopenharmony_ci		header |= PKT_OFFLOAD_HDR_SIZE_2((ip_hdrlen(skb) >> 8) & 0xf);
18262306a36Sopenharmony_ci		header2 |= PKT_OFFLOAD_HDR2_SIZE_2(ip_hdrlen(skb) & 0xff);
18362306a36Sopenharmony_ci		epkt |= PKT_OFFLOAD_EPKT_IP(0) | PKT_OFFLOAD_EPKT_CSUM_L2;
18462306a36Sopenharmony_ci		ip_proto = ip_hdr(skb)->protocol;
18562306a36Sopenharmony_ci		header_cnt += 2;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
18862306a36Sopenharmony_ci		header |= PKT_OFFLOAD_HDR_SIZE_2((IP6_HLEN >> 8) & 0xf);
18962306a36Sopenharmony_ci		header2 |= PKT_OFFLOAD_HDR2_SIZE_2(IP6_HLEN & 0xff);
19062306a36Sopenharmony_ci		epkt |= PKT_OFFLOAD_EPKT_IP(1) | PKT_OFFLOAD_EPKT_CSUM_L2;
19162306a36Sopenharmony_ci		ip_proto = ipv6_hdr(skb)->nexthdr;
19262306a36Sopenharmony_ci		header_cnt += 2;
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	default:
19562306a36Sopenharmony_ci		goto help;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	switch (ip_proto) {
19962306a36Sopenharmony_ci	case IPPROTO_TCP:
20062306a36Sopenharmony_ci		header2 |= PKT_OFFLOAD_HDR2_SIZE_3(tcp_hdrlen(skb));
20162306a36Sopenharmony_ci		epkt |= PKT_OFFLOAD_EPKT_TP(0) | PKT_OFFLOAD_EPKT_CSUM_L3;
20262306a36Sopenharmony_ci		header_cnt++;
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	case IPPROTO_UDP:
20562306a36Sopenharmony_ci		header2 |= PKT_OFFLOAD_HDR2_SIZE_3(UDP_HLEN);
20662306a36Sopenharmony_ci		epkt |= PKT_OFFLOAD_EPKT_TP(1) | PKT_OFFLOAD_EPKT_CSUM_L3;
20762306a36Sopenharmony_ci		header_cnt++;
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	default:
21062306a36Sopenharmony_ci		goto help;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	offload = (struct bcmasp_pkt_offload *)skb_push(skb, sizeof(*offload));
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	header |= PKT_OFFLOAD_HDR_OP | PKT_OFFLOAD_HDR_COUNT(header_cnt) |
21662306a36Sopenharmony_ci		  PKT_OFFLOAD_HDR_SIZE_1(ETH_HLEN);
21762306a36Sopenharmony_ci	epkt |= PKT_OFFLOAD_EPKT_OP;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	offload->nop = htonl(PKT_OFFLOAD_NOP);
22062306a36Sopenharmony_ci	offload->header = htonl(header);
22162306a36Sopenharmony_ci	offload->header2 = htonl(header2);
22262306a36Sopenharmony_ci	offload->epkt = htonl(epkt);
22362306a36Sopenharmony_ci	offload->end = htonl(PKT_OFFLOAD_END_OP);
22462306a36Sopenharmony_ci	*csum_hw = true;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return skb;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cihelp:
22962306a36Sopenharmony_ci	skb_checksum_help(skb);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return skb;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic unsigned long bcmasp_rx_edpkt_dma_rq(struct bcmasp_intf *intf)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	return rx_edpkt_dma_rq(intf, RX_EDPKT_DMA_VALID);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void bcmasp_rx_edpkt_cfg_wq(struct bcmasp_intf *intf, dma_addr_t addr)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	rx_edpkt_cfg_wq(intf, addr, RX_EDPKT_RING_BUFFER_READ);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void bcmasp_rx_edpkt_dma_wq(struct bcmasp_intf *intf, dma_addr_t addr)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	rx_edpkt_dma_wq(intf, addr, RX_EDPKT_DMA_READ);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic unsigned long bcmasp_tx_spb_dma_rq(struct bcmasp_intf *intf)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	return tx_spb_dma_rq(intf, TX_SPB_DMA_READ);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void bcmasp_tx_spb_dma_wq(struct bcmasp_intf *intf, dma_addr_t addr)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	tx_spb_dma_wq(intf, addr, TX_SPB_DMA_VALID);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic const struct bcmasp_intf_ops bcmasp_intf_ops = {
26062306a36Sopenharmony_ci	.rx_desc_read = bcmasp_rx_edpkt_dma_rq,
26162306a36Sopenharmony_ci	.rx_buffer_write = bcmasp_rx_edpkt_cfg_wq,
26262306a36Sopenharmony_ci	.rx_desc_write = bcmasp_rx_edpkt_dma_wq,
26362306a36Sopenharmony_ci	.tx_read = bcmasp_tx_spb_dma_rq,
26462306a36Sopenharmony_ci	.tx_write = bcmasp_tx_spb_dma_wq,
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
27062306a36Sopenharmony_ci	unsigned int total_bytes, size;
27162306a36Sopenharmony_ci	int spb_index, nr_frags, i, j;
27262306a36Sopenharmony_ci	struct bcmasp_tx_cb *txcb;
27362306a36Sopenharmony_ci	dma_addr_t mapping, valid;
27462306a36Sopenharmony_ci	struct bcmasp_desc *desc;
27562306a36Sopenharmony_ci	bool csum_hw = false;
27662306a36Sopenharmony_ci	struct device *kdev;
27762306a36Sopenharmony_ci	skb_frag_t *frag;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	kdev = &intf->parent->pdev->dev;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (tx_spb_ring_full(intf, nr_frags + 1)) {
28462306a36Sopenharmony_ci		netif_stop_queue(dev);
28562306a36Sopenharmony_ci		if (net_ratelimit())
28662306a36Sopenharmony_ci			netdev_err(dev, "Tx Ring Full!\n");
28762306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Save skb len before adding csum offload header */
29162306a36Sopenharmony_ci	total_bytes = skb->len;
29262306a36Sopenharmony_ci	skb = bcmasp_csum_offload(dev, skb, &csum_hw);
29362306a36Sopenharmony_ci	if (!skb)
29462306a36Sopenharmony_ci		return NETDEV_TX_OK;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	spb_index = intf->tx_spb_index;
29762306a36Sopenharmony_ci	valid = intf->tx_spb_dma_valid;
29862306a36Sopenharmony_ci	for (i = 0; i <= nr_frags; i++) {
29962306a36Sopenharmony_ci		if (!i) {
30062306a36Sopenharmony_ci			size = skb_headlen(skb);
30162306a36Sopenharmony_ci			if (!nr_frags && size < (ETH_ZLEN + ETH_FCS_LEN)) {
30262306a36Sopenharmony_ci				if (skb_put_padto(skb, ETH_ZLEN + ETH_FCS_LEN))
30362306a36Sopenharmony_ci					return NETDEV_TX_OK;
30462306a36Sopenharmony_ci				size = skb->len;
30562306a36Sopenharmony_ci			}
30662306a36Sopenharmony_ci			mapping = dma_map_single(kdev, skb->data, size,
30762306a36Sopenharmony_ci						 DMA_TO_DEVICE);
30862306a36Sopenharmony_ci		} else {
30962306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[i - 1];
31062306a36Sopenharmony_ci			size = skb_frag_size(frag);
31162306a36Sopenharmony_ci			mapping = skb_frag_dma_map(kdev, frag, 0, size,
31262306a36Sopenharmony_ci						   DMA_TO_DEVICE);
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		if (dma_mapping_error(kdev, mapping)) {
31662306a36Sopenharmony_ci			intf->mib.tx_dma_failed++;
31762306a36Sopenharmony_ci			spb_index = intf->tx_spb_index;
31862306a36Sopenharmony_ci			for (j = 0; j < i; j++) {
31962306a36Sopenharmony_ci				bcmasp_clean_txcb(intf, spb_index);
32062306a36Sopenharmony_ci				spb_index = incr_ring(spb_index,
32162306a36Sopenharmony_ci						      DESC_RING_COUNT);
32262306a36Sopenharmony_ci			}
32362306a36Sopenharmony_ci			/* Rewind so we do not have a hole */
32462306a36Sopenharmony_ci			spb_index = intf->tx_spb_index;
32562306a36Sopenharmony_ci			return NETDEV_TX_OK;
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		txcb = &intf->tx_cbs[spb_index];
32962306a36Sopenharmony_ci		desc = &intf->tx_spb_cpu[spb_index];
33062306a36Sopenharmony_ci		memset(desc, 0, sizeof(*desc));
33162306a36Sopenharmony_ci		txcb->skb = skb;
33262306a36Sopenharmony_ci		txcb->bytes_sent = total_bytes;
33362306a36Sopenharmony_ci		dma_unmap_addr_set(txcb, dma_addr, mapping);
33462306a36Sopenharmony_ci		dma_unmap_len_set(txcb, dma_len, size);
33562306a36Sopenharmony_ci		if (!i) {
33662306a36Sopenharmony_ci			desc->flags |= DESC_SOF;
33762306a36Sopenharmony_ci			if (csum_hw)
33862306a36Sopenharmony_ci				desc->flags |= DESC_EPKT_CMD;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		if (i == nr_frags) {
34262306a36Sopenharmony_ci			desc->flags |= DESC_EOF;
34362306a36Sopenharmony_ci			txcb->last = true;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		desc->buf = mapping;
34762306a36Sopenharmony_ci		desc->size = size;
34862306a36Sopenharmony_ci		desc->flags |= DESC_INT_EN;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		netif_dbg(intf, tx_queued, dev,
35162306a36Sopenharmony_ci			  "%s dma_buf=%pad dma_len=0x%x flags=0x%x index=0x%x\n",
35262306a36Sopenharmony_ci			  __func__, &mapping, desc->size, desc->flags,
35362306a36Sopenharmony_ci			  spb_index);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		spb_index = incr_ring(spb_index, DESC_RING_COUNT);
35662306a36Sopenharmony_ci		valid = incr_last_byte(valid, intf->tx_spb_dma_addr,
35762306a36Sopenharmony_ci				       DESC_RING_COUNT);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Ensure all descriptors have been written to DRAM for the
36162306a36Sopenharmony_ci	 * hardware to see up-to-date contents.
36262306a36Sopenharmony_ci	 */
36362306a36Sopenharmony_ci	wmb();
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	intf->tx_spb_index = spb_index;
36662306a36Sopenharmony_ci	intf->tx_spb_dma_valid = valid;
36762306a36Sopenharmony_ci	bcmasp_intf_tx_write(intf, intf->tx_spb_dma_valid);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (tx_spb_ring_full(intf, MAX_SKB_FRAGS + 1))
37062306a36Sopenharmony_ci		netif_stop_queue(dev);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return NETDEV_TX_OK;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic void bcmasp_netif_start(struct net_device *dev)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	bcmasp_set_rx_mode(dev);
38062306a36Sopenharmony_ci	napi_enable(&intf->tx_napi);
38162306a36Sopenharmony_ci	napi_enable(&intf->rx_napi);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	bcmasp_enable_rx_irq(intf, 1);
38462306a36Sopenharmony_ci	bcmasp_enable_tx_irq(intf, 1);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	phy_start(dev->phydev);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void umac_reset(struct bcmasp_intf *intf)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	umac_wl(intf, 0x0, UMC_CMD);
39262306a36Sopenharmony_ci	umac_wl(intf, UMC_CMD_SW_RESET, UMC_CMD);
39362306a36Sopenharmony_ci	usleep_range(10, 100);
39462306a36Sopenharmony_ci	umac_wl(intf, 0x0, UMC_CMD);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void umac_set_hw_addr(struct bcmasp_intf *intf,
39862306a36Sopenharmony_ci			     const unsigned char *addr)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	u32 mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) |
40162306a36Sopenharmony_ci		    addr[3];
40262306a36Sopenharmony_ci	u32 mac1 = (addr[4] << 8) | addr[5];
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	umac_wl(intf, mac0, UMC_MAC0);
40562306a36Sopenharmony_ci	umac_wl(intf, mac1, UMC_MAC1);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void umac_enable_set(struct bcmasp_intf *intf, u32 mask,
40962306a36Sopenharmony_ci			    unsigned int enable)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	u32 reg;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	reg = umac_rl(intf, UMC_CMD);
41462306a36Sopenharmony_ci	if (enable)
41562306a36Sopenharmony_ci		reg |= mask;
41662306a36Sopenharmony_ci	else
41762306a36Sopenharmony_ci		reg &= ~mask;
41862306a36Sopenharmony_ci	umac_wl(intf, reg, UMC_CMD);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* UniMAC stops on a packet boundary, wait for a full-sized packet
42162306a36Sopenharmony_ci	 * to be processed (1 msec).
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	if (enable == 0)
42462306a36Sopenharmony_ci		usleep_range(1000, 2000);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic void umac_init(struct bcmasp_intf *intf)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	umac_wl(intf, 0x800, UMC_FRM_LEN);
43062306a36Sopenharmony_ci	umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL);
43162306a36Sopenharmony_ci	umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ);
43262306a36Sopenharmony_ci	umac_enable_set(intf, UMC_CMD_PROMISC, 1);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int bcmasp_tx_poll(struct napi_struct *napi, int budget)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct bcmasp_intf *intf =
43862306a36Sopenharmony_ci		container_of(napi, struct bcmasp_intf, tx_napi);
43962306a36Sopenharmony_ci	struct bcmasp_intf_stats64 *stats = &intf->stats64;
44062306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
44162306a36Sopenharmony_ci	unsigned long read, released = 0;
44262306a36Sopenharmony_ci	struct bcmasp_tx_cb *txcb;
44362306a36Sopenharmony_ci	struct bcmasp_desc *desc;
44462306a36Sopenharmony_ci	dma_addr_t mapping;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	read = bcmasp_intf_tx_read(intf);
44762306a36Sopenharmony_ci	while (intf->tx_spb_dma_read != read) {
44862306a36Sopenharmony_ci		txcb = &intf->tx_cbs[intf->tx_spb_clean_index];
44962306a36Sopenharmony_ci		mapping = dma_unmap_addr(txcb, dma_addr);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		dma_unmap_single(kdev, mapping,
45262306a36Sopenharmony_ci				 dma_unmap_len(txcb, dma_len),
45362306a36Sopenharmony_ci				 DMA_TO_DEVICE);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		if (txcb->last) {
45662306a36Sopenharmony_ci			dev_consume_skb_any(txcb->skb);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci			u64_stats_update_begin(&stats->syncp);
45962306a36Sopenharmony_ci			u64_stats_inc(&stats->tx_packets);
46062306a36Sopenharmony_ci			u64_stats_add(&stats->tx_bytes, txcb->bytes_sent);
46162306a36Sopenharmony_ci			u64_stats_update_end(&stats->syncp);
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		desc = &intf->tx_spb_cpu[intf->tx_spb_clean_index];
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		netif_dbg(intf, tx_done, intf->ndev,
46762306a36Sopenharmony_ci			  "%s dma_buf=%pad dma_len=0x%x flags=0x%x c_index=0x%x\n",
46862306a36Sopenharmony_ci			  __func__, &mapping, desc->size, desc->flags,
46962306a36Sopenharmony_ci			  intf->tx_spb_clean_index);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		bcmasp_clean_txcb(intf, intf->tx_spb_clean_index);
47262306a36Sopenharmony_ci		released++;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		intf->tx_spb_clean_index = incr_ring(intf->tx_spb_clean_index,
47562306a36Sopenharmony_ci						     DESC_RING_COUNT);
47662306a36Sopenharmony_ci		intf->tx_spb_dma_read = incr_first_byte(intf->tx_spb_dma_read,
47762306a36Sopenharmony_ci							intf->tx_spb_dma_addr,
47862306a36Sopenharmony_ci							DESC_RING_COUNT);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* Ensure all descriptors have been written to DRAM for the hardware
48262306a36Sopenharmony_ci	 * to see updated contents.
48362306a36Sopenharmony_ci	 */
48462306a36Sopenharmony_ci	wmb();
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	napi_complete(&intf->tx_napi);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	bcmasp_enable_tx_irq(intf, 1);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (released)
49162306a36Sopenharmony_ci		netif_wake_queue(intf->ndev);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic int bcmasp_rx_poll(struct napi_struct *napi, int budget)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct bcmasp_intf *intf =
49962306a36Sopenharmony_ci		container_of(napi, struct bcmasp_intf, rx_napi);
50062306a36Sopenharmony_ci	struct bcmasp_intf_stats64 *stats = &intf->stats64;
50162306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
50262306a36Sopenharmony_ci	unsigned long processed = 0;
50362306a36Sopenharmony_ci	struct bcmasp_desc *desc;
50462306a36Sopenharmony_ci	struct sk_buff *skb;
50562306a36Sopenharmony_ci	dma_addr_t valid;
50662306a36Sopenharmony_ci	void *data;
50762306a36Sopenharmony_ci	u64 flags;
50862306a36Sopenharmony_ci	u32 len;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	valid = bcmasp_intf_rx_desc_read(intf) + 1;
51162306a36Sopenharmony_ci	if (valid == intf->rx_edpkt_dma_addr + DESC_RING_SIZE)
51262306a36Sopenharmony_ci		valid = intf->rx_edpkt_dma_addr;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	while ((processed < budget) && (valid != intf->rx_edpkt_dma_read)) {
51562306a36Sopenharmony_ci		desc = &intf->rx_edpkt_cpu[intf->rx_edpkt_index];
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		/* Ensure that descriptor has been fully written to DRAM by
51862306a36Sopenharmony_ci		 * hardware before reading by the CPU
51962306a36Sopenharmony_ci		 */
52062306a36Sopenharmony_ci		rmb();
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		/* Calculate virt addr by offsetting from physical addr */
52362306a36Sopenharmony_ci		data = intf->rx_ring_cpu +
52462306a36Sopenharmony_ci			(DESC_ADDR(desc->buf) - intf->rx_ring_dma);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		flags = DESC_FLAGS(desc->buf);
52762306a36Sopenharmony_ci		if (unlikely(flags & (DESC_CRC_ERR | DESC_RX_SYM_ERR))) {
52862306a36Sopenharmony_ci			if (net_ratelimit()) {
52962306a36Sopenharmony_ci				netif_err(intf, rx_status, intf->ndev,
53062306a36Sopenharmony_ci					  "flags=0x%llx\n", flags);
53162306a36Sopenharmony_ci			}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci			u64_stats_update_begin(&stats->syncp);
53462306a36Sopenharmony_ci			if (flags & DESC_CRC_ERR)
53562306a36Sopenharmony_ci				u64_stats_inc(&stats->rx_crc_errs);
53662306a36Sopenharmony_ci			if (flags & DESC_RX_SYM_ERR)
53762306a36Sopenharmony_ci				u64_stats_inc(&stats->rx_sym_errs);
53862306a36Sopenharmony_ci			u64_stats_update_end(&stats->syncp);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci			goto next;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		dma_sync_single_for_cpu(kdev, DESC_ADDR(desc->buf), desc->size,
54462306a36Sopenharmony_ci					DMA_FROM_DEVICE);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		len = desc->size;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		skb = napi_alloc_skb(napi, len);
54962306a36Sopenharmony_ci		if (!skb) {
55062306a36Sopenharmony_ci			u64_stats_update_begin(&stats->syncp);
55162306a36Sopenharmony_ci			u64_stats_inc(&stats->rx_dropped);
55262306a36Sopenharmony_ci			u64_stats_update_end(&stats->syncp);
55362306a36Sopenharmony_ci			intf->mib.alloc_rx_skb_failed++;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci			goto next;
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		skb_put(skb, len);
55962306a36Sopenharmony_ci		memcpy(skb->data, data, len);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		skb_pull(skb, 2);
56262306a36Sopenharmony_ci		len -= 2;
56362306a36Sopenharmony_ci		if (likely(intf->crc_fwd)) {
56462306a36Sopenharmony_ci			skb_trim(skb, len - ETH_FCS_LEN);
56562306a36Sopenharmony_ci			len -= ETH_FCS_LEN;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		if ((intf->ndev->features & NETIF_F_RXCSUM) &&
56962306a36Sopenharmony_ci		    (desc->buf & DESC_CHKSUM))
57062306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, intf->ndev);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
57762306a36Sopenharmony_ci		u64_stats_inc(&stats->rx_packets);
57862306a36Sopenharmony_ci		u64_stats_add(&stats->rx_bytes, len);
57962306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cinext:
58262306a36Sopenharmony_ci		bcmasp_intf_rx_buffer_write(intf, (DESC_ADDR(desc->buf) +
58362306a36Sopenharmony_ci					    desc->size));
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		processed++;
58662306a36Sopenharmony_ci		intf->rx_edpkt_dma_read =
58762306a36Sopenharmony_ci			incr_first_byte(intf->rx_edpkt_dma_read,
58862306a36Sopenharmony_ci					intf->rx_edpkt_dma_addr,
58962306a36Sopenharmony_ci					DESC_RING_COUNT);
59062306a36Sopenharmony_ci		intf->rx_edpkt_index = incr_ring(intf->rx_edpkt_index,
59162306a36Sopenharmony_ci						 DESC_RING_COUNT);
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	bcmasp_intf_rx_desc_write(intf, intf->rx_edpkt_dma_read);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (processed < budget) {
59762306a36Sopenharmony_ci		napi_complete_done(&intf->rx_napi, processed);
59862306a36Sopenharmony_ci		bcmasp_enable_rx_irq(intf, 1);
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return processed;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void bcmasp_adj_link(struct net_device *dev)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
60762306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
60862306a36Sopenharmony_ci	u32 cmd_bits = 0, reg;
60962306a36Sopenharmony_ci	int changed = 0;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (intf->old_link != phydev->link) {
61262306a36Sopenharmony_ci		changed = 1;
61362306a36Sopenharmony_ci		intf->old_link = phydev->link;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (intf->old_duplex != phydev->duplex) {
61762306a36Sopenharmony_ci		changed = 1;
61862306a36Sopenharmony_ci		intf->old_duplex = phydev->duplex;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	switch (phydev->speed) {
62262306a36Sopenharmony_ci	case SPEED_2500:
62362306a36Sopenharmony_ci		cmd_bits = UMC_CMD_SPEED_2500;
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci	case SPEED_1000:
62662306a36Sopenharmony_ci		cmd_bits = UMC_CMD_SPEED_1000;
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	case SPEED_100:
62962306a36Sopenharmony_ci		cmd_bits = UMC_CMD_SPEED_100;
63062306a36Sopenharmony_ci		break;
63162306a36Sopenharmony_ci	case SPEED_10:
63262306a36Sopenharmony_ci		cmd_bits = UMC_CMD_SPEED_10;
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	default:
63562306a36Sopenharmony_ci		break;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	cmd_bits <<= UMC_CMD_SPEED_SHIFT;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (phydev->duplex == DUPLEX_HALF)
64062306a36Sopenharmony_ci		cmd_bits |= UMC_CMD_HD_EN;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (intf->old_pause != phydev->pause) {
64362306a36Sopenharmony_ci		changed = 1;
64462306a36Sopenharmony_ci		intf->old_pause = phydev->pause;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (!phydev->pause)
64862306a36Sopenharmony_ci		cmd_bits |= UMC_CMD_RX_PAUSE_IGNORE | UMC_CMD_TX_PAUSE_IGNORE;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (!changed)
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (phydev->link) {
65462306a36Sopenharmony_ci		reg = umac_rl(intf, UMC_CMD);
65562306a36Sopenharmony_ci		reg &= ~((UMC_CMD_SPEED_MASK << UMC_CMD_SPEED_SHIFT) |
65662306a36Sopenharmony_ci			UMC_CMD_HD_EN | UMC_CMD_RX_PAUSE_IGNORE |
65762306a36Sopenharmony_ci			UMC_CMD_TX_PAUSE_IGNORE);
65862306a36Sopenharmony_ci		reg |= cmd_bits;
65962306a36Sopenharmony_ci		umac_wl(intf, reg, UMC_CMD);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		intf->eee.eee_active = phy_init_eee(phydev, 0) >= 0;
66262306a36Sopenharmony_ci		bcmasp_eee_enable_set(intf, intf->eee.eee_active);
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	reg = rgmii_rl(intf, RGMII_OOB_CNTRL);
66662306a36Sopenharmony_ci	if (phydev->link)
66762306a36Sopenharmony_ci		reg |= RGMII_LINK;
66862306a36Sopenharmony_ci	else
66962306a36Sopenharmony_ci		reg &= ~RGMII_LINK;
67062306a36Sopenharmony_ci	rgmii_wl(intf, reg, RGMII_OOB_CNTRL);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (changed)
67362306a36Sopenharmony_ci		phy_print_status(phydev);
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic int bcmasp_init_rx(struct bcmasp_intf *intf)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
67962306a36Sopenharmony_ci	struct page *buffer_pg;
68062306a36Sopenharmony_ci	dma_addr_t dma;
68162306a36Sopenharmony_ci	void *p;
68262306a36Sopenharmony_ci	u32 reg;
68362306a36Sopenharmony_ci	int ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	intf->rx_buf_order = get_order(RING_BUFFER_SIZE);
68662306a36Sopenharmony_ci	buffer_pg = alloc_pages(GFP_KERNEL, intf->rx_buf_order);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	dma = dma_map_page(kdev, buffer_pg, 0, RING_BUFFER_SIZE,
68962306a36Sopenharmony_ci			   DMA_FROM_DEVICE);
69062306a36Sopenharmony_ci	if (dma_mapping_error(kdev, dma)) {
69162306a36Sopenharmony_ci		__free_pages(buffer_pg, intf->rx_buf_order);
69262306a36Sopenharmony_ci		return -ENOMEM;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci	intf->rx_ring_cpu = page_to_virt(buffer_pg);
69562306a36Sopenharmony_ci	intf->rx_ring_dma = dma;
69662306a36Sopenharmony_ci	intf->rx_ring_dma_valid = intf->rx_ring_dma + RING_BUFFER_SIZE - 1;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	p = dma_alloc_coherent(kdev, DESC_RING_SIZE, &intf->rx_edpkt_dma_addr,
69962306a36Sopenharmony_ci			       GFP_KERNEL);
70062306a36Sopenharmony_ci	if (!p) {
70162306a36Sopenharmony_ci		ret = -ENOMEM;
70262306a36Sopenharmony_ci		goto free_rx_ring;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	intf->rx_edpkt_cpu = p;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	netif_napi_add(intf->ndev, &intf->rx_napi, bcmasp_rx_poll);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	intf->rx_edpkt_dma_read = intf->rx_edpkt_dma_addr;
70962306a36Sopenharmony_ci	intf->rx_edpkt_index = 0;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* Make sure channels are disabled */
71262306a36Sopenharmony_ci	rx_edpkt_cfg_wl(intf, 0x0, RX_EDPKT_CFG_ENABLE);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* Rx SPB */
71562306a36Sopenharmony_ci	rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_READ);
71662306a36Sopenharmony_ci	rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_WRITE);
71762306a36Sopenharmony_ci	rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_BASE);
71862306a36Sopenharmony_ci	rx_edpkt_cfg_wq(intf, intf->rx_ring_dma_valid,
71962306a36Sopenharmony_ci			RX_EDPKT_RING_BUFFER_END);
72062306a36Sopenharmony_ci	rx_edpkt_cfg_wq(intf, intf->rx_ring_dma_valid,
72162306a36Sopenharmony_ci			RX_EDPKT_RING_BUFFER_VALID);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* EDPKT */
72462306a36Sopenharmony_ci	rx_edpkt_cfg_wl(intf, (RX_EDPKT_CFG_CFG0_RBUF_4K <<
72562306a36Sopenharmony_ci			RX_EDPKT_CFG_CFG0_DBUF_SHIFT) |
72662306a36Sopenharmony_ci		       (RX_EDPKT_CFG_CFG0_64_ALN <<
72762306a36Sopenharmony_ci			RX_EDPKT_CFG_CFG0_BALN_SHIFT) |
72862306a36Sopenharmony_ci		       (RX_EDPKT_CFG_CFG0_EFRM_STUF),
72962306a36Sopenharmony_ci			RX_EDPKT_CFG_CFG0);
73062306a36Sopenharmony_ci	rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr, RX_EDPKT_DMA_WRITE);
73162306a36Sopenharmony_ci	rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr, RX_EDPKT_DMA_READ);
73262306a36Sopenharmony_ci	rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr, RX_EDPKT_DMA_BASE);
73362306a36Sopenharmony_ci	rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr + (DESC_RING_SIZE - 1),
73462306a36Sopenharmony_ci			RX_EDPKT_DMA_END);
73562306a36Sopenharmony_ci	rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr + (DESC_RING_SIZE - 1),
73662306a36Sopenharmony_ci			RX_EDPKT_DMA_VALID);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	reg = UMAC2FB_CFG_DEFAULT_EN |
73962306a36Sopenharmony_ci	      ((intf->channel + 11) << UMAC2FB_CFG_CHID_SHIFT);
74062306a36Sopenharmony_ci	reg |= (0xd << UMAC2FB_CFG_OK_SEND_SHIFT);
74162306a36Sopenharmony_ci	umac2fb_wl(intf, reg, UMAC2FB_CFG);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	return 0;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cifree_rx_ring:
74662306a36Sopenharmony_ci	dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE,
74762306a36Sopenharmony_ci		       DMA_FROM_DEVICE);
74862306a36Sopenharmony_ci	__free_pages(virt_to_page(intf->rx_ring_cpu), intf->rx_buf_order);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return ret;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void bcmasp_reclaim_free_all_rx(struct bcmasp_intf *intf)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	dma_free_coherent(kdev, DESC_RING_SIZE, intf->rx_edpkt_cpu,
75862306a36Sopenharmony_ci			  intf->rx_edpkt_dma_addr);
75962306a36Sopenharmony_ci	dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE,
76062306a36Sopenharmony_ci		       DMA_FROM_DEVICE);
76162306a36Sopenharmony_ci	__free_pages(virt_to_page(intf->rx_ring_cpu), intf->rx_buf_order);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic int bcmasp_init_tx(struct bcmasp_intf *intf)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
76762306a36Sopenharmony_ci	void *p;
76862306a36Sopenharmony_ci	int ret;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	p = dma_alloc_coherent(kdev, DESC_RING_SIZE, &intf->tx_spb_dma_addr,
77162306a36Sopenharmony_ci			       GFP_KERNEL);
77262306a36Sopenharmony_ci	if (!p)
77362306a36Sopenharmony_ci		return -ENOMEM;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	intf->tx_spb_cpu = p;
77662306a36Sopenharmony_ci	intf->tx_spb_dma_valid = intf->tx_spb_dma_addr + DESC_RING_SIZE - 1;
77762306a36Sopenharmony_ci	intf->tx_spb_dma_read = intf->tx_spb_dma_addr;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	intf->tx_cbs = kcalloc(DESC_RING_COUNT, sizeof(struct bcmasp_tx_cb),
78062306a36Sopenharmony_ci			       GFP_KERNEL);
78162306a36Sopenharmony_ci	if (!intf->tx_cbs) {
78262306a36Sopenharmony_ci		ret = -ENOMEM;
78362306a36Sopenharmony_ci		goto free_tx_spb;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	intf->tx_spb_index = 0;
78762306a36Sopenharmony_ci	intf->tx_spb_clean_index = 0;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	netif_napi_add_tx(intf->ndev, &intf->tx_napi, bcmasp_tx_poll);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* Make sure channels are disabled */
79262306a36Sopenharmony_ci	tx_spb_ctrl_wl(intf, 0x0, TX_SPB_CTRL_ENABLE);
79362306a36Sopenharmony_ci	tx_epkt_core_wl(intf, 0x0, TX_EPKT_C_CFG_MISC);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/* Tx SPB */
79662306a36Sopenharmony_ci	tx_spb_ctrl_wl(intf, ((intf->channel + 8) << TX_SPB_CTRL_XF_BID_SHIFT),
79762306a36Sopenharmony_ci		       TX_SPB_CTRL_XF_CTRL2);
79862306a36Sopenharmony_ci	tx_pause_ctrl_wl(intf, (1 << (intf->channel + 8)), TX_PAUSE_MAP_VECTOR);
79962306a36Sopenharmony_ci	tx_spb_top_wl(intf, 0x1e, TX_SPB_TOP_BLKOUT);
80062306a36Sopenharmony_ci	tx_spb_top_wl(intf, 0x0, TX_SPB_TOP_SPRE_BW_CTRL);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	tx_spb_dma_wq(intf, intf->tx_spb_dma_addr, TX_SPB_DMA_READ);
80362306a36Sopenharmony_ci	tx_spb_dma_wq(intf, intf->tx_spb_dma_addr, TX_SPB_DMA_BASE);
80462306a36Sopenharmony_ci	tx_spb_dma_wq(intf, intf->tx_spb_dma_valid, TX_SPB_DMA_END);
80562306a36Sopenharmony_ci	tx_spb_dma_wq(intf, intf->tx_spb_dma_valid, TX_SPB_DMA_VALID);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return 0;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cifree_tx_spb:
81062306a36Sopenharmony_ci	dma_free_coherent(kdev, DESC_RING_SIZE, intf->tx_spb_cpu,
81162306a36Sopenharmony_ci			  intf->tx_spb_dma_addr);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return ret;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic void bcmasp_reclaim_free_all_tx(struct bcmasp_intf *intf)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* Free descriptors */
82162306a36Sopenharmony_ci	dma_free_coherent(kdev, DESC_RING_SIZE, intf->tx_spb_cpu,
82262306a36Sopenharmony_ci			  intf->tx_spb_dma_addr);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* Free cbs */
82562306a36Sopenharmony_ci	kfree(intf->tx_cbs);
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic void bcmasp_ephy_enable_set(struct bcmasp_intf *intf, bool enable)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	u32 mask = RGMII_EPHY_CFG_IDDQ_BIAS | RGMII_EPHY_CFG_EXT_PWRDOWN |
83162306a36Sopenharmony_ci		   RGMII_EPHY_CFG_IDDQ_GLOBAL;
83262306a36Sopenharmony_ci	u32 reg;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	reg = rgmii_rl(intf, RGMII_EPHY_CNTRL);
83562306a36Sopenharmony_ci	if (enable) {
83662306a36Sopenharmony_ci		reg &= ~RGMII_EPHY_CK25_DIS;
83762306a36Sopenharmony_ci		rgmii_wl(intf, reg, RGMII_EPHY_CNTRL);
83862306a36Sopenharmony_ci		mdelay(1);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		reg &= ~mask;
84162306a36Sopenharmony_ci		reg |= RGMII_EPHY_RESET;
84262306a36Sopenharmony_ci		rgmii_wl(intf, reg, RGMII_EPHY_CNTRL);
84362306a36Sopenharmony_ci		mdelay(1);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		reg &= ~RGMII_EPHY_RESET;
84662306a36Sopenharmony_ci	} else {
84762306a36Sopenharmony_ci		reg |= mask | RGMII_EPHY_RESET;
84862306a36Sopenharmony_ci		rgmii_wl(intf, reg, RGMII_EPHY_CNTRL);
84962306a36Sopenharmony_ci		mdelay(1);
85062306a36Sopenharmony_ci		reg |= RGMII_EPHY_CK25_DIS;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci	rgmii_wl(intf, reg, RGMII_EPHY_CNTRL);
85362306a36Sopenharmony_ci	mdelay(1);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	/* Set or clear the LED control override to avoid lighting up LEDs
85662306a36Sopenharmony_ci	 * while the EPHY is powered off and drawing unnecessary current.
85762306a36Sopenharmony_ci	 */
85862306a36Sopenharmony_ci	reg = rgmii_rl(intf, RGMII_SYS_LED_CNTRL);
85962306a36Sopenharmony_ci	if (enable)
86062306a36Sopenharmony_ci		reg &= ~RGMII_SYS_LED_CNTRL_LINK_OVRD;
86162306a36Sopenharmony_ci	else
86262306a36Sopenharmony_ci		reg |= RGMII_SYS_LED_CNTRL_LINK_OVRD;
86362306a36Sopenharmony_ci	rgmii_wl(intf, reg, RGMII_SYS_LED_CNTRL);
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic void bcmasp_rgmii_mode_en_set(struct bcmasp_intf *intf, bool enable)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	u32 reg;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	reg = rgmii_rl(intf, RGMII_OOB_CNTRL);
87162306a36Sopenharmony_ci	reg &= ~RGMII_OOB_DIS;
87262306a36Sopenharmony_ci	if (enable)
87362306a36Sopenharmony_ci		reg |= RGMII_MODE_EN;
87462306a36Sopenharmony_ci	else
87562306a36Sopenharmony_ci		reg &= ~RGMII_MODE_EN;
87662306a36Sopenharmony_ci	rgmii_wl(intf, reg, RGMII_OOB_CNTRL);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic void bcmasp_netif_deinit(struct net_device *dev)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
88262306a36Sopenharmony_ci	u32 reg, timeout = 1000;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	napi_disable(&intf->tx_napi);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	bcmasp_enable_tx(intf, 0);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* Flush any TX packets in the pipe */
88962306a36Sopenharmony_ci	tx_spb_dma_wl(intf, TX_SPB_DMA_FIFO_FLUSH, TX_SPB_DMA_FIFO_CTRL);
89062306a36Sopenharmony_ci	do {
89162306a36Sopenharmony_ci		reg = tx_spb_dma_rl(intf, TX_SPB_DMA_FIFO_STATUS);
89262306a36Sopenharmony_ci		if (!(reg & TX_SPB_DMA_FIFO_FLUSH))
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		usleep_range(1000, 2000);
89562306a36Sopenharmony_ci	} while (timeout-- > 0);
89662306a36Sopenharmony_ci	tx_spb_dma_wl(intf, 0x0, TX_SPB_DMA_FIFO_CTRL);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	umac_enable_set(intf, UMC_CMD_TX_EN, 0);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	phy_stop(dev->phydev);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	umac_enable_set(intf, UMC_CMD_RX_EN, 0);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	bcmasp_flush_rx_port(intf);
90562306a36Sopenharmony_ci	usleep_range(1000, 2000);
90662306a36Sopenharmony_ci	bcmasp_enable_rx(intf, 0);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	napi_disable(&intf->rx_napi);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/* Disable interrupts */
91162306a36Sopenharmony_ci	bcmasp_enable_tx_irq(intf, 0);
91262306a36Sopenharmony_ci	bcmasp_enable_rx_irq(intf, 0);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	netif_napi_del(&intf->tx_napi);
91562306a36Sopenharmony_ci	bcmasp_reclaim_free_all_tx(intf);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	netif_napi_del(&intf->rx_napi);
91862306a36Sopenharmony_ci	bcmasp_reclaim_free_all_rx(intf);
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic int bcmasp_stop(struct net_device *dev)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	netif_dbg(intf, ifdown, dev, "bcmasp stop\n");
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/* Stop tx from updating HW */
92862306a36Sopenharmony_ci	netif_tx_disable(dev);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	bcmasp_netif_deinit(dev);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	phy_disconnect(dev->phydev);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* Disable internal EPHY or external PHY */
93562306a36Sopenharmony_ci	if (intf->internal_phy)
93662306a36Sopenharmony_ci		bcmasp_ephy_enable_set(intf, false);
93762306a36Sopenharmony_ci	else
93862306a36Sopenharmony_ci		bcmasp_rgmii_mode_en_set(intf, false);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* Disable the interface clocks */
94162306a36Sopenharmony_ci	bcmasp_core_clock_set_intf(intf, false);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	clk_disable_unprepare(intf->parent->clk);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return 0;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void bcmasp_configure_port(struct bcmasp_intf *intf)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	u32 reg, id_mode_dis = 0;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	reg = rgmii_rl(intf, RGMII_PORT_CNTRL);
95362306a36Sopenharmony_ci	reg &= ~RGMII_PORT_MODE_MASK;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	switch (intf->phy_interface) {
95662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
95762306a36Sopenharmony_ci		/* RGMII_NO_ID: TXC transitions at the same time as TXD
95862306a36Sopenharmony_ci		 *		(requires PCB or receiver-side delay)
95962306a36Sopenharmony_ci		 * RGMII:	Add 2ns delay on TXC (90 degree shift)
96062306a36Sopenharmony_ci		 *
96162306a36Sopenharmony_ci		 * ID is implicitly disabled for 100Mbps (RG)MII operation.
96262306a36Sopenharmony_ci		 */
96362306a36Sopenharmony_ci		id_mode_dis = RGMII_ID_MODE_DIS;
96462306a36Sopenharmony_ci		fallthrough;
96562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
96662306a36Sopenharmony_ci		reg |= RGMII_PORT_MODE_EXT_GPHY;
96762306a36Sopenharmony_ci		break;
96862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_MII:
96962306a36Sopenharmony_ci		reg |= RGMII_PORT_MODE_EXT_EPHY;
97062306a36Sopenharmony_ci		break;
97162306a36Sopenharmony_ci	default:
97262306a36Sopenharmony_ci		break;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (intf->internal_phy)
97662306a36Sopenharmony_ci		reg |= RGMII_PORT_MODE_EPHY;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	rgmii_wl(intf, reg, RGMII_PORT_CNTRL);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	reg = rgmii_rl(intf, RGMII_OOB_CNTRL);
98162306a36Sopenharmony_ci	reg &= ~RGMII_ID_MODE_DIS;
98262306a36Sopenharmony_ci	reg |= id_mode_dis;
98362306a36Sopenharmony_ci	rgmii_wl(intf, reg, RGMII_OOB_CNTRL);
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic int bcmasp_netif_init(struct net_device *dev, bool phy_connect)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
98962306a36Sopenharmony_ci	phy_interface_t phy_iface = intf->phy_interface;
99062306a36Sopenharmony_ci	u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE |
99162306a36Sopenharmony_ci			PHY_BRCM_DIS_TXCRXC_NOENRGY |
99262306a36Sopenharmony_ci			PHY_BRCM_IDDQ_SUSPEND;
99362306a36Sopenharmony_ci	struct phy_device *phydev = NULL;
99462306a36Sopenharmony_ci	int ret;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* Always enable interface clocks */
99762306a36Sopenharmony_ci	bcmasp_core_clock_set_intf(intf, true);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/* Enable internal PHY or external PHY before any MAC activity */
100062306a36Sopenharmony_ci	if (intf->internal_phy)
100162306a36Sopenharmony_ci		bcmasp_ephy_enable_set(intf, true);
100262306a36Sopenharmony_ci	else
100362306a36Sopenharmony_ci		bcmasp_rgmii_mode_en_set(intf, true);
100462306a36Sopenharmony_ci	bcmasp_configure_port(intf);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	/* This is an ugly quirk but we have not been correctly
100762306a36Sopenharmony_ci	 * interpreting the phy_interface values and we have done that
100862306a36Sopenharmony_ci	 * across different drivers, so at least we are consistent in
100962306a36Sopenharmony_ci	 * our mistakes.
101062306a36Sopenharmony_ci	 *
101162306a36Sopenharmony_ci	 * When the Generic PHY driver is in use either the PHY has
101262306a36Sopenharmony_ci	 * been strapped or programmed correctly by the boot loader so
101362306a36Sopenharmony_ci	 * we should stick to our incorrect interpretation since we
101462306a36Sopenharmony_ci	 * have validated it.
101562306a36Sopenharmony_ci	 *
101662306a36Sopenharmony_ci	 * Now when a dedicated PHY driver is in use, we need to
101762306a36Sopenharmony_ci	 * reverse the meaning of the phy_interface_mode values to
101862306a36Sopenharmony_ci	 * something that the PHY driver will interpret and act on such
101962306a36Sopenharmony_ci	 * that we have two mistakes canceling themselves so to speak.
102062306a36Sopenharmony_ci	 * We only do this for the two modes that GENET driver
102162306a36Sopenharmony_ci	 * officially supports on Broadcom STB chips:
102262306a36Sopenharmony_ci	 * PHY_INTERFACE_MODE_RGMII and PHY_INTERFACE_MODE_RGMII_TXID.
102362306a36Sopenharmony_ci	 * Other modes are not *officially* supported with the boot
102462306a36Sopenharmony_ci	 * loader and the scripted environment generating Device Tree
102562306a36Sopenharmony_ci	 * blobs for those platforms.
102662306a36Sopenharmony_ci	 *
102762306a36Sopenharmony_ci	 * Note that internal PHY and fixed-link configurations are not
102862306a36Sopenharmony_ci	 * affected because they use different phy_interface_t values
102962306a36Sopenharmony_ci	 * or the Generic PHY driver.
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci	switch (phy_iface) {
103262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
103362306a36Sopenharmony_ci		phy_iface = PHY_INTERFACE_MODE_RGMII_ID;
103462306a36Sopenharmony_ci		break;
103562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
103662306a36Sopenharmony_ci		phy_iface = PHY_INTERFACE_MODE_RGMII_RXID;
103762306a36Sopenharmony_ci		break;
103862306a36Sopenharmony_ci	default:
103962306a36Sopenharmony_ci		break;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (phy_connect) {
104362306a36Sopenharmony_ci		phydev = of_phy_connect(dev, intf->phy_dn,
104462306a36Sopenharmony_ci					bcmasp_adj_link, phy_flags,
104562306a36Sopenharmony_ci					phy_iface);
104662306a36Sopenharmony_ci		if (!phydev) {
104762306a36Sopenharmony_ci			ret = -ENODEV;
104862306a36Sopenharmony_ci			netdev_err(dev, "could not attach to PHY\n");
104962306a36Sopenharmony_ci			goto err_phy_disable;
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		/* Indicate that the MAC is responsible for PHY PM */
105362306a36Sopenharmony_ci		phydev->mac_managed_pm = true;
105462306a36Sopenharmony_ci	} else if (!intf->wolopts) {
105562306a36Sopenharmony_ci		ret = phy_resume(dev->phydev);
105662306a36Sopenharmony_ci		if (ret)
105762306a36Sopenharmony_ci			goto err_phy_disable;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	umac_reset(intf);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	umac_init(intf);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* Disable the UniMAC RX/TX */
106562306a36Sopenharmony_ci	umac_enable_set(intf, (UMC_CMD_RX_EN | UMC_CMD_TX_EN), 0);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	umac_set_hw_addr(intf, dev->dev_addr);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	intf->old_duplex = -1;
107062306a36Sopenharmony_ci	intf->old_link = -1;
107162306a36Sopenharmony_ci	intf->old_pause = -1;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	ret = bcmasp_init_tx(intf);
107462306a36Sopenharmony_ci	if (ret)
107562306a36Sopenharmony_ci		goto err_phy_disconnect;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* Turn on asp */
107862306a36Sopenharmony_ci	bcmasp_enable_tx(intf, 1);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	ret = bcmasp_init_rx(intf);
108162306a36Sopenharmony_ci	if (ret)
108262306a36Sopenharmony_ci		goto err_reclaim_tx;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	bcmasp_enable_rx(intf, 1);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* Turn on UniMAC TX/RX */
108762306a36Sopenharmony_ci	umac_enable_set(intf, (UMC_CMD_RX_EN | UMC_CMD_TX_EN), 1);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	intf->crc_fwd = !!(umac_rl(intf, UMC_CMD) & UMC_CMD_CRC_FWD);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	bcmasp_netif_start(dev);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	netif_start_queue(dev);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cierr_reclaim_tx:
109862306a36Sopenharmony_ci	bcmasp_reclaim_free_all_tx(intf);
109962306a36Sopenharmony_cierr_phy_disconnect:
110062306a36Sopenharmony_ci	if (phydev)
110162306a36Sopenharmony_ci		phy_disconnect(phydev);
110262306a36Sopenharmony_cierr_phy_disable:
110362306a36Sopenharmony_ci	if (intf->internal_phy)
110462306a36Sopenharmony_ci		bcmasp_ephy_enable_set(intf, false);
110562306a36Sopenharmony_ci	else
110662306a36Sopenharmony_ci		bcmasp_rgmii_mode_en_set(intf, false);
110762306a36Sopenharmony_ci	return ret;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic int bcmasp_open(struct net_device *dev)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
111362306a36Sopenharmony_ci	int ret;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	netif_dbg(intf, ifup, dev, "bcmasp open\n");
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	ret = clk_prepare_enable(intf->parent->clk);
111862306a36Sopenharmony_ci	if (ret)
111962306a36Sopenharmony_ci		return ret;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	ret = bcmasp_netif_init(dev, true);
112262306a36Sopenharmony_ci	if (ret)
112362306a36Sopenharmony_ci		clk_disable_unprepare(intf->parent->clk);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	return ret;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic void bcmasp_tx_timeout(struct net_device *dev, unsigned int txqueue)
112962306a36Sopenharmony_ci{
113062306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	netif_dbg(intf, tx_err, dev, "transmit timeout!\n");
113362306a36Sopenharmony_ci	intf->mib.tx_timeout_cnt++;
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic int bcmasp_get_phys_port_name(struct net_device *dev,
113762306a36Sopenharmony_ci				     char *name, size_t len)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (snprintf(name, len, "p%d", intf->port) >= len)
114262306a36Sopenharmony_ci		return -EINVAL;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	return 0;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic void bcmasp_get_stats64(struct net_device *dev,
114862306a36Sopenharmony_ci			       struct rtnl_link_stats64 *stats)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	struct bcmasp_intf *intf = netdev_priv(dev);
115162306a36Sopenharmony_ci	struct bcmasp_intf_stats64 *lstats;
115262306a36Sopenharmony_ci	unsigned int start;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	lstats = &intf->stats64;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	do {
115762306a36Sopenharmony_ci		start = u64_stats_fetch_begin(&lstats->syncp);
115862306a36Sopenharmony_ci		stats->rx_packets = u64_stats_read(&lstats->rx_packets);
115962306a36Sopenharmony_ci		stats->rx_bytes = u64_stats_read(&lstats->rx_bytes);
116062306a36Sopenharmony_ci		stats->rx_dropped = u64_stats_read(&lstats->rx_dropped);
116162306a36Sopenharmony_ci		stats->rx_crc_errors = u64_stats_read(&lstats->rx_crc_errs);
116262306a36Sopenharmony_ci		stats->rx_frame_errors = u64_stats_read(&lstats->rx_sym_errs);
116362306a36Sopenharmony_ci		stats->rx_errors = stats->rx_crc_errors + stats->rx_frame_errors;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		stats->tx_packets = u64_stats_read(&lstats->tx_packets);
116662306a36Sopenharmony_ci		stats->tx_bytes = u64_stats_read(&lstats->tx_bytes);
116762306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&lstats->syncp, start));
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic const struct net_device_ops bcmasp_netdev_ops = {
117162306a36Sopenharmony_ci	.ndo_open		= bcmasp_open,
117262306a36Sopenharmony_ci	.ndo_stop		= bcmasp_stop,
117362306a36Sopenharmony_ci	.ndo_start_xmit		= bcmasp_xmit,
117462306a36Sopenharmony_ci	.ndo_tx_timeout		= bcmasp_tx_timeout,
117562306a36Sopenharmony_ci	.ndo_set_rx_mode	= bcmasp_set_rx_mode,
117662306a36Sopenharmony_ci	.ndo_get_phys_port_name	= bcmasp_get_phys_port_name,
117762306a36Sopenharmony_ci	.ndo_eth_ioctl		= phy_do_ioctl_running,
117862306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
117962306a36Sopenharmony_ci	.ndo_get_stats64	= bcmasp_get_stats64,
118062306a36Sopenharmony_ci};
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void bcmasp_map_res(struct bcmasp_priv *priv, struct bcmasp_intf *intf)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	/* Per port */
118562306a36Sopenharmony_ci	intf->res.umac = priv->base + UMC_OFFSET(intf);
118662306a36Sopenharmony_ci	intf->res.umac2fb = priv->base + (priv->hw_info->umac2fb +
118762306a36Sopenharmony_ci					  (intf->port * 0x4));
118862306a36Sopenharmony_ci	intf->res.rgmii = priv->base + RGMII_OFFSET(intf);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/* Per ch */
119162306a36Sopenharmony_ci	intf->tx_spb_dma = priv->base + TX_SPB_DMA_OFFSET(intf);
119262306a36Sopenharmony_ci	intf->res.tx_spb_ctrl = priv->base + TX_SPB_CTRL_OFFSET(intf);
119362306a36Sopenharmony_ci	intf->res.tx_spb_top = priv->base + TX_SPB_TOP_OFFSET(intf);
119462306a36Sopenharmony_ci	intf->res.tx_epkt_core = priv->base + TX_EPKT_C_OFFSET(intf);
119562306a36Sopenharmony_ci	intf->res.tx_pause_ctrl = priv->base + TX_PAUSE_CTRL_OFFSET(intf);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	intf->rx_edpkt_dma = priv->base + RX_EDPKT_DMA_OFFSET(intf);
119862306a36Sopenharmony_ci	intf->rx_edpkt_cfg = priv->base + RX_EDPKT_CFG_OFFSET(intf);
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci#define MAX_IRQ_STR_LEN		64
120262306a36Sopenharmony_cistruct bcmasp_intf *bcmasp_interface_create(struct bcmasp_priv *priv,
120362306a36Sopenharmony_ci					    struct device_node *ndev_dn, int i)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	struct device *dev = &priv->pdev->dev;
120662306a36Sopenharmony_ci	struct bcmasp_intf *intf;
120762306a36Sopenharmony_ci	struct net_device *ndev;
120862306a36Sopenharmony_ci	int ch, port, ret;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (of_property_read_u32(ndev_dn, "reg", &port)) {
121162306a36Sopenharmony_ci		dev_warn(dev, "%s: invalid port number\n", ndev_dn->name);
121262306a36Sopenharmony_ci		goto err;
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	if (of_property_read_u32(ndev_dn, "brcm,channel", &ch)) {
121662306a36Sopenharmony_ci		dev_warn(dev, "%s: invalid ch number\n", ndev_dn->name);
121762306a36Sopenharmony_ci		goto err;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	ndev = alloc_etherdev(sizeof(struct bcmasp_intf));
122162306a36Sopenharmony_ci	if (!ndev) {
122262306a36Sopenharmony_ci		dev_warn(dev, "%s: unable to alloc ndev\n", ndev_dn->name);
122362306a36Sopenharmony_ci		goto err;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci	intf = netdev_priv(ndev);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	intf->parent = priv;
122862306a36Sopenharmony_ci	intf->ndev = ndev;
122962306a36Sopenharmony_ci	intf->channel = ch;
123062306a36Sopenharmony_ci	intf->port = port;
123162306a36Sopenharmony_ci	intf->ndev_dn = ndev_dn;
123262306a36Sopenharmony_ci	intf->index = i;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	ret = of_get_phy_mode(ndev_dn, &intf->phy_interface);
123562306a36Sopenharmony_ci	if (ret < 0) {
123662306a36Sopenharmony_ci		dev_err(dev, "invalid PHY mode property\n");
123762306a36Sopenharmony_ci		goto err_free_netdev;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	if (intf->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
124162306a36Sopenharmony_ci		intf->internal_phy = true;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	intf->phy_dn = of_parse_phandle(ndev_dn, "phy-handle", 0);
124462306a36Sopenharmony_ci	if (!intf->phy_dn && of_phy_is_fixed_link(ndev_dn)) {
124562306a36Sopenharmony_ci		ret = of_phy_register_fixed_link(ndev_dn);
124662306a36Sopenharmony_ci		if (ret) {
124762306a36Sopenharmony_ci			dev_warn(dev, "%s: failed to register fixed PHY\n",
124862306a36Sopenharmony_ci				 ndev_dn->name);
124962306a36Sopenharmony_ci			goto err_free_netdev;
125062306a36Sopenharmony_ci		}
125162306a36Sopenharmony_ci		intf->phy_dn = ndev_dn;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* Map resource */
125562306a36Sopenharmony_ci	bcmasp_map_res(priv, intf);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	if ((!phy_interface_mode_is_rgmii(intf->phy_interface) &&
125862306a36Sopenharmony_ci	     intf->phy_interface != PHY_INTERFACE_MODE_MII &&
125962306a36Sopenharmony_ci	     intf->phy_interface != PHY_INTERFACE_MODE_INTERNAL) ||
126062306a36Sopenharmony_ci	    (intf->port != 1 && intf->internal_phy)) {
126162306a36Sopenharmony_ci		netdev_err(intf->ndev, "invalid PHY mode: %s for port %d\n",
126262306a36Sopenharmony_ci			   phy_modes(intf->phy_interface), intf->port);
126362306a36Sopenharmony_ci		ret = -EINVAL;
126462306a36Sopenharmony_ci		goto err_free_netdev;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	ret = of_get_ethdev_address(ndev_dn, ndev);
126862306a36Sopenharmony_ci	if (ret) {
126962306a36Sopenharmony_ci		netdev_warn(ndev, "using random Ethernet MAC\n");
127062306a36Sopenharmony_ci		eth_hw_addr_random(ndev);
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, dev);
127462306a36Sopenharmony_ci	intf->ops = &bcmasp_intf_ops;
127562306a36Sopenharmony_ci	ndev->netdev_ops = &bcmasp_netdev_ops;
127662306a36Sopenharmony_ci	ndev->ethtool_ops = &bcmasp_ethtool_ops;
127762306a36Sopenharmony_ci	intf->msg_enable = netif_msg_init(-1, NETIF_MSG_DRV |
127862306a36Sopenharmony_ci					  NETIF_MSG_PROBE |
127962306a36Sopenharmony_ci					  NETIF_MSG_LINK);
128062306a36Sopenharmony_ci	ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
128162306a36Sopenharmony_ci			  NETIF_F_RXCSUM;
128262306a36Sopenharmony_ci	ndev->hw_features |= ndev->features;
128362306a36Sopenharmony_ci	ndev->needed_headroom += sizeof(struct bcmasp_pkt_offload);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	return intf;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cierr_free_netdev:
128862306a36Sopenharmony_ci	free_netdev(ndev);
128962306a36Sopenharmony_cierr:
129062306a36Sopenharmony_ci	return NULL;
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_civoid bcmasp_interface_destroy(struct bcmasp_intf *intf)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	if (intf->ndev->reg_state == NETREG_REGISTERED)
129662306a36Sopenharmony_ci		unregister_netdev(intf->ndev);
129762306a36Sopenharmony_ci	if (of_phy_is_fixed_link(intf->ndev_dn))
129862306a36Sopenharmony_ci		of_phy_deregister_fixed_link(intf->ndev_dn);
129962306a36Sopenharmony_ci	free_netdev(intf->ndev);
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic void bcmasp_suspend_to_wol(struct bcmasp_intf *intf)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct net_device *ndev = intf->ndev;
130562306a36Sopenharmony_ci	u32 reg;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	reg = umac_rl(intf, UMC_MPD_CTRL);
130862306a36Sopenharmony_ci	if (intf->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE))
130962306a36Sopenharmony_ci		reg |= UMC_MPD_CTRL_MPD_EN;
131062306a36Sopenharmony_ci	reg &= ~UMC_MPD_CTRL_PSW_EN;
131162306a36Sopenharmony_ci	if (intf->wolopts & WAKE_MAGICSECURE) {
131262306a36Sopenharmony_ci		/* Program the SecureOn password */
131362306a36Sopenharmony_ci		umac_wl(intf, get_unaligned_be16(&intf->sopass[0]),
131462306a36Sopenharmony_ci			UMC_PSW_MS);
131562306a36Sopenharmony_ci		umac_wl(intf, get_unaligned_be32(&intf->sopass[2]),
131662306a36Sopenharmony_ci			UMC_PSW_LS);
131762306a36Sopenharmony_ci		reg |= UMC_MPD_CTRL_PSW_EN;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci	umac_wl(intf, reg, UMC_MPD_CTRL);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (intf->wolopts & WAKE_FILTER)
132262306a36Sopenharmony_ci		bcmasp_netfilt_suspend(intf);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* UniMAC receive needs to be turned on */
132562306a36Sopenharmony_ci	umac_enable_set(intf, UMC_CMD_RX_EN, 1);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (intf->parent->wol_irq > 0) {
132862306a36Sopenharmony_ci		wakeup_intr2_core_wl(intf->parent, 0xffffffff,
132962306a36Sopenharmony_ci				     ASP_WAKEUP_INTR2_MASK_CLEAR);
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	netif_dbg(intf, wol, ndev, "entered WOL mode\n");
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ciint bcmasp_interface_suspend(struct bcmasp_intf *intf)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	struct device *kdev = &intf->parent->pdev->dev;
133862306a36Sopenharmony_ci	struct net_device *dev = intf->ndev;
133962306a36Sopenharmony_ci	int ret = 0;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if (!netif_running(dev))
134262306a36Sopenharmony_ci		return 0;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	netif_device_detach(dev);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	bcmasp_netif_deinit(dev);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	if (!intf->wolopts) {
134962306a36Sopenharmony_ci		ret = phy_suspend(dev->phydev);
135062306a36Sopenharmony_ci		if (ret)
135162306a36Sopenharmony_ci			goto out;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		if (intf->internal_phy)
135462306a36Sopenharmony_ci			bcmasp_ephy_enable_set(intf, false);
135562306a36Sopenharmony_ci		else
135662306a36Sopenharmony_ci			bcmasp_rgmii_mode_en_set(intf, false);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		/* If Wake-on-LAN is disabled, we can safely
135962306a36Sopenharmony_ci		 * disable the network interface clocks.
136062306a36Sopenharmony_ci		 */
136162306a36Sopenharmony_ci		bcmasp_core_clock_set_intf(intf, false);
136262306a36Sopenharmony_ci	}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (device_may_wakeup(kdev) && intf->wolopts)
136562306a36Sopenharmony_ci		bcmasp_suspend_to_wol(intf);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	clk_disable_unprepare(intf->parent->clk);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	return ret;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ciout:
137262306a36Sopenharmony_ci	bcmasp_netif_init(dev, false);
137362306a36Sopenharmony_ci	return ret;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic void bcmasp_resume_from_wol(struct bcmasp_intf *intf)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	u32 reg;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	reg = umac_rl(intf, UMC_MPD_CTRL);
138162306a36Sopenharmony_ci	reg &= ~UMC_MPD_CTRL_MPD_EN;
138262306a36Sopenharmony_ci	umac_wl(intf, reg, UMC_MPD_CTRL);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (intf->parent->wol_irq > 0) {
138562306a36Sopenharmony_ci		wakeup_intr2_core_wl(intf->parent, 0xffffffff,
138662306a36Sopenharmony_ci				     ASP_WAKEUP_INTR2_MASK_SET);
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ciint bcmasp_interface_resume(struct bcmasp_intf *intf)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	struct net_device *dev = intf->ndev;
139362306a36Sopenharmony_ci	int ret;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (!netif_running(dev))
139662306a36Sopenharmony_ci		return 0;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	ret = clk_prepare_enable(intf->parent->clk);
139962306a36Sopenharmony_ci	if (ret)
140062306a36Sopenharmony_ci		return ret;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	ret = bcmasp_netif_init(dev, false);
140362306a36Sopenharmony_ci	if (ret)
140462306a36Sopenharmony_ci		goto out;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	bcmasp_resume_from_wol(intf);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	if (intf->eee.eee_enabled)
140962306a36Sopenharmony_ci		bcmasp_eee_enable_set(intf, true);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	netif_device_attach(dev);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	return 0;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ciout:
141662306a36Sopenharmony_ci	clk_disable_unprepare(intf->parent->clk);
141762306a36Sopenharmony_ci	return ret;
141862306a36Sopenharmony_ci}
1419