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