mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-14 07:13:03 +00:00
b9add6413d
DMA operations should function on DMA addresses, not virtual addresses. Although these are usually the same in U-Boot, it is more correct to be explicit with our types here. Signed-off-by: Andrew Davis <afd@ti.com>
284 lines
5.9 KiB
C
284 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Direct Memory Access U-Class Simulation driver
|
|
*
|
|
* Copyright (C) 2018 Texas Instruments Incorporated <www.ti.com>
|
|
*
|
|
* Author: Grygorii Strashko <grygorii.strashko@ti.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <dm/read.h>
|
|
#include <dma-uclass.h>
|
|
#include <dt-structs.h>
|
|
#include <errno.h>
|
|
|
|
#define SANDBOX_DMA_CH_CNT 3
|
|
#define SANDBOX_DMA_BUF_SIZE 1024
|
|
|
|
struct sandbox_dma_chan {
|
|
struct sandbox_dma_dev *ud;
|
|
char name[20];
|
|
u32 id;
|
|
enum dma_direction dir;
|
|
bool in_use;
|
|
bool enabled;
|
|
};
|
|
|
|
struct sandbox_dma_dev {
|
|
struct device *dev;
|
|
u32 ch_count;
|
|
struct sandbox_dma_chan channels[SANDBOX_DMA_CH_CNT];
|
|
uchar buf[SANDBOX_DMA_BUF_SIZE];
|
|
uchar *buf_rx;
|
|
size_t data_len;
|
|
u32 meta;
|
|
};
|
|
|
|
static int sandbox_dma_transfer(struct udevice *dev, int direction,
|
|
dma_addr_t dst, dma_addr_t src, size_t len)
|
|
{
|
|
memcpy((void *)dst, (void *)src, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_of_xlate(struct dma *dma,
|
|
struct ofnode_phandle_args *args)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
debug("%s(dma id=%u)\n", __func__, args->args[0]);
|
|
|
|
if (args->args[0] >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
|
|
dma->id = args->args[0];
|
|
|
|
uc = &ud->channels[dma->id];
|
|
|
|
if (dma->id == 1)
|
|
uc->dir = DMA_MEM_TO_DEV;
|
|
else if (dma->id == 2)
|
|
uc->dir = DMA_DEV_TO_MEM;
|
|
else
|
|
uc->dir = DMA_MEM_TO_MEM;
|
|
debug("%s(dma id=%lu dir=%d)\n", __func__, dma->id, uc->dir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_request(struct dma *dma)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
if (dma->id >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
|
|
uc = &ud->channels[dma->id];
|
|
if (uc->in_use)
|
|
return -EBUSY;
|
|
|
|
uc->in_use = true;
|
|
debug("%s(dma id=%lu in_use=%d)\n", __func__, dma->id, uc->in_use);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_rfree(struct dma *dma)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
if (dma->id >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
|
|
uc = &ud->channels[dma->id];
|
|
if (!uc->in_use)
|
|
return -EINVAL;
|
|
|
|
uc->in_use = false;
|
|
ud->buf_rx = NULL;
|
|
ud->data_len = 0;
|
|
debug("%s(dma id=%lu in_use=%d)\n", __func__, dma->id, uc->in_use);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_enable(struct dma *dma)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
if (dma->id >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
|
|
uc = &ud->channels[dma->id];
|
|
if (!uc->in_use)
|
|
return -EINVAL;
|
|
if (uc->enabled)
|
|
return -EINVAL;
|
|
|
|
uc->enabled = true;
|
|
debug("%s(dma id=%lu enabled=%d)\n", __func__, dma->id, uc->enabled);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_disable(struct dma *dma)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
if (dma->id >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
|
|
uc = &ud->channels[dma->id];
|
|
if (!uc->in_use)
|
|
return -EINVAL;
|
|
if (!uc->enabled)
|
|
return -EINVAL;
|
|
|
|
uc->enabled = false;
|
|
debug("%s(dma id=%lu enabled=%d)\n", __func__, dma->id, uc->enabled);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_send(struct dma *dma,
|
|
void *src, size_t len, void *metadata)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
if (dma->id >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
if (!src || !metadata)
|
|
return -EINVAL;
|
|
|
|
debug("%s(dma id=%lu)\n", __func__, dma->id);
|
|
|
|
uc = &ud->channels[dma->id];
|
|
if (uc->dir != DMA_MEM_TO_DEV)
|
|
return -EINVAL;
|
|
if (!uc->in_use)
|
|
return -EINVAL;
|
|
if (!uc->enabled)
|
|
return -EINVAL;
|
|
if (len >= SANDBOX_DMA_BUF_SIZE)
|
|
return -EINVAL;
|
|
|
|
memcpy(ud->buf, src, len);
|
|
ud->data_len = len;
|
|
ud->meta = *((u32 *)metadata);
|
|
|
|
debug("%s(dma id=%lu len=%zu meta=%08x)\n",
|
|
__func__, dma->id, len, ud->meta);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sandbox_dma_receive(struct dma *dma, void **dst, void *metadata)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
struct sandbox_dma_chan *uc;
|
|
|
|
if (dma->id >= SANDBOX_DMA_CH_CNT)
|
|
return -EINVAL;
|
|
if (!dst || !metadata)
|
|
return -EINVAL;
|
|
|
|
uc = &ud->channels[dma->id];
|
|
if (uc->dir != DMA_DEV_TO_MEM)
|
|
return -EINVAL;
|
|
if (!uc->in_use)
|
|
return -EINVAL;
|
|
if (!uc->enabled)
|
|
return -EINVAL;
|
|
if (!ud->data_len)
|
|
return 0;
|
|
|
|
if (ud->buf_rx) {
|
|
memcpy(ud->buf_rx, ud->buf, ud->data_len);
|
|
*dst = ud->buf_rx;
|
|
} else {
|
|
memcpy(*dst, ud->buf, ud->data_len);
|
|
}
|
|
|
|
*((u32 *)metadata) = ud->meta;
|
|
|
|
debug("%s(dma id=%lu len=%zu meta=%08x %p)\n",
|
|
__func__, dma->id, ud->data_len, ud->meta, *dst);
|
|
|
|
return ud->data_len;
|
|
}
|
|
|
|
static int sandbox_dma_prepare_rcv_buf(struct dma *dma, void *dst, size_t size)
|
|
{
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dma->dev);
|
|
|
|
ud->buf_rx = dst;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dma_ops sandbox_dma_ops = {
|
|
.transfer = sandbox_dma_transfer,
|
|
.of_xlate = sandbox_dma_of_xlate,
|
|
.request = sandbox_dma_request,
|
|
.rfree = sandbox_dma_rfree,
|
|
.enable = sandbox_dma_enable,
|
|
.disable = sandbox_dma_disable,
|
|
.send = sandbox_dma_send,
|
|
.receive = sandbox_dma_receive,
|
|
.prepare_rcv_buf = sandbox_dma_prepare_rcv_buf,
|
|
};
|
|
|
|
static int sandbox_dma_probe(struct udevice *dev)
|
|
{
|
|
struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
struct sandbox_dma_dev *ud = dev_get_priv(dev);
|
|
int i, ret = 0;
|
|
|
|
uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM |
|
|
DMA_SUPPORTS_MEM_TO_DEV |
|
|
DMA_SUPPORTS_DEV_TO_MEM;
|
|
|
|
ud->ch_count = SANDBOX_DMA_CH_CNT;
|
|
ud->buf_rx = NULL;
|
|
ud->meta = 0;
|
|
ud->data_len = 0;
|
|
|
|
pr_err("Number of channels: %u\n", ud->ch_count);
|
|
|
|
for (i = 0; i < ud->ch_count; i++) {
|
|
struct sandbox_dma_chan *uc = &ud->channels[i];
|
|
|
|
uc->ud = ud;
|
|
uc->id = i;
|
|
sprintf(uc->name, "DMA chan%d\n", i);
|
|
uc->in_use = false;
|
|
uc->enabled = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct udevice_id sandbox_dma_ids[] = {
|
|
{ .compatible = "sandbox,dma" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(sandbox_dma) = {
|
|
.name = "sandbox-dma",
|
|
.id = UCLASS_DMA,
|
|
.of_match = sandbox_dma_ids,
|
|
.ops = &sandbox_dma_ops,
|
|
.probe = sandbox_dma_probe,
|
|
.priv_auto = sizeof(struct sandbox_dma_dev),
|
|
};
|