Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Justin Chen | 6574 | 99.68% | 11 | 78.57% |
Florian Fainelli | 16 | 0.24% | 2 | 14.29% |
Russell King | 5 | 0.08% | 1 | 7.14% |
Total | 6595 | 14 |
// SPDX-License-Identifier: GPL-2.0 #define pr_fmt(fmt) "bcmasp_intf: " fmt #include <asm/byteorder.h> #include <linux/brcmphy.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/etherdevice.h> #include <linux/netdevice.h> #include <linux/of_net.h> #include <linux/of_mdio.h> #include <linux/phy.h> #include <linux/phy_fixed.h> #include <linux/ptp_classify.h> #include <linux/platform_device.h> #include <net/ip.h> #include <net/ipv6.h> #include "bcmasp.h" #include "bcmasp_intf_defs.h" static int incr_ring(int index, int ring_count) { index++; if (index == ring_count) return 0; return index; } /* Points to last byte of descriptor */ static dma_addr_t incr_last_byte(dma_addr_t addr, dma_addr_t beg, int ring_count) { dma_addr_t end = beg + (ring_count * DESC_SIZE); addr += DESC_SIZE; if (addr > end) return beg + DESC_SIZE - 1; return addr; } /* Points to first byte of descriptor */ static dma_addr_t incr_first_byte(dma_addr_t addr, dma_addr_t beg, int ring_count) { dma_addr_t end = beg + (ring_count * DESC_SIZE); addr += DESC_SIZE; if (addr >= end) return beg; return addr; } static void bcmasp_enable_tx(struct bcmasp_intf *intf, int en) { if (en) { tx_spb_ctrl_wl(intf, TX_SPB_CTRL_ENABLE_EN, TX_SPB_CTRL_ENABLE); tx_epkt_core_wl(intf, (TX_EPKT_C_CFG_MISC_EN | TX_EPKT_C_CFG_MISC_PT | (intf->port << TX_EPKT_C_CFG_MISC_PS_SHIFT)), TX_EPKT_C_CFG_MISC); } else { tx_spb_ctrl_wl(intf, 0x0, TX_SPB_CTRL_ENABLE); tx_epkt_core_wl(intf, 0x0, TX_EPKT_C_CFG_MISC); } } static void bcmasp_enable_rx(struct bcmasp_intf *intf, int en) { if (en) rx_edpkt_cfg_wl(intf, RX_EDPKT_CFG_ENABLE_EN, RX_EDPKT_CFG_ENABLE); else rx_edpkt_cfg_wl(intf, 0x0, RX_EDPKT_CFG_ENABLE); } static void bcmasp_set_rx_mode(struct net_device *dev) { unsigned char mask[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct bcmasp_intf *intf = netdev_priv(dev); struct netdev_hw_addr *ha; int ret; spin_lock_bh(&intf->parent->mda_lock); bcmasp_disable_all_filters(intf); if (dev->flags & IFF_PROMISC) goto set_promisc; bcmasp_set_promisc(intf, 0); bcmasp_set_broad(intf, 1); bcmasp_set_oaddr(intf, dev->dev_addr, 1); if (dev->flags & IFF_ALLMULTI) { bcmasp_set_allmulti(intf, 1); } else { bcmasp_set_allmulti(intf, 0); netdev_for_each_mc_addr(ha, dev) { ret = bcmasp_set_en_mda_filter(intf, ha->addr, mask); if (ret) { intf->mib.mc_filters_full_cnt++; goto set_promisc; } } } netdev_for_each_uc_addr(ha, dev) { ret = bcmasp_set_en_mda_filter(intf, ha->addr, mask); if (ret) { intf->mib.uc_filters_full_cnt++; goto set_promisc; } } spin_unlock_bh(&intf->parent->mda_lock); return; set_promisc: bcmasp_set_promisc(intf, 1); intf->mib.promisc_filters_cnt++; /* disable all filters used by this port */ bcmasp_disable_all_filters(intf); spin_unlock_bh(&intf->parent->mda_lock); } static void bcmasp_clean_txcb(struct bcmasp_intf *intf, int index) { struct bcmasp_tx_cb *txcb = &intf->tx_cbs[index]; txcb->skb = NULL; dma_unmap_addr_set(txcb, dma_addr, 0); dma_unmap_len_set(txcb, dma_len, 0); txcb->last = false; } static int tx_spb_ring_full(struct bcmasp_intf *intf, int cnt) { int next_index, i; /* Check if we have enough room for cnt descriptors */ for (i = 0; i < cnt; i++) { next_index = incr_ring(intf->tx_spb_index, DESC_RING_COUNT); if (next_index == intf->tx_spb_clean_index) return 1; } return 0; } static struct sk_buff *bcmasp_csum_offload(struct net_device *dev, struct sk_buff *skb, bool *csum_hw) { struct bcmasp_intf *intf = netdev_priv(dev); u32 header = 0, header2 = 0, epkt = 0; struct bcmasp_pkt_offload *offload; unsigned int header_cnt = 0; u8 ip_proto; int ret; if (skb->ip_summed != CHECKSUM_PARTIAL) return skb; ret = skb_cow_head(skb, sizeof(*offload)); if (ret < 0) { intf->mib.tx_realloc_offload_failed++; goto help; } switch (skb->protocol) { case htons(ETH_P_IP): header |= PKT_OFFLOAD_HDR_SIZE_2((ip_hdrlen(skb) >> 8) & 0xf); header2 |= PKT_OFFLOAD_HDR2_SIZE_2(ip_hdrlen(skb) & 0xff); epkt |= PKT_OFFLOAD_EPKT_IP(0) | PKT_OFFLOAD_EPKT_CSUM_L2; ip_proto = ip_hdr(skb)->protocol; header_cnt += 2; break; case htons(ETH_P_IPV6): header |= PKT_OFFLOAD_HDR_SIZE_2((IP6_HLEN >> 8) & 0xf); header2 |= PKT_OFFLOAD_HDR2_SIZE_2(IP6_HLEN & 0xff); epkt |= PKT_OFFLOAD_EPKT_IP(1) | PKT_OFFLOAD_EPKT_CSUM_L2; ip_proto = ipv6_hdr(skb)->nexthdr; header_cnt += 2; break; default: goto help; } switch (ip_proto) { case IPPROTO_TCP: header2 |= PKT_OFFLOAD_HDR2_SIZE_3(tcp_hdrlen(skb)); epkt |= PKT_OFFLOAD_EPKT_TP(0) | PKT_OFFLOAD_EPKT_CSUM_L3; header_cnt++; break; case IPPROTO_UDP: header2 |= PKT_OFFLOAD_HDR2_SIZE_3(UDP_HLEN); epkt |= PKT_OFFLOAD_EPKT_TP(1) | PKT_OFFLOAD_EPKT_CSUM_L3; header_cnt++; break; default: goto help; } offload = (struct bcmasp_pkt_offload *)skb_push(skb, sizeof(*offload)); header |= PKT_OFFLOAD_HDR_OP | PKT_OFFLOAD_HDR_COUNT(header_cnt) | PKT_OFFLOAD_HDR_SIZE_1(ETH_HLEN); epkt |= PKT_OFFLOAD_EPKT_OP; offload->nop = htonl(PKT_OFFLOAD_NOP); offload->header = htonl(header); offload->header2 = htonl(header2); offload->epkt = htonl(epkt); offload->end = htonl(PKT_OFFLOAD_END_OP); *csum_hw = true; return skb; help: skb_checksum_help(skb); return skb; } static unsigned long bcmasp_rx_edpkt_dma_rq(struct bcmasp_intf *intf) { return rx_edpkt_dma_rq(intf, RX_EDPKT_DMA_VALID); } static void bcmasp_rx_edpkt_cfg_wq(struct bcmasp_intf *intf, dma_addr_t addr) { rx_edpkt_cfg_wq(intf, addr, RX_EDPKT_RING_BUFFER_READ); } static void bcmasp_rx_edpkt_dma_wq(struct bcmasp_intf *intf, dma_addr_t addr) { rx_edpkt_dma_wq(intf, addr, RX_EDPKT_DMA_READ); } static unsigned long bcmasp_tx_spb_dma_rq(struct bcmasp_intf *intf) { return tx_spb_dma_rq(intf, TX_SPB_DMA_READ); } static void bcmasp_tx_spb_dma_wq(struct bcmasp_intf *intf, dma_addr_t addr) { tx_spb_dma_wq(intf, addr, TX_SPB_DMA_VALID); } static const struct bcmasp_intf_ops bcmasp_intf_ops = { .rx_desc_read = bcmasp_rx_edpkt_dma_rq, .rx_buffer_write = bcmasp_rx_edpkt_cfg_wq, .rx_desc_write = bcmasp_rx_edpkt_dma_wq, .tx_read = bcmasp_tx_spb_dma_rq, .tx_write = bcmasp_tx_spb_dma_wq, }; static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); unsigned int total_bytes, size; int spb_index, nr_frags, i, j; struct bcmasp_tx_cb *txcb; dma_addr_t mapping, valid; struct bcmasp_desc *desc; bool csum_hw = false; struct device *kdev; skb_frag_t *frag; kdev = &intf->parent->pdev->dev; nr_frags = skb_shinfo(skb)->nr_frags; if (tx_spb_ring_full(intf, nr_frags + 1)) { netif_stop_queue(dev); if (net_ratelimit()) netdev_err(dev, "Tx Ring Full!\n"); return NETDEV_TX_BUSY; } /* Save skb len before adding csum offload header */ total_bytes = skb->len; skb = bcmasp_csum_offload(dev, skb, &csum_hw); if (!skb) return NETDEV_TX_OK; spb_index = intf->tx_spb_index; valid = intf->tx_spb_dma_valid; for (i = 0; i <= nr_frags; i++) { if (!i) { size = skb_headlen(skb); if (!nr_frags && size < (ETH_ZLEN + ETH_FCS_LEN)) { if (skb_put_padto(skb, ETH_ZLEN + ETH_FCS_LEN)) return NETDEV_TX_OK; size = skb->len; } mapping = dma_map_single(kdev, skb->data, size, DMA_TO_DEVICE); } else { frag = &skb_shinfo(skb)->frags[i - 1]; size = skb_frag_size(frag); mapping = skb_frag_dma_map(kdev, frag, 0, size, DMA_TO_DEVICE); } if (dma_mapping_error(kdev, mapping)) { intf->mib.tx_dma_failed++; spb_index = intf->tx_spb_index; for (j = 0; j < i; j++) { bcmasp_clean_txcb(intf, spb_index); spb_index = incr_ring(spb_index, DESC_RING_COUNT); } /* Rewind so we do not have a hole */ spb_index = intf->tx_spb_index; return NETDEV_TX_OK; } txcb = &intf->tx_cbs[spb_index]; desc = &intf->tx_spb_cpu[spb_index]; memset(desc, 0, sizeof(*desc)); txcb->skb = skb; txcb->bytes_sent = total_bytes; dma_unmap_addr_set(txcb, dma_addr, mapping); dma_unmap_len_set(txcb, dma_len, size); if (!i) { desc->flags |= DESC_SOF; if (csum_hw) desc->flags |= DESC_EPKT_CMD; } if (i == nr_frags) { desc->flags |= DESC_EOF; txcb->last = true; } desc->buf = mapping; desc->size = size; desc->flags |= DESC_INT_EN; netif_dbg(intf, tx_queued, dev, "%s dma_buf=%pad dma_len=0x%x flags=0x%x index=0x%x\n", __func__, &mapping, desc->size, desc->flags, spb_index); spb_index = incr_ring(spb_index, DESC_RING_COUNT); valid = incr_last_byte(valid, intf->tx_spb_dma_addr, DESC_RING_COUNT); } /* Ensure all descriptors have been written to DRAM for the * hardware to see up-to-date contents. */ wmb(); intf->tx_spb_index = spb_index; intf->tx_spb_dma_valid = valid; bcmasp_intf_tx_write(intf, intf->tx_spb_dma_valid); if (tx_spb_ring_full(intf, MAX_SKB_FRAGS + 1)) netif_stop_queue(dev); return NETDEV_TX_OK; } static void bcmasp_netif_start(struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); bcmasp_set_rx_mode(dev); napi_enable(&intf->tx_napi); napi_enable(&intf->rx_napi); bcmasp_enable_rx_irq(intf, 1); bcmasp_enable_tx_irq(intf, 1); bcmasp_enable_phy_irq(intf, 1); phy_start(dev->phydev); } static void umac_reset(struct bcmasp_intf *intf) { umac_wl(intf, 0x0, UMC_CMD); umac_wl(intf, UMC_CMD_SW_RESET, UMC_CMD); usleep_range(10, 100); /* We hold the umac in reset and bring it out of * reset when phy link is up. */ } static void umac_set_hw_addr(struct bcmasp_intf *intf, const unsigned char *addr) { u32 mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; u32 mac1 = (addr[4] << 8) | addr[5]; umac_wl(intf, mac0, UMC_MAC0); umac_wl(intf, mac1, UMC_MAC1); } static void umac_enable_set(struct bcmasp_intf *intf, u32 mask, unsigned int enable) { u32 reg; reg = umac_rl(intf, UMC_CMD); if (reg & UMC_CMD_SW_RESET) return; if (enable) reg |= mask; else reg &= ~mask; umac_wl(intf, reg, UMC_CMD); /* UniMAC stops on a packet boundary, wait for a full-sized packet * to be processed (1 msec). */ if (enable == 0) usleep_range(1000, 2000); } static void umac_init(struct bcmasp_intf *intf) { umac_wl(intf, 0x800, UMC_FRM_LEN); umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL); umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ); } static int bcmasp_tx_reclaim(struct bcmasp_intf *intf) { struct bcmasp_intf_stats64 *stats = &intf->stats64; struct device *kdev = &intf->parent->pdev->dev; unsigned long read, released = 0; struct bcmasp_tx_cb *txcb; struct bcmasp_desc *desc; dma_addr_t mapping; read = bcmasp_intf_tx_read(intf); while (intf->tx_spb_dma_read != read) { txcb = &intf->tx_cbs[intf->tx_spb_clean_index]; mapping = dma_unmap_addr(txcb, dma_addr); dma_unmap_single(kdev, mapping, dma_unmap_len(txcb, dma_len), DMA_TO_DEVICE); if (txcb->last) { dev_consume_skb_any(txcb->skb); u64_stats_update_begin(&stats->syncp); u64_stats_inc(&stats->tx_packets); u64_stats_add(&stats->tx_bytes, txcb->bytes_sent); u64_stats_update_end(&stats->syncp); } desc = &intf->tx_spb_cpu[intf->tx_spb_clean_index]; netif_dbg(intf, tx_done, intf->ndev, "%s dma_buf=%pad dma_len=0x%x flags=0x%x c_index=0x%x\n", __func__, &mapping, desc->size, desc->flags, intf->tx_spb_clean_index); bcmasp_clean_txcb(intf, intf->tx_spb_clean_index); released++; intf->tx_spb_clean_index = incr_ring(intf->tx_spb_clean_index, DESC_RING_COUNT); intf->tx_spb_dma_read = incr_first_byte(intf->tx_spb_dma_read, intf->tx_spb_dma_addr, DESC_RING_COUNT); } return released; } static int bcmasp_tx_poll(struct napi_struct *napi, int budget) { struct bcmasp_intf *intf = container_of(napi, struct bcmasp_intf, tx_napi); int released = 0; released = bcmasp_tx_reclaim(intf); napi_complete(&intf->tx_napi); bcmasp_enable_tx_irq(intf, 1); if (released) netif_wake_queue(intf->ndev); return 0; } static int bcmasp_rx_poll(struct napi_struct *napi, int budget) { struct bcmasp_intf *intf = container_of(napi, struct bcmasp_intf, rx_napi); struct bcmasp_intf_stats64 *stats = &intf->stats64; struct device *kdev = &intf->parent->pdev->dev; unsigned long processed = 0; struct bcmasp_desc *desc; struct sk_buff *skb; dma_addr_t valid; void *data; u64 flags; u32 len; valid = bcmasp_intf_rx_desc_read(intf) + 1; if (valid == intf->rx_edpkt_dma_addr + DESC_RING_SIZE) valid = intf->rx_edpkt_dma_addr; while ((processed < budget) && (valid != intf->rx_edpkt_dma_read)) { desc = &intf->rx_edpkt_cpu[intf->rx_edpkt_index]; /* Ensure that descriptor has been fully written to DRAM by * hardware before reading by the CPU */ rmb(); /* Calculate virt addr by offsetting from physical addr */ data = intf->rx_ring_cpu + (DESC_ADDR(desc->buf) - intf->rx_ring_dma); flags = DESC_FLAGS(desc->buf); if (unlikely(flags & (DESC_CRC_ERR | DESC_RX_SYM_ERR))) { if (net_ratelimit()) { netif_err(intf, rx_status, intf->ndev, "flags=0x%llx\n", flags); } u64_stats_update_begin(&stats->syncp); if (flags & DESC_CRC_ERR) u64_stats_inc(&stats->rx_crc_errs); if (flags & DESC_RX_SYM_ERR) u64_stats_inc(&stats->rx_sym_errs); u64_stats_update_end(&stats->syncp); goto next; } dma_sync_single_for_cpu(kdev, DESC_ADDR(desc->buf), desc->size, DMA_FROM_DEVICE); len = desc->size; skb = napi_alloc_skb(napi, len); if (!skb) { u64_stats_update_begin(&stats->syncp); u64_stats_inc(&stats->rx_dropped); u64_stats_update_end(&stats->syncp); intf->mib.alloc_rx_skb_failed++; goto next; } skb_put(skb, len); memcpy(skb->data, data, len); skb_pull(skb, 2); len -= 2; if (likely(intf->crc_fwd)) { skb_trim(skb, len - ETH_FCS_LEN); len -= ETH_FCS_LEN; } if ((intf->ndev->features & NETIF_F_RXCSUM) && (desc->buf & DESC_CHKSUM)) skb->ip_summed = CHECKSUM_UNNECESSARY; skb->protocol = eth_type_trans(skb, intf->ndev); napi_gro_receive(napi, skb); u64_stats_update_begin(&stats->syncp); u64_stats_inc(&stats->rx_packets); u64_stats_add(&stats->rx_bytes, len); u64_stats_update_end(&stats->syncp); next: bcmasp_intf_rx_buffer_write(intf, (DESC_ADDR(desc->buf) + desc->size)); processed++; intf->rx_edpkt_dma_read = incr_first_byte(intf->rx_edpkt_dma_read, intf->rx_edpkt_dma_addr, DESC_RING_COUNT); intf->rx_edpkt_index = incr_ring(intf->rx_edpkt_index, DESC_RING_COUNT); } bcmasp_intf_rx_desc_write(intf, intf->rx_edpkt_dma_read); if (processed < budget) { napi_complete_done(&intf->rx_napi, processed); bcmasp_enable_rx_irq(intf, 1); } return processed; } static void bcmasp_adj_link(struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); struct phy_device *phydev = dev->phydev; u32 cmd_bits = 0, reg; int changed = 0; bool active; if (intf->old_link != phydev->link) { changed = 1; intf->old_link = phydev->link; } if (intf->old_duplex != phydev->duplex) { changed = 1; intf->old_duplex = phydev->duplex; } switch (phydev->speed) { case SPEED_2500: cmd_bits = UMC_CMD_SPEED_2500; break; case SPEED_1000: cmd_bits = UMC_CMD_SPEED_1000; break; case SPEED_100: cmd_bits = UMC_CMD_SPEED_100; break; case SPEED_10: cmd_bits = UMC_CMD_SPEED_10; break; default: break; } cmd_bits <<= UMC_CMD_SPEED_SHIFT; if (phydev->duplex == DUPLEX_HALF) cmd_bits |= UMC_CMD_HD_EN; if (intf->old_pause != phydev->pause) { changed = 1; intf->old_pause = phydev->pause; } if (!phydev->pause) cmd_bits |= UMC_CMD_RX_PAUSE_IGNORE | UMC_CMD_TX_PAUSE_IGNORE; if (!changed) return; if (phydev->link) { reg = umac_rl(intf, UMC_CMD); reg &= ~((UMC_CMD_SPEED_MASK << UMC_CMD_SPEED_SHIFT) | UMC_CMD_HD_EN | UMC_CMD_RX_PAUSE_IGNORE | UMC_CMD_TX_PAUSE_IGNORE); reg |= cmd_bits; if (reg & UMC_CMD_SW_RESET) { reg &= ~UMC_CMD_SW_RESET; umac_wl(intf, reg, UMC_CMD); udelay(2); reg |= UMC_CMD_TX_EN | UMC_CMD_RX_EN | UMC_CMD_PROMISC; } umac_wl(intf, reg, UMC_CMD); active = phy_init_eee(phydev, 0) >= 0; bcmasp_eee_enable_set(intf, active); } reg = rgmii_rl(intf, RGMII_OOB_CNTRL); if (phydev->link) reg |= RGMII_LINK; else reg &= ~RGMII_LINK; rgmii_wl(intf, reg, RGMII_OOB_CNTRL); if (changed) phy_print_status(phydev); } static int bcmasp_alloc_buffers(struct bcmasp_intf *intf) { struct device *kdev = &intf->parent->pdev->dev; struct page *buffer_pg; /* Alloc RX */ intf->rx_buf_order = get_order(RING_BUFFER_SIZE); buffer_pg = alloc_pages(GFP_KERNEL, intf->rx_buf_order); if (!buffer_pg) return -ENOMEM; intf->rx_ring_cpu = page_to_virt(buffer_pg); intf->rx_ring_dma = dma_map_page(kdev, buffer_pg, 0, RING_BUFFER_SIZE, DMA_FROM_DEVICE); if (dma_mapping_error(kdev, intf->rx_ring_dma)) goto free_rx_buffer; intf->rx_edpkt_cpu = dma_alloc_coherent(kdev, DESC_RING_SIZE, &intf->rx_edpkt_dma_addr, GFP_KERNEL); if (!intf->rx_edpkt_cpu) goto free_rx_buffer_dma; /* Alloc TX */ intf->tx_spb_cpu = dma_alloc_coherent(kdev, DESC_RING_SIZE, &intf->tx_spb_dma_addr, GFP_KERNEL); if (!intf->tx_spb_cpu) goto free_rx_edpkt_dma; intf->tx_cbs = kcalloc(DESC_RING_COUNT, sizeof(struct bcmasp_tx_cb), GFP_KERNEL); if (!intf->tx_cbs) goto free_tx_spb_dma; return 0; free_tx_spb_dma: dma_free_coherent(kdev, DESC_RING_SIZE, intf->tx_spb_cpu, intf->tx_spb_dma_addr); free_rx_edpkt_dma: dma_free_coherent(kdev, DESC_RING_SIZE, intf->rx_edpkt_cpu, intf->rx_edpkt_dma_addr); free_rx_buffer_dma: dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE, DMA_FROM_DEVICE); free_rx_buffer: __free_pages(buffer_pg, intf->rx_buf_order); return -ENOMEM; } static void bcmasp_reclaim_free_buffers(struct bcmasp_intf *intf) { struct device *kdev = &intf->parent->pdev->dev; /* RX buffers */ dma_free_coherent(kdev, DESC_RING_SIZE, intf->rx_edpkt_cpu, intf->rx_edpkt_dma_addr); dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE, DMA_FROM_DEVICE); __free_pages(virt_to_page(intf->rx_ring_cpu), intf->rx_buf_order); /* TX buffers */ dma_free_coherent(kdev, DESC_RING_SIZE, intf->tx_spb_cpu, intf->tx_spb_dma_addr); kfree(intf->tx_cbs); } static void bcmasp_init_rx(struct bcmasp_intf *intf) { /* Restart from index 0 */ intf->rx_ring_dma_valid = intf->rx_ring_dma + RING_BUFFER_SIZE - 1; intf->rx_edpkt_dma_valid = intf->rx_edpkt_dma_addr + (DESC_RING_SIZE - 1); intf->rx_edpkt_dma_read = intf->rx_edpkt_dma_addr; intf->rx_edpkt_index = 0; /* Make sure channels are disabled */ rx_edpkt_cfg_wl(intf, 0x0, RX_EDPKT_CFG_ENABLE); /* Rx SPB */ rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_READ); rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_WRITE); rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_BASE); rx_edpkt_cfg_wq(intf, intf->rx_ring_dma_valid, RX_EDPKT_RING_BUFFER_END); rx_edpkt_cfg_wq(intf, intf->rx_ring_dma_valid, RX_EDPKT_RING_BUFFER_VALID); /* EDPKT */ rx_edpkt_cfg_wl(intf, (RX_EDPKT_CFG_CFG0_RBUF_4K << RX_EDPKT_CFG_CFG0_DBUF_SHIFT) | (RX_EDPKT_CFG_CFG0_64_ALN << RX_EDPKT_CFG_CFG0_BALN_SHIFT) | (RX_EDPKT_CFG_CFG0_EFRM_STUF), RX_EDPKT_CFG_CFG0); rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr, RX_EDPKT_DMA_WRITE); rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr, RX_EDPKT_DMA_READ); rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_addr, RX_EDPKT_DMA_BASE); rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_valid, RX_EDPKT_DMA_END); rx_edpkt_dma_wq(intf, intf->rx_edpkt_dma_valid, RX_EDPKT_DMA_VALID); umac2fb_wl(intf, UMAC2FB_CFG_DEFAULT_EN | ((intf->channel + 11) << UMAC2FB_CFG_CHID_SHIFT) | (0xd << UMAC2FB_CFG_OK_SEND_SHIFT), UMAC2FB_CFG); } static void bcmasp_init_tx(struct bcmasp_intf *intf) { /* Restart from index 0 */ intf->tx_spb_dma_valid = intf->tx_spb_dma_addr + DESC_RING_SIZE - 1; intf->tx_spb_dma_read = intf->tx_spb_dma_addr; intf->tx_spb_index = 0; intf->tx_spb_clean_index = 0; memset(intf->tx_cbs, 0, sizeof(struct bcmasp_tx_cb) * DESC_RING_COUNT); /* Make sure channels are disabled */ tx_spb_ctrl_wl(intf, 0x0, TX_SPB_CTRL_ENABLE); tx_epkt_core_wl(intf, 0x0, TX_EPKT_C_CFG_MISC); /* Tx SPB */ tx_spb_ctrl_wl(intf, ((intf->channel + 8) << TX_SPB_CTRL_XF_BID_SHIFT), TX_SPB_CTRL_XF_CTRL2); tx_pause_ctrl_wl(intf, (1 << (intf->channel + 8)), TX_PAUSE_MAP_VECTOR); tx_spb_top_wl(intf, 0x1e, TX_SPB_TOP_BLKOUT); tx_spb_top_wl(intf, 0x0, TX_SPB_TOP_SPRE_BW_CTRL); tx_spb_dma_wq(intf, intf->tx_spb_dma_addr, TX_SPB_DMA_READ); tx_spb_dma_wq(intf, intf->tx_spb_dma_addr, TX_SPB_DMA_BASE); tx_spb_dma_wq(intf, intf->tx_spb_dma_valid, TX_SPB_DMA_END); tx_spb_dma_wq(intf, intf->tx_spb_dma_valid, TX_SPB_DMA_VALID); } static void bcmasp_ephy_enable_set(struct bcmasp_intf *intf, bool enable) { u32 mask = RGMII_EPHY_CFG_IDDQ_BIAS | RGMII_EPHY_CFG_EXT_PWRDOWN | RGMII_EPHY_CFG_IDDQ_GLOBAL; u32 reg; reg = rgmii_rl(intf, RGMII_EPHY_CNTRL); if (enable) { reg &= ~RGMII_EPHY_CK25_DIS; rgmii_wl(intf, reg, RGMII_EPHY_CNTRL); mdelay(1); reg &= ~mask; reg |= RGMII_EPHY_RESET; rgmii_wl(intf, reg, RGMII_EPHY_CNTRL); mdelay(1); reg &= ~RGMII_EPHY_RESET; } else { reg |= mask | RGMII_EPHY_RESET; rgmii_wl(intf, reg, RGMII_EPHY_CNTRL); mdelay(1); reg |= RGMII_EPHY_CK25_DIS; } rgmii_wl(intf, reg, RGMII_EPHY_CNTRL); mdelay(1); /* Set or clear the LED control override to avoid lighting up LEDs * while the EPHY is powered off and drawing unnecessary current. */ reg = rgmii_rl(intf, RGMII_SYS_LED_CNTRL); if (enable) reg &= ~RGMII_SYS_LED_CNTRL_LINK_OVRD; else reg |= RGMII_SYS_LED_CNTRL_LINK_OVRD; rgmii_wl(intf, reg, RGMII_SYS_LED_CNTRL); } static void bcmasp_rgmii_mode_en_set(struct bcmasp_intf *intf, bool enable) { u32 reg; reg = rgmii_rl(intf, RGMII_OOB_CNTRL); reg &= ~RGMII_OOB_DIS; if (enable) reg |= RGMII_MODE_EN; else reg &= ~RGMII_MODE_EN; rgmii_wl(intf, reg, RGMII_OOB_CNTRL); } static void bcmasp_netif_deinit(struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); u32 reg, timeout = 1000; napi_disable(&intf->tx_napi); bcmasp_enable_tx(intf, 0); /* Flush any TX packets in the pipe */ tx_spb_dma_wl(intf, TX_SPB_DMA_FIFO_FLUSH, TX_SPB_DMA_FIFO_CTRL); do { reg = tx_spb_dma_rl(intf, TX_SPB_DMA_FIFO_STATUS); if (!(reg & TX_SPB_DMA_FIFO_FLUSH)) break; usleep_range(1000, 2000); } while (timeout-- > 0); tx_spb_dma_wl(intf, 0x0, TX_SPB_DMA_FIFO_CTRL); bcmasp_tx_reclaim(intf); umac_enable_set(intf, UMC_CMD_TX_EN, 0); phy_stop(dev->phydev); umac_enable_set(intf, UMC_CMD_RX_EN, 0); bcmasp_flush_rx_port(intf); usleep_range(1000, 2000); bcmasp_enable_rx(intf, 0); napi_disable(&intf->rx_napi); /* Disable interrupts */ bcmasp_enable_tx_irq(intf, 0); bcmasp_enable_rx_irq(intf, 0); bcmasp_enable_phy_irq(intf, 0); netif_napi_del(&intf->tx_napi); netif_napi_del(&intf->rx_napi); } static int bcmasp_stop(struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); netif_dbg(intf, ifdown, dev, "bcmasp stop\n"); /* Stop tx from updating HW */ netif_tx_disable(dev); bcmasp_netif_deinit(dev); bcmasp_reclaim_free_buffers(intf); phy_disconnect(dev->phydev); /* Disable internal EPHY or external PHY */ if (intf->internal_phy) bcmasp_ephy_enable_set(intf, false); else bcmasp_rgmii_mode_en_set(intf, false); /* Disable the interface clocks */ bcmasp_core_clock_set_intf(intf, false); clk_disable_unprepare(intf->parent->clk); return 0; } static void bcmasp_configure_port(struct bcmasp_intf *intf) { u32 reg, id_mode_dis = 0; reg = rgmii_rl(intf, RGMII_PORT_CNTRL); reg &= ~RGMII_PORT_MODE_MASK; switch (intf->phy_interface) { case PHY_INTERFACE_MODE_RGMII: /* RGMII_NO_ID: TXC transitions at the same time as TXD * (requires PCB or receiver-side delay) * RGMII: Add 2ns delay on TXC (90 degree shift) * * ID is implicitly disabled for 100Mbps (RG)MII operation. */ id_mode_dis = RGMII_ID_MODE_DIS; fallthrough; case PHY_INTERFACE_MODE_RGMII_TXID: reg |= RGMII_PORT_MODE_EXT_GPHY; break; case PHY_INTERFACE_MODE_MII: reg |= RGMII_PORT_MODE_EXT_EPHY; break; default: break; } if (intf->internal_phy) reg |= RGMII_PORT_MODE_EPHY; rgmii_wl(intf, reg, RGMII_PORT_CNTRL); reg = rgmii_rl(intf, RGMII_OOB_CNTRL); reg &= ~RGMII_ID_MODE_DIS; reg |= id_mode_dis; rgmii_wl(intf, reg, RGMII_OOB_CNTRL); } static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) { struct bcmasp_intf *intf = netdev_priv(dev); phy_interface_t phy_iface = intf->phy_interface; u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE | PHY_BRCM_DIS_TXCRXC_NOENRGY | PHY_BRCM_IDDQ_SUSPEND; struct phy_device *phydev = NULL; int ret; /* Always enable interface clocks */ bcmasp_core_clock_set_intf(intf, true); /* Enable internal PHY or external PHY before any MAC activity */ if (intf->internal_phy) bcmasp_ephy_enable_set(intf, true); else bcmasp_rgmii_mode_en_set(intf, true); bcmasp_configure_port(intf); /* This is an ugly quirk but we have not been correctly * interpreting the phy_interface values and we have done that * across different drivers, so at least we are consistent in * our mistakes. * * When the Generic PHY driver is in use either the PHY has * been strapped or programmed correctly by the boot loader so * we should stick to our incorrect interpretation since we * have validated it. * * Now when a dedicated PHY driver is in use, we need to * reverse the meaning of the phy_interface_mode values to * something that the PHY driver will interpret and act on such * that we have two mistakes canceling themselves so to speak. * We only do this for the two modes that GENET driver * officially supports on Broadcom STB chips: * PHY_INTERFACE_MODE_RGMII and PHY_INTERFACE_MODE_RGMII_TXID. * Other modes are not *officially* supported with the boot * loader and the scripted environment generating Device Tree * blobs for those platforms. * * Note that internal PHY and fixed-link configurations are not * affected because they use different phy_interface_t values * or the Generic PHY driver. */ switch (phy_iface) { case PHY_INTERFACE_MODE_RGMII: phy_iface = PHY_INTERFACE_MODE_RGMII_ID; break; case PHY_INTERFACE_MODE_RGMII_TXID: phy_iface = PHY_INTERFACE_MODE_RGMII_RXID; break; default: break; } if (phy_connect) { phydev = of_phy_connect(dev, intf->phy_dn, bcmasp_adj_link, phy_flags, phy_iface); if (!phydev) { ret = -ENODEV; netdev_err(dev, "could not attach to PHY\n"); goto err_phy_disable; } if (intf->internal_phy) dev->phydev->irq = PHY_MAC_INTERRUPT; /* Indicate that the MAC is responsible for PHY PM */ phydev->mac_managed_pm = true; } umac_reset(intf); umac_init(intf); umac_set_hw_addr(intf, dev->dev_addr); intf->old_duplex = -1; intf->old_link = -1; intf->old_pause = -1; bcmasp_init_tx(intf); netif_napi_add_tx(intf->ndev, &intf->tx_napi, bcmasp_tx_poll); bcmasp_enable_tx(intf, 1); bcmasp_init_rx(intf); netif_napi_add(intf->ndev, &intf->rx_napi, bcmasp_rx_poll); bcmasp_enable_rx(intf, 1); intf->crc_fwd = !!(umac_rl(intf, UMC_CMD) & UMC_CMD_CRC_FWD); bcmasp_netif_start(dev); netif_start_queue(dev); return 0; err_phy_disable: if (intf->internal_phy) bcmasp_ephy_enable_set(intf, false); else bcmasp_rgmii_mode_en_set(intf, false); return ret; } static int bcmasp_open(struct net_device *dev) { struct bcmasp_intf *intf = netdev_priv(dev); int ret; netif_dbg(intf, ifup, dev, "bcmasp open\n"); ret = bcmasp_alloc_buffers(intf); if (ret) return ret; ret = clk_prepare_enable(intf->parent->clk); if (ret) goto err_free_mem; ret = bcmasp_netif_init(dev, true); if (ret) { clk_disable_unprepare(intf->parent->clk); goto err_free_mem; } return ret; err_free_mem: bcmasp_reclaim_free_buffers(intf); return ret; } static void bcmasp_tx_timeout(struct net_device *dev, unsigned int txqueue) { struct bcmasp_intf *intf = netdev_priv(dev); netif_dbg(intf, tx_err, dev, "transmit timeout!\n"); intf->mib.tx_timeout_cnt++; } static int bcmasp_get_phys_port_name(struct net_device *dev, char *name, size_t len) { struct bcmasp_intf *intf = netdev_priv(dev); if (snprintf(name, len, "p%d", intf->port) >= len) return -EINVAL; return 0; } static void bcmasp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct bcmasp_intf *intf = netdev_priv(dev); struct bcmasp_intf_stats64 *lstats; unsigned int start; lstats = &intf->stats64; do { start = u64_stats_fetch_begin(&lstats->syncp); stats->rx_packets = u64_stats_read(&lstats->rx_packets); stats->rx_bytes = u64_stats_read(&lstats->rx_bytes); stats->rx_dropped = u64_stats_read(&lstats->rx_dropped); stats->rx_crc_errors = u64_stats_read(&lstats->rx_crc_errs); stats->rx_frame_errors = u64_stats_read(&lstats->rx_sym_errs); stats->rx_errors = stats->rx_crc_errors + stats->rx_frame_errors; stats->tx_packets = u64_stats_read(&lstats->tx_packets); stats->tx_bytes = u64_stats_read(&lstats->tx_bytes); } while (u64_stats_fetch_retry(&lstats->syncp, start)); } static const struct net_device_ops bcmasp_netdev_ops = { .ndo_open = bcmasp_open, .ndo_stop = bcmasp_stop, .ndo_start_xmit = bcmasp_xmit, .ndo_tx_timeout = bcmasp_tx_timeout, .ndo_set_rx_mode = bcmasp_set_rx_mode, .ndo_get_phys_port_name = bcmasp_get_phys_port_name, .ndo_eth_ioctl = phy_do_ioctl_running, .ndo_set_mac_address = eth_mac_addr, .ndo_get_stats64 = bcmasp_get_stats64, }; static void bcmasp_map_res(struct bcmasp_priv *priv, struct bcmasp_intf *intf) { /* Per port */ intf->res.umac = priv->base + UMC_OFFSET(intf); intf->res.umac2fb = priv->base + (priv->hw_info->umac2fb + (intf->port * 0x4)); intf->res.rgmii = priv->base + RGMII_OFFSET(intf); /* Per ch */ intf->tx_spb_dma = priv->base + TX_SPB_DMA_OFFSET(intf); intf->res.tx_spb_ctrl = priv->base + TX_SPB_CTRL_OFFSET(intf); intf->res.tx_spb_top = priv->base + TX_SPB_TOP_OFFSET(intf); intf->res.tx_epkt_core = priv->base + TX_EPKT_C_OFFSET(intf); intf->res.tx_pause_ctrl = priv->base + TX_PAUSE_CTRL_OFFSET(intf); intf->rx_edpkt_dma = priv->base + RX_EDPKT_DMA_OFFSET(intf); intf->rx_edpkt_cfg = priv->base + RX_EDPKT_CFG_OFFSET(intf); } #define MAX_IRQ_STR_LEN 64 struct bcmasp_intf *bcmasp_interface_create(struct bcmasp_priv *priv, struct device_node *ndev_dn, int i) { struct device *dev = &priv->pdev->dev; struct bcmasp_intf *intf; struct net_device *ndev; int ch, port, ret; if (of_property_read_u32(ndev_dn, "reg", &port)) { dev_warn(dev, "%s: invalid port number\n", ndev_dn->name); goto err; } if (of_property_read_u32(ndev_dn, "brcm,channel", &ch)) { dev_warn(dev, "%s: invalid ch number\n", ndev_dn->name); goto err; } ndev = alloc_etherdev(sizeof(struct bcmasp_intf)); if (!ndev) { dev_warn(dev, "%s: unable to alloc ndev\n", ndev_dn->name); goto err; } intf = netdev_priv(ndev); intf->parent = priv; intf->ndev = ndev; intf->channel = ch; intf->port = port; intf->ndev_dn = ndev_dn; intf->index = i; ret = of_get_phy_mode(ndev_dn, &intf->phy_interface); if (ret < 0) { dev_err(dev, "invalid PHY mode property\n"); goto err_free_netdev; } if (intf->phy_interface == PHY_INTERFACE_MODE_INTERNAL) intf->internal_phy = true; intf->phy_dn = of_parse_phandle(ndev_dn, "phy-handle", 0); if (!intf->phy_dn && of_phy_is_fixed_link(ndev_dn)) { ret = of_phy_register_fixed_link(ndev_dn); if (ret) { dev_warn(dev, "%s: failed to register fixed PHY\n", ndev_dn->name); goto err_free_netdev; } intf->phy_dn = ndev_dn; } /* Map resource */ bcmasp_map_res(priv, intf); if ((!phy_interface_mode_is_rgmii(intf->phy_interface) && intf->phy_interface != PHY_INTERFACE_MODE_MII && intf->phy_interface != PHY_INTERFACE_MODE_INTERNAL) || (intf->port != 1 && intf->internal_phy)) { netdev_err(intf->ndev, "invalid PHY mode: %s for port %d\n", phy_modes(intf->phy_interface), intf->port); ret = -EINVAL; goto err_free_netdev; } ret = of_get_ethdev_address(ndev_dn, ndev); if (ret) { netdev_warn(ndev, "using random Ethernet MAC\n"); eth_hw_addr_random(ndev); } SET_NETDEV_DEV(ndev, dev); intf->ops = &bcmasp_intf_ops; ndev->netdev_ops = &bcmasp_netdev_ops; ndev->ethtool_ops = &bcmasp_ethtool_ops; intf->msg_enable = netif_msg_init(-1, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK); ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_RXCSUM; ndev->hw_features |= ndev->features; ndev->needed_headroom += sizeof(struct bcmasp_pkt_offload); return intf; err_free_netdev: free_netdev(ndev); err: return NULL; } void bcmasp_interface_destroy(struct bcmasp_intf *intf) { if (intf->ndev->reg_state == NETREG_REGISTERED) unregister_netdev(intf->ndev); if (of_phy_is_fixed_link(intf->ndev_dn)) of_phy_deregister_fixed_link(intf->ndev_dn); free_netdev(intf->ndev); } static void bcmasp_suspend_to_wol(struct bcmasp_intf *intf) { struct net_device *ndev = intf->ndev; u32 reg; reg = umac_rl(intf, UMC_MPD_CTRL); if (intf->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) reg |= UMC_MPD_CTRL_MPD_EN; reg &= ~UMC_MPD_CTRL_PSW_EN; if (intf->wolopts & WAKE_MAGICSECURE) { /* Program the SecureOn password */ umac_wl(intf, get_unaligned_be16(&intf->sopass[0]), UMC_PSW_MS); umac_wl(intf, get_unaligned_be32(&intf->sopass[2]), UMC_PSW_LS); reg |= UMC_MPD_CTRL_PSW_EN; } umac_wl(intf, reg, UMC_MPD_CTRL); if (intf->wolopts & WAKE_FILTER) bcmasp_netfilt_suspend(intf); /* Bring UniMAC out of reset if needed and enable RX */ reg = umac_rl(intf, UMC_CMD); if (reg & UMC_CMD_SW_RESET) reg &= ~UMC_CMD_SW_RESET; reg |= UMC_CMD_RX_EN | UMC_CMD_PROMISC; umac_wl(intf, reg, UMC_CMD); umac_enable_set(intf, UMC_CMD_RX_EN, 1); if (intf->parent->wol_irq > 0) { wakeup_intr2_core_wl(intf->parent, 0xffffffff, ASP_WAKEUP_INTR2_MASK_CLEAR); } if (intf->eee.eee_enabled && intf->parent->eee_fixup) intf->parent->eee_fixup(intf, true); netif_dbg(intf, wol, ndev, "entered WOL mode\n"); } int bcmasp_interface_suspend(struct bcmasp_intf *intf) { struct device *kdev = &intf->parent->pdev->dev; struct net_device *dev = intf->ndev; if (!netif_running(dev)) return 0; netif_device_detach(dev); bcmasp_netif_deinit(dev); if (!intf->wolopts) { if (intf->internal_phy) bcmasp_ephy_enable_set(intf, false); else bcmasp_rgmii_mode_en_set(intf, false); /* If Wake-on-LAN is disabled, we can safely * disable the network interface clocks. */ bcmasp_core_clock_set_intf(intf, false); } if (device_may_wakeup(kdev) && intf->wolopts) bcmasp_suspend_to_wol(intf); clk_disable_unprepare(intf->parent->clk); return 0; } static void bcmasp_resume_from_wol(struct bcmasp_intf *intf) { u32 reg; if (intf->eee.eee_enabled && intf->parent->eee_fixup) intf->parent->eee_fixup(intf, false); reg = umac_rl(intf, UMC_MPD_CTRL); reg &= ~UMC_MPD_CTRL_MPD_EN; umac_wl(intf, reg, UMC_MPD_CTRL); if (intf->parent->wol_irq > 0) { wakeup_intr2_core_wl(intf->parent, 0xffffffff, ASP_WAKEUP_INTR2_MASK_SET); } } int bcmasp_interface_resume(struct bcmasp_intf *intf) { struct net_device *dev = intf->ndev; int ret; if (!netif_running(dev)) return 0; ret = clk_prepare_enable(intf->parent->clk); if (ret) return ret; ret = bcmasp_netif_init(dev, false); if (ret) goto out; bcmasp_resume_from_wol(intf); if (intf->eee.eee_enabled) bcmasp_eee_enable_set(intf, true); netif_device_attach(dev); return 0; out: clk_disable_unprepare(intf->parent->clk); return ret; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1