// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, MediaTek Inc.
* Copyright (c) 2021-2022, Intel Corporation.
*
* Authors:
* Amir Hanania <amir.hanania@intel.com>
* Haijun Liu <haijun.liu@mediatek.com>
* Eliot Lee <eliot.lee@intel.com>
* Moises Veleta <moises.veleta@intel.com>
* Ricardo Martinez <ricardo.martinez@linux.intel.com>
*
* Contributors:
* Andy Shevchenko <andriy.shevchenko@linux.intel.com>
* Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
* Sreehari Kancharla <sreehari.kancharla@intel.com>
*/
#include <linux/atomic.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include "t7xx_dpmaif.h"
#include "t7xx_hif_dpmaif.h"
#include "t7xx_hif_dpmaif_rx.h"
#include "t7xx_netdev.h"
#include "t7xx_pci.h"
#define DPMAIF_BAT_COUNT 8192
#define DPMAIF_FRG_COUNT 4814
#define DPMAIF_PIT_COUNT (DPMAIF_BAT_COUNT * 2)
#define DPMAIF_BAT_CNT_THRESHOLD 30
#define DPMAIF_PIT_CNT_THRESHOLD 60
#define DPMAIF_RX_PUSH_THRESHOLD_MASK GENMASK(2, 0)
#define DPMAIF_NOTIFY_RELEASE_COUNT 128
#define DPMAIF_POLL_PIT_TIME_US 20
#define DPMAIF_POLL_PIT_MAX_TIME_US 2000
#define DPMAIF_WQ_TIME_LIMIT_MS 2
#define DPMAIF_CS_RESULT_PASS 0
/* Packet type */
#define DES_PT_PD 0
#define DES_PT_MSG 1
/* Buffer type */
#define PKT_BUF_FRAG 1
static unsigned int t7xx_normal_pit_bid(const struct dpmaif_pit *pit_info)
{
u32 value;
value = FIELD_GET(PD_PIT_H_BID, le32_to_cpu(pit_info->pd.footer));
value <<= 13;
value += FIELD_GET(PD_PIT_BUFFER_ID, le32_to_cpu(pit_info->header));
return value;
}
static int t7xx_dpmaif_update_bat_wr_idx(struct dpmaif_ctrl *dpmaif_ctrl,
const unsigned int q_num, const unsigned int bat_cnt)
{
struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[q_num];
struct dpmaif_bat_request *bat_req = rxq->bat_req;
unsigned int old_rl_idx, new_wr_idx, old_wr_idx;
if (!rxq->que_started) {
dev_err(dpmaif_ctrl->dev, "RX queue %d has not been started\n", rxq->index);
return -EINVAL;
}
old_rl_idx = bat_req->bat_release_rd_idx;
old_wr_idx = bat_req->bat_wr_idx;
new_wr_idx = old_wr_idx + bat_cnt;
if (old_rl_idx > old_wr_idx && new_wr_idx >= old_rl_idx)
goto err_flow;
if (new_wr_idx >= bat_req->bat_size_cnt) {
new_wr_idx -= bat_req->bat_size_cnt;
if (new_wr_idx >= old_rl_idx)
goto err_flow;
}
bat_req->bat_wr_idx = new_wr_idx;
return 0;
err_flow:
dev_err(dpmaif_ctrl->dev, "RX BAT flow check fail\n");
return -EINVAL;
}
static bool t7xx_alloc_and_map_skb_info(const struct dpmaif_ctrl *dpmaif_ctrl,
const unsigned int size, struct dpmaif_bat_skb *cur_skb)
{
dma_addr_t data_bus_addr;
struct sk_buff *skb;
skb = __dev_alloc_skb(size, GFP_KERNEL);
if (!skb)
return false;
data_bus_addr = dma_map_single(dpmaif_ctrl->dev, skb->data, size, DMA_FROM_DEVICE);
if (dma_mapping_error(dpmaif_ctrl->dev, data_bus_addr)) {
dev_err_ratelimited(dpmaif_ctrl->dev, "DMA mapping error\n");
dev_kfree_skb_any(skb);
return false;
}
cur_skb->skb = skb;
cur_skb->data_bus_addr = data_bus_addr;
cur_skb->data_len = size;
return true;
}
static void t7xx_unmap_bat_skb(struct device *dev, struct dpmaif_bat_skb *bat_skb_base,
unsigned int index)
{
struct dpmaif_bat_skb *bat_skb = bat_skb_base + index;
if (bat_skb->skb) {
dma_unmap_single(dev, bat_skb->data_bus_addr, bat_skb->data_len, DMA_FROM_DEVICE);
dev_kfree_skb(bat_skb->skb);
bat_skb->skb = NULL;
}
}
/**
* t7xx_dpmaif_rx_buf_alloc() - Allocate buffers for the BAT ring.
* @dpmaif_ctrl: Pointer to DPMAIF context structure.
* @bat_req: Pointer to BAT request structure.
* @q_num: Queue number.
* @buf_cnt: Number of buffers to allocate.
* @initial: Indicates if the ring is being populated for the first time.
*