162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Core driver for the Intel integrated DMA 64-bit 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Intel Corporation 662306a36Sopenharmony_ci * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/dmaengine.h> 1262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1362306a36Sopenharmony_ci#include <linux/dmapool.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/dma/idma64.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "idma64.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* For now we support only two channels */ 2462306a36Sopenharmony_ci#define IDMA64_NR_CHAN 2 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct device *chan2dev(struct dma_chan *chan) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return &chan->dev->device; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void idma64_off(struct idma64 *idma64) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned short count = 100; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci dma_writel(idma64, CFG, 0); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci channel_clear_bit(idma64, MASK(XFER), idma64->all_chan_mask); 4262306a36Sopenharmony_ci channel_clear_bit(idma64, MASK(BLOCK), idma64->all_chan_mask); 4362306a36Sopenharmony_ci channel_clear_bit(idma64, MASK(SRC_TRAN), idma64->all_chan_mask); 4462306a36Sopenharmony_ci channel_clear_bit(idma64, MASK(DST_TRAN), idma64->all_chan_mask); 4562306a36Sopenharmony_ci channel_clear_bit(idma64, MASK(ERROR), idma64->all_chan_mask); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci do { 4862306a36Sopenharmony_ci cpu_relax(); 4962306a36Sopenharmony_ci } while (dma_readl(idma64, CFG) & IDMA64_CFG_DMA_EN && --count); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void idma64_on(struct idma64 *idma64) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci dma_writel(idma64, CFG, IDMA64_CFG_DMA_EN); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void idma64_chan_init(struct idma64 *idma64, struct idma64_chan *idma64c) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci u32 cfghi = IDMA64C_CFGH_SRC_PER(1) | IDMA64C_CFGH_DST_PER(0); 6262306a36Sopenharmony_ci u32 cfglo = 0; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Set default burst alignment */ 6562306a36Sopenharmony_ci cfglo |= IDMA64C_CFGL_DST_BURST_ALIGN | IDMA64C_CFGL_SRC_BURST_ALIGN; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci channel_writel(idma64c, CFG_LO, cfglo); 6862306a36Sopenharmony_ci channel_writel(idma64c, CFG_HI, cfghi); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Enable interrupts */ 7162306a36Sopenharmony_ci channel_set_bit(idma64, MASK(XFER), idma64c->mask); 7262306a36Sopenharmony_ci channel_set_bit(idma64, MASK(ERROR), idma64c->mask); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * Enforce the controller to be turned on. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * The iDMA is turned off in ->probe() and looses context during system 7862306a36Sopenharmony_ci * suspend / resume cycle. That's why we have to enable it each time we 7962306a36Sopenharmony_ci * use it. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci idma64_on(idma64); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void idma64_chan_stop(struct idma64 *idma64, struct idma64_chan *idma64c) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci channel_clear_bit(idma64, CH_EN, idma64c->mask); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void idma64_chan_start(struct idma64 *idma64, struct idma64_chan *idma64c) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct idma64_desc *desc = idma64c->desc; 9262306a36Sopenharmony_ci struct idma64_hw_desc *hw = &desc->hw[0]; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci channel_writeq(idma64c, SAR, 0); 9562306a36Sopenharmony_ci channel_writeq(idma64c, DAR, 0); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci channel_writel(idma64c, CTL_HI, IDMA64C_CTLH_BLOCK_TS(~0UL)); 9862306a36Sopenharmony_ci channel_writel(idma64c, CTL_LO, IDMA64C_CTLL_LLP_S_EN | IDMA64C_CTLL_LLP_D_EN); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci channel_writeq(idma64c, LLP, hw->llp); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci channel_set_bit(idma64, CH_EN, idma64c->mask); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void idma64_stop_transfer(struct idma64_chan *idma64c) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct idma64 *idma64 = to_idma64(idma64c->vchan.chan.device); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci idma64_chan_stop(idma64, idma64c); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void idma64_start_transfer(struct idma64_chan *idma64c) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct idma64 *idma64 = to_idma64(idma64c->vchan.chan.device); 11562306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Get the next descriptor */ 11862306a36Sopenharmony_ci vdesc = vchan_next_desc(&idma64c->vchan); 11962306a36Sopenharmony_ci if (!vdesc) { 12062306a36Sopenharmony_ci idma64c->desc = NULL; 12162306a36Sopenharmony_ci return; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci list_del(&vdesc->node); 12562306a36Sopenharmony_ci idma64c->desc = to_idma64_desc(vdesc); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* Configure the channel */ 12862306a36Sopenharmony_ci idma64_chan_init(idma64, idma64c); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Start the channel with a new descriptor */ 13162306a36Sopenharmony_ci idma64_chan_start(idma64, idma64c); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void idma64_chan_irq(struct idma64 *idma64, unsigned short c, 13762306a36Sopenharmony_ci u32 status_err, u32 status_xfer) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct idma64_chan *idma64c = &idma64->chan[c]; 14062306a36Sopenharmony_ci struct dma_chan_percpu *stat; 14162306a36Sopenharmony_ci struct idma64_desc *desc; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci stat = this_cpu_ptr(idma64c->vchan.chan.local); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci spin_lock(&idma64c->vchan.lock); 14662306a36Sopenharmony_ci desc = idma64c->desc; 14762306a36Sopenharmony_ci if (desc) { 14862306a36Sopenharmony_ci if (status_err & (1 << c)) { 14962306a36Sopenharmony_ci dma_writel(idma64, CLEAR(ERROR), idma64c->mask); 15062306a36Sopenharmony_ci desc->status = DMA_ERROR; 15162306a36Sopenharmony_ci } else if (status_xfer & (1 << c)) { 15262306a36Sopenharmony_ci dma_writel(idma64, CLEAR(XFER), idma64c->mask); 15362306a36Sopenharmony_ci desc->status = DMA_COMPLETE; 15462306a36Sopenharmony_ci vchan_cookie_complete(&desc->vdesc); 15562306a36Sopenharmony_ci stat->bytes_transferred += desc->length; 15662306a36Sopenharmony_ci idma64_start_transfer(idma64c); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* idma64_start_transfer() updates idma64c->desc */ 16062306a36Sopenharmony_ci if (idma64c->desc == NULL || desc->status == DMA_ERROR) 16162306a36Sopenharmony_ci idma64_stop_transfer(idma64c); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci spin_unlock(&idma64c->vchan.lock); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic irqreturn_t idma64_irq(int irq, void *dev) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct idma64 *idma64 = dev; 16962306a36Sopenharmony_ci u32 status = dma_readl(idma64, STATUS_INT); 17062306a36Sopenharmony_ci u32 status_xfer; 17162306a36Sopenharmony_ci u32 status_err; 17262306a36Sopenharmony_ci unsigned short i; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci dev_vdbg(idma64->dma.dev, "%s: status=%#x\n", __func__, status); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Check if we have any interrupt from the DMA controller */ 17762306a36Sopenharmony_ci if (!status) 17862306a36Sopenharmony_ci return IRQ_NONE; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci status_xfer = dma_readl(idma64, RAW(XFER)); 18162306a36Sopenharmony_ci status_err = dma_readl(idma64, RAW(ERROR)); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < idma64->dma.chancnt; i++) 18462306a36Sopenharmony_ci idma64_chan_irq(idma64, i, status_err, status_xfer); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return IRQ_HANDLED; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic struct idma64_desc *idma64_alloc_desc(unsigned int ndesc) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct idma64_desc *desc; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 19662306a36Sopenharmony_ci if (!desc) 19762306a36Sopenharmony_ci return NULL; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci desc->hw = kcalloc(ndesc, sizeof(*desc->hw), GFP_NOWAIT); 20062306a36Sopenharmony_ci if (!desc->hw) { 20162306a36Sopenharmony_ci kfree(desc); 20262306a36Sopenharmony_ci return NULL; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return desc; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void idma64_desc_free(struct idma64_chan *idma64c, 20962306a36Sopenharmony_ci struct idma64_desc *desc) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct idma64_hw_desc *hw; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (desc->ndesc) { 21462306a36Sopenharmony_ci unsigned int i = desc->ndesc; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci do { 21762306a36Sopenharmony_ci hw = &desc->hw[--i]; 21862306a36Sopenharmony_ci dma_pool_free(idma64c->pool, hw->lli, hw->llp); 21962306a36Sopenharmony_ci } while (i); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci kfree(desc->hw); 22362306a36Sopenharmony_ci kfree(desc); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void idma64_vdesc_free(struct virt_dma_desc *vdesc) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(vdesc->tx.chan); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci idma64_desc_free(idma64c, to_idma64_desc(vdesc)); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void idma64_hw_desc_fill(struct idma64_hw_desc *hw, 23462306a36Sopenharmony_ci struct dma_slave_config *config, 23562306a36Sopenharmony_ci enum dma_transfer_direction direction, u64 llp) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct idma64_lli *lli = hw->lli; 23862306a36Sopenharmony_ci u64 sar, dar; 23962306a36Sopenharmony_ci u32 ctlhi = IDMA64C_CTLH_BLOCK_TS(hw->len); 24062306a36Sopenharmony_ci u32 ctllo = IDMA64C_CTLL_LLP_S_EN | IDMA64C_CTLL_LLP_D_EN; 24162306a36Sopenharmony_ci u32 src_width, dst_width; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 24462306a36Sopenharmony_ci sar = hw->phys; 24562306a36Sopenharmony_ci dar = config->dst_addr; 24662306a36Sopenharmony_ci ctllo |= IDMA64C_CTLL_DST_FIX | IDMA64C_CTLL_SRC_INC | 24762306a36Sopenharmony_ci IDMA64C_CTLL_FC_M2P; 24862306a36Sopenharmony_ci src_width = __ffs(sar | hw->len | 4); 24962306a36Sopenharmony_ci dst_width = __ffs(config->dst_addr_width); 25062306a36Sopenharmony_ci } else { /* DMA_DEV_TO_MEM */ 25162306a36Sopenharmony_ci sar = config->src_addr; 25262306a36Sopenharmony_ci dar = hw->phys; 25362306a36Sopenharmony_ci ctllo |= IDMA64C_CTLL_DST_INC | IDMA64C_CTLL_SRC_FIX | 25462306a36Sopenharmony_ci IDMA64C_CTLL_FC_P2M; 25562306a36Sopenharmony_ci src_width = __ffs(config->src_addr_width); 25662306a36Sopenharmony_ci dst_width = __ffs(dar | hw->len | 4); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci lli->sar = sar; 26062306a36Sopenharmony_ci lli->dar = dar; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci lli->ctlhi = ctlhi; 26362306a36Sopenharmony_ci lli->ctllo = ctllo | 26462306a36Sopenharmony_ci IDMA64C_CTLL_SRC_MSIZE(config->src_maxburst) | 26562306a36Sopenharmony_ci IDMA64C_CTLL_DST_MSIZE(config->dst_maxburst) | 26662306a36Sopenharmony_ci IDMA64C_CTLL_DST_WIDTH(dst_width) | 26762306a36Sopenharmony_ci IDMA64C_CTLL_SRC_WIDTH(src_width); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci lli->llp = llp; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void idma64_desc_fill(struct idma64_chan *idma64c, 27362306a36Sopenharmony_ci struct idma64_desc *desc) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct dma_slave_config *config = &idma64c->config; 27662306a36Sopenharmony_ci unsigned int i = desc->ndesc; 27762306a36Sopenharmony_ci struct idma64_hw_desc *hw = &desc->hw[i - 1]; 27862306a36Sopenharmony_ci struct idma64_lli *lli = hw->lli; 27962306a36Sopenharmony_ci u64 llp = 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Fill the hardware descriptors and link them to a list */ 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci hw = &desc->hw[--i]; 28462306a36Sopenharmony_ci idma64_hw_desc_fill(hw, config, desc->direction, llp); 28562306a36Sopenharmony_ci llp = hw->llp; 28662306a36Sopenharmony_ci desc->length += hw->len; 28762306a36Sopenharmony_ci } while (i); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Trigger an interrupt after the last block is transfered */ 29062306a36Sopenharmony_ci lli->ctllo |= IDMA64C_CTLL_INT_EN; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Disable LLP transfer in the last block */ 29362306a36Sopenharmony_ci lli->ctllo &= ~(IDMA64C_CTLL_LLP_S_EN | IDMA64C_CTLL_LLP_D_EN); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *idma64_prep_slave_sg( 29762306a36Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, 29862306a36Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 29962306a36Sopenharmony_ci unsigned long flags, void *context) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 30262306a36Sopenharmony_ci struct idma64_desc *desc; 30362306a36Sopenharmony_ci struct scatterlist *sg; 30462306a36Sopenharmony_ci unsigned int i; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci desc = idma64_alloc_desc(sg_len); 30762306a36Sopenharmony_ci if (!desc) 30862306a36Sopenharmony_ci return NULL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 31162306a36Sopenharmony_ci struct idma64_hw_desc *hw = &desc->hw[i]; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Allocate DMA capable memory for hardware descriptor */ 31462306a36Sopenharmony_ci hw->lli = dma_pool_alloc(idma64c->pool, GFP_NOWAIT, &hw->llp); 31562306a36Sopenharmony_ci if (!hw->lli) { 31662306a36Sopenharmony_ci desc->ndesc = i; 31762306a36Sopenharmony_ci idma64_desc_free(idma64c, desc); 31862306a36Sopenharmony_ci return NULL; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci hw->phys = sg_dma_address(sg); 32262306a36Sopenharmony_ci hw->len = sg_dma_len(sg); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci desc->ndesc = sg_len; 32662306a36Sopenharmony_ci desc->direction = direction; 32762306a36Sopenharmony_ci desc->status = DMA_IN_PROGRESS; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci idma64_desc_fill(idma64c, desc); 33062306a36Sopenharmony_ci return vchan_tx_prep(&idma64c->vchan, &desc->vdesc, flags); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void idma64_issue_pending(struct dma_chan *chan) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 33662306a36Sopenharmony_ci unsigned long flags; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci spin_lock_irqsave(&idma64c->vchan.lock, flags); 33962306a36Sopenharmony_ci if (vchan_issue_pending(&idma64c->vchan) && !idma64c->desc) 34062306a36Sopenharmony_ci idma64_start_transfer(idma64c); 34162306a36Sopenharmony_ci spin_unlock_irqrestore(&idma64c->vchan.lock, flags); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic size_t idma64_active_desc_size(struct idma64_chan *idma64c) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct idma64_desc *desc = idma64c->desc; 34762306a36Sopenharmony_ci struct idma64_hw_desc *hw; 34862306a36Sopenharmony_ci size_t bytes = desc->length; 34962306a36Sopenharmony_ci u64 llp = channel_readq(idma64c, LLP); 35062306a36Sopenharmony_ci u32 ctlhi = channel_readl(idma64c, CTL_HI); 35162306a36Sopenharmony_ci unsigned int i = 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci do { 35462306a36Sopenharmony_ci hw = &desc->hw[i]; 35562306a36Sopenharmony_ci if (hw->llp == llp) 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci bytes -= hw->len; 35862306a36Sopenharmony_ci } while (++i < desc->ndesc); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!i) 36162306a36Sopenharmony_ci return bytes; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* The current chunk is not fully transfered yet */ 36462306a36Sopenharmony_ci bytes += desc->hw[--i].len; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return bytes - IDMA64C_CTLH_BLOCK_TS(ctlhi); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic enum dma_status idma64_tx_status(struct dma_chan *chan, 37062306a36Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *state) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 37362306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 37462306a36Sopenharmony_ci enum dma_status status; 37562306a36Sopenharmony_ci size_t bytes; 37662306a36Sopenharmony_ci unsigned long flags; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci status = dma_cookie_status(chan, cookie, state); 37962306a36Sopenharmony_ci if (status == DMA_COMPLETE) 38062306a36Sopenharmony_ci return status; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci spin_lock_irqsave(&idma64c->vchan.lock, flags); 38362306a36Sopenharmony_ci vdesc = vchan_find_desc(&idma64c->vchan, cookie); 38462306a36Sopenharmony_ci if (idma64c->desc && cookie == idma64c->desc->vdesc.tx.cookie) { 38562306a36Sopenharmony_ci bytes = idma64_active_desc_size(idma64c); 38662306a36Sopenharmony_ci dma_set_residue(state, bytes); 38762306a36Sopenharmony_ci status = idma64c->desc->status; 38862306a36Sopenharmony_ci } else if (vdesc) { 38962306a36Sopenharmony_ci bytes = to_idma64_desc(vdesc)->length; 39062306a36Sopenharmony_ci dma_set_residue(state, bytes); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci spin_unlock_irqrestore(&idma64c->vchan.lock, flags); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return status; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void convert_burst(u32 *maxburst) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci if (*maxburst) 40062306a36Sopenharmony_ci *maxburst = __fls(*maxburst); 40162306a36Sopenharmony_ci else 40262306a36Sopenharmony_ci *maxburst = 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int idma64_slave_config(struct dma_chan *chan, 40662306a36Sopenharmony_ci struct dma_slave_config *config) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci memcpy(&idma64c->config, config, sizeof(idma64c->config)); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci convert_burst(&idma64c->config.src_maxburst); 41362306a36Sopenharmony_ci convert_burst(&idma64c->config.dst_maxburst); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void idma64_chan_deactivate(struct idma64_chan *idma64c, bool drain) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci unsigned short count = 100; 42162306a36Sopenharmony_ci u32 cfglo; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci cfglo = channel_readl(idma64c, CFG_LO); 42462306a36Sopenharmony_ci if (drain) 42562306a36Sopenharmony_ci cfglo |= IDMA64C_CFGL_CH_DRAIN; 42662306a36Sopenharmony_ci else 42762306a36Sopenharmony_ci cfglo &= ~IDMA64C_CFGL_CH_DRAIN; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci channel_writel(idma64c, CFG_LO, cfglo | IDMA64C_CFGL_CH_SUSP); 43062306a36Sopenharmony_ci do { 43162306a36Sopenharmony_ci udelay(1); 43262306a36Sopenharmony_ci cfglo = channel_readl(idma64c, CFG_LO); 43362306a36Sopenharmony_ci } while (!(cfglo & IDMA64C_CFGL_FIFO_EMPTY) && --count); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void idma64_chan_activate(struct idma64_chan *idma64c) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci u32 cfglo; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cfglo = channel_readl(idma64c, CFG_LO); 44162306a36Sopenharmony_ci channel_writel(idma64c, CFG_LO, cfglo & ~IDMA64C_CFGL_CH_SUSP); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int idma64_pause(struct dma_chan *chan) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 44762306a36Sopenharmony_ci unsigned long flags; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci spin_lock_irqsave(&idma64c->vchan.lock, flags); 45062306a36Sopenharmony_ci if (idma64c->desc && idma64c->desc->status == DMA_IN_PROGRESS) { 45162306a36Sopenharmony_ci idma64_chan_deactivate(idma64c, false); 45262306a36Sopenharmony_ci idma64c->desc->status = DMA_PAUSED; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci spin_unlock_irqrestore(&idma64c->vchan.lock, flags); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int idma64_resume(struct dma_chan *chan) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 46262306a36Sopenharmony_ci unsigned long flags; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci spin_lock_irqsave(&idma64c->vchan.lock, flags); 46562306a36Sopenharmony_ci if (idma64c->desc && idma64c->desc->status == DMA_PAUSED) { 46662306a36Sopenharmony_ci idma64c->desc->status = DMA_IN_PROGRESS; 46762306a36Sopenharmony_ci idma64_chan_activate(idma64c); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci spin_unlock_irqrestore(&idma64c->vchan.lock, flags); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int idma64_terminate_all(struct dma_chan *chan) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 47762306a36Sopenharmony_ci unsigned long flags; 47862306a36Sopenharmony_ci LIST_HEAD(head); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci spin_lock_irqsave(&idma64c->vchan.lock, flags); 48162306a36Sopenharmony_ci idma64_chan_deactivate(idma64c, true); 48262306a36Sopenharmony_ci idma64_stop_transfer(idma64c); 48362306a36Sopenharmony_ci if (idma64c->desc) { 48462306a36Sopenharmony_ci idma64_vdesc_free(&idma64c->desc->vdesc); 48562306a36Sopenharmony_ci idma64c->desc = NULL; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci vchan_get_all_descriptors(&idma64c->vchan, &head); 48862306a36Sopenharmony_ci spin_unlock_irqrestore(&idma64c->vchan.lock, flags); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci vchan_dma_desc_free_list(&idma64c->vchan, &head); 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void idma64_synchronize(struct dma_chan *chan) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci vchan_synchronize(&idma64c->vchan); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int idma64_alloc_chan_resources(struct dma_chan *chan) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Create a pool of consistent memory blocks for hardware descriptors */ 50662306a36Sopenharmony_ci idma64c->pool = dma_pool_create(dev_name(chan2dev(chan)), 50762306a36Sopenharmony_ci chan->device->dev, 50862306a36Sopenharmony_ci sizeof(struct idma64_lli), 8, 0); 50962306a36Sopenharmony_ci if (!idma64c->pool) { 51062306a36Sopenharmony_ci dev_err(chan2dev(chan), "No memory for descriptors\n"); 51162306a36Sopenharmony_ci return -ENOMEM; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void idma64_free_chan_resources(struct dma_chan *chan) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct idma64_chan *idma64c = to_idma64_chan(chan); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci vchan_free_chan_resources(to_virt_chan(chan)); 52262306a36Sopenharmony_ci dma_pool_destroy(idma64c->pool); 52362306a36Sopenharmony_ci idma64c->pool = NULL; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci#define IDMA64_BUSWIDTHS \ 52962306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 53062306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 53162306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int idma64_probe(struct idma64_chip *chip) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct idma64 *idma64; 53662306a36Sopenharmony_ci unsigned short nr_chan = IDMA64_NR_CHAN; 53762306a36Sopenharmony_ci unsigned short i; 53862306a36Sopenharmony_ci int ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci idma64 = devm_kzalloc(chip->dev, sizeof(*idma64), GFP_KERNEL); 54162306a36Sopenharmony_ci if (!idma64) 54262306a36Sopenharmony_ci return -ENOMEM; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci idma64->regs = chip->regs; 54562306a36Sopenharmony_ci chip->idma64 = idma64; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci idma64->chan = devm_kcalloc(chip->dev, nr_chan, sizeof(*idma64->chan), 54862306a36Sopenharmony_ci GFP_KERNEL); 54962306a36Sopenharmony_ci if (!idma64->chan) 55062306a36Sopenharmony_ci return -ENOMEM; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci idma64->all_chan_mask = (1 << nr_chan) - 1; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Turn off iDMA controller */ 55562306a36Sopenharmony_ci idma64_off(idma64); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = devm_request_irq(chip->dev, chip->irq, idma64_irq, IRQF_SHARED, 55862306a36Sopenharmony_ci dev_name(chip->dev), idma64); 55962306a36Sopenharmony_ci if (ret) 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci INIT_LIST_HEAD(&idma64->dma.channels); 56362306a36Sopenharmony_ci for (i = 0; i < nr_chan; i++) { 56462306a36Sopenharmony_ci struct idma64_chan *idma64c = &idma64->chan[i]; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci idma64c->vchan.desc_free = idma64_vdesc_free; 56762306a36Sopenharmony_ci vchan_init(&idma64c->vchan, &idma64->dma); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci idma64c->regs = idma64->regs + i * IDMA64_CH_LENGTH; 57062306a36Sopenharmony_ci idma64c->mask = BIT(i); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, idma64->dma.cap_mask); 57462306a36Sopenharmony_ci dma_cap_set(DMA_PRIVATE, idma64->dma.cap_mask); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci idma64->dma.device_alloc_chan_resources = idma64_alloc_chan_resources; 57762306a36Sopenharmony_ci idma64->dma.device_free_chan_resources = idma64_free_chan_resources; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci idma64->dma.device_prep_slave_sg = idma64_prep_slave_sg; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci idma64->dma.device_issue_pending = idma64_issue_pending; 58262306a36Sopenharmony_ci idma64->dma.device_tx_status = idma64_tx_status; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci idma64->dma.device_config = idma64_slave_config; 58562306a36Sopenharmony_ci idma64->dma.device_pause = idma64_pause; 58662306a36Sopenharmony_ci idma64->dma.device_resume = idma64_resume; 58762306a36Sopenharmony_ci idma64->dma.device_terminate_all = idma64_terminate_all; 58862306a36Sopenharmony_ci idma64->dma.device_synchronize = idma64_synchronize; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci idma64->dma.src_addr_widths = IDMA64_BUSWIDTHS; 59162306a36Sopenharmony_ci idma64->dma.dst_addr_widths = IDMA64_BUSWIDTHS; 59262306a36Sopenharmony_ci idma64->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 59362306a36Sopenharmony_ci idma64->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci idma64->dma.dev = chip->sysdev; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci dma_set_max_seg_size(idma64->dma.dev, IDMA64C_CTLH_BLOCK_TS_MASK); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci ret = dma_async_device_register(&idma64->dma); 60062306a36Sopenharmony_ci if (ret) 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci dev_info(chip->dev, "Found Intel integrated DMA 64-bit\n"); 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void idma64_remove(struct idma64_chip *chip) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct idma64 *idma64 = chip->idma64; 61062306a36Sopenharmony_ci unsigned short i; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci dma_async_device_unregister(&idma64->dma); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * Explicitly call devm_request_irq() to avoid the side effects with 61662306a36Sopenharmony_ci * the scheduled tasklets. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci devm_free_irq(chip->dev, chip->irq, idma64); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci for (i = 0; i < idma64->dma.chancnt; i++) { 62162306a36Sopenharmony_ci struct idma64_chan *idma64c = &idma64->chan[i]; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci tasklet_kill(&idma64c->vchan.task); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int idma64_platform_probe(struct platform_device *pdev) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct idma64_chip *chip; 63262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 63362306a36Sopenharmony_ci struct device *sysdev = dev->parent; 63462306a36Sopenharmony_ci int ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 63762306a36Sopenharmony_ci if (!chip) 63862306a36Sopenharmony_ci return -ENOMEM; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci chip->irq = platform_get_irq(pdev, 0); 64162306a36Sopenharmony_ci if (chip->irq < 0) 64262306a36Sopenharmony_ci return chip->irq; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci chip->regs = devm_platform_ioremap_resource(pdev, 0); 64562306a36Sopenharmony_ci if (IS_ERR(chip->regs)) 64662306a36Sopenharmony_ci return PTR_ERR(chip->regs); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); 64962306a36Sopenharmony_ci if (ret) 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci chip->dev = dev; 65362306a36Sopenharmony_ci chip->sysdev = sysdev; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ret = idma64_probe(chip); 65662306a36Sopenharmony_ci if (ret) 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci platform_set_drvdata(pdev, chip); 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int idma64_platform_remove(struct platform_device *pdev) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct idma64_chip *chip = platform_get_drvdata(pdev); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci idma64_remove(chip); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic int __maybe_unused idma64_pm_suspend(struct device *dev) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct idma64_chip *chip = dev_get_drvdata(dev); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci idma64_off(chip->idma64); 67762306a36Sopenharmony_ci return 0; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int __maybe_unused idma64_pm_resume(struct device *dev) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct idma64_chip *chip = dev_get_drvdata(dev); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci idma64_on(chip->idma64); 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic const struct dev_pm_ops idma64_dev_pm_ops = { 68962306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(idma64_pm_suspend, idma64_pm_resume) 69062306a36Sopenharmony_ci}; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic struct platform_driver idma64_platform_driver = { 69362306a36Sopenharmony_ci .probe = idma64_platform_probe, 69462306a36Sopenharmony_ci .remove = idma64_platform_remove, 69562306a36Sopenharmony_ci .driver = { 69662306a36Sopenharmony_ci .name = LPSS_IDMA64_DRIVER_NAME, 69762306a36Sopenharmony_ci .pm = &idma64_dev_pm_ops, 69862306a36Sopenharmony_ci }, 69962306a36Sopenharmony_ci}; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cimodule_platform_driver(idma64_platform_driver); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 70462306a36Sopenharmony_ciMODULE_DESCRIPTION("iDMA64 core driver"); 70562306a36Sopenharmony_ciMODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); 70662306a36Sopenharmony_ciMODULE_ALIAS("platform:" LPSS_IDMA64_DRIVER_NAME); 707