/*
* OMAP DMAengine support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/omap-dma.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/of_dma.h>
#include <linux/of_device.h>
#include "virt-dma.h"
#define OMAP_SDMA_REQUESTS 127
#define OMAP_SDMA_CHANNELS 32
struct omap_dmadev {
struct dma_device ddev;
spinlock_t lock;
void __iomem *base;
const struct omap_dma_reg *reg_map;
struct omap_system_dma_plat_info *plat;
bool legacy;
bool ll123_supported;
struct dma_pool *desc_pool;
unsigned dma_requests;
spinlock_t irq_lock;
uint32_t irq_enable_mask;
struct omap_chan **lch_map;
};
struct omap_chan {
struct virt_dma_chan vc;
void __iomem *channel_base;
const struct omap_dma_reg *reg_map;
uint32_t ccr;
struct dma_slave_config cfg;
unsigned dma_sig;
bool cyclic;
bool paused;
bool running;
int dma_ch;
struct omap_desc *desc;
unsigned sgidx;
};
#define DESC_NXT_SV_REFRESH (0x1 << 24)
#define DESC_NXT_SV_REUSE (0x2 << 24)
#define DESC_NXT_DV_REFRESH (0x1 << 26)
#define DESC_NXT_DV_REUSE (0x2 << 26)
#define DESC_NTYPE_TYPE2 (0x2 << 29)
/* Type 2 descriptor with Source or Destination address update */
struct omap_type2_desc {
uint32_t next_desc;
uint32_t en;
uint32_t addr; /* src or dst */
uint16_t fn;
uint16_t cicr;
int16_t cdei;
int16_t csei;
int32_t cdfi;
int32_t csfi;
} __packed;
struct omap_sg {
dma_addr_t addr;
uint32_t en; /* number of elements (24-bit) */
uint32_t fn; /* number of frames (16-bit) */
int32_t fi; /* for double indexing */
int16_t ei; /* for double indexing */
/* Linked list */
struct omap_type2_desc *t2_desc;
dma_addr_t t2_desc_paddr;
};
struct omap_desc {
struct virt_dma_desc vd;
bool using_ll;
enum dma_transfer_direction dir;
dma_addr_t dev_addr;
int32_t fi; /* for OMAP_DMA_SYNC_PACKET / double indexing */
int16_t ei; /* for double indexing */
uint8_t es; /* CSDP_DATA_TYPE_xxx */
uint32_t ccr; /* CCR value */
uint16_t clnk_ctrl; /* CLNK_CTRL value */
uint16_t cicr; /* CICR value */
uint32_t csdp; /* CSDP value */
unsigned sglen;
struct omap_sg sg[0];
};
enum {
CAPS_0_SUPPORT_LL123 = BIT(20), /* Linked List type1/2/3 */
CAPS_0_SUPPORT_LL4 = BIT(21), /* Linked List type4 */
CCR_FS = BIT(5),
CCR_READ_PRIORITY = BIT(6),
CCR_ENABLE = BIT(7),
CCR_AUTO_INIT = BIT(8), /* OMAP1 only */
CCR_REPEAT = BIT(9), /* OMAP1 only */
CCR_OMAP31_DISABLE = BIT(10), /* OMAP1 only */
CCR_SUSPEND_SENSITIVE = BIT(8), /* OMAP2+ only */
CCR_RD_ACTIVE = BIT(9), /* OMAP2+ only */
CCR_WR_ACTIVE = BIT(10), /* OMAP2+ only */
CCR_SRC_AMODE_CONSTANT = 0 << 12,
CCR_SRC_AMODE_POSTINC = 1 << 12,
CCR_SRC_AMODE_SGLIDX = 2 << 12,
CCR_SRC_AMODE_DBLIDX = 3 << 12,
CCR_DST_AMODE_CONSTANT = 0 << 14,
CCR_DST_AMODE_POSTINC = 1 << 14,
CCR_DST_AMODE_SGLIDX = 2 << 14,
CCR_DST_AMODE_DBLIDX = 3 << 14,
CCR_CONSTANT_FILL = BIT(16),
CCR_TRANSPARENT_COPY = BIT(17),
CCR_BS = BIT(18),
CCR_SUPERVISOR = BIT(22),
CCR_PREFETCH = BIT(23),
CCR_TRIGGER_SRC = BIT(24),
CCR_BUFFERING_DISABLE = BIT(25),
CCR_WRITE_PRIORITY = BIT(26),
CCR_SYNC_ELEMENT = 0,
CCR_SYNC_FRAME = CCR_FS,
CCR_SYNC_BLOCK = CCR_BS,
CCR_SYNC_PACKET = CCR_BS | CCR_FS,
CSDP_DATA_TYPE_8 = 0,
CSDP_DATA_TYPE_16 = 1,
CSDP_DATA_TYPE_32 = 2,
CSDP_SRC_PORT_EMIFF = 0 << 2, /* OMAP1 only */
CSDP_SRC_PORT_EMIFS = 1 << 2, /* OMAP1 only */
CSDP_SRC_PORT_OCP_T1 = 2 << 2, /* OMAP1 only */
CSDP_SRC_PORT_TIPB = 3 << 2, /* OMAP1 only */
CSDP_SRC_PORT_OCP_T2 = 4 << 2, /* OMAP1 only */
CSDP_SRC_PORT_MPUI = 5 << 2, /* OMAP1 only */
CSDP_SRC_PACKED = BIT(6),
CSDP_SRC_BURST_1 = 0 << 7,
CSDP_SRC_BURST_16 = 1 << 7,
CSDP_SRC_BURST_32 = 2 << 7,
CSDP_SRC_BURST_64 = 3 << 7,
CSDP_DST_PORT_EMIFF = 0 << 9, /* OMAP1 only */
CSDP_DST_PORT_EMIFS = 1 << 9, /* OMAP1 only */
CSDP_DST_PORT_OCP_T1 = 2 << 9, /* OMAP1 only */
CSDP_DST_PORT_TIPB = 3 << 9, /* OMAP1 only */
CSDP_DST_PORT_OCP_T2 = 4 << 9, /* OMAP1 only */
CSDP_DST_PORT_MPUI = 5 << 9, /* OMAP1 only */
CSDP_DST_PACKED = BIT(13),
CSDP_DST_BURST_1 = 0 << 14,
CSDP_DST_BURST_16 = 1 << 14,
CSDP_DST_BURST_32 = 2 << 14,
CSDP_DST_BURST_64 = 3 << 14,
CICR_TOUT_IE = BIT(0), /* OMAP1 only */
CICR_DROP_IE = BIT(1),
CICR_HALF_IE = BIT(2),
CICR_FRAME_IE = BIT(3),
CICR_LAST_IE = BIT(4),
CICR_BLOCK_IE = BIT(5),
CICR_PKT_IE = BIT(7), /* OMAP2+ only */
CICR_TRANS_ERR_IE = BIT(8), /* OMAP2+ only */
CICR_SUPERVISOR_ERR_IE = BIT(10), /* OMAP2+ only */
CICR_MISALIGNED_ERR_IE = BIT(11), /* OMAP2+ only */
CICR_DRAIN_IE = BIT(12), /* OMAP2+ only */
CICR_SUPER_BLOCK_IE = BIT(14), /* OMAP2+ only */
CLNK_CTRL_ENABLE_LNK = BIT(15),
CDP_DST_VALID_INC = 0 << 0,
CDP_DST_VALID_RELOAD = 1 << 0,
CDP_DST_VALID_REUSE = 2 << 0,
CDP_SRC_VALID_INC = 0 << 2,
CDP_SRC_VALID_RELOAD = 1 << 2,
CDP_SRC_VALID_REUSE = 2 << 2,
CDP_NTYPE_TYPE1 = 1 << 4,
CDP_NTYPE_TYPE2 = 2 << 4,
CDP_NTYPE_TYPE3 = 3 << 4,
CDP_TMODE_NORMAL = 0 << 8,
CDP_TMODE_LLIST = 1 << 8,
CDP_FAST = BIT(10),
};
static const unsigned es_bytes[] = {
[CSDP_DATA_TYPE_8] = 1,
[CSDP_DATA_TYPE_16] = 2,
[CSDP_DATA_TYPE_32] = 4,
};
static struct of_dma_filter_info omap_dma_info = {
.filter_fn = omap_dma_filter_fn,
};
static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
{
return container_of(d, struct omap_dmadev, ddev);
}
static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
{
return container_of(c, struct omap_chan, vc.chan);
}
static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
{
return container_of(t, struct omap_desc, vd.tx);
}
static void omap_dma_desc_free(struct virt_dma_desc *vd)
{
struct omap_desc *d = to_omap_dma_desc(&vd->tx);
if (d->using_ll) {
struct omap_dmadev *od = to_omap_dma_dev(vd->tx.chan->device);
int i;
for (i = 0; i < d->sglen; i++) {
if (d->sg[i].t2_desc)
dma_pool_free(od->desc_pool, d->sg[i].t2_desc,
d->sg[i].t2_desc_paddr);
}
}
kfree(d);
}
static void omap_dma_fill_type2_desc(struct omap_desc *d, int idx,
enum dma_transfer_direction dir, bool last)
{
struct omap_sg *sg = &d->sg[idx];
struct omap_type2_desc *t2_desc = sg->t2_desc;
if (idx)
d->sg[idx - 1].t2_desc->next_desc = sg->t2_desc_pa
|