Files
linux-nv-oot/drivers/pci/controller/tegra-pcie-edma.c
Jon Hunter ab65399274 drivers: Fix missing headers for Linux v6.8
For Linux v6.8, commit ef175b29a242 ("of: Stop circularly including
of_device.h and of_platform.h") updated the OF headers included by these
header files. This breaks the build for various drivers and so fix this
by including the headers that are actually needed for each driver.

Bug 4448428

Change-Id: Ia40ab13f865d5631c96855ecc49145848f99c996
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3032442
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Tested-by: Laxman Dewangan <ldewangan@nvidia.com>
2024-01-25 09:11:21 -08:00

842 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* PCIe EDMA Library Framework
*
* Copyright (C) 2021-2024 NVIDIA Corporation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/tegra-pcie-edma.h>
#include <linux/limits.h>
#include "tegra-pcie-dma-osi.h"
/** Default number of descriptors used */
#define NUM_EDMA_DESC 4096
/* DMA base offset starts at 0x20000 from ATU_DMA base */
#define DMA_OFFSET 0x20000
/** Calculates timeout based on size.
* Time in nano sec = size in bytes / (1000000 * 2).
* 2Gbps is max speed for Gen 1 with 2.5GT/s at 8/10 encoding.
* Convert to milli seconds and add 1sec timeout
*/
#define GET_SYNC_TIMEOUT(s) ((((s) * 8UL) / 2000000) + 1000)
#define INCR_DESC(idx, i) ((idx) = ((idx) + (i)) % (ch->desc_sz))
struct edma_chan {
void *desc;
void __iomem *remap_desc;
struct tegra_pcie_edma_xfer_info *ring;
dma_addr_t dma_iova;
uint32_t desc_sz;
/* descriptor size that is allocated for a channel */
u64 edma_desc_size;
/** Index from where cleanup needs to be done */
volatile uint32_t r_idx;
/** Index from where descriptor update is needed */
volatile uint32_t w_idx;
struct mutex lock;
wait_queue_head_t wq;
edma_chan_type_t type;
u64 wcount;
u64 rcount;
bool busy;
bool pcs;
bool db_pos;
/** This field is updated to abort or de-init to stop further xfer submits */
edma_xfer_status_t st;
};
struct edma_prv {
u32 irq;
char *irq_name;
bool is_remote_dma;
uint16_t msi_data;
uint64_t msi_addr;
/** EDMA base address */
void __iomem *edma_base;
/** EDMA base address size */
uint32_t edma_base_size;
struct device *dev;
struct edma_chan tx[DMA_WR_CHNL_NUM];
struct edma_chan rx[DMA_RD_CHNL_NUM];
/* BIT(0) - Write initialized, BIT(1) - Read initialized */
uint32_t ch_init;
};
/** TODO: Define osi_ll_init strcuture and make this as OSI */
static inline void edma_ll_ch_init(void __iomem *edma_base, uint8_t ch,
dma_addr_t ll_phy_addr, bool rw_type,
bool is_remote_dma)
{
uint32_t int_mask_val = OSI_BIT(ch);
uint32_t val;
/** configure write by default and overwrite if read */
uint32_t int_mask = DMA_WRITE_INT_MASK_OFF;
uint32_t ctrl1_offset = DMA_CH_CONTROL1_OFF_WRCH;
uint32_t low_offset = DMA_LLP_LOW_OFF_WRCH;
uint32_t high_offset = DMA_LLP_HIGH_OFF_WRCH;
uint32_t lle_ccs = DMA_CH_CONTROL1_OFF_WRCH_LIE | DMA_CH_CONTROL1_OFF_WRCH_LLE |
DMA_CH_CONTROL1_OFF_WRCH_CCS;
uint32_t rie = DMA_CH_CONTROL1_OFF_WRCH_RIE;
uint32_t err_off = DMA_WRITE_LINKED_LIST_ERR_EN_OFF;
uint32_t err_val = 0;
if (!rw_type) {
int_mask = DMA_READ_INT_MASK_OFF;
low_offset = DMA_LLP_LOW_OFF_RDCH;
high_offset = DMA_LLP_HIGH_OFF_RDCH;
ctrl1_offset = DMA_CH_CONTROL1_OFF_RDCH;
lle_ccs = DMA_CH_CONTROL1_OFF_RDCH_LIE | DMA_CH_CONTROL1_OFF_RDCH_LLE |
DMA_CH_CONTROL1_OFF_RDCH_CCS;
rie = DMA_CH_CONTROL1_OFF_RDCH_RIE;
err_off = DMA_READ_LINKED_LIST_ERR_EN_OFF;
}
/* Enable LIE or RIE for all write channels */
val = dma_common_rd(edma_base, int_mask);
err_val = dma_common_rd(edma_base, err_off);
if (!is_remote_dma) {
val &= ~int_mask_val;
val &= ~(int_mask_val << 16);
err_val |= OSI_BIT((16 + ch));
} else {
val |= int_mask_val;
val |= (int_mask_val << 16);
err_val |= OSI_BIT(ch);
}
dma_common_wr(edma_base, val, int_mask);
dma_common_wr(edma_base, err_val, err_off);
val = lle_ccs;
/* Enable RIE for remote DMA */
val |= (is_remote_dma ? rie : 0);
dma_channel_wr(edma_base, ch, val, ctrl1_offset);
dma_channel_wr(edma_base, ch, lower_32_bits(ll_phy_addr), low_offset);
dma_channel_wr(edma_base, ch, upper_32_bits(ll_phy_addr), high_offset);
}
static inline void edma_hw_init(void *cookie, bool rw_type)
{
struct edma_prv *prv = (struct edma_prv *)cookie;
uint32_t msi_data;
u32 eng_off[2] = {DMA_WRITE_ENGINE_EN_OFF, DMA_READ_ENGINE_EN_OFF};
if (prv->ch_init & OSI_BIT(rw_type))
dma_common_wr(prv->edma_base, WRITE_ENABLE, eng_off[rw_type]);
/* Program MSI addr & data for remote DMA use case */
if (prv->is_remote_dma) {
msi_data = prv->msi_data;
msi_data |= (msi_data << 16);
dma_common_wr(prv->edma_base, lower_32_bits(prv->msi_addr),
DMA_WRITE_DONE_IMWR_LOW_OFF);
dma_common_wr(prv->edma_base, upper_32_bits(prv->msi_addr),
DMA_WRITE_DONE_IMWR_HIGH_OFF);
dma_common_wr(prv->edma_base, lower_32_bits(prv->msi_addr),
DMA_WRITE_ABORT_IMWR_LOW_OFF);
dma_common_wr(prv->edma_base, upper_32_bits(prv->msi_addr),
DMA_WRITE_ABORT_IMWR_HIGH_OFF);
dma_common_wr(prv->edma_base, msi_data,
DMA_WRITE_CH01_IMWR_DATA_OFF);
dma_common_wr(prv->edma_base, msi_data,
DMA_WRITE_CH23_IMWR_DATA_OFF);
dma_common_wr(prv->edma_base, lower_32_bits(prv->msi_addr),
DMA_READ_DONE_IMWR_LOW_OFF);
dma_common_wr(prv->edma_base, upper_32_bits(prv->msi_addr),
DMA_READ_DONE_IMWR_HIGH_OFF);
dma_common_wr(prv->edma_base, lower_32_bits(prv->msi_addr),
DMA_READ_ABORT_IMWR_LOW_OFF);
dma_common_wr(prv->edma_base, upper_32_bits(prv->msi_addr),
DMA_READ_ABORT_IMWR_HIGH_OFF);
dma_common_wr(prv->edma_base, msi_data,
DMA_READ_CH01_IMWR_DATA_OFF);
}
}
static inline int edma_ch_init(struct edma_prv *prv, struct edma_chan *ch)
{
struct edma_dblock *db;
dma_addr_t addr;
uint32_t j;
if ((ch->desc_sz <= 1) || (ch->desc_sz & (ch->desc_sz - 1)))
return -EINVAL;
if (prv->is_remote_dma)
memset_io(ch->remap_desc, 0,
(sizeof(struct edma_dblock)) * ((ch->desc_sz / 2) + 1));
else
memset(ch->desc, 0, (sizeof(struct edma_dblock)) * ((ch->desc_sz / 2) + 1));
db = (struct edma_dblock *)ch->desc + ((ch->desc_sz / 2) - 1);
db->llp.sar_low = lower_32_bits(ch->dma_iova);
db->llp.sar_high = upper_32_bits(ch->dma_iova);
db->llp.ctrl_reg.ctrl_e.llp = 1;
db->llp.ctrl_reg.ctrl_e.tcb = 1;
for (j = 0; j < (ch->desc_sz / 2 - 1); j++) {
db = (struct edma_dblock *)ch->desc + j;
addr = ch->dma_iova + (sizeof(struct edma_dblock) * (j + 1));
db->llp.sar_low = lower_32_bits(addr);
db->llp.sar_high = upper_32_bits(addr);
db->llp.ctrl_reg.ctrl_e.llp = 1;
}
ch->wcount = 0;
ch->rcount = 0;
ch->w_idx = 0;
ch->r_idx = 0;
ch->pcs = 1;
ch->st = EDMA_XFER_SUCCESS;
if (!ch->ring) {
ch->ring = kcalloc(ch->desc_sz, sizeof(*ch->ring), GFP_KERNEL);
if (!ch->ring)
return -ENOMEM;
}
return 0;
}
static inline void edma_hw_deinit(void *cookie, bool rw_type)
{
struct edma_prv *prv = (struct edma_prv *)cookie;
int i;
u32 eng_off[2] = {DMA_WRITE_ENGINE_EN_OFF, DMA_READ_ENGINE_EN_OFF};
u32 ctrl_off[2] = {DMA_CH_CONTROL1_OFF_WRCH, DMA_CH_CONTROL1_OFF_RDCH};
u32 mode_cnt[2] = {DMA_WR_CHNL_NUM, DMA_RD_CHNL_NUM};
if (prv->ch_init & OSI_BIT(rw_type)) {
dma_common_wr(prv->edma_base, 0, eng_off[rw_type]);
for (i = 0; i < mode_cnt[rw_type]; i++)
dma_channel_wr(prv->edma_base, i, 0, ctrl_off[rw_type]);
}
}
/** From OSI */
static inline u32 get_dma_idx_from_llp(struct edma_prv *prv, u8 chan, struct edma_chan *ch,
u32 type)
{
u64 cur_iova;
u64 high_iova, tmp_iova;
u32 cur_idx;
u32 llp_low_off[2] = {DMA_LLP_LOW_OFF_WRCH, DMA_LLP_LOW_OFF_RDCH};
u32 llp_high_off[2] = {DMA_LLP_HIGH_OFF_WRCH, DMA_LLP_HIGH_OFF_RDCH};
u64 block_idx, idx_in_block;
/*
* Read current element address in DMA_LLP register which pending
* DMA request and validate for spill over.
*/
high_iova = dma_channel_rd(prv->edma_base, chan, llp_high_off[type]);
cur_iova = (high_iova << 32);
cur_iova |= dma_channel_rd(prv->edma_base, chan, llp_low_off[type]);
tmp_iova = dma_channel_rd(prv->edma_base, chan, llp_high_off[type]);
if (tmp_iova > high_iova) {
/* Take latest reading of low offset and use it with new high offset */
cur_iova = dma_channel_rd(prv->edma_base, chan, llp_low_off[type]);
cur_iova |= (tmp_iova << 32);
}
if ((cur_iova >= ch->dma_iova) && ((cur_iova - ch->dma_iova) < ch->edma_desc_size)) {
/* Compute DMA desc index */
block_idx = ((cur_iova - ch->dma_iova) / sizeof(struct edma_dblock));
idx_in_block = (cur_iova & (sizeof(struct edma_dblock) - 1)) /
sizeof(struct edma_hw_desc);
cur_idx = (u32)(((block_idx * 2UL) + idx_in_block) & UINT_MAX);
} else {
ch->st = EDMA_XFER_ABORT;
/* Set Read index to ensure all callbacks gets error return */
cur_idx = (ch->r_idx - 1U) % ch->desc_sz;
}
return cur_idx % (ch->desc_sz);
}
static inline void process_r_idx(struct edma_chan *ch, edma_xfer_status_t st, u32 idx)
{
u32 count = 0;
struct edma_hw_desc *dma_ll_virt;
struct edma_dblock *db;
struct tegra_pcie_edma_xfer_info *ring;
while ((ch->r_idx != idx) && (count < ch->desc_sz)) {
count++;
ring = &ch->ring[ch->r_idx];
db = (struct edma_dblock *)ch->desc + ch->r_idx / 2;
dma_ll_virt = &db->desc[ch->r_idx % 2];
INCR_DESC(ch->r_idx, 1);
ch->rcount++;
/* clear lie and rie if any set */
dma_ll_virt->ctrl_reg.ctrl_e.lie = 0;
dma_ll_virt->ctrl_reg.ctrl_e.rie = 0;
if (ch->type == EDMA_CHAN_XFER_ASYNC && ring->complete) {
ring->complete(ring->priv, st, NULL);
/* Clear ring callback and priv variables */
ring->complete = NULL;
ring->priv = NULL;
}
}
}
static inline void process_ch_irq(struct edma_prv *prv, u32 chan, struct edma_chan *ch,
u32 type)
{
u32 idx;
idx = get_dma_idx_from_llp(prv, (u8)(chan & 0xFF), ch, type);
if (ch->type == EDMA_CHAN_XFER_SYNC) {
if (ch->busy) {
ch->busy = false;
wake_up(&ch->wq);
} else
dev_info(prv->dev, "SYNC mode with chan %d busy not set r_idx %d, cur_idx %d, w_idx is %d\n",
chan, ch->r_idx, idx, ch->w_idx);
}
if (ch->st == EDMA_XFER_ABORT) {
dev_info(prv->dev, "Abort: ch %d at r_idx %d->idx %d, w_idx is %d\n", chan,
ch->r_idx, idx, ch->w_idx);
if (ch->r_idx == idx)
goto process_abort;
}
process_r_idx(ch, EDMA_XFER_SUCCESS, idx);
process_abort:
if (ch->st == EDMA_XFER_ABORT)
process_r_idx(ch, EDMA_XFER_ABORT, ch->w_idx);
}
static irqreturn_t edma_irq(int irq, void *cookie)
{
/* Disable irq before wake thread handler */
disable_irq_nosync((u32)(irq & INT_MAX));
return IRQ_WAKE_THREAD;
}
static void edma_check_and_ring_db(struct edma_prv *prv, u8 bit, u32 ctrl_off, u32 db_off)
{
u32 reg = dma_channel_rd(prv->edma_base, bit, ctrl_off);
#define CS_POS 5U
#define CS_MASK (OSI_BIT(5) | OSI_BIT(6))
#define CS_RUNNING 2U
reg &= CS_MASK;
reg >>= CS_POS;
/*
* There are pending descriptors to process, but
* EDMA HW is not running. Ring the doorbell again
* to ensure that descriptors are processed.
*/
if (reg != CS_RUNNING)
dma_common_wr(prv->edma_base, bit, db_off);
}
static irqreturn_t edma_irq_handler(int irq, void *cookie)
{
struct edma_prv *prv = (struct edma_prv *)cookie;
int bit = 0;
u32 val, i = 0;
struct edma_chan *chan[2] = {&prv->tx[0], &prv->rx[0]};
struct edma_chan *ch;
u32 int_status_off[2] = {DMA_WRITE_INT_STATUS_OFF, DMA_READ_INT_STATUS_OFF};
u32 int_clear_off[2] = {DMA_WRITE_INT_CLEAR_OFF, DMA_READ_INT_CLEAR_OFF};
u32 mode_cnt[2] = {DMA_WR_CHNL_NUM, DMA_RD_CHNL_NUM};
u32 ctrl_off[2] = {DMA_CH_CONTROL1_OFF_WRCH, DMA_CH_CONTROL1_OFF_RDCH};
u32 db_off[2] = {DMA_WRITE_DOORBELL_OFF, DMA_READ_DOORBELL_OFF};
for (i = 0; i < 2; i++) {
if (!(prv->ch_init & OSI_BIT(i)))
continue;
val = dma_common_rd(prv->edma_base, int_status_off[i]);
if ((val & OSI_GENMASK(31, 16)) != 0U) {
/**
* If ABORT, immediately update state for all channels as aborted.
* This setting stop further SW queuing
*/
dev_info(prv->dev, "Abort int status 0x%x", val);
for (bit = 0; bit < mode_cnt[i]; bit++) {
ch = chan[i] + bit;
ch->st = EDMA_XFER_ABORT;
}
edma_hw_deinit(prv, !!i);
/** Perform abort handling */
for (bit = 0; bit < mode_cnt[i]; bit++) {
ch = chan[i] + bit;
if (!ch->ring)
continue;
/* Clear ABORT and DONE interrupt, as abort handles both */
dma_common_wr(prv->edma_base, OSI_BIT(16 + bit) | OSI_BIT(bit),
int_clear_off[i]);
/** wait until exisitng xfer submit completed */
mutex_lock(&ch->lock);
mutex_unlock(&ch->lock);
process_ch_irq(prv, bit, ch, i);
edma_ch_init(prv, ch);
edma_ll_ch_init(prv->edma_base, (u8)(bit & 0xFF), ch->dma_iova,
(i == 0), prv->is_remote_dma);
}
edma_hw_init(prv, !!i);
} else {
for (bit = 0; bit < mode_cnt[i]; bit++) {
ch = chan[i] + bit;
if (OSI_BIT(bit) & val) {
dma_common_wr(prv->edma_base, OSI_BIT(bit),
int_clear_off[i]);
process_ch_irq(prv, bit, ch, i);
/* Check if there are pending descriptors */
if (ch->w_idx != ch->r_idx)
edma_check_and_ring_db(prv, (u8)(bit & 0xFF),
ctrl_off[i], db_off[i]);
}
}
}
}
/* Must enable before exit */
enable_irq((u32)(irq & INT_MAX));
return IRQ_HANDLED;
}
void *tegra_pcie_edma_initialize(struct tegra_pcie_edma_init_info *info)
{
struct edma_prv *prv;
struct resource *dma_res;
int32_t ret, i, j, l_irq = 0;
struct edma_chan *ch = NULL;
struct edma_chan *chan[2];
u32 mode_cnt[2] = {DMA_WR_CHNL_NUM, DMA_RD_CHNL_NUM};
struct tegra_pcie_edma_chans_info *chan_info[2];
struct tegra_pcie_edma_chans_info *ch_info;
struct platform_device *pdev;
prv = kzalloc(sizeof(*prv), GFP_KERNEL);
if (!prv) {
pr_err("Failed to allocate memory for edma_prv\n");
return NULL;
}
chan[0] = &prv->tx[0];
chan[1] = &prv->rx[0];
chan_info[0] = &info->tx[0];
chan_info[1] = &info->rx[0];
if (info->edma_remote != NULL) {
if (!info->edma_remote->dev) {
pr_err("%s: dev pointer is NULL\n", __func__);
goto free_priv;
}
prv->dev = info->edma_remote->dev;
if (info->edma_remote->msi_irq > INT_MAX) {
pr_err("%s: msi_irq is out of range\n", __func__);
goto free_priv;
}
prv->irq = (int)info->edma_remote->msi_irq;
prv->msi_data = info->edma_remote->msi_data;
prv->msi_addr = info->edma_remote->msi_addr;
prv->is_remote_dma = true;
prv->edma_base = devm_ioremap(prv->dev, info->edma_remote->dma_phy_base,
info->edma_remote->dma_size);
if (IS_ERR(prv->edma_base)) {
dev_err(prv->dev, "dma region map failed.\n");
goto free_priv;
}
} else if (info->np != NULL) {
prv->is_remote_dma = false;
pdev = of_find_device_by_node(info->np);
if (!pdev) {
pr_err("Unable to retrieve pdev node\n");
goto free_priv;
}
prv->dev = &pdev->dev;
dma_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"atu_dma");
if (!dma_res) {
dev_err(prv->dev, "missing atu_dma resource in DT\n");
goto put_dev;
}
prv->edma_base = devm_ioremap(prv->dev, dma_res->start + DMA_OFFSET,
resource_size(dma_res) - DMA_OFFSET);
if (IS_ERR(prv->edma_base)) {
dev_err(prv->dev, "dma region map failed.\n");
goto put_dev;
}
l_irq = platform_get_irq_byname(pdev, "intr");
if (l_irq <= 0) {
dev_err(prv->dev, "failed to get intr interrupt\n");
goto put_dev;
} else {
prv->irq = (u32)l_irq;
}
} else {
pr_err("Neither device node nor edma remote available");
goto free_priv;
}
for (j = 0; j < 2; j++) {
for (i = 0; i < mode_cnt[j]; i++) {
ch_info = chan_info[j] + i;
ch = chan[j] + i;
if (ch_info->num_descriptors == 0)
continue;
ch->type = ch_info->ch_type;
ch->desc_sz = ch_info->num_descriptors;
ch->edma_desc_size = (sizeof(struct edma_dblock)) * ((ch->desc_sz / 2) + 1);
if (prv->is_remote_dma) {
ch->dma_iova = ch_info->desc_iova;
ch->remap_desc = devm_ioremap(prv->dev, ch_info->desc_phy_base,
(sizeof(struct edma_dblock)) *
((ch->desc_sz / 2) + 1));
ch->desc = (__force void *)ch->remap_desc;
if (!ch->desc) {
dev_err(prv->dev, "desc region map failed, phy: 0x%llx\n",
ch_info->desc_phy_base);
goto dma_iounmap;
}
} else {
ch->desc = dma_alloc_coherent(prv->dev, ch->edma_desc_size,
&ch->dma_iova, GFP_KERNEL);
if (!ch->desc) {
dev_err(prv->dev, "Cannot allocate required descriptos(%d) of size (%lu) for channel:%d type: %d\n",
ch->desc_sz,
(sizeof(struct edma_hw_desc) * ch->desc_sz), i, j);
goto dma_iounmap;
}
}
prv->ch_init |= OSI_BIT(j);
if (edma_ch_init(prv, ch) < 0)
goto free_dma_desc;
edma_ll_ch_init(prv->edma_base, (u8)(i & 0xFF), ch->dma_iova, (j == 0),
prv->is_remote_dma);
}
}
prv->irq_name = kasprintf(GFP_KERNEL, "%s_edma_lib", dev_name(prv->dev));
if (!prv->irq_name)
goto free_ring;
ret = request_threaded_irq(prv->irq, edma_irq, edma_irq_handler,
IRQF_SHARED,
prv->irq_name, prv);
if (ret < 0) {
dev_err(prv->dev, "failed to request \"intr\" irq\n");
goto free_irq_name;
}
for (i = 0; i < DMA_WR_CHNL_NUM; i++) {
mutex_init(&prv->tx[i].lock);
init_waitqueue_head(&prv->tx[i].wq);
}
for (i = 0; i < DMA_RD_CHNL_NUM; i++) {
mutex_init(&prv->rx[i].lock);
init_waitqueue_head(&prv->rx[i].wq);
}
edma_hw_init(prv, false);
edma_hw_init(prv, true);
dev_info(prv->dev, "%s: success", __func__);
return prv;
free_irq_name:
kfree(prv->irq_name);
free_ring:
for (j = 0; j < 2; j++) {
for (i = 0; i < mode_cnt[j]; i++) {
ch = chan[j] + i;
kfree(ch->ring);
}
}
free_dma_desc:
for (j = 0; j < 2; j++) {
for (i = 0; i < mode_cnt[j]; i++) {
ch = chan[j] + i;
if (prv->is_remote_dma && ch->desc)
devm_iounmap(prv->dev, ch->remap_desc);
else if (ch->desc)
dma_free_coherent(prv->dev, ch->edma_desc_size,
ch->desc, ch->dma_iova);
}
}
dma_iounmap:
devm_iounmap(prv->dev, prv->edma_base);
put_dev:
if (!prv->is_remote_dma)
put_device(prv->dev);
free_priv:
kfree(prv);
return NULL;
}
EXPORT_SYMBOL_GPL(tegra_pcie_edma_initialize);
edma_xfer_status_t tegra_pcie_edma_submit_xfer(void *cookie,
struct tegra_pcie_edma_xfer_info *tx_info)
{
struct edma_prv *prv = (struct edma_prv *)cookie;
struct edma_chan *ch;
struct edma_hw_desc *dma_ll_virt = NULL;
struct edma_dblock *db;
int i;
u64 total_sz = 0;
edma_xfer_status_t st = EDMA_XFER_SUCCESS;
u32 avail, to_ms;
struct tegra_pcie_edma_xfer_info *ring;
u32 int_status_off[2] = {DMA_WRITE_INT_STATUS_OFF, DMA_READ_INT_STATUS_OFF};
u32 doorbell_off[2] = {DMA_WRITE_DOORBELL_OFF, DMA_READ_DOORBELL_OFF};
u32 mode_cnt[2] = {DMA_WR_CHNL_NUM, DMA_RD_CHNL_NUM};
bool pcs, final_pcs = false;
long ret, to_jif;
if (!prv || !tx_info || tx_info->nents == 0 || !tx_info->desc ||
(tx_info->type < EDMA_XFER_WRITE || tx_info->type > EDMA_XFER_READ) ||
tx_info->channel_num >= mode_cnt[tx_info->type])
return EDMA_XFER_FAIL_INVAL_INPUTS;
ch = (tx_info->type == EDMA_XFER_WRITE) ? &prv->tx[tx_info->channel_num] :
&prv->rx[tx_info->channel_num];
if (!ch->desc_sz)
return EDMA_XFER_FAIL_INVAL_INPUTS;
if ((tx_info->complete == NULL) && (ch->type == EDMA_CHAN_XFER_ASYNC))
return EDMA_XFER_FAIL_INVAL_INPUTS;
/* Get hold of the hardware - locking */
mutex_lock(&ch->lock);
/* Channel busy flag should be updated before channel status check */
ch->busy = true;
if (ch->st != EDMA_XFER_SUCCESS) {
st = ch->st;
goto unlock;
}
avail = (ch->r_idx - ch->w_idx - 1U) & (ch->desc_sz - 1U);
if (tx_info->nents > avail) {
dev_dbg(prv->dev, "Descriptors full. w_idx %d. r_idx %d, avail %d, req %d\n",
ch->w_idx, ch->r_idx, avail, tx_info->nents);
st = EDMA_XFER_FAIL_NOMEM;
goto unlock;
}
dev_dbg(prv->dev, "xmit for %d nents at %d widx and %d ridx\n",
tx_info->nents, ch->w_idx, ch->r_idx);
db = (struct edma_dblock *)ch->desc + (ch->w_idx/2);
for (i = 0; i < tx_info->nents; i++) {
dma_ll_virt = &db->desc[ch->db_pos];
dma_ll_virt->size = tx_info->desc[i].sz;
/* calculate number of packets and add those many headers */
total_sz += (u64)(((tx_info->desc[i].sz / ch->desc_sz) + 1) * 30ULL);
total_sz += tx_info->desc[i].sz;
dma_ll_virt->sar_low = lower_32_bits(tx_info->desc[i].src);
dma_ll_virt->sar_high = upper_32_bits(tx_info->desc[i].src);
dma_ll_virt->dar_low = lower_32_bits(tx_info->desc[i].dst);
dma_ll_virt->dar_high = upper_32_bits(tx_info->desc[i].dst);
/* Set LIE or RIE in last element */
if (i == tx_info->nents - 1) {
dma_ll_virt->ctrl_reg.ctrl_e.lie = 1;
dma_ll_virt->ctrl_reg.ctrl_e.rie = !!prv->is_remote_dma;
final_pcs = ch->pcs;
} else {
/* CB should be updated last in the descriptor */
dma_ll_virt->ctrl_reg.ctrl_e.cb = ch->pcs;
}
ch->db_pos = !ch->db_pos;
avail = ch->w_idx;
ch->w_idx++;
if (!ch->db_pos) {
ch->wcount = 0;
db->llp.ctrl_reg.ctrl_e.cb = ch->pcs;
if (ch->w_idx == ch->desc_sz) {
ch->pcs = !ch->pcs;
db->llp.ctrl_reg.ctrl_e.cb = ch->pcs;
dev_dbg(prv->dev, "Toggled pcs at w_idx %d\n", ch->w_idx);
ch->w_idx = 0;
}
db = (struct edma_dblock *)ch->desc + (ch->w_idx/2);
}
}
ring = &ch->ring[avail];
ring->priv = tx_info->priv;
ring->complete = tx_info->complete;
/* Update CB post SW ring update to order callback and transfer updates */
dma_ll_virt->ctrl_reg.ctrl_e.cb = final_pcs;
/* desc write should not go OOO wrt DMA DB ring */
wmb();
/* use smb_wmb to synchronize across cores */
smp_wmb();
/* Read back CB and check with CCS to ensure that descriptor update is reflected. */
pcs = dma_ll_virt->ctrl_reg.ctrl_e.cb;
if (pcs != (dma_channel_rd(prv->edma_base, tx_info->channel_num, DMA_CH_CONTROL1_OFF_WRCH) &
OSI_BIT(8)))
dev_dbg(prv->dev, "read pcs != CCS failed. But expected sometimes: %d\n", pcs);
/* Ensure that all reads and writes are ordered */
mb();
/* Ensure that all reads and writes are ordered */
smp_mb();
dma_common_wr(prv->edma_base, tx_info->channel_num, doorbell_off[tx_info->type]);
if (ch->type == EDMA_CHAN_XFER_SYNC) {
total_sz = GET_SYNC_TIMEOUT(total_sz);
to_ms = (total_sz > UINT_MAX) ? UINT_MAX : (u32)(total_sz & UINT_MAX);
to_jif = msecs_to_jiffies(to_ms) > LONG_MAX ?
LONG_MAX : msecs_to_jiffies(to_ms) & LONG_MAX;
ret = wait_event_timeout(ch->wq, !ch->busy, to_jif);
if (ret == 0) {
/* dummy print to avoid misra-c voilations */
dev_dbg(prv->dev, "read back pcs: %d\n", pcs);
dev_err(prv->dev, "%s: timeout at %d ch, w_idx(%d), r_idx(%d)\n",
__func__, tx_info->channel_num, ch->w_idx,
ch->r_idx);
dev_err(prv->dev, "%s: int status 0x%x", __func__,
dma_common_rd(prv->edma_base, int_status_off[tx_info->type]));
st = EDMA_XFER_FAIL_TIMEOUT;
goto unlock;
} else {
st = ch->st;
}
dev_dbg(prv->dev, "xmit done for %d nents at %d widx and %d ridx\n",
tx_info->nents, ch->w_idx, ch->r_idx);
}
unlock:
/* release hardware - unlocking */
mutex_unlock(&ch->lock);
return st;
}
EXPORT_SYMBOL_GPL(tegra_pcie_edma_submit_xfer);
static void edma_stop(struct edma_prv *prv, edma_xfer_status_t st)
{
struct edma_chan *chan[2], *ch;
int i, j;
u32 mode_cnt[2] = {DMA_WR_CHNL_NUM, DMA_RD_CHNL_NUM};
bool sync_irq = false;
chan[0] = &prv->tx[0];
chan[1] = &prv->rx[0];
/* wake up xfer function waiting on dma completion in sync mode */
for (j = 0; j < 2; j++) {
for (i = 0; i < mode_cnt[j]; i++) {
ch = chan[j] + i;
ch->st = st;
if ((ch->type == EDMA_CHAN_XFER_SYNC) && ch->busy) {
ch->busy = false;
wake_up(&ch->wq);
}
/** wait until exisitng xfer submit completed */
mutex_lock(&ch->lock);
mutex_unlock(&ch->lock);
if (ch->w_idx != ch->r_idx)
sync_irq = true;
}
}
edma_hw_deinit(prv, false);
edma_hw_deinit(prv, true);
if (sync_irq)
synchronize_irq(prv->irq);
for (j = 0; j < 2; j++) {
for (i = 0; i < mode_cnt[j]; i++) {
ch = chan[j] + i;
if (prv->ch_init & OSI_BIT(i))
process_r_idx(ch, st, ch->w_idx);
}
}
}
bool tegra_pcie_edma_stop(void *cookie)
{
struct edma_prv *prv = (struct edma_prv *)cookie;
if (cookie == NULL)
return false;
edma_stop(prv, EDMA_XFER_ABORT);
return true;
}
EXPORT_SYMBOL_GPL(tegra_pcie_edma_stop);
void tegra_pcie_edma_deinit(void *cookie)
{
struct edma_prv *prv = (struct edma_prv *)cookie;
struct edma_chan *chan[2], *ch;
int i, j;
u32 mode_cnt[2] = {DMA_WR_CHNL_NUM, DMA_RD_CHNL_NUM};
if (cookie == NULL)
return;
edma_stop(prv, EDMA_XFER_DEINIT);
free_irq(prv->irq, prv);
kfree(prv->irq_name);
chan[0] = &prv->tx[0];
chan[1] = &prv->rx[0];
for (j = 0; j < 2; j++) {
for (i = 0; i < mode_cnt[j]; i++) {
ch = chan[j] + i;
if (prv->is_remote_dma && ch->desc)
devm_iounmap(prv->dev, ch->remap_desc);
else if (ch->desc)
dma_free_coherent(prv->dev, ch->edma_desc_size,
ch->desc, ch->dma_iova);
kfree(ch->ring);
}
}
devm_iounmap(prv->dev, prv->edma_base);
if (!prv->is_remote_dma)
put_device(prv->dev);
kfree(prv);
}
EXPORT_SYMBOL_GPL(tegra_pcie_edma_deinit);
MODULE_LICENSE("GPL v2");