162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP DMAengine support 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/cpu_pm.h> 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/dmaengine.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/dmapool.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/omap-dma.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_dma.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "../virt-dma.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define OMAP_SDMA_REQUESTS 127 2562306a36Sopenharmony_ci#define OMAP_SDMA_CHANNELS 32 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct omap_dma_config { 2862306a36Sopenharmony_ci int lch_end; 2962306a36Sopenharmony_ci unsigned int rw_priority:1; 3062306a36Sopenharmony_ci unsigned int needs_busy_check:1; 3162306a36Sopenharmony_ci unsigned int may_lose_context:1; 3262306a36Sopenharmony_ci unsigned int needs_lch_clear:1; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct omap_dma_context { 3662306a36Sopenharmony_ci u32 irqenable_l0; 3762306a36Sopenharmony_ci u32 irqenable_l1; 3862306a36Sopenharmony_ci u32 ocp_sysconfig; 3962306a36Sopenharmony_ci u32 gcr; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct omap_dmadev { 4362306a36Sopenharmony_ci struct dma_device ddev; 4462306a36Sopenharmony_ci spinlock_t lock; 4562306a36Sopenharmony_ci void __iomem *base; 4662306a36Sopenharmony_ci const struct omap_dma_reg *reg_map; 4762306a36Sopenharmony_ci struct omap_system_dma_plat_info *plat; 4862306a36Sopenharmony_ci const struct omap_dma_config *cfg; 4962306a36Sopenharmony_ci struct notifier_block nb; 5062306a36Sopenharmony_ci struct omap_dma_context context; 5162306a36Sopenharmony_ci int lch_count; 5262306a36Sopenharmony_ci DECLARE_BITMAP(lch_bitmap, OMAP_SDMA_CHANNELS); 5362306a36Sopenharmony_ci struct mutex lch_lock; /* for assigning logical channels */ 5462306a36Sopenharmony_ci bool legacy; 5562306a36Sopenharmony_ci bool ll123_supported; 5662306a36Sopenharmony_ci struct dma_pool *desc_pool; 5762306a36Sopenharmony_ci unsigned dma_requests; 5862306a36Sopenharmony_ci spinlock_t irq_lock; 5962306a36Sopenharmony_ci uint32_t irq_enable_mask; 6062306a36Sopenharmony_ci struct omap_chan **lch_map; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct omap_chan { 6462306a36Sopenharmony_ci struct virt_dma_chan vc; 6562306a36Sopenharmony_ci void __iomem *channel_base; 6662306a36Sopenharmony_ci const struct omap_dma_reg *reg_map; 6762306a36Sopenharmony_ci uint32_t ccr; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci struct dma_slave_config cfg; 7062306a36Sopenharmony_ci unsigned dma_sig; 7162306a36Sopenharmony_ci bool cyclic; 7262306a36Sopenharmony_ci bool paused; 7362306a36Sopenharmony_ci bool running; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci int dma_ch; 7662306a36Sopenharmony_ci struct omap_desc *desc; 7762306a36Sopenharmony_ci unsigned sgidx; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define DESC_NXT_SV_REFRESH (0x1 << 24) 8162306a36Sopenharmony_ci#define DESC_NXT_SV_REUSE (0x2 << 24) 8262306a36Sopenharmony_ci#define DESC_NXT_DV_REFRESH (0x1 << 26) 8362306a36Sopenharmony_ci#define DESC_NXT_DV_REUSE (0x2 << 26) 8462306a36Sopenharmony_ci#define DESC_NTYPE_TYPE2 (0x2 << 29) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Type 2 descriptor with Source or Destination address update */ 8762306a36Sopenharmony_cistruct omap_type2_desc { 8862306a36Sopenharmony_ci uint32_t next_desc; 8962306a36Sopenharmony_ci uint32_t en; 9062306a36Sopenharmony_ci uint32_t addr; /* src or dst */ 9162306a36Sopenharmony_ci uint16_t fn; 9262306a36Sopenharmony_ci uint16_t cicr; 9362306a36Sopenharmony_ci int16_t cdei; 9462306a36Sopenharmony_ci int16_t csei; 9562306a36Sopenharmony_ci int32_t cdfi; 9662306a36Sopenharmony_ci int32_t csfi; 9762306a36Sopenharmony_ci} __packed; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct omap_sg { 10062306a36Sopenharmony_ci dma_addr_t addr; 10162306a36Sopenharmony_ci uint32_t en; /* number of elements (24-bit) */ 10262306a36Sopenharmony_ci uint32_t fn; /* number of frames (16-bit) */ 10362306a36Sopenharmony_ci int32_t fi; /* for double indexing */ 10462306a36Sopenharmony_ci int16_t ei; /* for double indexing */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Linked list */ 10762306a36Sopenharmony_ci struct omap_type2_desc *t2_desc; 10862306a36Sopenharmony_ci dma_addr_t t2_desc_paddr; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct omap_desc { 11262306a36Sopenharmony_ci struct virt_dma_desc vd; 11362306a36Sopenharmony_ci bool using_ll; 11462306a36Sopenharmony_ci enum dma_transfer_direction dir; 11562306a36Sopenharmony_ci dma_addr_t dev_addr; 11662306a36Sopenharmony_ci bool polled; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci int32_t fi; /* for OMAP_DMA_SYNC_PACKET / double indexing */ 11962306a36Sopenharmony_ci int16_t ei; /* for double indexing */ 12062306a36Sopenharmony_ci uint8_t es; /* CSDP_DATA_TYPE_xxx */ 12162306a36Sopenharmony_ci uint32_t ccr; /* CCR value */ 12262306a36Sopenharmony_ci uint16_t clnk_ctrl; /* CLNK_CTRL value */ 12362306a36Sopenharmony_ci uint16_t cicr; /* CICR value */ 12462306a36Sopenharmony_ci uint32_t csdp; /* CSDP value */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci unsigned sglen; 12762306a36Sopenharmony_ci struct omap_sg sg[]; 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cienum { 13162306a36Sopenharmony_ci CAPS_0_SUPPORT_LL123 = BIT(20), /* Linked List type1/2/3 */ 13262306a36Sopenharmony_ci CAPS_0_SUPPORT_LL4 = BIT(21), /* Linked List type4 */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci CCR_FS = BIT(5), 13562306a36Sopenharmony_ci CCR_READ_PRIORITY = BIT(6), 13662306a36Sopenharmony_ci CCR_ENABLE = BIT(7), 13762306a36Sopenharmony_ci CCR_AUTO_INIT = BIT(8), /* OMAP1 only */ 13862306a36Sopenharmony_ci CCR_REPEAT = BIT(9), /* OMAP1 only */ 13962306a36Sopenharmony_ci CCR_OMAP31_DISABLE = BIT(10), /* OMAP1 only */ 14062306a36Sopenharmony_ci CCR_SUSPEND_SENSITIVE = BIT(8), /* OMAP2+ only */ 14162306a36Sopenharmony_ci CCR_RD_ACTIVE = BIT(9), /* OMAP2+ only */ 14262306a36Sopenharmony_ci CCR_WR_ACTIVE = BIT(10), /* OMAP2+ only */ 14362306a36Sopenharmony_ci CCR_SRC_AMODE_CONSTANT = 0 << 12, 14462306a36Sopenharmony_ci CCR_SRC_AMODE_POSTINC = 1 << 12, 14562306a36Sopenharmony_ci CCR_SRC_AMODE_SGLIDX = 2 << 12, 14662306a36Sopenharmony_ci CCR_SRC_AMODE_DBLIDX = 3 << 12, 14762306a36Sopenharmony_ci CCR_DST_AMODE_CONSTANT = 0 << 14, 14862306a36Sopenharmony_ci CCR_DST_AMODE_POSTINC = 1 << 14, 14962306a36Sopenharmony_ci CCR_DST_AMODE_SGLIDX = 2 << 14, 15062306a36Sopenharmony_ci CCR_DST_AMODE_DBLIDX = 3 << 14, 15162306a36Sopenharmony_ci CCR_CONSTANT_FILL = BIT(16), 15262306a36Sopenharmony_ci CCR_TRANSPARENT_COPY = BIT(17), 15362306a36Sopenharmony_ci CCR_BS = BIT(18), 15462306a36Sopenharmony_ci CCR_SUPERVISOR = BIT(22), 15562306a36Sopenharmony_ci CCR_PREFETCH = BIT(23), 15662306a36Sopenharmony_ci CCR_TRIGGER_SRC = BIT(24), 15762306a36Sopenharmony_ci CCR_BUFFERING_DISABLE = BIT(25), 15862306a36Sopenharmony_ci CCR_WRITE_PRIORITY = BIT(26), 15962306a36Sopenharmony_ci CCR_SYNC_ELEMENT = 0, 16062306a36Sopenharmony_ci CCR_SYNC_FRAME = CCR_FS, 16162306a36Sopenharmony_ci CCR_SYNC_BLOCK = CCR_BS, 16262306a36Sopenharmony_ci CCR_SYNC_PACKET = CCR_BS | CCR_FS, 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci CSDP_DATA_TYPE_8 = 0, 16562306a36Sopenharmony_ci CSDP_DATA_TYPE_16 = 1, 16662306a36Sopenharmony_ci CSDP_DATA_TYPE_32 = 2, 16762306a36Sopenharmony_ci CSDP_SRC_PORT_EMIFF = 0 << 2, /* OMAP1 only */ 16862306a36Sopenharmony_ci CSDP_SRC_PORT_EMIFS = 1 << 2, /* OMAP1 only */ 16962306a36Sopenharmony_ci CSDP_SRC_PORT_OCP_T1 = 2 << 2, /* OMAP1 only */ 17062306a36Sopenharmony_ci CSDP_SRC_PORT_TIPB = 3 << 2, /* OMAP1 only */ 17162306a36Sopenharmony_ci CSDP_SRC_PORT_OCP_T2 = 4 << 2, /* OMAP1 only */ 17262306a36Sopenharmony_ci CSDP_SRC_PORT_MPUI = 5 << 2, /* OMAP1 only */ 17362306a36Sopenharmony_ci CSDP_SRC_PACKED = BIT(6), 17462306a36Sopenharmony_ci CSDP_SRC_BURST_1 = 0 << 7, 17562306a36Sopenharmony_ci CSDP_SRC_BURST_16 = 1 << 7, 17662306a36Sopenharmony_ci CSDP_SRC_BURST_32 = 2 << 7, 17762306a36Sopenharmony_ci CSDP_SRC_BURST_64 = 3 << 7, 17862306a36Sopenharmony_ci CSDP_DST_PORT_EMIFF = 0 << 9, /* OMAP1 only */ 17962306a36Sopenharmony_ci CSDP_DST_PORT_EMIFS = 1 << 9, /* OMAP1 only */ 18062306a36Sopenharmony_ci CSDP_DST_PORT_OCP_T1 = 2 << 9, /* OMAP1 only */ 18162306a36Sopenharmony_ci CSDP_DST_PORT_TIPB = 3 << 9, /* OMAP1 only */ 18262306a36Sopenharmony_ci CSDP_DST_PORT_OCP_T2 = 4 << 9, /* OMAP1 only */ 18362306a36Sopenharmony_ci CSDP_DST_PORT_MPUI = 5 << 9, /* OMAP1 only */ 18462306a36Sopenharmony_ci CSDP_DST_PACKED = BIT(13), 18562306a36Sopenharmony_ci CSDP_DST_BURST_1 = 0 << 14, 18662306a36Sopenharmony_ci CSDP_DST_BURST_16 = 1 << 14, 18762306a36Sopenharmony_ci CSDP_DST_BURST_32 = 2 << 14, 18862306a36Sopenharmony_ci CSDP_DST_BURST_64 = 3 << 14, 18962306a36Sopenharmony_ci CSDP_WRITE_NON_POSTED = 0 << 16, 19062306a36Sopenharmony_ci CSDP_WRITE_POSTED = 1 << 16, 19162306a36Sopenharmony_ci CSDP_WRITE_LAST_NON_POSTED = 2 << 16, 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci CICR_TOUT_IE = BIT(0), /* OMAP1 only */ 19462306a36Sopenharmony_ci CICR_DROP_IE = BIT(1), 19562306a36Sopenharmony_ci CICR_HALF_IE = BIT(2), 19662306a36Sopenharmony_ci CICR_FRAME_IE = BIT(3), 19762306a36Sopenharmony_ci CICR_LAST_IE = BIT(4), 19862306a36Sopenharmony_ci CICR_BLOCK_IE = BIT(5), 19962306a36Sopenharmony_ci CICR_PKT_IE = BIT(7), /* OMAP2+ only */ 20062306a36Sopenharmony_ci CICR_TRANS_ERR_IE = BIT(8), /* OMAP2+ only */ 20162306a36Sopenharmony_ci CICR_SUPERVISOR_ERR_IE = BIT(10), /* OMAP2+ only */ 20262306a36Sopenharmony_ci CICR_MISALIGNED_ERR_IE = BIT(11), /* OMAP2+ only */ 20362306a36Sopenharmony_ci CICR_DRAIN_IE = BIT(12), /* OMAP2+ only */ 20462306a36Sopenharmony_ci CICR_SUPER_BLOCK_IE = BIT(14), /* OMAP2+ only */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci CLNK_CTRL_ENABLE_LNK = BIT(15), 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci CDP_DST_VALID_INC = 0 << 0, 20962306a36Sopenharmony_ci CDP_DST_VALID_RELOAD = 1 << 0, 21062306a36Sopenharmony_ci CDP_DST_VALID_REUSE = 2 << 0, 21162306a36Sopenharmony_ci CDP_SRC_VALID_INC = 0 << 2, 21262306a36Sopenharmony_ci CDP_SRC_VALID_RELOAD = 1 << 2, 21362306a36Sopenharmony_ci CDP_SRC_VALID_REUSE = 2 << 2, 21462306a36Sopenharmony_ci CDP_NTYPE_TYPE1 = 1 << 4, 21562306a36Sopenharmony_ci CDP_NTYPE_TYPE2 = 2 << 4, 21662306a36Sopenharmony_ci CDP_NTYPE_TYPE3 = 3 << 4, 21762306a36Sopenharmony_ci CDP_TMODE_NORMAL = 0 << 8, 21862306a36Sopenharmony_ci CDP_TMODE_LLIST = 1 << 8, 21962306a36Sopenharmony_ci CDP_FAST = BIT(10), 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const unsigned es_bytes[] = { 22362306a36Sopenharmony_ci [CSDP_DATA_TYPE_8] = 1, 22462306a36Sopenharmony_ci [CSDP_DATA_TYPE_16] = 2, 22562306a36Sopenharmony_ci [CSDP_DATA_TYPE_32] = 4, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param); 22962306a36Sopenharmony_cistatic struct of_dma_filter_info omap_dma_info = { 23062306a36Sopenharmony_ci .filter_fn = omap_dma_filter_fn, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci return container_of(d, struct omap_dmadev, ddev); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci return container_of(c, struct omap_chan, vc.chan); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci return container_of(t, struct omap_desc, vd.tx); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void omap_dma_desc_free(struct virt_dma_desc *vd) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct omap_desc *d = to_omap_dma_desc(&vd->tx); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (d->using_ll) { 25362306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(vd->tx.chan->device); 25462306a36Sopenharmony_ci int i; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci for (i = 0; i < d->sglen; i++) { 25762306a36Sopenharmony_ci if (d->sg[i].t2_desc) 25862306a36Sopenharmony_ci dma_pool_free(od->desc_pool, d->sg[i].t2_desc, 25962306a36Sopenharmony_ci d->sg[i].t2_desc_paddr); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci kfree(d); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void omap_dma_fill_type2_desc(struct omap_desc *d, int idx, 26762306a36Sopenharmony_ci enum dma_transfer_direction dir, bool last) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct omap_sg *sg = &d->sg[idx]; 27062306a36Sopenharmony_ci struct omap_type2_desc *t2_desc = sg->t2_desc; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (idx) 27362306a36Sopenharmony_ci d->sg[idx - 1].t2_desc->next_desc = sg->t2_desc_paddr; 27462306a36Sopenharmony_ci if (last) 27562306a36Sopenharmony_ci t2_desc->next_desc = 0xfffffffc; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci t2_desc->en = sg->en; 27862306a36Sopenharmony_ci t2_desc->addr = sg->addr; 27962306a36Sopenharmony_ci t2_desc->fn = sg->fn & 0xffff; 28062306a36Sopenharmony_ci t2_desc->cicr = d->cicr; 28162306a36Sopenharmony_ci if (!last) 28262306a36Sopenharmony_ci t2_desc->cicr &= ~CICR_BLOCK_IE; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci switch (dir) { 28562306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 28662306a36Sopenharmony_ci t2_desc->cdei = sg->ei; 28762306a36Sopenharmony_ci t2_desc->csei = d->ei; 28862306a36Sopenharmony_ci t2_desc->cdfi = sg->fi; 28962306a36Sopenharmony_ci t2_desc->csfi = d->fi; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci t2_desc->en |= DESC_NXT_DV_REFRESH; 29262306a36Sopenharmony_ci t2_desc->en |= DESC_NXT_SV_REUSE; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 29562306a36Sopenharmony_ci t2_desc->cdei = d->ei; 29662306a36Sopenharmony_ci t2_desc->csei = sg->ei; 29762306a36Sopenharmony_ci t2_desc->cdfi = d->fi; 29862306a36Sopenharmony_ci t2_desc->csfi = sg->fi; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci t2_desc->en |= DESC_NXT_SV_REFRESH; 30162306a36Sopenharmony_ci t2_desc->en |= DESC_NXT_DV_REUSE; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci default: 30462306a36Sopenharmony_ci return; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci t2_desc->en |= DESC_NTYPE_TYPE2; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void omap_dma_write(uint32_t val, unsigned type, void __iomem *addr) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci switch (type) { 31362306a36Sopenharmony_ci case OMAP_DMA_REG_16BIT: 31462306a36Sopenharmony_ci writew_relaxed(val, addr); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci case OMAP_DMA_REG_2X16BIT: 31762306a36Sopenharmony_ci writew_relaxed(val, addr); 31862306a36Sopenharmony_ci writew_relaxed(val >> 16, addr + 2); 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case OMAP_DMA_REG_32BIT: 32162306a36Sopenharmony_ci writel_relaxed(val, addr); 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci default: 32462306a36Sopenharmony_ci WARN_ON(1); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic unsigned omap_dma_read(unsigned type, void __iomem *addr) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci unsigned val; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci switch (type) { 33362306a36Sopenharmony_ci case OMAP_DMA_REG_16BIT: 33462306a36Sopenharmony_ci val = readw_relaxed(addr); 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case OMAP_DMA_REG_2X16BIT: 33762306a36Sopenharmony_ci val = readw_relaxed(addr); 33862306a36Sopenharmony_ci val |= readw_relaxed(addr + 2) << 16; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case OMAP_DMA_REG_32BIT: 34162306a36Sopenharmony_ci val = readl_relaxed(addr); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci default: 34462306a36Sopenharmony_ci WARN_ON(1); 34562306a36Sopenharmony_ci val = 0; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return val; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void omap_dma_glbl_write(struct omap_dmadev *od, unsigned reg, unsigned val) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci const struct omap_dma_reg *r = od->reg_map + reg; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci WARN_ON(r->stride); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci omap_dma_write(val, r->type, od->base + r->offset); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic unsigned omap_dma_glbl_read(struct omap_dmadev *od, unsigned reg) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci const struct omap_dma_reg *r = od->reg_map + reg; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci WARN_ON(r->stride); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return omap_dma_read(r->type, od->base + r->offset); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void omap_dma_chan_write(struct omap_chan *c, unsigned reg, unsigned val) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci const struct omap_dma_reg *r = c->reg_map + reg; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci omap_dma_write(val, r->type, c->channel_base + r->offset); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic unsigned omap_dma_chan_read(struct omap_chan *c, unsigned reg) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci const struct omap_dma_reg *r = c->reg_map + reg; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return omap_dma_read(r->type, c->channel_base + r->offset); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void omap_dma_clear_csr(struct omap_chan *c) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci if (dma_omap1()) 38662306a36Sopenharmony_ci omap_dma_chan_read(c, CSR); 38762306a36Sopenharmony_ci else 38862306a36Sopenharmony_ci omap_dma_chan_write(c, CSR, ~0); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic unsigned omap_dma_get_csr(struct omap_chan *c) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci unsigned val = omap_dma_chan_read(c, CSR); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (!dma_omap1()) 39662306a36Sopenharmony_ci omap_dma_chan_write(c, CSR, val); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return val; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void omap_dma_clear_lch(struct omap_dmadev *od, int lch) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct omap_chan *c; 40462306a36Sopenharmony_ci int i; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci c = od->lch_map[lch]; 40762306a36Sopenharmony_ci if (!c) 40862306a36Sopenharmony_ci return; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci for (i = CSDP; i <= od->cfg->lch_end; i++) 41162306a36Sopenharmony_ci omap_dma_chan_write(c, i, 0); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c, 41562306a36Sopenharmony_ci unsigned lch) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci c->channel_base = od->base + od->plat->channel_stride * lch; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci od->lch_map[lch] = c; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void omap_dma_start(struct omap_chan *c, struct omap_desc *d) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 42562306a36Sopenharmony_ci uint16_t cicr = d->cicr; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) 42862306a36Sopenharmony_ci omap_dma_chan_write(c, CPC, 0); 42962306a36Sopenharmony_ci else 43062306a36Sopenharmony_ci omap_dma_chan_write(c, CDAC, 0); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci omap_dma_clear_csr(c); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (d->using_ll) { 43562306a36Sopenharmony_ci uint32_t cdp = CDP_TMODE_LLIST | CDP_NTYPE_TYPE2 | CDP_FAST; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (d->dir == DMA_DEV_TO_MEM) 43862306a36Sopenharmony_ci cdp |= (CDP_DST_VALID_RELOAD | CDP_SRC_VALID_REUSE); 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci cdp |= (CDP_DST_VALID_REUSE | CDP_SRC_VALID_RELOAD); 44162306a36Sopenharmony_ci omap_dma_chan_write(c, CDP, cdp); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci omap_dma_chan_write(c, CNDP, d->sg[0].t2_desc_paddr); 44462306a36Sopenharmony_ci omap_dma_chan_write(c, CCDN, 0); 44562306a36Sopenharmony_ci omap_dma_chan_write(c, CCFN, 0xffff); 44662306a36Sopenharmony_ci omap_dma_chan_write(c, CCEN, 0xffffff); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci cicr &= ~CICR_BLOCK_IE; 44962306a36Sopenharmony_ci } else if (od->ll123_supported) { 45062306a36Sopenharmony_ci omap_dma_chan_write(c, CDP, 0); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Enable interrupts */ 45462306a36Sopenharmony_ci omap_dma_chan_write(c, CICR, cicr); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Enable channel */ 45762306a36Sopenharmony_ci omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci c->running = true; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void omap_dma_drain_chan(struct omap_chan *c) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci int i; 46562306a36Sopenharmony_ci u32 val; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Wait for sDMA FIFO to drain */ 46862306a36Sopenharmony_ci for (i = 0; ; i++) { 46962306a36Sopenharmony_ci val = omap_dma_chan_read(c, CCR); 47062306a36Sopenharmony_ci if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))) 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (i > 100) 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci udelay(5); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)) 48062306a36Sopenharmony_ci dev_err(c->vc.chan.device->dev, 48162306a36Sopenharmony_ci "DMA drain did not complete on lch %d\n", 48262306a36Sopenharmony_ci c->dma_ch); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int omap_dma_stop(struct omap_chan *c) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 48862306a36Sopenharmony_ci uint32_t val; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* disable irq */ 49162306a36Sopenharmony_ci omap_dma_chan_write(c, CICR, 0); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci omap_dma_clear_csr(c); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci val = omap_dma_chan_read(c, CCR); 49662306a36Sopenharmony_ci if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) { 49762306a36Sopenharmony_ci uint32_t sysconfig; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG); 50062306a36Sopenharmony_ci val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK; 50162306a36Sopenharmony_ci val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE); 50262306a36Sopenharmony_ci omap_dma_glbl_write(od, OCP_SYSCONFIG, val); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci val = omap_dma_chan_read(c, CCR); 50562306a36Sopenharmony_ci val &= ~CCR_ENABLE; 50662306a36Sopenharmony_ci omap_dma_chan_write(c, CCR, val); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!(c->ccr & CCR_BUFFERING_DISABLE)) 50962306a36Sopenharmony_ci omap_dma_drain_chan(c); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig); 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci if (!(val & CCR_ENABLE)) 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci val &= ~CCR_ENABLE; 51762306a36Sopenharmony_ci omap_dma_chan_write(c, CCR, val); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (!(c->ccr & CCR_BUFFERING_DISABLE)) 52062306a36Sopenharmony_ci omap_dma_drain_chan(c); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mb(); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) { 52662306a36Sopenharmony_ci val = omap_dma_chan_read(c, CLNK_CTRL); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (dma_omap1()) 52962306a36Sopenharmony_ci val |= 1 << 14; /* set the STOP_LNK bit */ 53062306a36Sopenharmony_ci else 53162306a36Sopenharmony_ci val &= ~CLNK_CTRL_ENABLE_LNK; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci omap_dma_chan_write(c, CLNK_CTRL, val); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci c->running = false; 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct omap_sg *sg = d->sg + c->sgidx; 54262306a36Sopenharmony_ci unsigned cxsa, cxei, cxfi; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) { 54562306a36Sopenharmony_ci cxsa = CDSA; 54662306a36Sopenharmony_ci cxei = CDEI; 54762306a36Sopenharmony_ci cxfi = CDFI; 54862306a36Sopenharmony_ci } else { 54962306a36Sopenharmony_ci cxsa = CSSA; 55062306a36Sopenharmony_ci cxei = CSEI; 55162306a36Sopenharmony_ci cxfi = CSFI; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci omap_dma_chan_write(c, cxsa, sg->addr); 55562306a36Sopenharmony_ci omap_dma_chan_write(c, cxei, sg->ei); 55662306a36Sopenharmony_ci omap_dma_chan_write(c, cxfi, sg->fi); 55762306a36Sopenharmony_ci omap_dma_chan_write(c, CEN, sg->en); 55862306a36Sopenharmony_ci omap_dma_chan_write(c, CFN, sg->fn); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci omap_dma_start(c, d); 56162306a36Sopenharmony_ci c->sgidx++; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic void omap_dma_start_desc(struct omap_chan *c) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct virt_dma_desc *vd = vchan_next_desc(&c->vc); 56762306a36Sopenharmony_ci struct omap_desc *d; 56862306a36Sopenharmony_ci unsigned cxsa, cxei, cxfi; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (!vd) { 57162306a36Sopenharmony_ci c->desc = NULL; 57262306a36Sopenharmony_ci return; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci list_del(&vd->node); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci c->desc = d = to_omap_dma_desc(&vd->tx); 57862306a36Sopenharmony_ci c->sgidx = 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * This provides the necessary barrier to ensure data held in 58262306a36Sopenharmony_ci * DMA coherent memory is visible to the DMA engine prior to 58362306a36Sopenharmony_ci * the transfer starting. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci mb(); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci omap_dma_chan_write(c, CCR, d->ccr); 58862306a36Sopenharmony_ci if (dma_omap1()) 58962306a36Sopenharmony_ci omap_dma_chan_write(c, CCR2, d->ccr >> 16); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) { 59262306a36Sopenharmony_ci cxsa = CSSA; 59362306a36Sopenharmony_ci cxei = CSEI; 59462306a36Sopenharmony_ci cxfi = CSFI; 59562306a36Sopenharmony_ci } else { 59662306a36Sopenharmony_ci cxsa = CDSA; 59762306a36Sopenharmony_ci cxei = CDEI; 59862306a36Sopenharmony_ci cxfi = CDFI; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci omap_dma_chan_write(c, cxsa, d->dev_addr); 60262306a36Sopenharmony_ci omap_dma_chan_write(c, cxei, d->ei); 60362306a36Sopenharmony_ci omap_dma_chan_write(c, cxfi, d->fi); 60462306a36Sopenharmony_ci omap_dma_chan_write(c, CSDP, d->csdp); 60562306a36Sopenharmony_ci omap_dma_chan_write(c, CLNK_CTRL, d->clnk_ctrl); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci omap_dma_start_sg(c, d); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void omap_dma_callback(int ch, u16 status, void *data) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct omap_chan *c = data; 61362306a36Sopenharmony_ci struct omap_desc *d; 61462306a36Sopenharmony_ci unsigned long flags; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 61762306a36Sopenharmony_ci d = c->desc; 61862306a36Sopenharmony_ci if (d) { 61962306a36Sopenharmony_ci if (c->cyclic) { 62062306a36Sopenharmony_ci vchan_cyclic_callback(&d->vd); 62162306a36Sopenharmony_ci } else if (d->using_ll || c->sgidx == d->sglen) { 62262306a36Sopenharmony_ci omap_dma_start_desc(c); 62362306a36Sopenharmony_ci vchan_cookie_complete(&d->vd); 62462306a36Sopenharmony_ci } else { 62562306a36Sopenharmony_ci omap_dma_start_sg(c, d); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic irqreturn_t omap_dma_irq(int irq, void *devid) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct omap_dmadev *od = devid; 63462306a36Sopenharmony_ci unsigned status, channel; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci spin_lock(&od->irq_lock); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci status = omap_dma_glbl_read(od, IRQSTATUS_L1); 63962306a36Sopenharmony_ci status &= od->irq_enable_mask; 64062306a36Sopenharmony_ci if (status == 0) { 64162306a36Sopenharmony_ci spin_unlock(&od->irq_lock); 64262306a36Sopenharmony_ci return IRQ_NONE; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci while ((channel = ffs(status)) != 0) { 64662306a36Sopenharmony_ci unsigned mask, csr; 64762306a36Sopenharmony_ci struct omap_chan *c; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci channel -= 1; 65062306a36Sopenharmony_ci mask = BIT(channel); 65162306a36Sopenharmony_ci status &= ~mask; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci c = od->lch_map[channel]; 65462306a36Sopenharmony_ci if (c == NULL) { 65562306a36Sopenharmony_ci /* This should never happen */ 65662306a36Sopenharmony_ci dev_err(od->ddev.dev, "invalid channel %u\n", channel); 65762306a36Sopenharmony_ci continue; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci csr = omap_dma_get_csr(c); 66162306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQSTATUS_L1, mask); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci omap_dma_callback(channel, csr, c); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci spin_unlock(&od->irq_lock); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci return IRQ_HANDLED; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int omap_dma_get_lch(struct omap_dmadev *od, int *lch) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci int channel; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci mutex_lock(&od->lch_lock); 67662306a36Sopenharmony_ci channel = find_first_zero_bit(od->lch_bitmap, od->lch_count); 67762306a36Sopenharmony_ci if (channel >= od->lch_count) 67862306a36Sopenharmony_ci goto out_busy; 67962306a36Sopenharmony_ci set_bit(channel, od->lch_bitmap); 68062306a36Sopenharmony_ci mutex_unlock(&od->lch_lock); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci omap_dma_clear_lch(od, channel); 68362306a36Sopenharmony_ci *lch = channel; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciout_busy: 68862306a36Sopenharmony_ci mutex_unlock(&od->lch_lock); 68962306a36Sopenharmony_ci *lch = -EINVAL; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return -EBUSY; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic void omap_dma_put_lch(struct omap_dmadev *od, int lch) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci omap_dma_clear_lch(od, lch); 69762306a36Sopenharmony_ci mutex_lock(&od->lch_lock); 69862306a36Sopenharmony_ci clear_bit(lch, od->lch_bitmap); 69962306a36Sopenharmony_ci mutex_unlock(&od->lch_lock); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic inline bool omap_dma_legacy(struct omap_dmadev *od) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci return IS_ENABLED(CONFIG_ARCH_OMAP1) && od->legacy; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int omap_dma_alloc_chan_resources(struct dma_chan *chan) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 71062306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 71162306a36Sopenharmony_ci struct device *dev = od->ddev.dev; 71262306a36Sopenharmony_ci int ret; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (omap_dma_legacy(od)) { 71562306a36Sopenharmony_ci ret = omap_request_dma(c->dma_sig, "DMA engine", 71662306a36Sopenharmony_ci omap_dma_callback, c, &c->dma_ch); 71762306a36Sopenharmony_ci } else { 71862306a36Sopenharmony_ci ret = omap_dma_get_lch(od, &c->dma_ch); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci dev_dbg(dev, "allocating channel %u for %u\n", c->dma_ch, c->dma_sig); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (ret >= 0) { 72462306a36Sopenharmony_ci omap_dma_assign(od, c, c->dma_ch); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (!omap_dma_legacy(od)) { 72762306a36Sopenharmony_ci unsigned val; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci spin_lock_irq(&od->irq_lock); 73062306a36Sopenharmony_ci val = BIT(c->dma_ch); 73162306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQSTATUS_L1, val); 73262306a36Sopenharmony_ci od->irq_enable_mask |= val; 73362306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci val = omap_dma_glbl_read(od, IRQENABLE_L0); 73662306a36Sopenharmony_ci val &= ~BIT(c->dma_ch); 73762306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L0, val); 73862306a36Sopenharmony_ci spin_unlock_irq(&od->irq_lock); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (dma_omap1()) { 74362306a36Sopenharmony_ci if (__dma_omap16xx(od->plat->dma_attr)) { 74462306a36Sopenharmony_ci c->ccr = CCR_OMAP31_DISABLE; 74562306a36Sopenharmony_ci /* Duplicate what plat-omap/dma.c does */ 74662306a36Sopenharmony_ci c->ccr |= c->dma_ch + 1; 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci c->ccr = c->dma_sig & 0x1f; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci } else { 75162306a36Sopenharmony_ci c->ccr = c->dma_sig & 0x1f; 75262306a36Sopenharmony_ci c->ccr |= (c->dma_sig & ~0x1f) << 14; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING) 75562306a36Sopenharmony_ci c->ccr |= CCR_BUFFERING_DISABLE; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return ret; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic void omap_dma_free_chan_resources(struct dma_chan *chan) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 76362306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!omap_dma_legacy(od)) { 76662306a36Sopenharmony_ci spin_lock_irq(&od->irq_lock); 76762306a36Sopenharmony_ci od->irq_enable_mask &= ~BIT(c->dma_ch); 76862306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask); 76962306a36Sopenharmony_ci spin_unlock_irq(&od->irq_lock); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci c->channel_base = NULL; 77362306a36Sopenharmony_ci od->lch_map[c->dma_ch] = NULL; 77462306a36Sopenharmony_ci vchan_free_chan_resources(&c->vc); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (omap_dma_legacy(od)) 77762306a36Sopenharmony_ci omap_free_dma(c->dma_ch); 77862306a36Sopenharmony_ci else 77962306a36Sopenharmony_ci omap_dma_put_lch(od, c->dma_ch); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dev_dbg(od->ddev.dev, "freeing channel %u used for %u\n", c->dma_ch, 78262306a36Sopenharmony_ci c->dma_sig); 78362306a36Sopenharmony_ci c->dma_sig = 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic size_t omap_dma_sg_size(struct omap_sg *sg) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci return sg->en * sg->fn; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic size_t omap_dma_desc_size(struct omap_desc *d) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci unsigned i; 79462306a36Sopenharmony_ci size_t size; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci for (size = i = 0; i < d->sglen; i++) 79762306a36Sopenharmony_ci size += omap_dma_sg_size(&d->sg[i]); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return size * es_bytes[d->es]; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci unsigned i; 80562306a36Sopenharmony_ci size_t size, es_size = es_bytes[d->es]; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci for (size = i = 0; i < d->sglen; i++) { 80862306a36Sopenharmony_ci size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (size) 81162306a36Sopenharmony_ci size += this_size; 81262306a36Sopenharmony_ci else if (addr >= d->sg[i].addr && 81362306a36Sopenharmony_ci addr < d->sg[i].addr + this_size) 81462306a36Sopenharmony_ci size += d->sg[i].addr + this_size - addr; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci return size; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* 82062306a36Sopenharmony_ci * OMAP 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is 82162306a36Sopenharmony_ci * read before the DMA controller finished disabling the channel. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_cistatic uint32_t omap_dma_chan_read_3_3(struct omap_chan *c, unsigned reg) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 82662306a36Sopenharmony_ci uint32_t val; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci val = omap_dma_chan_read(c, reg); 82962306a36Sopenharmony_ci if (val == 0 && od->plat->errata & DMA_ERRATA_3_3) 83062306a36Sopenharmony_ci val = omap_dma_chan_read(c, reg); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return val; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic dma_addr_t omap_dma_get_src_pos(struct omap_chan *c) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 83862306a36Sopenharmony_ci dma_addr_t addr, cdac; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) { 84162306a36Sopenharmony_ci addr = omap_dma_chan_read(c, CPC); 84262306a36Sopenharmony_ci } else { 84362306a36Sopenharmony_ci addr = omap_dma_chan_read_3_3(c, CSAC); 84462306a36Sopenharmony_ci cdac = omap_dma_chan_read_3_3(c, CDAC); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* 84762306a36Sopenharmony_ci * CDAC == 0 indicates that the DMA transfer on the channel has 84862306a36Sopenharmony_ci * not been started (no data has been transferred so far). 84962306a36Sopenharmony_ci * Return the programmed source start address in this case. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci if (cdac == 0) 85262306a36Sopenharmony_ci addr = omap_dma_chan_read(c, CSSA); 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (dma_omap1()) 85662306a36Sopenharmony_ci addr |= omap_dma_chan_read(c, CSSA) & 0xffff0000; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return addr; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic dma_addr_t omap_dma_get_dst_pos(struct omap_chan *c) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device); 86462306a36Sopenharmony_ci dma_addr_t addr; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) { 86762306a36Sopenharmony_ci addr = omap_dma_chan_read(c, CPC); 86862306a36Sopenharmony_ci } else { 86962306a36Sopenharmony_ci addr = omap_dma_chan_read_3_3(c, CDAC); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* 87262306a36Sopenharmony_ci * CDAC == 0 indicates that the DMA transfer on the channel 87362306a36Sopenharmony_ci * has not been started (no data has been transferred so 87462306a36Sopenharmony_ci * far). Return the programmed destination start address in 87562306a36Sopenharmony_ci * this case. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ci if (addr == 0) 87862306a36Sopenharmony_ci addr = omap_dma_chan_read(c, CDSA); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (dma_omap1()) 88262306a36Sopenharmony_ci addr |= omap_dma_chan_read(c, CDSA) & 0xffff0000; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci return addr; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic enum dma_status omap_dma_tx_status(struct dma_chan *chan, 88862306a36Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 89162306a36Sopenharmony_ci enum dma_status ret; 89262306a36Sopenharmony_ci unsigned long flags; 89362306a36Sopenharmony_ci struct omap_desc *d = NULL; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 89662306a36Sopenharmony_ci if (ret == DMA_COMPLETE) 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 90062306a36Sopenharmony_ci if (c->desc && c->desc->vd.tx.cookie == cookie) 90162306a36Sopenharmony_ci d = c->desc; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (!txstate) 90462306a36Sopenharmony_ci goto out; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (d) { 90762306a36Sopenharmony_ci dma_addr_t pos; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (d->dir == DMA_MEM_TO_DEV) 91062306a36Sopenharmony_ci pos = omap_dma_get_src_pos(c); 91162306a36Sopenharmony_ci else if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) 91262306a36Sopenharmony_ci pos = omap_dma_get_dst_pos(c); 91362306a36Sopenharmony_ci else 91462306a36Sopenharmony_ci pos = 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci txstate->residue = omap_dma_desc_size_pos(d, pos); 91762306a36Sopenharmony_ci } else { 91862306a36Sopenharmony_ci struct virt_dma_desc *vd = vchan_find_desc(&c->vc, cookie); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (vd) 92162306a36Sopenharmony_ci txstate->residue = omap_dma_desc_size( 92262306a36Sopenharmony_ci to_omap_dma_desc(&vd->tx)); 92362306a36Sopenharmony_ci else 92462306a36Sopenharmony_ci txstate->residue = 0; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ciout: 92862306a36Sopenharmony_ci if (ret == DMA_IN_PROGRESS && c->paused) { 92962306a36Sopenharmony_ci ret = DMA_PAUSED; 93062306a36Sopenharmony_ci } else if (d && d->polled && c->running) { 93162306a36Sopenharmony_ci uint32_t ccr = omap_dma_chan_read(c, CCR); 93262306a36Sopenharmony_ci /* 93362306a36Sopenharmony_ci * The channel is no longer active, set the return value 93462306a36Sopenharmony_ci * accordingly and mark it as completed 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci if (!(ccr & CCR_ENABLE)) { 93762306a36Sopenharmony_ci ret = DMA_COMPLETE; 93862306a36Sopenharmony_ci omap_dma_start_desc(c); 93962306a36Sopenharmony_ci vchan_cookie_complete(&d->vd); 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return ret; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic void omap_dma_issue_pending(struct dma_chan *chan) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 95162306a36Sopenharmony_ci unsigned long flags; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 95462306a36Sopenharmony_ci if (vchan_issue_pending(&c->vc) && !c->desc) 95562306a36Sopenharmony_ci omap_dma_start_desc(c); 95662306a36Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_slave_sg( 96062306a36Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen, 96162306a36Sopenharmony_ci enum dma_transfer_direction dir, unsigned long tx_flags, void *context) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 96462306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 96562306a36Sopenharmony_ci enum dma_slave_buswidth dev_width; 96662306a36Sopenharmony_ci struct scatterlist *sgent; 96762306a36Sopenharmony_ci struct omap_desc *d; 96862306a36Sopenharmony_ci dma_addr_t dev_addr; 96962306a36Sopenharmony_ci unsigned i, es, en, frame_bytes; 97062306a36Sopenharmony_ci bool ll_failed = false; 97162306a36Sopenharmony_ci u32 burst; 97262306a36Sopenharmony_ci u32 port_window, port_window_bytes; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 97562306a36Sopenharmony_ci dev_addr = c->cfg.src_addr; 97662306a36Sopenharmony_ci dev_width = c->cfg.src_addr_width; 97762306a36Sopenharmony_ci burst = c->cfg.src_maxburst; 97862306a36Sopenharmony_ci port_window = c->cfg.src_port_window_size; 97962306a36Sopenharmony_ci } else if (dir == DMA_MEM_TO_DEV) { 98062306a36Sopenharmony_ci dev_addr = c->cfg.dst_addr; 98162306a36Sopenharmony_ci dev_width = c->cfg.dst_addr_width; 98262306a36Sopenharmony_ci burst = c->cfg.dst_maxburst; 98362306a36Sopenharmony_ci port_window = c->cfg.dst_port_window_size; 98462306a36Sopenharmony_ci } else { 98562306a36Sopenharmony_ci dev_err(chan->device->dev, "%s: bad direction?\n", __func__); 98662306a36Sopenharmony_ci return NULL; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* Bus width translates to the element size (ES) */ 99062306a36Sopenharmony_ci switch (dev_width) { 99162306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 99262306a36Sopenharmony_ci es = CSDP_DATA_TYPE_8; 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 99562306a36Sopenharmony_ci es = CSDP_DATA_TYPE_16; 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 99862306a36Sopenharmony_ci es = CSDP_DATA_TYPE_32; 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci default: /* not reached */ 100162306a36Sopenharmony_ci return NULL; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* Now allocate and setup the descriptor. */ 100562306a36Sopenharmony_ci d = kzalloc(struct_size(d, sg, sglen), GFP_ATOMIC); 100662306a36Sopenharmony_ci if (!d) 100762306a36Sopenharmony_ci return NULL; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci d->dir = dir; 101062306a36Sopenharmony_ci d->dev_addr = dev_addr; 101162306a36Sopenharmony_ci d->es = es; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* When the port_window is used, one frame must cover the window */ 101462306a36Sopenharmony_ci if (port_window) { 101562306a36Sopenharmony_ci burst = port_window; 101662306a36Sopenharmony_ci port_window_bytes = port_window * es_bytes[es]; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci d->ei = 1; 101962306a36Sopenharmony_ci /* 102062306a36Sopenharmony_ci * One frame covers the port_window and by configure 102162306a36Sopenharmony_ci * the source frame index to be -1 * (port_window - 1) 102262306a36Sopenharmony_ci * we instruct the sDMA that after a frame is processed 102362306a36Sopenharmony_ci * it should move back to the start of the window. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci d->fi = -(port_window_bytes - 1); 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci d->ccr = c->ccr | CCR_SYNC_FRAME; 102962306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 103062306a36Sopenharmony_ci d->csdp = CSDP_DST_BURST_64 | CSDP_DST_PACKED; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC; 103362306a36Sopenharmony_ci if (port_window) { 103462306a36Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_DBLIDX; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (port_window_bytes >= 64) 103762306a36Sopenharmony_ci d->csdp |= CSDP_SRC_BURST_64; 103862306a36Sopenharmony_ci else if (port_window_bytes >= 32) 103962306a36Sopenharmony_ci d->csdp |= CSDP_SRC_BURST_32; 104062306a36Sopenharmony_ci else if (port_window_bytes >= 16) 104162306a36Sopenharmony_ci d->csdp |= CSDP_SRC_BURST_16; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci } else { 104462306a36Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_CONSTANT; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci } else { 104762306a36Sopenharmony_ci d->csdp = CSDP_SRC_BURST_64 | CSDP_SRC_PACKED; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_POSTINC; 105062306a36Sopenharmony_ci if (port_window) { 105162306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_DBLIDX; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (port_window_bytes >= 64) 105462306a36Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64; 105562306a36Sopenharmony_ci else if (port_window_bytes >= 32) 105662306a36Sopenharmony_ci d->csdp |= CSDP_DST_BURST_32; 105762306a36Sopenharmony_ci else if (port_window_bytes >= 16) 105862306a36Sopenharmony_ci d->csdp |= CSDP_DST_BURST_16; 105962306a36Sopenharmony_ci } else { 106062306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_CONSTANT; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci d->cicr = CICR_DROP_IE | CICR_BLOCK_IE; 106562306a36Sopenharmony_ci d->csdp |= es; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (dma_omap1()) { 106862306a36Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 107162306a36Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_TIPB; 107262306a36Sopenharmony_ci else 107362306a36Sopenharmony_ci d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF; 107462306a36Sopenharmony_ci } else { 107562306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 107662306a36Sopenharmony_ci d->ccr |= CCR_TRIGGER_SRC; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (port_window) 108162306a36Sopenharmony_ci d->csdp |= CSDP_WRITE_LAST_NON_POSTED; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS) 108462306a36Sopenharmony_ci d->clnk_ctrl = c->dma_ch; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* 108762306a36Sopenharmony_ci * Build our scatterlist entries: each contains the address, 108862306a36Sopenharmony_ci * the number of elements (EN) in each frame, and the number of 108962306a36Sopenharmony_ci * frames (FN). Number of bytes for this entry = ES * EN * FN. 109062306a36Sopenharmony_ci * 109162306a36Sopenharmony_ci * Burst size translates to number of elements with frame sync. 109262306a36Sopenharmony_ci * Note: DMA engine defines burst to be the number of dev-width 109362306a36Sopenharmony_ci * transfers. 109462306a36Sopenharmony_ci */ 109562306a36Sopenharmony_ci en = burst; 109662306a36Sopenharmony_ci frame_bytes = es_bytes[es] * en; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (sglen >= 2) 109962306a36Sopenharmony_ci d->using_ll = od->ll123_supported; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci for_each_sg(sgl, sgent, sglen, i) { 110262306a36Sopenharmony_ci struct omap_sg *osg = &d->sg[i]; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci osg->addr = sg_dma_address(sgent); 110562306a36Sopenharmony_ci osg->en = en; 110662306a36Sopenharmony_ci osg->fn = sg_dma_len(sgent) / frame_bytes; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (d->using_ll) { 110962306a36Sopenharmony_ci osg->t2_desc = dma_pool_alloc(od->desc_pool, GFP_ATOMIC, 111062306a36Sopenharmony_ci &osg->t2_desc_paddr); 111162306a36Sopenharmony_ci if (!osg->t2_desc) { 111262306a36Sopenharmony_ci dev_err(chan->device->dev, 111362306a36Sopenharmony_ci "t2_desc[%d] allocation failed\n", i); 111462306a36Sopenharmony_ci ll_failed = true; 111562306a36Sopenharmony_ci d->using_ll = false; 111662306a36Sopenharmony_ci continue; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci omap_dma_fill_type2_desc(d, i, dir, (i == sglen - 1)); 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci d->sglen = sglen; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Release the dma_pool entries if one allocation failed */ 112662306a36Sopenharmony_ci if (ll_failed) { 112762306a36Sopenharmony_ci for (i = 0; i < d->sglen; i++) { 112862306a36Sopenharmony_ci struct omap_sg *osg = &d->sg[i]; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (osg->t2_desc) { 113162306a36Sopenharmony_ci dma_pool_free(od->desc_pool, osg->t2_desc, 113262306a36Sopenharmony_ci osg->t2_desc_paddr); 113362306a36Sopenharmony_ci osg->t2_desc = NULL; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, tx_flags); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( 114262306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, 114362306a36Sopenharmony_ci size_t period_len, enum dma_transfer_direction dir, unsigned long flags) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 114662306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 114762306a36Sopenharmony_ci enum dma_slave_buswidth dev_width; 114862306a36Sopenharmony_ci struct omap_desc *d; 114962306a36Sopenharmony_ci dma_addr_t dev_addr; 115062306a36Sopenharmony_ci unsigned es; 115162306a36Sopenharmony_ci u32 burst; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 115462306a36Sopenharmony_ci dev_addr = c->cfg.src_addr; 115562306a36Sopenharmony_ci dev_width = c->cfg.src_addr_width; 115662306a36Sopenharmony_ci burst = c->cfg.src_maxburst; 115762306a36Sopenharmony_ci } else if (dir == DMA_MEM_TO_DEV) { 115862306a36Sopenharmony_ci dev_addr = c->cfg.dst_addr; 115962306a36Sopenharmony_ci dev_width = c->cfg.dst_addr_width; 116062306a36Sopenharmony_ci burst = c->cfg.dst_maxburst; 116162306a36Sopenharmony_ci } else { 116262306a36Sopenharmony_ci dev_err(chan->device->dev, "%s: bad direction?\n", __func__); 116362306a36Sopenharmony_ci return NULL; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* Bus width translates to the element size (ES) */ 116762306a36Sopenharmony_ci switch (dev_width) { 116862306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 116962306a36Sopenharmony_ci es = CSDP_DATA_TYPE_8; 117062306a36Sopenharmony_ci break; 117162306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 117262306a36Sopenharmony_ci es = CSDP_DATA_TYPE_16; 117362306a36Sopenharmony_ci break; 117462306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 117562306a36Sopenharmony_ci es = CSDP_DATA_TYPE_32; 117662306a36Sopenharmony_ci break; 117762306a36Sopenharmony_ci default: /* not reached */ 117862306a36Sopenharmony_ci return NULL; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Now allocate and setup the descriptor. */ 118262306a36Sopenharmony_ci d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); 118362306a36Sopenharmony_ci if (!d) 118462306a36Sopenharmony_ci return NULL; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci d->dir = dir; 118762306a36Sopenharmony_ci d->dev_addr = dev_addr; 118862306a36Sopenharmony_ci d->fi = burst; 118962306a36Sopenharmony_ci d->es = es; 119062306a36Sopenharmony_ci d->sg[0].addr = buf_addr; 119162306a36Sopenharmony_ci d->sg[0].en = period_len / es_bytes[es]; 119262306a36Sopenharmony_ci d->sg[0].fn = buf_len / period_len; 119362306a36Sopenharmony_ci d->sglen = 1; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci d->ccr = c->ccr; 119662306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 119762306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT; 119862306a36Sopenharmony_ci else 119962306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci d->cicr = CICR_DROP_IE; 120262306a36Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 120362306a36Sopenharmony_ci d->cicr |= CICR_FRAME_IE; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci d->csdp = es; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (dma_omap1()) { 120862306a36Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 121162306a36Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_MPUI; 121262306a36Sopenharmony_ci else 121362306a36Sopenharmony_ci d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF; 121462306a36Sopenharmony_ci } else { 121562306a36Sopenharmony_ci if (burst) 121662306a36Sopenharmony_ci d->ccr |= CCR_SYNC_PACKET; 121762306a36Sopenharmony_ci else 121862306a36Sopenharmony_ci d->ccr |= CCR_SYNC_ELEMENT; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 122162306a36Sopenharmony_ci d->ccr |= CCR_TRIGGER_SRC; 122262306a36Sopenharmony_ci d->csdp |= CSDP_DST_PACKED; 122362306a36Sopenharmony_ci } else { 122462306a36Sopenharmony_ci d->csdp |= CSDP_SRC_PACKED; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) 123362306a36Sopenharmony_ci d->ccr |= CCR_AUTO_INIT | CCR_REPEAT; 123462306a36Sopenharmony_ci else 123562306a36Sopenharmony_ci d->clnk_ctrl = c->dma_ch | CLNK_CTRL_ENABLE_LNK; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci c->cyclic = true; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, flags); 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_memcpy( 124362306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 124462306a36Sopenharmony_ci size_t len, unsigned long tx_flags) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 124762306a36Sopenharmony_ci struct omap_desc *d; 124862306a36Sopenharmony_ci uint8_t data_type; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); 125162306a36Sopenharmony_ci if (!d) 125262306a36Sopenharmony_ci return NULL; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci data_type = __ffs((src | dest | len)); 125562306a36Sopenharmony_ci if (data_type > CSDP_DATA_TYPE_32) 125662306a36Sopenharmony_ci data_type = CSDP_DATA_TYPE_32; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci d->dir = DMA_MEM_TO_MEM; 125962306a36Sopenharmony_ci d->dev_addr = src; 126062306a36Sopenharmony_ci d->fi = 0; 126162306a36Sopenharmony_ci d->es = data_type; 126262306a36Sopenharmony_ci d->sg[0].en = len / BIT(data_type); 126362306a36Sopenharmony_ci d->sg[0].fn = 1; 126462306a36Sopenharmony_ci d->sg[0].addr = dest; 126562306a36Sopenharmony_ci d->sglen = 1; 126662306a36Sopenharmony_ci d->ccr = c->ccr; 126762306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_POSTINC; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (tx_flags & DMA_PREP_INTERRUPT) 127062306a36Sopenharmony_ci d->cicr |= CICR_FRAME_IE; 127162306a36Sopenharmony_ci else 127262306a36Sopenharmony_ci d->polled = true; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci d->csdp = data_type; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (dma_omap1()) { 127762306a36Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 127862306a36Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF; 127962306a36Sopenharmony_ci } else { 128062306a36Sopenharmony_ci d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED; 128162306a36Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 128262306a36Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, tx_flags); 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_interleaved( 128962306a36Sopenharmony_ci struct dma_chan *chan, struct dma_interleaved_template *xt, 129062306a36Sopenharmony_ci unsigned long flags) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 129362306a36Sopenharmony_ci struct omap_desc *d; 129462306a36Sopenharmony_ci struct omap_sg *sg; 129562306a36Sopenharmony_ci uint8_t data_type; 129662306a36Sopenharmony_ci size_t src_icg, dst_icg; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* Slave mode is not supported */ 129962306a36Sopenharmony_ci if (is_slave_direction(xt->dir)) 130062306a36Sopenharmony_ci return NULL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (xt->frame_size != 1 || xt->numf == 0) 130362306a36Sopenharmony_ci return NULL; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC); 130662306a36Sopenharmony_ci if (!d) 130762306a36Sopenharmony_ci return NULL; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci data_type = __ffs((xt->src_start | xt->dst_start | xt->sgl[0].size)); 131062306a36Sopenharmony_ci if (data_type > CSDP_DATA_TYPE_32) 131162306a36Sopenharmony_ci data_type = CSDP_DATA_TYPE_32; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci sg = &d->sg[0]; 131462306a36Sopenharmony_ci d->dir = DMA_MEM_TO_MEM; 131562306a36Sopenharmony_ci d->dev_addr = xt->src_start; 131662306a36Sopenharmony_ci d->es = data_type; 131762306a36Sopenharmony_ci sg->en = xt->sgl[0].size / BIT(data_type); 131862306a36Sopenharmony_ci sg->fn = xt->numf; 131962306a36Sopenharmony_ci sg->addr = xt->dst_start; 132062306a36Sopenharmony_ci d->sglen = 1; 132162306a36Sopenharmony_ci d->ccr = c->ccr; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); 132462306a36Sopenharmony_ci dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); 132562306a36Sopenharmony_ci if (src_icg) { 132662306a36Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_DBLIDX; 132762306a36Sopenharmony_ci d->ei = 1; 132862306a36Sopenharmony_ci d->fi = src_icg + 1; 132962306a36Sopenharmony_ci } else if (xt->src_inc) { 133062306a36Sopenharmony_ci d->ccr |= CCR_SRC_AMODE_POSTINC; 133162306a36Sopenharmony_ci d->fi = 0; 133262306a36Sopenharmony_ci } else { 133362306a36Sopenharmony_ci dev_err(chan->device->dev, 133462306a36Sopenharmony_ci "%s: SRC constant addressing is not supported\n", 133562306a36Sopenharmony_ci __func__); 133662306a36Sopenharmony_ci kfree(d); 133762306a36Sopenharmony_ci return NULL; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (dst_icg) { 134162306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_DBLIDX; 134262306a36Sopenharmony_ci sg->ei = 1; 134362306a36Sopenharmony_ci sg->fi = dst_icg + 1; 134462306a36Sopenharmony_ci } else if (xt->dst_inc) { 134562306a36Sopenharmony_ci d->ccr |= CCR_DST_AMODE_POSTINC; 134662306a36Sopenharmony_ci sg->fi = 0; 134762306a36Sopenharmony_ci } else { 134862306a36Sopenharmony_ci dev_err(chan->device->dev, 134962306a36Sopenharmony_ci "%s: DST constant addressing is not supported\n", 135062306a36Sopenharmony_ci __func__); 135162306a36Sopenharmony_ci kfree(d); 135262306a36Sopenharmony_ci return NULL; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci d->cicr = CICR_DROP_IE | CICR_FRAME_IE; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci d->csdp = data_type; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (dma_omap1()) { 136062306a36Sopenharmony_ci d->cicr |= CICR_TOUT_IE; 136162306a36Sopenharmony_ci d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF; 136262306a36Sopenharmony_ci } else { 136362306a36Sopenharmony_ci d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED; 136462306a36Sopenharmony_ci d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE; 136562306a36Sopenharmony_ci d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return vchan_tx_prep(&c->vc, &d->vd, flags); 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic int omap_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || 137662306a36Sopenharmony_ci cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) 137762306a36Sopenharmony_ci return -EINVAL; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (cfg->src_maxburst > chan->device->max_burst || 138062306a36Sopenharmony_ci cfg->dst_maxburst > chan->device->max_burst) 138162306a36Sopenharmony_ci return -EINVAL; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci memcpy(&c->cfg, cfg, sizeof(c->cfg)); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci return 0; 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic int omap_dma_terminate_all(struct dma_chan *chan) 138962306a36Sopenharmony_ci{ 139062306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 139162306a36Sopenharmony_ci unsigned long flags; 139262306a36Sopenharmony_ci LIST_HEAD(head); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci spin_lock_irqsave(&c->vc.lock, flags); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* 139762306a36Sopenharmony_ci * Stop DMA activity: we assume the callback will not be called 139862306a36Sopenharmony_ci * after omap_dma_stop() returns (even if it does, it will see 139962306a36Sopenharmony_ci * c->desc is NULL and exit.) 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ci if (c->desc) { 140262306a36Sopenharmony_ci vchan_terminate_vdesc(&c->desc->vd); 140362306a36Sopenharmony_ci c->desc = NULL; 140462306a36Sopenharmony_ci /* Avoid stopping the dma twice */ 140562306a36Sopenharmony_ci if (!c->paused) 140662306a36Sopenharmony_ci omap_dma_stop(c); 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci c->cyclic = false; 141062306a36Sopenharmony_ci c->paused = false; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci vchan_get_all_descriptors(&c->vc, &head); 141362306a36Sopenharmony_ci spin_unlock_irqrestore(&c->vc.lock, flags); 141462306a36Sopenharmony_ci vchan_dma_desc_free_list(&c->vc, &head); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci return 0; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_cistatic void omap_dma_synchronize(struct dma_chan *chan) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci vchan_synchronize(&c->vc); 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic int omap_dma_pause(struct dma_chan *chan) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 142962306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 143062306a36Sopenharmony_ci unsigned long flags; 143162306a36Sopenharmony_ci int ret = -EINVAL; 143262306a36Sopenharmony_ci bool can_pause = false; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci spin_lock_irqsave(&od->irq_lock, flags); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci if (!c->desc) 143762306a36Sopenharmony_ci goto out; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (c->cyclic) 144062306a36Sopenharmony_ci can_pause = true; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* 144362306a36Sopenharmony_ci * We do not allow DMA_MEM_TO_DEV transfers to be paused. 144462306a36Sopenharmony_ci * From the AM572x TRM, 16.1.4.18 Disabling a Channel During Transfer: 144562306a36Sopenharmony_ci * "When a channel is disabled during a transfer, the channel undergoes 144662306a36Sopenharmony_ci * an abort, unless it is hardware-source-synchronized …". 144762306a36Sopenharmony_ci * A source-synchronised channel is one where the fetching of data is 144862306a36Sopenharmony_ci * under control of the device. In other words, a device-to-memory 144962306a36Sopenharmony_ci * transfer. So, a destination-synchronised channel (which would be a 145062306a36Sopenharmony_ci * memory-to-device transfer) undergoes an abort if the CCR_ENABLE 145162306a36Sopenharmony_ci * bit is cleared. 145262306a36Sopenharmony_ci * From 16.1.4.20.4.6.2 Abort: "If an abort trigger occurs, the channel 145362306a36Sopenharmony_ci * aborts immediately after completion of current read/write 145462306a36Sopenharmony_ci * transactions and then the FIFO is cleaned up." The term "cleaned up" 145562306a36Sopenharmony_ci * is not defined. TI recommends to check that RD_ACTIVE and WR_ACTIVE 145662306a36Sopenharmony_ci * are both clear _before_ disabling the channel, otherwise data loss 145762306a36Sopenharmony_ci * will occur. 145862306a36Sopenharmony_ci * The problem is that if the channel is active, then device activity 145962306a36Sopenharmony_ci * can result in DMA activity starting between reading those as both 146062306a36Sopenharmony_ci * clear and the write to DMA_CCR to clear the enable bit hitting the 146162306a36Sopenharmony_ci * hardware. If the DMA hardware can't drain the data in its FIFO to the 146262306a36Sopenharmony_ci * destination, then data loss "might" occur (say if we write to an UART 146362306a36Sopenharmony_ci * and the UART is not accepting any further data). 146462306a36Sopenharmony_ci */ 146562306a36Sopenharmony_ci else if (c->desc->dir == DMA_DEV_TO_MEM) 146662306a36Sopenharmony_ci can_pause = true; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (can_pause && !c->paused) { 146962306a36Sopenharmony_ci ret = omap_dma_stop(c); 147062306a36Sopenharmony_ci if (!ret) 147162306a36Sopenharmony_ci c->paused = true; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ciout: 147462306a36Sopenharmony_ci spin_unlock_irqrestore(&od->irq_lock, flags); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci return ret; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic int omap_dma_resume(struct dma_chan *chan) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 148262306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 148362306a36Sopenharmony_ci unsigned long flags; 148462306a36Sopenharmony_ci int ret = -EINVAL; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci spin_lock_irqsave(&od->irq_lock, flags); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (c->paused && c->desc) { 148962306a36Sopenharmony_ci mb(); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* Restore channel link register */ 149262306a36Sopenharmony_ci omap_dma_chan_write(c, CLNK_CTRL, c->desc->clnk_ctrl); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci omap_dma_start(c, c->desc); 149562306a36Sopenharmony_ci c->paused = false; 149662306a36Sopenharmony_ci ret = 0; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci spin_unlock_irqrestore(&od->irq_lock, flags); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci return ret; 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic int omap_dma_chan_init(struct omap_dmadev *od) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct omap_chan *c; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci c = kzalloc(sizeof(*c), GFP_KERNEL); 150862306a36Sopenharmony_ci if (!c) 150962306a36Sopenharmony_ci return -ENOMEM; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci c->reg_map = od->reg_map; 151262306a36Sopenharmony_ci c->vc.desc_free = omap_dma_desc_free; 151362306a36Sopenharmony_ci vchan_init(&c->vc, &od->ddev); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci return 0; 151662306a36Sopenharmony_ci} 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cistatic void omap_dma_free(struct omap_dmadev *od) 151962306a36Sopenharmony_ci{ 152062306a36Sopenharmony_ci while (!list_empty(&od->ddev.channels)) { 152162306a36Sopenharmony_ci struct omap_chan *c = list_first_entry(&od->ddev.channels, 152262306a36Sopenharmony_ci struct omap_chan, vc.chan.device_node); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci list_del(&c->vc.chan.device_node); 152562306a36Sopenharmony_ci tasklet_kill(&c->vc.task); 152662306a36Sopenharmony_ci kfree(c); 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci/* Currently used by omap2 & 3 to block deeper SoC idle states */ 153162306a36Sopenharmony_cistatic bool omap_dma_busy(struct omap_dmadev *od) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci struct omap_chan *c; 153462306a36Sopenharmony_ci int lch = -1; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci while (1) { 153762306a36Sopenharmony_ci lch = find_next_bit(od->lch_bitmap, od->lch_count, lch + 1); 153862306a36Sopenharmony_ci if (lch >= od->lch_count) 153962306a36Sopenharmony_ci break; 154062306a36Sopenharmony_ci c = od->lch_map[lch]; 154162306a36Sopenharmony_ci if (!c) 154262306a36Sopenharmony_ci continue; 154362306a36Sopenharmony_ci if (omap_dma_chan_read(c, CCR) & CCR_ENABLE) 154462306a36Sopenharmony_ci return true; 154562306a36Sopenharmony_ci } 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci return false; 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci/* Currently only used for omap2. For omap1, also a check for lcd_dma is needed */ 155162306a36Sopenharmony_cistatic int omap_dma_busy_notifier(struct notifier_block *nb, 155262306a36Sopenharmony_ci unsigned long cmd, void *v) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct omap_dmadev *od; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci od = container_of(nb, struct omap_dmadev, nb); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci switch (cmd) { 155962306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 156062306a36Sopenharmony_ci if (omap_dma_busy(od)) 156162306a36Sopenharmony_ci return NOTIFY_BAD; 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: 156462306a36Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 156562306a36Sopenharmony_ci break; 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci return NOTIFY_OK; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci/* 157262306a36Sopenharmony_ci * We are using IRQENABLE_L1, and legacy DMA code was using IRQENABLE_L0. 157362306a36Sopenharmony_ci * As the DSP may be using IRQENABLE_L2 and L3, let's not touch those for 157462306a36Sopenharmony_ci * now. Context save seems to be only currently needed on omap3. 157562306a36Sopenharmony_ci */ 157662306a36Sopenharmony_cistatic void omap_dma_context_save(struct omap_dmadev *od) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci od->context.irqenable_l0 = omap_dma_glbl_read(od, IRQENABLE_L0); 157962306a36Sopenharmony_ci od->context.irqenable_l1 = omap_dma_glbl_read(od, IRQENABLE_L1); 158062306a36Sopenharmony_ci od->context.ocp_sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG); 158162306a36Sopenharmony_ci od->context.gcr = omap_dma_glbl_read(od, GCR); 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic void omap_dma_context_restore(struct omap_dmadev *od) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci int i; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci omap_dma_glbl_write(od, GCR, od->context.gcr); 158962306a36Sopenharmony_ci omap_dma_glbl_write(od, OCP_SYSCONFIG, od->context.ocp_sysconfig); 159062306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L0, od->context.irqenable_l0); 159162306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, od->context.irqenable_l1); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci /* Clear IRQSTATUS_L0 as legacy DMA code is no longer doing it */ 159462306a36Sopenharmony_ci if (od->plat->errata & DMA_ROMCODE_BUG) 159562306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQSTATUS_L0, 0); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* Clear dma channels */ 159862306a36Sopenharmony_ci for (i = 0; i < od->lch_count; i++) 159962306a36Sopenharmony_ci omap_dma_clear_lch(od, i); 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci/* Currently only used for omap3 */ 160362306a36Sopenharmony_cistatic int omap_dma_context_notifier(struct notifier_block *nb, 160462306a36Sopenharmony_ci unsigned long cmd, void *v) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci struct omap_dmadev *od; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci od = container_of(nb, struct omap_dmadev, nb); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci switch (cmd) { 161162306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 161262306a36Sopenharmony_ci if (omap_dma_busy(od)) 161362306a36Sopenharmony_ci return NOTIFY_BAD; 161462306a36Sopenharmony_ci omap_dma_context_save(od); 161562306a36Sopenharmony_ci break; 161662306a36Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */ 161762306a36Sopenharmony_ci break; 161862306a36Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 161962306a36Sopenharmony_ci omap_dma_context_restore(od); 162062306a36Sopenharmony_ci break; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci return NOTIFY_OK; 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic void omap_dma_init_gcr(struct omap_dmadev *od, int arb_rate, 162762306a36Sopenharmony_ci int max_fifo_depth, int tparams) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci u32 val; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci /* Set only for omap2430 and later */ 163262306a36Sopenharmony_ci if (!od->cfg->rw_priority) 163362306a36Sopenharmony_ci return; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci if (max_fifo_depth == 0) 163662306a36Sopenharmony_ci max_fifo_depth = 1; 163762306a36Sopenharmony_ci if (arb_rate == 0) 163862306a36Sopenharmony_ci arb_rate = 1; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci val = 0xff & max_fifo_depth; 164162306a36Sopenharmony_ci val |= (0x3 & tparams) << 12; 164262306a36Sopenharmony_ci val |= (arb_rate & 0xff) << 16; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci omap_dma_glbl_write(od, GCR, val); 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci#define OMAP_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 164862306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 164962306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci/* 165262306a36Sopenharmony_ci * No flags currently set for default configuration as omap1 is still 165362306a36Sopenharmony_ci * using platform data. 165462306a36Sopenharmony_ci */ 165562306a36Sopenharmony_cistatic const struct omap_dma_config default_cfg; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic int omap_dma_probe(struct platform_device *pdev) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci const struct omap_dma_config *conf; 166062306a36Sopenharmony_ci struct omap_dmadev *od; 166162306a36Sopenharmony_ci int rc, i, irq; 166262306a36Sopenharmony_ci u32 val; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); 166562306a36Sopenharmony_ci if (!od) 166662306a36Sopenharmony_ci return -ENOMEM; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci od->base = devm_platform_ioremap_resource(pdev, 0); 166962306a36Sopenharmony_ci if (IS_ERR(od->base)) 167062306a36Sopenharmony_ci return PTR_ERR(od->base); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci conf = of_device_get_match_data(&pdev->dev); 167362306a36Sopenharmony_ci if (conf) { 167462306a36Sopenharmony_ci od->cfg = conf; 167562306a36Sopenharmony_ci od->plat = dev_get_platdata(&pdev->dev); 167662306a36Sopenharmony_ci if (!od->plat) { 167762306a36Sopenharmony_ci dev_err(&pdev->dev, "omap_system_dma_plat_info is missing"); 167862306a36Sopenharmony_ci return -ENODEV; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci } else if (IS_ENABLED(CONFIG_ARCH_OMAP1)) { 168162306a36Sopenharmony_ci od->cfg = &default_cfg; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci od->plat = omap_get_plat_info(); 168462306a36Sopenharmony_ci if (!od->plat) 168562306a36Sopenharmony_ci return -EPROBE_DEFER; 168662306a36Sopenharmony_ci } else { 168762306a36Sopenharmony_ci return -ENODEV; 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci od->reg_map = od->plat->reg_map; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); 169362306a36Sopenharmony_ci dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); 169462306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, od->ddev.cap_mask); 169562306a36Sopenharmony_ci dma_cap_set(DMA_INTERLEAVE, od->ddev.cap_mask); 169662306a36Sopenharmony_ci od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources; 169762306a36Sopenharmony_ci od->ddev.device_free_chan_resources = omap_dma_free_chan_resources; 169862306a36Sopenharmony_ci od->ddev.device_tx_status = omap_dma_tx_status; 169962306a36Sopenharmony_ci od->ddev.device_issue_pending = omap_dma_issue_pending; 170062306a36Sopenharmony_ci od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; 170162306a36Sopenharmony_ci od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic; 170262306a36Sopenharmony_ci od->ddev.device_prep_dma_memcpy = omap_dma_prep_dma_memcpy; 170362306a36Sopenharmony_ci od->ddev.device_prep_interleaved_dma = omap_dma_prep_dma_interleaved; 170462306a36Sopenharmony_ci od->ddev.device_config = omap_dma_slave_config; 170562306a36Sopenharmony_ci od->ddev.device_pause = omap_dma_pause; 170662306a36Sopenharmony_ci od->ddev.device_resume = omap_dma_resume; 170762306a36Sopenharmony_ci od->ddev.device_terminate_all = omap_dma_terminate_all; 170862306a36Sopenharmony_ci od->ddev.device_synchronize = omap_dma_synchronize; 170962306a36Sopenharmony_ci od->ddev.src_addr_widths = OMAP_DMA_BUSWIDTHS; 171062306a36Sopenharmony_ci od->ddev.dst_addr_widths = OMAP_DMA_BUSWIDTHS; 171162306a36Sopenharmony_ci od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 171262306a36Sopenharmony_ci if (__dma_omap15xx(od->plat->dma_attr)) 171362306a36Sopenharmony_ci od->ddev.residue_granularity = 171462306a36Sopenharmony_ci DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 171562306a36Sopenharmony_ci else 171662306a36Sopenharmony_ci od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 171762306a36Sopenharmony_ci od->ddev.max_burst = SZ_16M - 1; /* CCEN: 24bit unsigned */ 171862306a36Sopenharmony_ci od->ddev.dev = &pdev->dev; 171962306a36Sopenharmony_ci INIT_LIST_HEAD(&od->ddev.channels); 172062306a36Sopenharmony_ci mutex_init(&od->lch_lock); 172162306a36Sopenharmony_ci spin_lock_init(&od->lock); 172262306a36Sopenharmony_ci spin_lock_init(&od->irq_lock); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci /* Number of DMA requests */ 172562306a36Sopenharmony_ci od->dma_requests = OMAP_SDMA_REQUESTS; 172662306a36Sopenharmony_ci if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node, 172762306a36Sopenharmony_ci "dma-requests", 172862306a36Sopenharmony_ci &od->dma_requests)) { 172962306a36Sopenharmony_ci dev_info(&pdev->dev, 173062306a36Sopenharmony_ci "Missing dma-requests property, using %u.\n", 173162306a36Sopenharmony_ci OMAP_SDMA_REQUESTS); 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci /* Number of available logical channels */ 173562306a36Sopenharmony_ci if (!pdev->dev.of_node) { 173662306a36Sopenharmony_ci od->lch_count = od->plat->dma_attr->lch_count; 173762306a36Sopenharmony_ci if (unlikely(!od->lch_count)) 173862306a36Sopenharmony_ci od->lch_count = OMAP_SDMA_CHANNELS; 173962306a36Sopenharmony_ci } else if (of_property_read_u32(pdev->dev.of_node, "dma-channels", 174062306a36Sopenharmony_ci &od->lch_count)) { 174162306a36Sopenharmony_ci dev_info(&pdev->dev, 174262306a36Sopenharmony_ci "Missing dma-channels property, using %u.\n", 174362306a36Sopenharmony_ci OMAP_SDMA_CHANNELS); 174462306a36Sopenharmony_ci od->lch_count = OMAP_SDMA_CHANNELS; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci /* Mask of allowed logical channels */ 174862306a36Sopenharmony_ci if (pdev->dev.of_node && !of_property_read_u32(pdev->dev.of_node, 174962306a36Sopenharmony_ci "dma-channel-mask", 175062306a36Sopenharmony_ci &val)) { 175162306a36Sopenharmony_ci /* Tag channels not in mask as reserved */ 175262306a36Sopenharmony_ci val = ~val; 175362306a36Sopenharmony_ci bitmap_from_arr32(od->lch_bitmap, &val, od->lch_count); 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci if (od->plat->dma_attr->dev_caps & HS_CHANNELS_RESERVED) 175662306a36Sopenharmony_ci bitmap_set(od->lch_bitmap, 0, 2); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci od->lch_map = devm_kcalloc(&pdev->dev, od->lch_count, 175962306a36Sopenharmony_ci sizeof(*od->lch_map), 176062306a36Sopenharmony_ci GFP_KERNEL); 176162306a36Sopenharmony_ci if (!od->lch_map) 176262306a36Sopenharmony_ci return -ENOMEM; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci for (i = 0; i < od->dma_requests; i++) { 176562306a36Sopenharmony_ci rc = omap_dma_chan_init(od); 176662306a36Sopenharmony_ci if (rc) { 176762306a36Sopenharmony_ci omap_dma_free(od); 176862306a36Sopenharmony_ci return rc; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci irq = platform_get_irq(pdev, 1); 177362306a36Sopenharmony_ci if (irq <= 0) { 177462306a36Sopenharmony_ci dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq); 177562306a36Sopenharmony_ci od->legacy = true; 177662306a36Sopenharmony_ci } else { 177762306a36Sopenharmony_ci /* Disable all interrupts */ 177862306a36Sopenharmony_ci od->irq_enable_mask = 0; 177962306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L1, 0); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq, 178262306a36Sopenharmony_ci IRQF_SHARED, "omap-dma-engine", od); 178362306a36Sopenharmony_ci if (rc) { 178462306a36Sopenharmony_ci omap_dma_free(od); 178562306a36Sopenharmony_ci return rc; 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci if (omap_dma_glbl_read(od, CAPS_0) & CAPS_0_SUPPORT_LL123) 179062306a36Sopenharmony_ci od->ll123_supported = true; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci od->ddev.filter.map = od->plat->slave_map; 179362306a36Sopenharmony_ci od->ddev.filter.mapcnt = od->plat->slavecnt; 179462306a36Sopenharmony_ci od->ddev.filter.fn = omap_dma_filter_fn; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci if (od->ll123_supported) { 179762306a36Sopenharmony_ci od->desc_pool = dma_pool_create(dev_name(&pdev->dev), 179862306a36Sopenharmony_ci &pdev->dev, 179962306a36Sopenharmony_ci sizeof(struct omap_type2_desc), 180062306a36Sopenharmony_ci 4, 0); 180162306a36Sopenharmony_ci if (!od->desc_pool) { 180262306a36Sopenharmony_ci dev_err(&pdev->dev, 180362306a36Sopenharmony_ci "unable to allocate descriptor pool\n"); 180462306a36Sopenharmony_ci od->ll123_supported = false; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci rc = dma_async_device_register(&od->ddev); 180962306a36Sopenharmony_ci if (rc) { 181062306a36Sopenharmony_ci pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n", 181162306a36Sopenharmony_ci rc); 181262306a36Sopenharmony_ci omap_dma_free(od); 181362306a36Sopenharmony_ci return rc; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci platform_set_drvdata(pdev, od); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci if (pdev->dev.of_node) { 181962306a36Sopenharmony_ci omap_dma_info.dma_cap = od->ddev.cap_mask; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* Device-tree DMA controller registration */ 182262306a36Sopenharmony_ci rc = of_dma_controller_register(pdev->dev.of_node, 182362306a36Sopenharmony_ci of_dma_simple_xlate, &omap_dma_info); 182462306a36Sopenharmony_ci if (rc) { 182562306a36Sopenharmony_ci pr_warn("OMAP-DMA: failed to register DMA controller\n"); 182662306a36Sopenharmony_ci dma_async_device_unregister(&od->ddev); 182762306a36Sopenharmony_ci omap_dma_free(od); 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci omap_dma_init_gcr(od, DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci if (od->cfg->needs_busy_check) { 183462306a36Sopenharmony_ci od->nb.notifier_call = omap_dma_busy_notifier; 183562306a36Sopenharmony_ci cpu_pm_register_notifier(&od->nb); 183662306a36Sopenharmony_ci } else if (od->cfg->may_lose_context) { 183762306a36Sopenharmony_ci od->nb.notifier_call = omap_dma_context_notifier; 183862306a36Sopenharmony_ci cpu_pm_register_notifier(&od->nb); 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci dev_info(&pdev->dev, "OMAP DMA engine driver%s\n", 184262306a36Sopenharmony_ci od->ll123_supported ? " (LinkedList1/2/3 supported)" : ""); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci return rc; 184562306a36Sopenharmony_ci} 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_cistatic int omap_dma_remove(struct platform_device *pdev) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci struct omap_dmadev *od = platform_get_drvdata(pdev); 185062306a36Sopenharmony_ci int irq; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci if (od->cfg->may_lose_context) 185362306a36Sopenharmony_ci cpu_pm_unregister_notifier(&od->nb); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (pdev->dev.of_node) 185662306a36Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci irq = platform_get_irq(pdev, 1); 185962306a36Sopenharmony_ci devm_free_irq(&pdev->dev, irq, od); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci dma_async_device_unregister(&od->ddev); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if (!omap_dma_legacy(od)) { 186462306a36Sopenharmony_ci /* Disable all interrupts */ 186562306a36Sopenharmony_ci omap_dma_glbl_write(od, IRQENABLE_L0, 0); 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci if (od->ll123_supported) 186962306a36Sopenharmony_ci dma_pool_destroy(od->desc_pool); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci omap_dma_free(od); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci return 0; 187462306a36Sopenharmony_ci} 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_cistatic const struct omap_dma_config omap2420_data = { 187762306a36Sopenharmony_ci .lch_end = CCFN, 187862306a36Sopenharmony_ci .rw_priority = true, 187962306a36Sopenharmony_ci .needs_lch_clear = true, 188062306a36Sopenharmony_ci .needs_busy_check = true, 188162306a36Sopenharmony_ci}; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_cistatic const struct omap_dma_config omap2430_data = { 188462306a36Sopenharmony_ci .lch_end = CCFN, 188562306a36Sopenharmony_ci .rw_priority = true, 188662306a36Sopenharmony_ci .needs_lch_clear = true, 188762306a36Sopenharmony_ci}; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_cistatic const struct omap_dma_config omap3430_data = { 189062306a36Sopenharmony_ci .lch_end = CCFN, 189162306a36Sopenharmony_ci .rw_priority = true, 189262306a36Sopenharmony_ci .needs_lch_clear = true, 189362306a36Sopenharmony_ci .may_lose_context = true, 189462306a36Sopenharmony_ci}; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic const struct omap_dma_config omap3630_data = { 189762306a36Sopenharmony_ci .lch_end = CCDN, 189862306a36Sopenharmony_ci .rw_priority = true, 189962306a36Sopenharmony_ci .needs_lch_clear = true, 190062306a36Sopenharmony_ci .may_lose_context = true, 190162306a36Sopenharmony_ci}; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_cistatic const struct omap_dma_config omap4_data = { 190462306a36Sopenharmony_ci .lch_end = CCDN, 190562306a36Sopenharmony_ci .rw_priority = true, 190662306a36Sopenharmony_ci .needs_lch_clear = true, 190762306a36Sopenharmony_ci}; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_cistatic const struct of_device_id omap_dma_match[] = { 191062306a36Sopenharmony_ci { .compatible = "ti,omap2420-sdma", .data = &omap2420_data, }, 191162306a36Sopenharmony_ci { .compatible = "ti,omap2430-sdma", .data = &omap2430_data, }, 191262306a36Sopenharmony_ci { .compatible = "ti,omap3430-sdma", .data = &omap3430_data, }, 191362306a36Sopenharmony_ci { .compatible = "ti,omap3630-sdma", .data = &omap3630_data, }, 191462306a36Sopenharmony_ci { .compatible = "ti,omap4430-sdma", .data = &omap4_data, }, 191562306a36Sopenharmony_ci {}, 191662306a36Sopenharmony_ci}; 191762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_dma_match); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic struct platform_driver omap_dma_driver = { 192062306a36Sopenharmony_ci .probe = omap_dma_probe, 192162306a36Sopenharmony_ci .remove = omap_dma_remove, 192262306a36Sopenharmony_ci .driver = { 192362306a36Sopenharmony_ci .name = "omap-dma-engine", 192462306a36Sopenharmony_ci .of_match_table = omap_dma_match, 192562306a36Sopenharmony_ci }, 192662306a36Sopenharmony_ci}; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci if (chan->device->dev->driver == &omap_dma_driver.driver) { 193162306a36Sopenharmony_ci struct omap_dmadev *od = to_omap_dma_dev(chan->device); 193262306a36Sopenharmony_ci struct omap_chan *c = to_omap_dma_chan(chan); 193362306a36Sopenharmony_ci unsigned req = *(unsigned *)param; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci if (req <= od->dma_requests) { 193662306a36Sopenharmony_ci c->dma_sig = req; 193762306a36Sopenharmony_ci return true; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci return false; 194162306a36Sopenharmony_ci} 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_cistatic int omap_dma_init(void) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci return platform_driver_register(&omap_dma_driver); 194662306a36Sopenharmony_ci} 194762306a36Sopenharmony_cisubsys_initcall(omap_dma_init); 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic void __exit omap_dma_exit(void) 195062306a36Sopenharmony_ci{ 195162306a36Sopenharmony_ci platform_driver_unregister(&omap_dma_driver); 195262306a36Sopenharmony_ci} 195362306a36Sopenharmony_cimodule_exit(omap_dma_exit); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ciMODULE_AUTHOR("Russell King"); 195662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1957