1753a026cSClément Léger // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2753a026cSClément Léger /* 3753a026cSClément Léger * Microsemi SoCs FDMA driver 4753a026cSClément Léger * 5753a026cSClément Léger * Copyright (c) 2021 Microchip 6753a026cSClément Léger * 7753a026cSClément Léger * Page recycling code is mostly taken from gianfar driver. 8753a026cSClément Léger */ 9753a026cSClément Léger 10753a026cSClément Léger #include <linux/align.h> 11753a026cSClément Léger #include <linux/bitops.h> 12753a026cSClément Léger #include <linux/dmapool.h> 13753a026cSClément Léger #include <linux/dsa/ocelot.h> 14753a026cSClément Léger #include <linux/netdevice.h> 15753a026cSClément Léger #include <linux/of_platform.h> 16753a026cSClément Léger #include <linux/skbuff.h> 17753a026cSClément Léger 18753a026cSClément Léger #include "ocelot_fdma.h" 19753a026cSClément Léger #include "ocelot_qs.h" 20753a026cSClément Léger 21753a026cSClément Léger DEFINE_STATIC_KEY_FALSE(ocelot_fdma_enabled); 22753a026cSClément Léger 23753a026cSClément Léger static void ocelot_fdma_writel(struct ocelot *ocelot, u32 reg, u32 data) 24753a026cSClément Léger { 25753a026cSClément Léger regmap_write(ocelot->targets[FDMA], reg, data); 26753a026cSClément Léger } 27753a026cSClément Léger 28753a026cSClément Léger static u32 ocelot_fdma_readl(struct ocelot *ocelot, u32 reg) 29753a026cSClément Léger { 30753a026cSClément Léger u32 retval; 31753a026cSClément Léger 32753a026cSClément Léger regmap_read(ocelot->targets[FDMA], reg, &retval); 33753a026cSClément Léger 34753a026cSClément Léger return retval; 35753a026cSClément Léger } 36753a026cSClément Léger 37753a026cSClément Léger static dma_addr_t ocelot_fdma_idx_dma(dma_addr_t base, u16 idx) 38753a026cSClément Léger { 39753a026cSClément Léger return base + idx * sizeof(struct ocelot_fdma_dcb); 40753a026cSClément Léger } 41753a026cSClément Léger 42753a026cSClément Léger static u16 ocelot_fdma_dma_idx(dma_addr_t base, dma_addr_t dma) 43753a026cSClément Léger { 44753a026cSClément Léger return (dma - base) / sizeof(struct ocelot_fdma_dcb); 45753a026cSClément Léger } 46753a026cSClément Léger 47753a026cSClément Léger static u16 ocelot_fdma_idx_next(u16 idx, u16 ring_sz) 48753a026cSClément Léger { 49753a026cSClément Léger return unlikely(idx == ring_sz - 1) ? 0 : idx + 1; 50753a026cSClément Léger } 51753a026cSClément Léger 52753a026cSClément Léger static u16 ocelot_fdma_idx_prev(u16 idx, u16 ring_sz) 53753a026cSClément Léger { 54753a026cSClément Léger return unlikely(idx == 0) ? ring_sz - 1 : idx - 1; 55753a026cSClément Léger } 56753a026cSClément Léger 57753a026cSClément Léger static int ocelot_fdma_rx_ring_free(struct ocelot_fdma *fdma) 58753a026cSClément Léger { 59753a026cSClément Léger struct ocelot_fdma_rx_ring *rx_ring = &fdma->rx_ring; 60753a026cSClément Léger 61753a026cSClément Léger if (rx_ring->next_to_use >= rx_ring->next_to_clean) 62753a026cSClément Léger return OCELOT_FDMA_RX_RING_SIZE - 63753a026cSClément Léger (rx_ring->next_to_use - rx_ring->next_to_clean) - 1; 64753a026cSClément Léger else 65753a026cSClément Léger return rx_ring->next_to_clean - rx_ring->next_to_use - 1; 66753a026cSClément Léger } 67753a026cSClément Léger 68753a026cSClément Léger static int ocelot_fdma_tx_ring_free(struct ocelot_fdma *fdma) 69753a026cSClément Léger { 70753a026cSClément Léger struct ocelot_fdma_tx_ring *tx_ring = &fdma->tx_ring; 71753a026cSClément Léger 72753a026cSClément Léger if (tx_ring->next_to_use >= tx_ring->next_to_clean) 73753a026cSClément Léger return OCELOT_FDMA_TX_RING_SIZE - 74753a026cSClément Léger (tx_ring->next_to_use - tx_ring->next_to_clean) - 1; 75753a026cSClément Léger else 76753a026cSClément Léger return tx_ring->next_to_clean - tx_ring->next_to_use - 1; 77753a026cSClément Léger } 78753a026cSClément Léger 79753a026cSClément Léger static bool ocelot_fdma_tx_ring_empty(struct ocelot_fdma *fdma) 80753a026cSClément Léger { 81753a026cSClément Léger struct ocelot_fdma_tx_ring *tx_ring = &fdma->tx_ring; 82753a026cSClément Léger 83753a026cSClément Léger return tx_ring->next_to_clean == tx_ring->next_to_use; 84753a026cSClément Léger } 85753a026cSClément Léger 86753a026cSClément Léger static void ocelot_fdma_activate_chan(struct ocelot *ocelot, dma_addr_t dma, 87753a026cSClément Léger int chan) 88753a026cSClément Léger { 89753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_DCB_LLP(chan), dma); 90753a026cSClément Léger /* Barrier to force memory writes to DCB to be completed before starting 91753a026cSClément Léger * the channel. 92753a026cSClément Léger */ 93753a026cSClément Léger wmb(); 94753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_ACTIVATE, BIT(chan)); 95753a026cSClément Léger } 96753a026cSClément Léger 97753a026cSClément Léger static int ocelot_fdma_wait_chan_safe(struct ocelot *ocelot, int chan) 98753a026cSClément Léger { 99753a026cSClément Léger unsigned long timeout; 100753a026cSClément Léger u32 safe; 101753a026cSClément Léger 102753a026cSClément Léger timeout = jiffies + usecs_to_jiffies(OCELOT_FDMA_CH_SAFE_TIMEOUT_US); 103753a026cSClément Léger do { 104753a026cSClément Léger safe = ocelot_fdma_readl(ocelot, MSCC_FDMA_CH_SAFE); 105753a026cSClément Léger if (safe & BIT(chan)) 106753a026cSClément Léger return 0; 107753a026cSClément Léger } while (time_after(jiffies, timeout)); 108753a026cSClément Léger 109753a026cSClément Léger return -ETIMEDOUT; 110753a026cSClément Léger } 111753a026cSClément Léger 112753a026cSClément Léger static void ocelot_fdma_dcb_set_data(struct ocelot_fdma_dcb *dcb, 113753a026cSClément Léger dma_addr_t dma_addr, 114753a026cSClément Léger size_t size) 115753a026cSClément Léger { 116753a026cSClément Léger u32 offset = dma_addr & 0x3; 117753a026cSClément Léger 118753a026cSClément Léger dcb->llp = 0; 119753a026cSClément Léger dcb->datap = ALIGN_DOWN(dma_addr, 4); 120753a026cSClément Léger dcb->datal = ALIGN_DOWN(size, 4); 121753a026cSClément Léger dcb->stat = MSCC_FDMA_DCB_STAT_BLOCKO(offset); 122753a026cSClément Léger } 123753a026cSClément Léger 124753a026cSClément Léger static bool ocelot_fdma_rx_alloc_page(struct ocelot *ocelot, 125753a026cSClément Léger struct ocelot_fdma_rx_buf *rxb) 126753a026cSClément Léger { 127753a026cSClément Léger dma_addr_t mapping; 128753a026cSClément Léger struct page *page; 129753a026cSClément Léger 130753a026cSClément Léger page = dev_alloc_page(); 131753a026cSClément Léger if (unlikely(!page)) 132753a026cSClément Léger return false; 133753a026cSClément Léger 134753a026cSClément Léger mapping = dma_map_page(ocelot->dev, page, 0, PAGE_SIZE, 135753a026cSClément Léger DMA_FROM_DEVICE); 136753a026cSClément Léger if (unlikely(dma_mapping_error(ocelot->dev, mapping))) { 137753a026cSClément Léger __free_page(page); 138753a026cSClément Léger return false; 139753a026cSClément Léger } 140753a026cSClément Léger 141753a026cSClément Léger rxb->page = page; 142753a026cSClément Léger rxb->page_offset = 0; 143753a026cSClément Léger rxb->dma_addr = mapping; 144753a026cSClément Léger 145753a026cSClément Léger return true; 146753a026cSClément Léger } 147753a026cSClément Léger 148753a026cSClément Léger static int ocelot_fdma_alloc_rx_buffs(struct ocelot *ocelot, u16 alloc_cnt) 149753a026cSClément Léger { 150753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 151753a026cSClément Léger struct ocelot_fdma_rx_ring *rx_ring; 152753a026cSClément Léger struct ocelot_fdma_rx_buf *rxb; 153753a026cSClément Léger struct ocelot_fdma_dcb *dcb; 154753a026cSClément Léger dma_addr_t dma_addr; 155753a026cSClément Léger int ret = 0; 156753a026cSClément Léger u16 idx; 157753a026cSClément Léger 158753a026cSClément Léger rx_ring = &fdma->rx_ring; 159753a026cSClément Léger idx = rx_ring->next_to_use; 160753a026cSClément Léger 161753a026cSClément Léger while (alloc_cnt--) { 162753a026cSClément Léger rxb = &rx_ring->bufs[idx]; 163753a026cSClément Léger /* try reuse page */ 164753a026cSClément Léger if (unlikely(!rxb->page)) { 165753a026cSClément Léger if (unlikely(!ocelot_fdma_rx_alloc_page(ocelot, rxb))) { 166753a026cSClément Léger dev_err_ratelimited(ocelot->dev, 167753a026cSClément Léger "Failed to allocate rx\n"); 168753a026cSClément Léger ret = -ENOMEM; 169753a026cSClément Léger break; 170753a026cSClément Léger } 171753a026cSClément Léger } 172753a026cSClément Léger 173753a026cSClément Léger dcb = &rx_ring->dcbs[idx]; 174753a026cSClément Léger dma_addr = rxb->dma_addr + rxb->page_offset; 175753a026cSClément Léger ocelot_fdma_dcb_set_data(dcb, dma_addr, OCELOT_FDMA_RXB_SIZE); 176753a026cSClément Léger 177753a026cSClément Léger idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE); 178753a026cSClément Léger /* Chain the DCB to the next one */ 179753a026cSClément Léger dcb->llp = ocelot_fdma_idx_dma(rx_ring->dcbs_dma, idx); 180753a026cSClément Léger } 181753a026cSClément Léger 182753a026cSClément Léger rx_ring->next_to_use = idx; 183753a026cSClément Léger rx_ring->next_to_alloc = idx; 184753a026cSClément Léger 185753a026cSClément Léger return ret; 186753a026cSClément Léger } 187753a026cSClément Léger 188753a026cSClément Léger static bool ocelot_fdma_tx_dcb_set_skb(struct ocelot *ocelot, 189753a026cSClément Léger struct ocelot_fdma_tx_buf *tx_buf, 190753a026cSClément Léger struct ocelot_fdma_dcb *dcb, 191753a026cSClément Léger struct sk_buff *skb) 192753a026cSClément Léger { 193753a026cSClément Léger dma_addr_t mapping; 194753a026cSClément Léger 195753a026cSClément Léger mapping = dma_map_single(ocelot->dev, skb->data, skb->len, 196753a026cSClément Léger DMA_TO_DEVICE); 197753a026cSClément Léger if (unlikely(dma_mapping_error(ocelot->dev, mapping))) 198753a026cSClément Léger return false; 199753a026cSClément Léger 200753a026cSClément Léger dma_unmap_addr_set(tx_buf, dma_addr, mapping); 201753a026cSClément Léger 202753a026cSClément Léger ocelot_fdma_dcb_set_data(dcb, mapping, OCELOT_FDMA_RX_SIZE); 203753a026cSClément Léger tx_buf->skb = skb; 204753a026cSClément Léger dcb->stat |= MSCC_FDMA_DCB_STAT_BLOCKL(skb->len); 205753a026cSClément Léger dcb->stat |= MSCC_FDMA_DCB_STAT_SOF | MSCC_FDMA_DCB_STAT_EOF; 206753a026cSClément Léger 207753a026cSClément Léger return true; 208753a026cSClément Léger } 209753a026cSClément Léger 210753a026cSClément Léger static bool ocelot_fdma_check_stop_rx(struct ocelot *ocelot) 211753a026cSClément Léger { 212753a026cSClément Léger u32 llp; 213753a026cSClément Léger 214753a026cSClément Léger /* Check if the FDMA hits the DCB with LLP == NULL */ 215753a026cSClément Léger llp = ocelot_fdma_readl(ocelot, MSCC_FDMA_DCB_LLP(MSCC_FDMA_XTR_CHAN)); 216753a026cSClément Léger if (unlikely(llp)) 217753a026cSClément Léger return false; 218753a026cSClément Léger 219753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_DISABLE, 220753a026cSClément Léger BIT(MSCC_FDMA_XTR_CHAN)); 221753a026cSClément Léger 222753a026cSClément Léger return true; 223753a026cSClément Léger } 224753a026cSClément Léger 225753a026cSClément Léger static void ocelot_fdma_rx_set_llp(struct ocelot_fdma_rx_ring *rx_ring) 226753a026cSClément Léger { 227753a026cSClément Léger struct ocelot_fdma_dcb *dcb; 228753a026cSClément Léger unsigned int idx; 229753a026cSClément Léger 230753a026cSClément Léger idx = ocelot_fdma_idx_prev(rx_ring->next_to_use, 231753a026cSClément Léger OCELOT_FDMA_RX_RING_SIZE); 232753a026cSClément Léger dcb = &rx_ring->dcbs[idx]; 233753a026cSClément Léger dcb->llp = 0; 234753a026cSClément Léger } 235753a026cSClément Léger 236753a026cSClément Léger static void ocelot_fdma_rx_restart(struct ocelot *ocelot) 237753a026cSClément Léger { 238753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 239753a026cSClément Léger struct ocelot_fdma_rx_ring *rx_ring; 240753a026cSClément Léger const u8 chan = MSCC_FDMA_XTR_CHAN; 241753a026cSClément Léger dma_addr_t new_llp, dma_base; 242753a026cSClément Léger unsigned int idx; 243753a026cSClément Léger u32 llp_prev; 244753a026cSClément Léger int ret; 245753a026cSClément Léger 246753a026cSClément Léger rx_ring = &fdma->rx_ring; 247753a026cSClément Léger ret = ocelot_fdma_wait_chan_safe(ocelot, chan); 248753a026cSClément Léger if (ret) { 249753a026cSClément Léger dev_err_ratelimited(ocelot->dev, 250753a026cSClément Léger "Unable to stop RX channel\n"); 251753a026cSClément Léger return; 252753a026cSClément Léger } 253753a026cSClément Léger 254753a026cSClément Léger ocelot_fdma_rx_set_llp(rx_ring); 255753a026cSClément Léger 256753a026cSClément Léger /* FDMA stopped on the last DCB that contained a NULL LLP, since 257753a026cSClément Léger * we processed some DCBs in RX, there is free space, and we must set 258753a026cSClément Léger * DCB_LLP to point to the next DCB 259753a026cSClément Léger */ 260753a026cSClément Léger llp_prev = ocelot_fdma_readl(ocelot, MSCC_FDMA_DCB_LLP_PREV(chan)); 261753a026cSClément Léger dma_base = rx_ring->dcbs_dma; 262753a026cSClément Léger 263753a026cSClément Léger /* Get the next DMA addr located after LLP == NULL DCB */ 264753a026cSClément Léger idx = ocelot_fdma_dma_idx(dma_base, llp_prev); 265753a026cSClément Léger idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE); 266753a026cSClément Léger new_llp = ocelot_fdma_idx_dma(dma_base, idx); 267753a026cSClément Léger 268753a026cSClément Léger /* Finally reactivate the channel */ 269753a026cSClément Léger ocelot_fdma_activate_chan(ocelot, new_llp, chan); 270753a026cSClément Léger } 271753a026cSClément Léger 272753a026cSClément Léger static bool ocelot_fdma_add_rx_frag(struct ocelot_fdma_rx_buf *rxb, u32 stat, 273753a026cSClément Léger struct sk_buff *skb, bool first) 274753a026cSClément Léger { 275753a026cSClément Léger int size = MSCC_FDMA_DCB_STAT_BLOCKL(stat); 276753a026cSClément Léger struct page *page = rxb->page; 277753a026cSClément Léger 278753a026cSClément Léger if (likely(first)) { 279753a026cSClément Léger skb_put(skb, size); 280753a026cSClément Léger } else { 281753a026cSClément Léger skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 282753a026cSClément Léger rxb->page_offset, size, OCELOT_FDMA_RX_SIZE); 283753a026cSClément Léger } 284753a026cSClément Léger 285753a026cSClément Léger /* Try to reuse page */ 286753a026cSClément Léger if (unlikely(page_ref_count(page) != 1 || page_is_pfmemalloc(page))) 287753a026cSClément Léger return false; 288753a026cSClément Léger 289753a026cSClément Léger /* Change offset to the other half */ 290753a026cSClément Léger rxb->page_offset ^= OCELOT_FDMA_RX_SIZE; 291753a026cSClément Léger 292753a026cSClément Léger page_ref_inc(page); 293753a026cSClément Léger 294753a026cSClément Léger return true; 295753a026cSClément Léger } 296753a026cSClément Léger 297753a026cSClément Léger static void ocelot_fdma_reuse_rx_page(struct ocelot *ocelot, 298753a026cSClément Léger struct ocelot_fdma_rx_buf *old_rxb) 299753a026cSClément Léger { 300753a026cSClément Léger struct ocelot_fdma_rx_ring *rx_ring = &ocelot->fdma->rx_ring; 301753a026cSClément Léger struct ocelot_fdma_rx_buf *new_rxb; 302753a026cSClément Léger 303753a026cSClément Léger new_rxb = &rx_ring->bufs[rx_ring->next_to_alloc]; 304753a026cSClément Léger rx_ring->next_to_alloc = ocelot_fdma_idx_next(rx_ring->next_to_alloc, 305753a026cSClément Léger OCELOT_FDMA_RX_RING_SIZE); 306753a026cSClément Léger 307753a026cSClément Léger /* Copy page reference */ 308753a026cSClément Léger *new_rxb = *old_rxb; 309753a026cSClément Léger 310753a026cSClément Léger /* Sync for use by the device */ 311753a026cSClément Léger dma_sync_single_range_for_device(ocelot->dev, old_rxb->dma_addr, 312753a026cSClément Léger old_rxb->page_offset, 313753a026cSClément Léger OCELOT_FDMA_RX_SIZE, DMA_FROM_DEVICE); 314753a026cSClément Léger } 315753a026cSClément Léger 316753a026cSClément Léger static struct sk_buff *ocelot_fdma_get_skb(struct ocelot *ocelot, u32 stat, 317753a026cSClément Léger struct ocelot_fdma_rx_buf *rxb, 318753a026cSClément Léger struct sk_buff *skb) 319753a026cSClément Léger { 320753a026cSClément Léger bool first = false; 321753a026cSClément Léger 322753a026cSClément Léger /* Allocate skb head and data */ 323753a026cSClément Léger if (likely(!skb)) { 324753a026cSClément Léger void *buff_addr = page_address(rxb->page) + 325753a026cSClément Léger rxb->page_offset; 326753a026cSClément Léger 327753a026cSClément Léger skb = build_skb(buff_addr, OCELOT_FDMA_SKBFRAG_SIZE); 328753a026cSClément Léger if (unlikely(!skb)) { 329753a026cSClément Léger dev_err_ratelimited(ocelot->dev, 330753a026cSClément Léger "build_skb failed !\n"); 331753a026cSClément Léger return NULL; 332753a026cSClément Léger } 333753a026cSClément Léger first = true; 334753a026cSClément Léger } 335753a026cSClément Léger 336753a026cSClément Léger dma_sync_single_range_for_cpu(ocelot->dev, rxb->dma_addr, 337753a026cSClément Léger rxb->page_offset, OCELOT_FDMA_RX_SIZE, 338753a026cSClément Léger DMA_FROM_DEVICE); 339753a026cSClément Léger 340753a026cSClément Léger if (ocelot_fdma_add_rx_frag(rxb, stat, skb, first)) { 341753a026cSClément Léger /* Reuse the free half of the page for the next_to_alloc DCB*/ 342753a026cSClément Léger ocelot_fdma_reuse_rx_page(ocelot, rxb); 343753a026cSClément Léger } else { 344753a026cSClément Léger /* page cannot be reused, unmap it */ 345753a026cSClément Léger dma_unmap_page(ocelot->dev, rxb->dma_addr, PAGE_SIZE, 346753a026cSClément Léger DMA_FROM_DEVICE); 347753a026cSClément Léger } 348753a026cSClément Léger 349753a026cSClément Léger /* clear rx buff content */ 350753a026cSClément Léger rxb->page = NULL; 351753a026cSClément Léger 352753a026cSClément Léger return skb; 353753a026cSClément Léger } 354753a026cSClément Léger 355753a026cSClément Léger static bool ocelot_fdma_receive_skb(struct ocelot *ocelot, struct sk_buff *skb) 356753a026cSClément Léger { 357753a026cSClément Léger struct net_device *ndev; 358753a026cSClément Léger void *xfh = skb->data; 359753a026cSClément Léger u64 timestamp; 360753a026cSClément Léger u64 src_port; 361753a026cSClément Léger 362753a026cSClément Léger skb_pull(skb, OCELOT_TAG_LEN); 363753a026cSClément Léger 364753a026cSClément Léger ocelot_xfh_get_src_port(xfh, &src_port); 365753a026cSClément Léger if (unlikely(src_port >= ocelot->num_phys_ports)) 366753a026cSClément Léger return false; 367753a026cSClément Léger 368753a026cSClément Léger ndev = ocelot_port_to_netdev(ocelot, src_port); 369753a026cSClément Léger if (unlikely(!ndev)) 370753a026cSClément Léger return false; 371753a026cSClément Léger 372753a026cSClément Léger pskb_trim(skb, skb->len - ETH_FCS_LEN); 373753a026cSClément Léger 374753a026cSClément Léger skb->dev = ndev; 375753a026cSClément Léger skb->protocol = eth_type_trans(skb, skb->dev); 376753a026cSClément Léger skb->dev->stats.rx_bytes += skb->len; 377753a026cSClément Léger skb->dev->stats.rx_packets++; 378753a026cSClément Léger 379753a026cSClément Léger if (ocelot->ptp) { 380753a026cSClément Léger ocelot_xfh_get_rew_val(xfh, ×tamp); 381753a026cSClément Léger ocelot_ptp_rx_timestamp(ocelot, skb, timestamp); 382753a026cSClément Léger } 383753a026cSClément Léger 384753a026cSClément Léger if (likely(!skb_defer_rx_timestamp(skb))) 385753a026cSClément Léger netif_receive_skb(skb); 386753a026cSClément Léger 387753a026cSClément Léger return true; 388753a026cSClément Léger } 389753a026cSClément Léger 390753a026cSClément Léger static int ocelot_fdma_rx_get(struct ocelot *ocelot, int budget) 391753a026cSClément Léger { 392753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 393753a026cSClément Léger struct ocelot_fdma_rx_ring *rx_ring; 394753a026cSClément Léger struct ocelot_fdma_rx_buf *rxb; 395753a026cSClément Léger struct ocelot_fdma_dcb *dcb; 396753a026cSClément Léger struct sk_buff *skb; 397753a026cSClément Léger int work_done = 0; 398753a026cSClément Léger int cleaned_cnt; 399753a026cSClément Léger u32 stat; 400753a026cSClément Léger u16 idx; 401753a026cSClément Léger 402753a026cSClément Léger cleaned_cnt = ocelot_fdma_rx_ring_free(fdma); 403753a026cSClément Léger rx_ring = &fdma->rx_ring; 404753a026cSClément Léger skb = rx_ring->skb; 405753a026cSClément Léger 406753a026cSClément Léger while (budget--) { 407753a026cSClément Léger idx = rx_ring->next_to_clean; 408753a026cSClément Léger dcb = &rx_ring->dcbs[idx]; 409753a026cSClément Léger stat = dcb->stat; 410753a026cSClément Léger if (MSCC_FDMA_DCB_STAT_BLOCKL(stat) == 0) 411753a026cSClément Léger break; 412753a026cSClément Léger 413753a026cSClément Léger /* New packet is a start of frame but we already got a skb set, 414753a026cSClément Léger * we probably lost an EOF packet, free skb 415753a026cSClément Léger */ 416753a026cSClément Léger if (unlikely(skb && (stat & MSCC_FDMA_DCB_STAT_SOF))) { 417753a026cSClément Léger dev_kfree_skb(skb); 418753a026cSClément Léger skb = NULL; 419753a026cSClément Léger } 420753a026cSClément Léger 421753a026cSClément Léger rxb = &rx_ring->bufs[idx]; 422753a026cSClément Léger /* Fetch next to clean buffer from the rx_ring */ 423753a026cSClément Léger skb = ocelot_fdma_get_skb(ocelot, stat, rxb, skb); 424753a026cSClément Léger if (unlikely(!skb)) 425753a026cSClément Léger break; 426753a026cSClément Léger 427753a026cSClément Léger work_done++; 428753a026cSClément Léger cleaned_cnt++; 429753a026cSClément Léger 430753a026cSClément Léger idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE); 431753a026cSClément Léger rx_ring->next_to_clean = idx; 432753a026cSClément Léger 433753a026cSClément Léger if (unlikely(stat & MSCC_FDMA_DCB_STAT_ABORT || 434753a026cSClément Léger stat & MSCC_FDMA_DCB_STAT_PD)) { 435753a026cSClément Léger dev_err_ratelimited(ocelot->dev, 436753a026cSClément Léger "DCB aborted or pruned\n"); 437753a026cSClément Léger dev_kfree_skb(skb); 438753a026cSClément Léger skb = NULL; 439753a026cSClément Léger continue; 440753a026cSClément Léger } 441753a026cSClément Léger 442753a026cSClément Léger /* We still need to process the other fragment of the packet 443753a026cSClément Léger * before delivering it to the network stack 444753a026cSClément Léger */ 445753a026cSClément Léger if (!(stat & MSCC_FDMA_DCB_STAT_EOF)) 446753a026cSClément Léger continue; 447753a026cSClément Léger 448753a026cSClément Léger if (unlikely(!ocelot_fdma_receive_skb(ocelot, skb))) 449753a026cSClément Léger dev_kfree_skb(skb); 450753a026cSClément Léger 451753a026cSClément Léger skb = NULL; 452753a026cSClément Léger } 453753a026cSClément Léger 454753a026cSClément Léger rx_ring->skb = skb; 455753a026cSClément Léger 456753a026cSClément Léger if (cleaned_cnt) 457753a026cSClément Léger ocelot_fdma_alloc_rx_buffs(ocelot, cleaned_cnt); 458753a026cSClément Léger 459753a026cSClément Léger return work_done; 460753a026cSClément Léger } 461753a026cSClément Léger 462753a026cSClément Léger static void ocelot_fdma_wakeup_netdev(struct ocelot *ocelot) 463753a026cSClément Léger { 464753a026cSClément Léger struct ocelot_port_private *priv; 465753a026cSClément Léger struct ocelot_port *ocelot_port; 466753a026cSClément Léger struct net_device *dev; 467753a026cSClément Léger int port; 468753a026cSClément Léger 469753a026cSClément Léger for (port = 0; port < ocelot->num_phys_ports; port++) { 470753a026cSClément Léger ocelot_port = ocelot->ports[port]; 471753a026cSClément Léger if (!ocelot_port) 472753a026cSClément Léger continue; 473753a026cSClément Léger priv = container_of(ocelot_port, struct ocelot_port_private, 474753a026cSClément Léger port); 475753a026cSClément Léger dev = priv->dev; 476753a026cSClément Léger 477753a026cSClément Léger if (unlikely(netif_queue_stopped(dev))) 478753a026cSClément Léger netif_wake_queue(dev); 479753a026cSClément Léger } 480753a026cSClément Léger } 481753a026cSClément Léger 482753a026cSClément Léger static void ocelot_fdma_tx_cleanup(struct ocelot *ocelot, int budget) 483753a026cSClément Léger { 484753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 485753a026cSClément Léger struct ocelot_fdma_tx_ring *tx_ring; 486753a026cSClément Léger struct ocelot_fdma_tx_buf *buf; 487753a026cSClément Léger unsigned int new_null_llp_idx; 488753a026cSClément Léger struct ocelot_fdma_dcb *dcb; 489753a026cSClément Léger bool end_of_list = false; 490753a026cSClément Léger struct sk_buff *skb; 491753a026cSClément Léger dma_addr_t dma; 492753a026cSClément Léger u32 dcb_llp; 493753a026cSClément Léger u16 ntc; 494753a026cSClément Léger int ret; 495753a026cSClément Léger 496753a026cSClément Léger tx_ring = &fdma->tx_ring; 497753a026cSClément Léger 498753a026cSClément Léger /* Purge the TX packets that have been sent up to the NULL llp or the 499753a026cSClément Léger * end of done list. 500753a026cSClément Léger */ 501753a026cSClément Léger while (!ocelot_fdma_tx_ring_empty(fdma)) { 502753a026cSClément Léger ntc = tx_ring->next_to_clean; 503753a026cSClément Léger dcb = &tx_ring->dcbs[ntc]; 504753a026cSClément Léger if (!(dcb->stat & MSCC_FDMA_DCB_STAT_PD)) 505753a026cSClément Léger break; 506753a026cSClément Léger 507753a026cSClément Léger buf = &tx_ring->bufs[ntc]; 508753a026cSClément Léger skb = buf->skb; 509753a026cSClément Léger dma_unmap_single(ocelot->dev, dma_unmap_addr(buf, dma_addr), 510753a026cSClément Léger skb->len, DMA_TO_DEVICE); 511753a026cSClément Léger napi_consume_skb(skb, budget); 512753a026cSClément Léger dcb_llp = dcb->llp; 513753a026cSClément Léger 514753a026cSClément Léger /* Only update after accessing all dcb fields */ 515753a026cSClément Léger tx_ring->next_to_clean = ocelot_fdma_idx_next(ntc, 516753a026cSClément Léger OCELOT_FDMA_TX_RING_SIZE); 517753a026cSClément Léger 518753a026cSClément Léger /* If we hit the NULL LLP, stop, we might need to reload FDMA */ 519753a026cSClément Léger if (dcb_llp == 0) { 520753a026cSClément Léger end_of_list = true; 521753a026cSClément Léger break; 522753a026cSClément Léger } 523753a026cSClément Léger } 524753a026cSClément Léger 525753a026cSClément Léger /* No need to try to wake if there were no TX cleaned_cnt up. */ 526753a026cSClément Léger if (ocelot_fdma_tx_ring_free(fdma)) 527753a026cSClément Léger ocelot_fdma_wakeup_netdev(ocelot); 528753a026cSClément Léger 529753a026cSClément Léger /* If there is still some DCBs to be processed by the FDMA or if the 530753a026cSClément Léger * pending list is empty, there is no need to restart the FDMA. 531753a026cSClément Léger */ 532753a026cSClément Léger if (!end_of_list || ocelot_fdma_tx_ring_empty(fdma)) 533753a026cSClément Léger return; 534753a026cSClément Léger 535753a026cSClément Léger ret = ocelot_fdma_wait_chan_safe(ocelot, MSCC_FDMA_INJ_CHAN); 536753a026cSClément Léger if (ret) { 537753a026cSClément Léger dev_warn(ocelot->dev, 538753a026cSClément Léger "Failed to wait for TX channel to stop\n"); 539753a026cSClément Léger return; 540753a026cSClément Léger } 541753a026cSClément Léger 542753a026cSClément Léger /* Set NULL LLP to be the last DCB used */ 543753a026cSClément Léger new_null_llp_idx = ocelot_fdma_idx_prev(tx_ring->next_to_use, 544753a026cSClément Léger OCELOT_FDMA_TX_RING_SIZE); 545753a026cSClément Léger dcb = &tx_ring->dcbs[new_null_llp_idx]; 546753a026cSClément Léger dcb->llp = 0; 547753a026cSClément Léger 548753a026cSClément Léger dma = ocelot_fdma_idx_dma(tx_ring->dcbs_dma, tx_ring->next_to_clean); 549753a026cSClément Léger ocelot_fdma_activate_chan(ocelot, dma, MSCC_FDMA_INJ_CHAN); 550753a026cSClément Léger } 551753a026cSClément Léger 552753a026cSClément Léger static int ocelot_fdma_napi_poll(struct napi_struct *napi, int budget) 553753a026cSClément Léger { 554753a026cSClément Léger struct ocelot_fdma *fdma = container_of(napi, struct ocelot_fdma, napi); 555753a026cSClément Léger struct ocelot *ocelot = fdma->ocelot; 556753a026cSClément Léger int work_done = 0; 557753a026cSClément Léger bool rx_stopped; 558753a026cSClément Léger 559753a026cSClément Léger ocelot_fdma_tx_cleanup(ocelot, budget); 560753a026cSClément Léger 561753a026cSClément Léger rx_stopped = ocelot_fdma_check_stop_rx(ocelot); 562753a026cSClément Léger 563753a026cSClément Léger work_done = ocelot_fdma_rx_get(ocelot, budget); 564753a026cSClément Léger 565753a026cSClément Léger if (rx_stopped) 566753a026cSClément Léger ocelot_fdma_rx_restart(ocelot); 567753a026cSClément Léger 568753a026cSClément Léger if (work_done < budget) { 569753a026cSClément Léger napi_complete_done(&fdma->napi, work_done); 570753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 571753a026cSClément Léger BIT(MSCC_FDMA_INJ_CHAN) | 572753a026cSClément Léger BIT(MSCC_FDMA_XTR_CHAN)); 573753a026cSClément Léger } 574753a026cSClément Léger 575753a026cSClément Léger return work_done; 576753a026cSClément Léger } 577753a026cSClément Léger 578753a026cSClément Léger static irqreturn_t ocelot_fdma_interrupt(int irq, void *dev_id) 579753a026cSClément Léger { 580753a026cSClément Léger u32 ident, llp, frm, err, err_code; 581753a026cSClément Léger struct ocelot *ocelot = dev_id; 582753a026cSClément Léger 583753a026cSClément Léger ident = ocelot_fdma_readl(ocelot, MSCC_FDMA_INTR_IDENT); 584753a026cSClément Léger frm = ocelot_fdma_readl(ocelot, MSCC_FDMA_INTR_FRM); 585753a026cSClément Léger llp = ocelot_fdma_readl(ocelot, MSCC_FDMA_INTR_LLP); 586753a026cSClément Léger 587753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_LLP, llp & ident); 588753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_FRM, frm & ident); 589753a026cSClément Léger if (frm || llp) { 590753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 0); 591753a026cSClément Léger napi_schedule(&ocelot->fdma->napi); 592753a026cSClément Léger } 593753a026cSClément Léger 594753a026cSClément Léger err = ocelot_fdma_readl(ocelot, MSCC_FDMA_EVT_ERR); 595753a026cSClément Léger if (unlikely(err)) { 596753a026cSClément Léger err_code = ocelot_fdma_readl(ocelot, MSCC_FDMA_EVT_ERR_CODE); 597753a026cSClément Léger dev_err_ratelimited(ocelot->dev, 598753a026cSClément Léger "Error ! chans mask: %#x, code: %#x\n", 599753a026cSClément Léger err, err_code); 600753a026cSClément Léger 601753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_EVT_ERR, err); 602753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_EVT_ERR_CODE, err_code); 603753a026cSClément Léger } 604753a026cSClément Léger 605753a026cSClément Léger return IRQ_HANDLED; 606753a026cSClément Léger } 607753a026cSClément Léger 608753a026cSClément Léger static void ocelot_fdma_send_skb(struct ocelot *ocelot, 609753a026cSClément Léger struct ocelot_fdma *fdma, struct sk_buff *skb) 610753a026cSClément Léger { 611753a026cSClément Léger struct ocelot_fdma_tx_ring *tx_ring = &fdma->tx_ring; 612753a026cSClément Léger struct ocelot_fdma_tx_buf *tx_buf; 613753a026cSClément Léger struct ocelot_fdma_dcb *dcb; 614753a026cSClément Léger dma_addr_t dma; 615753a026cSClément Léger u16 next_idx; 616753a026cSClément Léger 617753a026cSClément Léger dcb = &tx_ring->dcbs[tx_ring->next_to_use]; 618753a026cSClément Léger tx_buf = &tx_ring->bufs[tx_ring->next_to_use]; 619753a026cSClément Léger if (!ocelot_fdma_tx_dcb_set_skb(ocelot, tx_buf, dcb, skb)) { 620753a026cSClément Léger dev_kfree_skb_any(skb); 621753a026cSClément Léger return; 622753a026cSClément Léger } 623753a026cSClément Léger 624753a026cSClément Léger next_idx = ocelot_fdma_idx_next(tx_ring->next_to_use, 625753a026cSClément Léger OCELOT_FDMA_TX_RING_SIZE); 626753a026cSClément Léger skb_tx_timestamp(skb); 627753a026cSClément Léger 628753a026cSClément Léger /* If the FDMA TX chan is empty, then enqueue the DCB directly */ 629753a026cSClément Léger if (ocelot_fdma_tx_ring_empty(fdma)) { 630753a026cSClément Léger dma = ocelot_fdma_idx_dma(tx_ring->dcbs_dma, 631753a026cSClément Léger tx_ring->next_to_use); 632753a026cSClément Léger ocelot_fdma_activate_chan(ocelot, dma, MSCC_FDMA_INJ_CHAN); 633753a026cSClément Léger } else { 634753a026cSClément Léger /* Chain the DCBs */ 635753a026cSClément Léger dcb->llp = ocelot_fdma_idx_dma(tx_ring->dcbs_dma, next_idx); 636753a026cSClément Léger } 637753a026cSClément Léger 638753a026cSClément Léger tx_ring->next_to_use = next_idx; 639753a026cSClément Léger } 640753a026cSClément Léger 641753a026cSClément Léger static int ocelot_fdma_prepare_skb(struct ocelot *ocelot, int port, u32 rew_op, 642753a026cSClément Léger struct sk_buff *skb, struct net_device *dev) 643753a026cSClément Léger { 644753a026cSClément Léger int needed_headroom = max_t(int, OCELOT_TAG_LEN - skb_headroom(skb), 0); 645753a026cSClément Léger int needed_tailroom = max_t(int, ETH_FCS_LEN - skb_tailroom(skb), 0); 646753a026cSClément Léger void *ifh; 647753a026cSClément Léger int err; 648753a026cSClément Léger 649753a026cSClément Léger if (unlikely(needed_headroom || needed_tailroom || 650753a026cSClément Léger skb_header_cloned(skb))) { 651753a026cSClément Léger err = pskb_expand_head(skb, needed_headroom, needed_tailroom, 652753a026cSClément Léger GFP_ATOMIC); 653753a026cSClément Léger if (unlikely(err)) { 654753a026cSClément Léger dev_kfree_skb_any(skb); 655753a026cSClément Léger return 1; 656753a026cSClément Léger } 657753a026cSClément Léger } 658753a026cSClément Léger 659753a026cSClément Léger err = skb_linearize(skb); 660753a026cSClément Léger if (err) { 661753a026cSClément Léger net_err_ratelimited("%s: skb_linearize error (%d)!\n", 662753a026cSClément Léger dev->name, err); 663753a026cSClément Léger dev_kfree_skb_any(skb); 664753a026cSClément Léger return 1; 665753a026cSClément Léger } 666753a026cSClément Léger 667753a026cSClément Léger ifh = skb_push(skb, OCELOT_TAG_LEN); 668753a026cSClément Léger skb_put(skb, ETH_FCS_LEN); 669753a026cSClément Léger memset(ifh, 0, OCELOT_TAG_LEN); 670753a026cSClément Léger ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb)); 671753a026cSClément Léger 672753a026cSClément Léger return 0; 673753a026cSClément Léger } 674753a026cSClément Léger 675753a026cSClément Léger int ocelot_fdma_inject_frame(struct ocelot *ocelot, int port, u32 rew_op, 676753a026cSClément Léger struct sk_buff *skb, struct net_device *dev) 677753a026cSClément Léger { 678753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 679753a026cSClément Léger int ret = NETDEV_TX_OK; 680753a026cSClément Léger 681753a026cSClément Léger spin_lock(&fdma->tx_ring.xmit_lock); 682753a026cSClément Léger 683753a026cSClément Léger if (ocelot_fdma_tx_ring_free(fdma) == 0) { 684753a026cSClément Léger netif_stop_queue(dev); 685753a026cSClément Léger ret = NETDEV_TX_BUSY; 686753a026cSClément Léger goto out; 687753a026cSClément Léger } 688753a026cSClément Léger 689753a026cSClément Léger if (ocelot_fdma_prepare_skb(ocelot, port, rew_op, skb, dev)) 690753a026cSClément Léger goto out; 691753a026cSClément Léger 692753a026cSClément Léger ocelot_fdma_send_skb(ocelot, fdma, skb); 693753a026cSClément Léger 694753a026cSClément Léger out: 695753a026cSClément Léger spin_unlock(&fdma->tx_ring.xmit_lock); 696753a026cSClément Léger 697753a026cSClément Léger return ret; 698753a026cSClément Léger } 699753a026cSClément Léger 700753a026cSClément Léger static void ocelot_fdma_free_rx_ring(struct ocelot *ocelot) 701753a026cSClément Léger { 702753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 703753a026cSClément Léger struct ocelot_fdma_rx_ring *rx_ring; 704753a026cSClément Léger struct ocelot_fdma_rx_buf *rxb; 705753a026cSClément Léger u16 idx; 706753a026cSClément Léger 707753a026cSClément Léger rx_ring = &fdma->rx_ring; 708753a026cSClément Léger idx = rx_ring->next_to_clean; 709753a026cSClément Léger 710753a026cSClément Léger /* Free the pages held in the RX ring */ 711753a026cSClément Léger while (idx != rx_ring->next_to_use) { 712753a026cSClément Léger rxb = &rx_ring->bufs[idx]; 713753a026cSClément Léger dma_unmap_page(ocelot->dev, rxb->dma_addr, PAGE_SIZE, 714753a026cSClément Léger DMA_FROM_DEVICE); 715753a026cSClément Léger __free_page(rxb->page); 716753a026cSClément Léger idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_RX_RING_SIZE); 717753a026cSClément Léger } 718753a026cSClément Léger 719753a026cSClément Léger if (fdma->rx_ring.skb) 720753a026cSClément Léger dev_kfree_skb_any(fdma->rx_ring.skb); 721753a026cSClément Léger } 722753a026cSClément Léger 723753a026cSClément Léger static void ocelot_fdma_free_tx_ring(struct ocelot *ocelot) 724753a026cSClément Léger { 725753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 726753a026cSClément Léger struct ocelot_fdma_tx_ring *tx_ring; 727753a026cSClément Léger struct ocelot_fdma_tx_buf *txb; 728753a026cSClément Léger struct sk_buff *skb; 729753a026cSClément Léger u16 idx; 730753a026cSClément Léger 731753a026cSClément Léger tx_ring = &fdma->tx_ring; 732753a026cSClément Léger idx = tx_ring->next_to_clean; 733753a026cSClément Léger 734753a026cSClément Léger while (idx != tx_ring->next_to_use) { 735753a026cSClément Léger txb = &tx_ring->bufs[idx]; 736753a026cSClément Léger skb = txb->skb; 7373cfcda2aSClément Léger dma_unmap_single(ocelot->dev, dma_unmap_addr(txb, dma_addr), 7383cfcda2aSClément Léger skb->len, DMA_TO_DEVICE); 739753a026cSClément Léger dev_kfree_skb_any(skb); 740753a026cSClément Léger idx = ocelot_fdma_idx_next(idx, OCELOT_FDMA_TX_RING_SIZE); 741753a026cSClément Léger } 742753a026cSClément Léger } 743753a026cSClément Léger 744753a026cSClément Léger static int ocelot_fdma_rings_alloc(struct ocelot *ocelot) 745753a026cSClément Léger { 746753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 747753a026cSClément Léger struct ocelot_fdma_dcb *dcbs; 748753a026cSClément Léger unsigned int adjust; 749753a026cSClément Léger dma_addr_t dcbs_dma; 750753a026cSClément Léger int ret; 751753a026cSClément Léger 752753a026cSClément Léger /* Create a pool of consistent memory blocks for hardware descriptors */ 753753a026cSClément Léger fdma->dcbs_base = dmam_alloc_coherent(ocelot->dev, 754753a026cSClément Léger OCELOT_DCBS_HW_ALLOC_SIZE, 755753a026cSClément Léger &fdma->dcbs_dma_base, GFP_KERNEL); 756753a026cSClément Léger if (!fdma->dcbs_base) 757753a026cSClément Léger return -ENOMEM; 758753a026cSClément Léger 759753a026cSClément Léger /* DCBs must be aligned on a 32bit boundary */ 760753a026cSClément Léger dcbs = fdma->dcbs_base; 761753a026cSClément Léger dcbs_dma = fdma->dcbs_dma_base; 762753a026cSClément Léger if (!IS_ALIGNED(dcbs_dma, 4)) { 763753a026cSClément Léger adjust = dcbs_dma & 0x3; 764753a026cSClément Léger dcbs_dma = ALIGN(dcbs_dma, 4); 765753a026cSClément Léger dcbs = (void *)dcbs + adjust; 766753a026cSClément Léger } 767753a026cSClément Léger 768753a026cSClément Léger /* TX queue */ 769753a026cSClément Léger fdma->tx_ring.dcbs = dcbs; 770753a026cSClément Léger fdma->tx_ring.dcbs_dma = dcbs_dma; 771753a026cSClément Léger spin_lock_init(&fdma->tx_ring.xmit_lock); 772753a026cSClément Léger 773753a026cSClément Léger /* RX queue */ 774753a026cSClément Léger fdma->rx_ring.dcbs = dcbs + OCELOT_FDMA_TX_RING_SIZE; 775753a026cSClément Léger fdma->rx_ring.dcbs_dma = dcbs_dma + OCELOT_FDMA_TX_DCB_SIZE; 776753a026cSClément Léger ret = ocelot_fdma_alloc_rx_buffs(ocelot, 777753a026cSClément Léger ocelot_fdma_tx_ring_free(fdma)); 778753a026cSClément Léger if (ret) { 779753a026cSClément Léger ocelot_fdma_free_rx_ring(ocelot); 780753a026cSClément Léger return ret; 781753a026cSClément Léger } 782753a026cSClément Léger 783753a026cSClément Léger /* Set the last DCB LLP as NULL, this is normally done when restarting 784753a026cSClément Léger * the RX chan, but this is for the first run 785753a026cSClément Léger */ 786753a026cSClément Léger ocelot_fdma_rx_set_llp(&fdma->rx_ring); 787753a026cSClément Léger 788753a026cSClément Léger return 0; 789753a026cSClément Léger } 790753a026cSClément Léger 791753a026cSClément Léger void ocelot_fdma_netdev_init(struct ocelot *ocelot, struct net_device *dev) 792753a026cSClément Léger { 793753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 794753a026cSClément Léger 795753a026cSClément Léger dev->needed_headroom = OCELOT_TAG_LEN; 796753a026cSClément Léger dev->needed_tailroom = ETH_FCS_LEN; 797753a026cSClément Léger 798753a026cSClément Léger if (fdma->ndev) 799753a026cSClément Léger return; 800753a026cSClément Léger 801753a026cSClément Léger fdma->ndev = dev; 802*b707b89fSJakub Kicinski netif_napi_add_weight(dev, &fdma->napi, ocelot_fdma_napi_poll, 803753a026cSClément Léger OCELOT_FDMA_WEIGHT); 804753a026cSClément Léger } 805753a026cSClément Léger 806753a026cSClément Léger void ocelot_fdma_netdev_deinit(struct ocelot *ocelot, struct net_device *dev) 807753a026cSClément Léger { 808753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 809753a026cSClément Léger 810753a026cSClément Léger if (fdma->ndev == dev) { 811753a026cSClément Léger netif_napi_del(&fdma->napi); 812753a026cSClément Léger fdma->ndev = NULL; 813753a026cSClément Léger } 814753a026cSClément Léger } 815753a026cSClément Léger 816753a026cSClément Léger void ocelot_fdma_init(struct platform_device *pdev, struct ocelot *ocelot) 817753a026cSClément Léger { 818753a026cSClément Léger struct device *dev = ocelot->dev; 819753a026cSClément Léger struct ocelot_fdma *fdma; 820753a026cSClément Léger int ret; 821753a026cSClément Léger 822753a026cSClément Léger fdma = devm_kzalloc(dev, sizeof(*fdma), GFP_KERNEL); 823753a026cSClément Léger if (!fdma) 824753a026cSClément Léger return; 825753a026cSClément Léger 826753a026cSClément Léger ocelot->fdma = fdma; 827753a026cSClément Léger ocelot->dev->coherent_dma_mask = DMA_BIT_MASK(32); 828753a026cSClément Léger 829753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 0); 830753a026cSClément Léger 831753a026cSClément Léger fdma->ocelot = ocelot; 832753a026cSClément Léger fdma->irq = platform_get_irq_byname(pdev, "fdma"); 833753a026cSClément Léger ret = devm_request_irq(dev, fdma->irq, ocelot_fdma_interrupt, 0, 834753a026cSClément Léger dev_name(dev), ocelot); 835753a026cSClément Léger if (ret) 836753a026cSClément Léger goto err_free_fdma; 837753a026cSClément Léger 838753a026cSClément Léger ret = ocelot_fdma_rings_alloc(ocelot); 839753a026cSClément Léger if (ret) 840753a026cSClément Léger goto err_free_irq; 841753a026cSClément Léger 842753a026cSClément Léger static_branch_enable(&ocelot_fdma_enabled); 843753a026cSClément Léger 844753a026cSClément Léger return; 845753a026cSClément Léger 846753a026cSClément Léger err_free_irq: 847753a026cSClément Léger devm_free_irq(dev, fdma->irq, fdma); 848753a026cSClément Léger err_free_fdma: 849753a026cSClément Léger devm_kfree(dev, fdma); 850753a026cSClément Léger 851753a026cSClément Léger ocelot->fdma = NULL; 852753a026cSClément Léger } 853753a026cSClément Léger 854753a026cSClément Léger void ocelot_fdma_start(struct ocelot *ocelot) 855753a026cSClément Léger { 856753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 857753a026cSClément Léger 858753a026cSClément Léger /* Reconfigure for extraction and injection using DMA */ 859753a026cSClément Léger ocelot_write_rix(ocelot, QS_INJ_GRP_CFG_MODE(2), QS_INJ_GRP_CFG, 0); 860753a026cSClément Léger ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(0), QS_INJ_CTRL, 0); 861753a026cSClément Léger 862753a026cSClément Léger ocelot_write_rix(ocelot, QS_XTR_GRP_CFG_MODE(2), QS_XTR_GRP_CFG, 0); 863753a026cSClément Léger 864753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_LLP, 0xffffffff); 865753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_FRM, 0xffffffff); 866753a026cSClément Léger 867753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_LLP_ENA, 868753a026cSClément Léger BIT(MSCC_FDMA_INJ_CHAN) | BIT(MSCC_FDMA_XTR_CHAN)); 869753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_FRM_ENA, 870753a026cSClément Léger BIT(MSCC_FDMA_XTR_CHAN)); 871753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 872753a026cSClément Léger BIT(MSCC_FDMA_INJ_CHAN) | BIT(MSCC_FDMA_XTR_CHAN)); 873753a026cSClément Léger 874753a026cSClément Léger napi_enable(&fdma->napi); 875753a026cSClément Léger 876753a026cSClément Léger ocelot_fdma_activate_chan(ocelot, ocelot->fdma->rx_ring.dcbs_dma, 877753a026cSClément Léger MSCC_FDMA_XTR_CHAN); 878753a026cSClément Léger } 879753a026cSClément Léger 880753a026cSClément Léger void ocelot_fdma_deinit(struct ocelot *ocelot) 881753a026cSClément Léger { 882753a026cSClément Léger struct ocelot_fdma *fdma = ocelot->fdma; 883753a026cSClément Léger 884753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_INTR_ENA, 0); 885753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_FORCEDIS, 886753a026cSClément Léger BIT(MSCC_FDMA_XTR_CHAN)); 887753a026cSClément Léger ocelot_fdma_writel(ocelot, MSCC_FDMA_CH_FORCEDIS, 888753a026cSClément Léger BIT(MSCC_FDMA_INJ_CHAN)); 889753a026cSClément Léger napi_synchronize(&fdma->napi); 890753a026cSClément Léger napi_disable(&fdma->napi); 891753a026cSClément Léger 892753a026cSClément Léger ocelot_fdma_free_rx_ring(ocelot); 893753a026cSClément Léger ocelot_fdma_free_tx_ring(ocelot); 894753a026cSClément Léger } 895