Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ezdma from kernel-space #10

Open
ht-hieu opened this issue Mar 1, 2017 · 4 comments
Open

Use ezdma from kernel-space #10

ht-hieu opened this issue Mar 1, 2017 · 4 comments

Comments

@ht-hieu
Copy link

ht-hieu commented Mar 1, 2017

Hi.
I'm writing a DMA driver for my hardware. I don't know about parameter was set on FPGA. Your code works well in user-space. I tried you your code in my driver, but it cannot run get_user_pages_fast with my buffer. I also followed dmatest.c in xilinx's kernel, it work but when I transmit with higher speed, it showed error: xilinx-dma 40410000.dma: Channel ef31b890 has errors 11 cdr 0 cdr msb 0 tdr 0 tdr msb 0.
Can you help me solve this error? Thank you.
Here is my code:

static int dma_read(struct dma_priv *priv, uint8_t __kernel *buffer,
                      size_t count, uint32_t timeout)
{
        struct dma_device *device = priv->chan->device;
        struct dma_async_tx_descriptor *rxd = NULL;
        dma_addr_t dma_src;
        struct scatterlist rx_sg[1];
        struct completion rx_cmp;
        int i, ret=0;
        if (down_interruptible(&priv->sem)) {
                return -ERESTARTSYS;
        }

        dma_src = dma_map_page(device->dev, virt_to_page(buffer),
                               offset_in_page(buffer), count, DMA_DEV_TO_MEM);
        if (dma_mapping_error(device->dev, dma_src)) {
                error("Mapping error\n");
                ret = -1;
                goto read_map_err;
        }
        sg_init_table(rx_sg, 1);
        sg_dma_address(&rx_sg[0]) = dma_src;
        sg_dma_len(&rx_sg[0]) = count;
        rxd = dmaengine_prep_slave_single(priv->chan, dma_src, count,
                                          DMA_FROM_DEVICE, DMA_PREP_INTERRUPT);
        if (!rxd) {
                error(" prep err\n");
                dma_unmap_single(device->dev, dma_src, count, DMA_MEM_TO_DEV);
                ret = -1;
                goto read_out;
        }
        init_completion(&priv->completion);
        rxd->callback = dma_tx_callback;
        rxd->callback_param = priv;

        priv->cookie = dmaengine_submit(rxd);
        if (dma_submit_error(priv->cookie)) {
                warn("submit error\n");
        }
        dma_async_issue_pending(priv->chan);
        wait_for_completion(&priv->completion);

        if (dma_async_is_tx_complete(priv->chan, priv->cookie, NULL, NULL)
                != DMA_COMPLETE) {
                error("DMA error\n");
                ret = -1;
                goto read_out;
        }
        ret = priv->bytes_xfered;
read_map_err:
        dma_unmap_single(device->dev, dma_src, count, DMA_MEM_TO_DEV);
read_out:
        up(&priv->sem);
        return ret;
}
@jeremytrimble
Copy link
Owner

@gochit,

ezdma is designed for use from userspace only, and get_user_pages_fast() is intended only for userspace (process virtual) memory, so I'm not surprised that it's not working when being called from within the kernel.

I think you're on the right track with your code above (calling the dmaengine API directly from within the kernel), but as far as debugging your specific issue I'd have to know more context. The meanings of register values printed in dmesg are explained in the AXI DMA Documentation (PG021).

@ht-hieu
Copy link
Author

ht-hieu commented Mar 31, 2017

@jeremytrimble ,

Thank you for your response, according to AXI DMA Documentation, DMA driver turned on 2 bits:

DMA channel halted. For Scatter / Gather Mode this bit gets set when DMACR.RS = 0 and DMA and 
scatter Gather (SG) operations have halted. For Direct Register mode (C_INCLUDE_SG = 0) this bit gets 
set when DMACR.RS = 0 and DMA operations have halted. There can be a lag of time between when 
DMACR.RS = 0 and when DMASR.Halted = 1
DMA Internal Error. Internal error occurs if the buffer length specified in the fetched descriptor
is set to 0. This error condition causes the AXI DMA to halt gracefully. The DMACR.RS bit is set 
to 0, and when the engine has completely shut down, the DMASR.Halted bit is set to 1.

I don't know why channel turned off and this error only occurs when I increase the speed to larger than thread hold (2Mbit/s). My code is very simple, it receives bytes from DMA and sends to another module in an infinity loop.

@jeremytrimble
Copy link
Owner

@gochit: You mentioned that you didn't know how the core's parameters were set when your FPGA image was built -- perhaps as you increase the size of your transfers you are exceeding the configured length of the transfer size register. When the upper bits of your transfer size are lost, the truncated result might be all zeros, which would cause the error you see.

In some versions of the xilinx AXI DMA driver, you can set the register length as a device tree parameter. Even if you don't know exactly how the AXI DMA's register length was configured in your FPGA image, you could always set the value in the device tree to something lower and it should still work, at the expense of using more descriptors than necessary.

@ht-hieu
Copy link
Author

ht-hieu commented Apr 18, 2017

@jeremytrimble

I still don't know why this error occurs. When I can reduce the frequency of errors by increase buffer size which is mapped to DMA. When I increased writing speed, your driver got the same error.
BTW, I replaced DMA single with DMA coherent and the performance is much better, now I can ignore this error 😀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants