// SPDX-License-Identifier: GPL-2.0+ /* * Direct Memory Access U-Class Simulation driver * * Copyright (C) 2018 Texas Instruments Incorporated * * Author: Grygorii Strashko */ #include #include #include #include #include #include #include #include #include #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), };