// SPDX-License-Identifier: GPL-2.0+
#include <linux/bpf.h>
#include <linux/filter.h>
#include <net/page_pool/helpers.h>
#include "lan966x_main.h"
static int lan966x_fdma_channel_active(struct lan966x *lan966x)
{
return lan_rd(lan966x, FDMA_CH_ACTIVE);
}
static struct page *lan966x_fdma_rx_alloc_page(struct lan966x_rx *rx,
struct lan966x_db *db)
{
struct page *page;
page = page_pool_dev_alloc_pages(rx->page_pool);
if (unlikely(!page))
return NULL;
db->dataptr = page_pool_get_dma_addr(page) + XDP_PACKET_HEADROOM;
return page;
}
static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx)
{
int i, j;
for (i = 0; i < FDMA_DCB_MAX; ++i) {
for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j)
page_pool_put_full_page(rx->page_pool,
rx->page[i][j], false);
}
}
static void lan966x_fdma_rx_free_page(struct lan966x_rx *rx)
{
struct page *page;
page = rx->page[rx->dcb_index][rx->db_index];
if (unlikely(!page))
return;
page_pool_recycle_direct(rx->page_pool, page);
}
static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx,
struct lan966x_rx_dcb *dcb,
u64 nextptr)
{
struct lan966x_db *db;
int i;
for (i = 0; i < FDMA_RX_DCB_MAX_DBS; ++i) {
db = &dcb->db[i];
db->status = FDMA_DCB_STATUS_INTR;
}
dcb->nextptr = FDMA_DCB_INVALID_DATA;
dcb->info = FDMA_DCB_INFO_DATAL(PAGE_SIZE << rx->page_order);
rx->last_entry->nextptr = nextptr;
rx->last_entry = dcb;
}
static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx)
{
struct lan966x *lan966x = rx->lan966x;
struct page_pool_params pp_params = {
.order = rx->page_order,
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.pool_size = FDMA_DCB_MAX,
.nid = NUMA_NO_NODE,
.dev = lan966x->dev,
.dma_dir = DMA_FROM_DEVICE,
.offset = XDP_PACKET_HEADROOM,
.max_len = rx->max_mtu -
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
};
if (lan966x_xdp_present(lan966x))
pp_params.dma_dir = DMA_BIDIRECTIONAL;
rx->page_pool = page_pool_create(&pp_params);
for (int i = 0; i < lan966x->num_phys_ports; ++i) {
struct lan966x_port *port;
if (!lan966x->ports[i])
continue;
port = lan966x->ports[i];
xdp_rxq_info_unreg_mem_model(&port->xdp_rxq);
xdp_rxq_info_reg_mem_model(&port->xdp_rxq, MEM_TYPE_PAGE_POOL,
rx->page_pool);
}
return PTR_ERR_OR_ZERO(rx->page_pool);
}
static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx)
{
struct lan966x *lan966x = rx->lan966x;
struct lan966x_rx_dcb *dcb;
struct lan966x_db *db;
struct page *page;
int i, j;
int size;
if (lan966x_fdma_rx_alloc_page_pool(rx))
return PTR_ERR(rx->page_pool);
/* calculate how many pages are needed to allocate the dcbs */
size = sizeof(struct lan966x_rx_dcb) * FDMA_DCB_MAX;
size = ALIGN(size, PAGE_SIZE);
rx->dcbs = dma_alloc_coherent(lan966x->dev, size, &rx->dma, GFP_KERNEL);
if (!rx->dcbs)
return -ENOMEM;
rx->last_entry = rx->dcbs;
rx->db_index = 0;
rx->dcb_index = 0;
/* Now for each dcb allocate the dbs */
for (i = 0; i < FDMA_DCB_MAX; ++i) {
dcb = &rx->dcbs[i];
dcb->info = 0;
/* For each db allocate a page and map it to the DB dataptr. */
for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) {
db = &dcb->db[j];
page = lan966x_fdma_rx_alloc_page(rx, db);
if (!page)
return -ENOMEM;
db->status = 0;
rx->page[i][j] = page;
}
lan966x_fdma_rx_add_dcb(rx, dcb, rx->dma + sizeof(*dcb) * i);
}
return 0;
}
static <