162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale MPC85xx, MPC83xx DMA Engine support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: 862306a36Sopenharmony_ci * Zhang Wei <wei.zhang@freescale.com>, Jul 2007 962306a36Sopenharmony_ci * Ebony Zhu <ebony.zhu@freescale.com>, May 2007 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Description: 1262306a36Sopenharmony_ci * DMA engine driver for Freescale MPC8540 DMA controller, which is 1362306a36Sopenharmony_ci * also fit for MPC8560, MPC8555, MPC8548, MPC8641, and etc. 1462306a36Sopenharmony_ci * The support for MPC8349 DMA controller is also added. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This driver instructs the DMA controller to issue the PCI Read Multiple 1762306a36Sopenharmony_ci * command for PCI read operations, instead of using the default PCI Read Line 1862306a36Sopenharmony_ci * command. Please be aware that this setting may result in read pre-fetching 1962306a36Sopenharmony_ci * on some platforms. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/pci.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci#include <linux/dmaengine.h> 2862306a36Sopenharmony_ci#include <linux/delay.h> 2962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3062306a36Sopenharmony_ci#include <linux/dmapool.h> 3162306a36Sopenharmony_ci#include <linux/of.h> 3262306a36Sopenharmony_ci#include <linux/of_address.h> 3362306a36Sopenharmony_ci#include <linux/of_irq.h> 3462306a36Sopenharmony_ci#include <linux/platform_device.h> 3562306a36Sopenharmony_ci#include <linux/fsldma.h> 3662306a36Sopenharmony_ci#include "dmaengine.h" 3762306a36Sopenharmony_ci#include "fsldma.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define chan_dbg(chan, fmt, arg...) \ 4062306a36Sopenharmony_ci dev_dbg(chan->dev, "%s: " fmt, chan->name, ##arg) 4162306a36Sopenharmony_ci#define chan_err(chan, fmt, arg...) \ 4262306a36Sopenharmony_ci dev_err(chan->dev, "%s: " fmt, chan->name, ##arg) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const char msg_ld_oom[] = "No free memory for link descriptor"; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Register Helpers 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void set_sr(struct fsldma_chan *chan, u32 val) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci FSL_DMA_OUT(chan, &chan->regs->sr, val, 32); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic u32 get_sr(struct fsldma_chan *chan) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return FSL_DMA_IN(chan, &chan->regs->sr, 32); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void set_mr(struct fsldma_chan *chan, u32 val) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci FSL_DMA_OUT(chan, &chan->regs->mr, val, 32); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic u32 get_mr(struct fsldma_chan *chan) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return FSL_DMA_IN(chan, &chan->regs->mr, 32); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void set_cdar(struct fsldma_chan *chan, dma_addr_t addr) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic dma_addr_t get_cdar(struct fsldma_chan *chan) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void set_bcr(struct fsldma_chan *chan, u32 val) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic u32 get_bcr(struct fsldma_chan *chan) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return FSL_DMA_IN(chan, &chan->regs->bcr, 32); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * Descriptor Helpers 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void set_desc_cnt(struct fsldma_chan *chan, 9562306a36Sopenharmony_ci struct fsl_dma_ld_hw *hw, u32 count) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci hw->count = CPU_TO_DMA(chan, count, 32); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void set_desc_src(struct fsldma_chan *chan, 10162306a36Sopenharmony_ci struct fsl_dma_ld_hw *hw, dma_addr_t src) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci u64 snoop_bits; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) 10662306a36Sopenharmony_ci ? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0; 10762306a36Sopenharmony_ci hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void set_desc_dst(struct fsldma_chan *chan, 11162306a36Sopenharmony_ci struct fsl_dma_ld_hw *hw, dma_addr_t dst) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u64 snoop_bits; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) 11662306a36Sopenharmony_ci ? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0; 11762306a36Sopenharmony_ci hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void set_desc_next(struct fsldma_chan *chan, 12162306a36Sopenharmony_ci struct fsl_dma_ld_hw *hw, dma_addr_t next) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci u64 snoop_bits; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX) 12662306a36Sopenharmony_ci ? FSL_DMA_SNEN : 0; 12762306a36Sopenharmony_ci hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void set_ld_eol(struct fsldma_chan *chan, struct fsl_desc_sw *desc) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci u64 snoop_bits; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX) 13562306a36Sopenharmony_ci ? FSL_DMA_SNEN : 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci desc->hw.next_ln_addr = CPU_TO_DMA(chan, 13862306a36Sopenharmony_ci DMA_TO_CPU(chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL 13962306a36Sopenharmony_ci | snoop_bits, 64); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * DMA Engine Hardware Control Helpers 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void dma_init(struct fsldma_chan *chan) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci /* Reset the channel */ 14962306a36Sopenharmony_ci set_mr(chan, 0); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci switch (chan->feature & FSL_DMA_IP_MASK) { 15262306a36Sopenharmony_ci case FSL_DMA_IP_85XX: 15362306a36Sopenharmony_ci /* Set the channel to below modes: 15462306a36Sopenharmony_ci * EIE - Error interrupt enable 15562306a36Sopenharmony_ci * EOLNIE - End of links interrupt enable 15662306a36Sopenharmony_ci * BWC - Bandwidth sharing among channels 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci set_mr(chan, FSL_DMA_MR_BWC | FSL_DMA_MR_EIE 15962306a36Sopenharmony_ci | FSL_DMA_MR_EOLNIE); 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case FSL_DMA_IP_83XX: 16262306a36Sopenharmony_ci /* Set the channel to below modes: 16362306a36Sopenharmony_ci * EOTIE - End-of-transfer interrupt enable 16462306a36Sopenharmony_ci * PRC_RM - PCI read multiple 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci set_mr(chan, FSL_DMA_MR_EOTIE | FSL_DMA_MR_PRC_RM); 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int dma_is_idle(struct fsldma_chan *chan) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u32 sr = get_sr(chan); 17462306a36Sopenharmony_ci return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * Start the DMA controller 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Preconditions: 18162306a36Sopenharmony_ci * - the CDAR register must point to the start descriptor 18262306a36Sopenharmony_ci * - the MRn[CS] bit must be cleared 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic void dma_start(struct fsldma_chan *chan) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 mode; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mode = get_mr(chan); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) { 19162306a36Sopenharmony_ci set_bcr(chan, 0); 19262306a36Sopenharmony_ci mode |= FSL_DMA_MR_EMP_EN; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_EMP_EN; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (chan->feature & FSL_DMA_CHAN_START_EXT) { 19862306a36Sopenharmony_ci mode |= FSL_DMA_MR_EMS_EN; 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_EMS_EN; 20162306a36Sopenharmony_ci mode |= FSL_DMA_MR_CS; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci set_mr(chan, mode); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void dma_halt(struct fsldma_chan *chan) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u32 mode; 21062306a36Sopenharmony_ci int i; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* read the mode register */ 21362306a36Sopenharmony_ci mode = get_mr(chan); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* 21662306a36Sopenharmony_ci * The 85xx controller supports channel abort, which will stop 21762306a36Sopenharmony_ci * the current transfer. On 83xx, this bit is the transfer error 21862306a36Sopenharmony_ci * mask bit, which should not be changed. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) { 22162306a36Sopenharmony_ci mode |= FSL_DMA_MR_CA; 22262306a36Sopenharmony_ci set_mr(chan, mode); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_CA; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* stop the DMA controller */ 22862306a36Sopenharmony_ci mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN); 22962306a36Sopenharmony_ci set_mr(chan, mode); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* wait for the DMA controller to become idle */ 23262306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 23362306a36Sopenharmony_ci if (dma_is_idle(chan)) 23462306a36Sopenharmony_ci return; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci udelay(10); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!dma_is_idle(chan)) 24062306a36Sopenharmony_ci chan_err(chan, "DMA halt timeout!\n"); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * fsl_chan_set_src_loop_size - Set source address hold transfer size 24562306a36Sopenharmony_ci * @chan : Freescale DMA channel 24662306a36Sopenharmony_ci * @size : Address loop size, 0 for disable loop 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * The set source address hold transfer size. The source 24962306a36Sopenharmony_ci * address hold or loop transfer size is when the DMA transfer 25062306a36Sopenharmony_ci * data from source address (SA), if the loop size is 4, the DMA will 25162306a36Sopenharmony_ci * read data from SA, SA + 1, SA + 2, SA + 3, then loop back to SA, 25262306a36Sopenharmony_ci * SA + 1 ... and so on. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_cistatic void fsl_chan_set_src_loop_size(struct fsldma_chan *chan, int size) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci u32 mode; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci mode = get_mr(chan); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci switch (size) { 26162306a36Sopenharmony_ci case 0: 26262306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_SAHE; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case 1: 26562306a36Sopenharmony_ci case 2: 26662306a36Sopenharmony_ci case 4: 26762306a36Sopenharmony_ci case 8: 26862306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_SAHTS_MASK; 26962306a36Sopenharmony_ci mode |= FSL_DMA_MR_SAHE | (__ilog2(size) << 14); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci set_mr(chan, mode); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * fsl_chan_set_dst_loop_size - Set destination address hold transfer size 27862306a36Sopenharmony_ci * @chan : Freescale DMA channel 27962306a36Sopenharmony_ci * @size : Address loop size, 0 for disable loop 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * The set destination address hold transfer size. The destination 28262306a36Sopenharmony_ci * address hold or loop transfer size is when the DMA transfer 28362306a36Sopenharmony_ci * data to destination address (TA), if the loop size is 4, the DMA will 28462306a36Sopenharmony_ci * write data to TA, TA + 1, TA + 2, TA + 3, then loop back to TA, 28562306a36Sopenharmony_ci * TA + 1 ... and so on. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic void fsl_chan_set_dst_loop_size(struct fsldma_chan *chan, int size) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci u32 mode; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mode = get_mr(chan); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci switch (size) { 29462306a36Sopenharmony_ci case 0: 29562306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_DAHE; 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case 1: 29862306a36Sopenharmony_ci case 2: 29962306a36Sopenharmony_ci case 4: 30062306a36Sopenharmony_ci case 8: 30162306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_DAHTS_MASK; 30262306a36Sopenharmony_ci mode |= FSL_DMA_MR_DAHE | (__ilog2(size) << 16); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci set_mr(chan, mode); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/** 31062306a36Sopenharmony_ci * fsl_chan_set_request_count - Set DMA Request Count for external control 31162306a36Sopenharmony_ci * @chan : Freescale DMA channel 31262306a36Sopenharmony_ci * @size : Number of bytes to transfer in a single request 31362306a36Sopenharmony_ci * 31462306a36Sopenharmony_ci * The Freescale DMA channel can be controlled by the external signal DREQ#. 31562306a36Sopenharmony_ci * The DMA request count is how many bytes are allowed to transfer before 31662306a36Sopenharmony_ci * pausing the channel, after which a new assertion of DREQ# resumes channel 31762306a36Sopenharmony_ci * operation. 31862306a36Sopenharmony_ci * 31962306a36Sopenharmony_ci * A size of 0 disables external pause control. The maximum size is 1024. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistatic void fsl_chan_set_request_count(struct fsldma_chan *chan, int size) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci u32 mode; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci BUG_ON(size > 1024); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci mode = get_mr(chan); 32862306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_BWC_MASK; 32962306a36Sopenharmony_ci mode |= (__ilog2(size) << 24) & FSL_DMA_MR_BWC_MASK; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci set_mr(chan, mode); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * fsl_chan_toggle_ext_pause - Toggle channel external pause status 33662306a36Sopenharmony_ci * @chan : Freescale DMA channel 33762306a36Sopenharmony_ci * @enable : 0 is disabled, 1 is enabled. 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * The Freescale DMA channel can be controlled by the external signal DREQ#. 34062306a36Sopenharmony_ci * The DMA Request Count feature should be used in addition to this feature 34162306a36Sopenharmony_ci * to set the number of bytes to transfer before pausing the channel. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistatic void fsl_chan_toggle_ext_pause(struct fsldma_chan *chan, int enable) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci if (enable) 34662306a36Sopenharmony_ci chan->feature |= FSL_DMA_CHAN_PAUSE_EXT; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/** 35262306a36Sopenharmony_ci * fsl_chan_toggle_ext_start - Toggle channel external start status 35362306a36Sopenharmony_ci * @chan : Freescale DMA channel 35462306a36Sopenharmony_ci * @enable : 0 is disabled, 1 is enabled. 35562306a36Sopenharmony_ci * 35662306a36Sopenharmony_ci * If enable the external start, the channel can be started by an 35762306a36Sopenharmony_ci * external DMA start pin. So the dma_start() does not start the 35862306a36Sopenharmony_ci * transfer immediately. The DMA channel will wait for the 35962306a36Sopenharmony_ci * control pin asserted. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci if (enable) 36462306a36Sopenharmony_ci chan->feature |= FSL_DMA_CHAN_START_EXT; 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci chan->feature &= ~FSL_DMA_CHAN_START_EXT; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint fsl_dma_external_start(struct dma_chan *dchan, int enable) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct fsldma_chan *chan; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (!dchan) 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci chan = to_fsl_chan(dchan); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci fsl_chan_toggle_ext_start(chan, enable); 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_dma_external_start); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void append_ld_queue(struct fsldma_chan *chan, struct fsl_desc_sw *desc) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (list_empty(&chan->ld_pending)) 38862306a36Sopenharmony_ci goto out_splice; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * Add the hardware descriptor to the chain of hardware descriptors 39262306a36Sopenharmony_ci * that already exists in memory. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * This will un-set the EOL bit of the existing transaction, and the 39562306a36Sopenharmony_ci * last link in this transaction will become the EOL descriptor. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci set_desc_next(chan, &tail->hw, desc->async_tx.phys); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * Add the software descriptor and all children to the list 40162306a36Sopenharmony_ci * of pending transactions 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ciout_splice: 40462306a36Sopenharmony_ci list_splice_tail_init(&desc->tx_list, &chan->ld_pending); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct fsldma_chan *chan = to_fsl_chan(tx->chan); 41062306a36Sopenharmony_ci struct fsl_desc_sw *desc = tx_to_fsl_desc(tx); 41162306a36Sopenharmony_ci struct fsl_desc_sw *child; 41262306a36Sopenharmony_ci dma_cookie_t cookie = -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci#ifdef CONFIG_PM 41762306a36Sopenharmony_ci if (unlikely(chan->pm_state != RUNNING)) { 41862306a36Sopenharmony_ci chan_dbg(chan, "cannot submit due to suspend\n"); 41962306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 42062306a36Sopenharmony_ci return -1; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci#endif 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * assign cookies to all of the software descriptors 42662306a36Sopenharmony_ci * that make up this transaction 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci list_for_each_entry(child, &desc->tx_list, node) { 42962306a36Sopenharmony_ci cookie = dma_cookie_assign(&child->async_tx); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* put this transaction onto the tail of the pending queue */ 43362306a36Sopenharmony_ci append_ld_queue(chan, desc); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return cookie; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/** 44162306a36Sopenharmony_ci * fsl_dma_free_descriptor - Free descriptor from channel's DMA pool. 44262306a36Sopenharmony_ci * @chan : Freescale DMA channel 44362306a36Sopenharmony_ci * @desc: descriptor to be freed 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_cistatic void fsl_dma_free_descriptor(struct fsldma_chan *chan, 44662306a36Sopenharmony_ci struct fsl_desc_sw *desc) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci list_del(&desc->node); 44962306a36Sopenharmony_ci chan_dbg(chan, "LD %p free\n", desc); 45062306a36Sopenharmony_ci dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/** 45462306a36Sopenharmony_ci * fsl_dma_alloc_descriptor - Allocate descriptor from channel's DMA pool. 45562306a36Sopenharmony_ci * @chan : Freescale DMA channel 45662306a36Sopenharmony_ci * 45762306a36Sopenharmony_ci * Return - The descriptor allocated. NULL for failed. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic struct fsl_desc_sw *fsl_dma_alloc_descriptor(struct fsldma_chan *chan) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct fsl_desc_sw *desc; 46262306a36Sopenharmony_ci dma_addr_t pdesc; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci desc = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &pdesc); 46562306a36Sopenharmony_ci if (!desc) { 46662306a36Sopenharmony_ci chan_dbg(chan, "out of memory for link descriptor\n"); 46762306a36Sopenharmony_ci return NULL; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->tx_list); 47162306a36Sopenharmony_ci dma_async_tx_descriptor_init(&desc->async_tx, &chan->common); 47262306a36Sopenharmony_ci desc->async_tx.tx_submit = fsl_dma_tx_submit; 47362306a36Sopenharmony_ci desc->async_tx.phys = pdesc; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci chan_dbg(chan, "LD %p allocated\n", desc); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return desc; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/** 48162306a36Sopenharmony_ci * fsldma_clean_completed_descriptor - free all descriptors which 48262306a36Sopenharmony_ci * has been completed and acked 48362306a36Sopenharmony_ci * @chan: Freescale DMA channel 48462306a36Sopenharmony_ci * 48562306a36Sopenharmony_ci * This function is used on all completed and acked descriptors. 48662306a36Sopenharmony_ci * All descriptors should only be freed in this function. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_cistatic void fsldma_clean_completed_descriptor(struct fsldma_chan *chan) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct fsl_desc_sw *desc, *_desc; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Run the callback for each descriptor, in order */ 49362306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &chan->ld_completed, node) 49462306a36Sopenharmony_ci if (async_tx_test_ack(&desc->async_tx)) 49562306a36Sopenharmony_ci fsl_dma_free_descriptor(chan, desc); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/** 49962306a36Sopenharmony_ci * fsldma_run_tx_complete_actions - cleanup a single link descriptor 50062306a36Sopenharmony_ci * @chan: Freescale DMA channel 50162306a36Sopenharmony_ci * @desc: descriptor to cleanup and free 50262306a36Sopenharmony_ci * @cookie: Freescale DMA transaction identifier 50362306a36Sopenharmony_ci * 50462306a36Sopenharmony_ci * This function is used on a descriptor which has been executed by the DMA 50562306a36Sopenharmony_ci * controller. It will run any callbacks, submit any dependencies. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_cistatic dma_cookie_t fsldma_run_tx_complete_actions(struct fsldma_chan *chan, 50862306a36Sopenharmony_ci struct fsl_desc_sw *desc, dma_cookie_t cookie) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct dma_async_tx_descriptor *txd = &desc->async_tx; 51162306a36Sopenharmony_ci dma_cookie_t ret = cookie; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci BUG_ON(txd->cookie < 0); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (txd->cookie > 0) { 51662306a36Sopenharmony_ci ret = txd->cookie; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci dma_descriptor_unmap(txd); 51962306a36Sopenharmony_ci /* Run the link descriptor callback function */ 52062306a36Sopenharmony_ci dmaengine_desc_get_callback_invoke(txd, NULL); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Run any dependencies */ 52462306a36Sopenharmony_ci dma_run_dependencies(txd); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/** 53062306a36Sopenharmony_ci * fsldma_clean_running_descriptor - move the completed descriptor from 53162306a36Sopenharmony_ci * ld_running to ld_completed 53262306a36Sopenharmony_ci * @chan: Freescale DMA channel 53362306a36Sopenharmony_ci * @desc: the descriptor which is completed 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * Free the descriptor directly if acked by async_tx api, or move it to 53662306a36Sopenharmony_ci * queue ld_completed. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic void fsldma_clean_running_descriptor(struct fsldma_chan *chan, 53962306a36Sopenharmony_ci struct fsl_desc_sw *desc) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci /* Remove from the list of transactions */ 54262306a36Sopenharmony_ci list_del(&desc->node); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * the client is allowed to attach dependent operations 54662306a36Sopenharmony_ci * until 'ack' is set 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (!async_tx_test_ack(&desc->async_tx)) { 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * Move this descriptor to the list of descriptors which is 55162306a36Sopenharmony_ci * completed, but still awaiting the 'ack' bit to be set. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci list_add_tail(&desc->node, &chan->ld_completed); 55462306a36Sopenharmony_ci return; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/** 56162306a36Sopenharmony_ci * fsl_chan_xfer_ld_queue - transfer any pending transactions 56262306a36Sopenharmony_ci * @chan : Freescale DMA channel 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * HARDWARE STATE: idle 56562306a36Sopenharmony_ci * LOCKING: must hold chan->desc_lock 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct fsl_desc_sw *desc; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * If the list of pending descriptors is empty, then we 57362306a36Sopenharmony_ci * don't need to do any work at all 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci if (list_empty(&chan->ld_pending)) { 57662306a36Sopenharmony_ci chan_dbg(chan, "no pending LDs\n"); 57762306a36Sopenharmony_ci return; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * The DMA controller is not idle, which means that the interrupt 58262306a36Sopenharmony_ci * handler will start any queued transactions when it runs after 58362306a36Sopenharmony_ci * this transaction finishes 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci if (!chan->idle) { 58662306a36Sopenharmony_ci chan_dbg(chan, "DMA controller still busy\n"); 58762306a36Sopenharmony_ci return; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* 59162306a36Sopenharmony_ci * If there are some link descriptors which have not been 59262306a36Sopenharmony_ci * transferred, we need to start the controller 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * Move all elements from the queue of pending transactions 59762306a36Sopenharmony_ci * onto the list of running transactions 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci chan_dbg(chan, "idle, starting controller\n"); 60062306a36Sopenharmony_ci desc = list_first_entry(&chan->ld_pending, struct fsl_desc_sw, node); 60162306a36Sopenharmony_ci list_splice_tail_init(&chan->ld_pending, &chan->ld_running); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* 60462306a36Sopenharmony_ci * The 85xx DMA controller doesn't clear the channel start bit 60562306a36Sopenharmony_ci * automatically at the end of a transfer. Therefore we must clear 60662306a36Sopenharmony_ci * it in software before starting the transfer. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) { 60962306a36Sopenharmony_ci u32 mode; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci mode = get_mr(chan); 61262306a36Sopenharmony_ci mode &= ~FSL_DMA_MR_CS; 61362306a36Sopenharmony_ci set_mr(chan, mode); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * Program the descriptor's address into the DMA controller, 61862306a36Sopenharmony_ci * then start the DMA transaction 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci set_cdar(chan, desc->async_tx.phys); 62162306a36Sopenharmony_ci get_cdar(chan); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci dma_start(chan); 62462306a36Sopenharmony_ci chan->idle = false; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/** 62862306a36Sopenharmony_ci * fsldma_cleanup_descriptors - cleanup link descriptors which are completed 62962306a36Sopenharmony_ci * and move them to ld_completed to free until flag 'ack' is set 63062306a36Sopenharmony_ci * @chan: Freescale DMA channel 63162306a36Sopenharmony_ci * 63262306a36Sopenharmony_ci * This function is used on descriptors which have been executed by the DMA 63362306a36Sopenharmony_ci * controller. It will run any callbacks, submit any dependencies, then 63462306a36Sopenharmony_ci * free these descriptors if flag 'ack' is set. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_cistatic void fsldma_cleanup_descriptors(struct fsldma_chan *chan) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct fsl_desc_sw *desc, *_desc; 63962306a36Sopenharmony_ci dma_cookie_t cookie = 0; 64062306a36Sopenharmony_ci dma_addr_t curr_phys = get_cdar(chan); 64162306a36Sopenharmony_ci int seen_current = 0; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci fsldma_clean_completed_descriptor(chan); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Run the callback for each descriptor, in order */ 64662306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) { 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * do not advance past the current descriptor loaded into the 64962306a36Sopenharmony_ci * hardware channel, subsequent descriptors are either in 65062306a36Sopenharmony_ci * process or have not been submitted 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci if (seen_current) 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* 65662306a36Sopenharmony_ci * stop the search if we reach the current descriptor and the 65762306a36Sopenharmony_ci * channel is busy 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ci if (desc->async_tx.phys == curr_phys) { 66062306a36Sopenharmony_ci seen_current = 1; 66162306a36Sopenharmony_ci if (!dma_is_idle(chan)) 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci cookie = fsldma_run_tx_complete_actions(chan, desc, cookie); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci fsldma_clean_running_descriptor(chan, desc); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * Start any pending transactions automatically 67262306a36Sopenharmony_ci * 67362306a36Sopenharmony_ci * In the ideal case, we keep the DMA controller busy while we go 67462306a36Sopenharmony_ci * ahead and free the descriptors below. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci fsl_chan_xfer_ld_queue(chan); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (cookie > 0) 67962306a36Sopenharmony_ci chan->common.completed_cookie = cookie; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/** 68362306a36Sopenharmony_ci * fsl_dma_alloc_chan_resources - Allocate resources for DMA channel. 68462306a36Sopenharmony_ci * @chan : Freescale DMA channel 68562306a36Sopenharmony_ci * 68662306a36Sopenharmony_ci * This function will create a dma pool for descriptor allocation. 68762306a36Sopenharmony_ci * 68862306a36Sopenharmony_ci * Return - The number of descriptors allocated. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistatic int fsl_dma_alloc_chan_resources(struct dma_chan *dchan) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct fsldma_chan *chan = to_fsl_chan(dchan); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* Has this channel already been allocated? */ 69562306a36Sopenharmony_ci if (chan->desc_pool) 69662306a36Sopenharmony_ci return 1; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * We need the descriptor to be aligned to 32bytes 70062306a36Sopenharmony_ci * for meeting FSL DMA specification requirement. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci chan->desc_pool = dma_pool_create(chan->name, chan->dev, 70362306a36Sopenharmony_ci sizeof(struct fsl_desc_sw), 70462306a36Sopenharmony_ci __alignof__(struct fsl_desc_sw), 0); 70562306a36Sopenharmony_ci if (!chan->desc_pool) { 70662306a36Sopenharmony_ci chan_err(chan, "unable to allocate descriptor pool\n"); 70762306a36Sopenharmony_ci return -ENOMEM; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* there is at least one descriptor free to be allocated */ 71162306a36Sopenharmony_ci return 1; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci/** 71562306a36Sopenharmony_ci * fsldma_free_desc_list - Free all descriptors in a queue 71662306a36Sopenharmony_ci * @chan: Freescae DMA channel 71762306a36Sopenharmony_ci * @list: the list to free 71862306a36Sopenharmony_ci * 71962306a36Sopenharmony_ci * LOCKING: must hold chan->desc_lock 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_cistatic void fsldma_free_desc_list(struct fsldma_chan *chan, 72262306a36Sopenharmony_ci struct list_head *list) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci struct fsl_desc_sw *desc, *_desc; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, list, node) 72762306a36Sopenharmony_ci fsl_dma_free_descriptor(chan, desc); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void fsldma_free_desc_list_reverse(struct fsldma_chan *chan, 73162306a36Sopenharmony_ci struct list_head *list) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct fsl_desc_sw *desc, *_desc; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci list_for_each_entry_safe_reverse(desc, _desc, list, node) 73662306a36Sopenharmony_ci fsl_dma_free_descriptor(chan, desc); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/** 74062306a36Sopenharmony_ci * fsl_dma_free_chan_resources - Free all resources of the channel. 74162306a36Sopenharmony_ci * @chan : Freescale DMA channel 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_cistatic void fsl_dma_free_chan_resources(struct dma_chan *dchan) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct fsldma_chan *chan = to_fsl_chan(dchan); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci chan_dbg(chan, "free all channel resources\n"); 74862306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 74962306a36Sopenharmony_ci fsldma_cleanup_descriptors(chan); 75062306a36Sopenharmony_ci fsldma_free_desc_list(chan, &chan->ld_pending); 75162306a36Sopenharmony_ci fsldma_free_desc_list(chan, &chan->ld_running); 75262306a36Sopenharmony_ci fsldma_free_desc_list(chan, &chan->ld_completed); 75362306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci dma_pool_destroy(chan->desc_pool); 75662306a36Sopenharmony_ci chan->desc_pool = NULL; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 76062306a36Sopenharmony_cifsl_dma_prep_memcpy(struct dma_chan *dchan, 76162306a36Sopenharmony_ci dma_addr_t dma_dst, dma_addr_t dma_src, 76262306a36Sopenharmony_ci size_t len, unsigned long flags) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct fsldma_chan *chan; 76562306a36Sopenharmony_ci struct fsl_desc_sw *first = NULL, *prev = NULL, *new; 76662306a36Sopenharmony_ci size_t copy; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (!dchan) 76962306a36Sopenharmony_ci return NULL; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!len) 77262306a36Sopenharmony_ci return NULL; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci chan = to_fsl_chan(dchan); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci do { 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Allocate the link descriptor from DMA pool */ 77962306a36Sopenharmony_ci new = fsl_dma_alloc_descriptor(chan); 78062306a36Sopenharmony_ci if (!new) { 78162306a36Sopenharmony_ci chan_err(chan, "%s\n", msg_ld_oom); 78262306a36Sopenharmony_ci goto fail; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci set_desc_cnt(chan, &new->hw, copy); 78862306a36Sopenharmony_ci set_desc_src(chan, &new->hw, dma_src); 78962306a36Sopenharmony_ci set_desc_dst(chan, &new->hw, dma_dst); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (!first) 79262306a36Sopenharmony_ci first = new; 79362306a36Sopenharmony_ci else 79462306a36Sopenharmony_ci set_desc_next(chan, &prev->hw, new->async_tx.phys); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci new->async_tx.cookie = 0; 79762306a36Sopenharmony_ci async_tx_ack(&new->async_tx); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci prev = new; 80062306a36Sopenharmony_ci len -= copy; 80162306a36Sopenharmony_ci dma_src += copy; 80262306a36Sopenharmony_ci dma_dst += copy; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Insert the link descriptor to the LD ring */ 80562306a36Sopenharmony_ci list_add_tail(&new->node, &first->tx_list); 80662306a36Sopenharmony_ci } while (len); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci new->async_tx.flags = flags; /* client is in control of this ack */ 80962306a36Sopenharmony_ci new->async_tx.cookie = -EBUSY; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Set End-of-link to the last link descriptor of new list */ 81262306a36Sopenharmony_ci set_ld_eol(chan, new); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return &first->async_tx; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cifail: 81762306a36Sopenharmony_ci if (!first) 81862306a36Sopenharmony_ci return NULL; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci fsldma_free_desc_list_reverse(chan, &first->tx_list); 82162306a36Sopenharmony_ci return NULL; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int fsl_dma_device_terminate_all(struct dma_chan *dchan) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct fsldma_chan *chan; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!dchan) 82962306a36Sopenharmony_ci return -EINVAL; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci chan = to_fsl_chan(dchan); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Halt the DMA engine */ 83662306a36Sopenharmony_ci dma_halt(chan); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Remove and free all of the descriptors in the LD queue */ 83962306a36Sopenharmony_ci fsldma_free_desc_list(chan, &chan->ld_pending); 84062306a36Sopenharmony_ci fsldma_free_desc_list(chan, &chan->ld_running); 84162306a36Sopenharmony_ci fsldma_free_desc_list(chan, &chan->ld_completed); 84262306a36Sopenharmony_ci chan->idle = true; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic int fsl_dma_device_config(struct dma_chan *dchan, 84962306a36Sopenharmony_ci struct dma_slave_config *config) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct fsldma_chan *chan; 85262306a36Sopenharmony_ci int size; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (!dchan) 85562306a36Sopenharmony_ci return -EINVAL; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci chan = to_fsl_chan(dchan); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* make sure the channel supports setting burst size */ 86062306a36Sopenharmony_ci if (!chan->set_request_count) 86162306a36Sopenharmony_ci return -ENXIO; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* we set the controller burst size depending on direction */ 86462306a36Sopenharmony_ci if (config->direction == DMA_MEM_TO_DEV) 86562306a36Sopenharmony_ci size = config->dst_addr_width * config->dst_maxburst; 86662306a36Sopenharmony_ci else 86762306a36Sopenharmony_ci size = config->src_addr_width * config->src_maxburst; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci chan->set_request_count(chan, size); 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/** 87562306a36Sopenharmony_ci * fsl_dma_memcpy_issue_pending - Issue the DMA start command 87662306a36Sopenharmony_ci * @chan : Freescale DMA channel 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_cistatic void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct fsldma_chan *chan = to_fsl_chan(dchan); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 88362306a36Sopenharmony_ci fsl_chan_xfer_ld_queue(chan); 88462306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/** 88862306a36Sopenharmony_ci * fsl_tx_status - Determine the DMA status 88962306a36Sopenharmony_ci * @chan : Freescale DMA channel 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_cistatic enum dma_status fsl_tx_status(struct dma_chan *dchan, 89262306a36Sopenharmony_ci dma_cookie_t cookie, 89362306a36Sopenharmony_ci struct dma_tx_state *txstate) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct fsldma_chan *chan = to_fsl_chan(dchan); 89662306a36Sopenharmony_ci enum dma_status ret; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci ret = dma_cookie_status(dchan, cookie, txstate); 89962306a36Sopenharmony_ci if (ret == DMA_COMPLETE) 90062306a36Sopenharmony_ci return ret; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 90362306a36Sopenharmony_ci fsldma_cleanup_descriptors(chan); 90462306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci return dma_cookie_status(dchan, cookie, txstate); 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 91062306a36Sopenharmony_ci/* Interrupt Handling */ 91162306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic irqreturn_t fsldma_chan_irq(int irq, void *data) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct fsldma_chan *chan = data; 91662306a36Sopenharmony_ci u32 stat; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* save and clear the status register */ 91962306a36Sopenharmony_ci stat = get_sr(chan); 92062306a36Sopenharmony_ci set_sr(chan, stat); 92162306a36Sopenharmony_ci chan_dbg(chan, "irq: stat = 0x%x\n", stat); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* check that this was really our device */ 92462306a36Sopenharmony_ci stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH); 92562306a36Sopenharmony_ci if (!stat) 92662306a36Sopenharmony_ci return IRQ_NONE; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (stat & FSL_DMA_SR_TE) 92962306a36Sopenharmony_ci chan_err(chan, "Transfer Error!\n"); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* 93262306a36Sopenharmony_ci * Programming Error 93362306a36Sopenharmony_ci * The DMA_INTERRUPT async_tx is a NULL transfer, which will 93462306a36Sopenharmony_ci * trigger a PE interrupt. 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci if (stat & FSL_DMA_SR_PE) { 93762306a36Sopenharmony_ci chan_dbg(chan, "irq: Programming Error INT\n"); 93862306a36Sopenharmony_ci stat &= ~FSL_DMA_SR_PE; 93962306a36Sopenharmony_ci if (get_bcr(chan) != 0) 94062306a36Sopenharmony_ci chan_err(chan, "Programming Error!\n"); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* 94462306a36Sopenharmony_ci * For MPC8349, EOCDI event need to update cookie 94562306a36Sopenharmony_ci * and start the next transfer if it exist. 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_ci if (stat & FSL_DMA_SR_EOCDI) { 94862306a36Sopenharmony_ci chan_dbg(chan, "irq: End-of-Chain link INT\n"); 94962306a36Sopenharmony_ci stat &= ~FSL_DMA_SR_EOCDI; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* 95362306a36Sopenharmony_ci * If it current transfer is the end-of-transfer, 95462306a36Sopenharmony_ci * we should clear the Channel Start bit for 95562306a36Sopenharmony_ci * prepare next transfer. 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_ci if (stat & FSL_DMA_SR_EOLNI) { 95862306a36Sopenharmony_ci chan_dbg(chan, "irq: End-of-link INT\n"); 95962306a36Sopenharmony_ci stat &= ~FSL_DMA_SR_EOLNI; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* check that the DMA controller is really idle */ 96362306a36Sopenharmony_ci if (!dma_is_idle(chan)) 96462306a36Sopenharmony_ci chan_err(chan, "irq: controller not idle!\n"); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* check that we handled all of the bits */ 96762306a36Sopenharmony_ci if (stat) 96862306a36Sopenharmony_ci chan_err(chan, "irq: unhandled sr 0x%08x\n", stat); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* 97162306a36Sopenharmony_ci * Schedule the tasklet to handle all cleanup of the current 97262306a36Sopenharmony_ci * transaction. It will start a new transaction if there is 97362306a36Sopenharmony_ci * one pending. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_ci tasklet_schedule(&chan->tasklet); 97662306a36Sopenharmony_ci chan_dbg(chan, "irq: Exit\n"); 97762306a36Sopenharmony_ci return IRQ_HANDLED; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic void dma_do_tasklet(struct tasklet_struct *t) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct fsldma_chan *chan = from_tasklet(chan, t, tasklet); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci chan_dbg(chan, "tasklet entry\n"); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci spin_lock(&chan->desc_lock); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* the hardware is now idle and ready for more */ 98962306a36Sopenharmony_ci chan->idle = true; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* Run all cleanup for descriptors which have been completed */ 99262306a36Sopenharmony_ci fsldma_cleanup_descriptors(chan); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci spin_unlock(&chan->desc_lock); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci chan_dbg(chan, "tasklet exit\n"); 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic irqreturn_t fsldma_ctrl_irq(int irq, void *data) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct fsldma_device *fdev = data; 100262306a36Sopenharmony_ci struct fsldma_chan *chan; 100362306a36Sopenharmony_ci unsigned int handled = 0; 100462306a36Sopenharmony_ci u32 gsr, mask; 100562306a36Sopenharmony_ci int i; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci gsr = (fdev->feature & FSL_DMA_BIG_ENDIAN) ? in_be32(fdev->regs) 100862306a36Sopenharmony_ci : in_le32(fdev->regs); 100962306a36Sopenharmony_ci mask = 0xff000000; 101062306a36Sopenharmony_ci dev_dbg(fdev->dev, "IRQ: gsr 0x%.8x\n", gsr); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 101362306a36Sopenharmony_ci chan = fdev->chan[i]; 101462306a36Sopenharmony_ci if (!chan) 101562306a36Sopenharmony_ci continue; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (gsr & mask) { 101862306a36Sopenharmony_ci dev_dbg(fdev->dev, "IRQ: chan %d\n", chan->id); 101962306a36Sopenharmony_ci fsldma_chan_irq(irq, chan); 102062306a36Sopenharmony_ci handled++; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci gsr &= ~mask; 102462306a36Sopenharmony_ci mask >>= 8; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return IRQ_RETVAL(handled); 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic void fsldma_free_irqs(struct fsldma_device *fdev) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct fsldma_chan *chan; 103362306a36Sopenharmony_ci int i; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (fdev->irq) { 103662306a36Sopenharmony_ci dev_dbg(fdev->dev, "free per-controller IRQ\n"); 103762306a36Sopenharmony_ci free_irq(fdev->irq, fdev); 103862306a36Sopenharmony_ci return; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 104262306a36Sopenharmony_ci chan = fdev->chan[i]; 104362306a36Sopenharmony_ci if (chan && chan->irq) { 104462306a36Sopenharmony_ci chan_dbg(chan, "free per-channel IRQ\n"); 104562306a36Sopenharmony_ci free_irq(chan->irq, chan); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic int fsldma_request_irqs(struct fsldma_device *fdev) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct fsldma_chan *chan; 105362306a36Sopenharmony_ci int ret; 105462306a36Sopenharmony_ci int i; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* if we have a per-controller IRQ, use that */ 105762306a36Sopenharmony_ci if (fdev->irq) { 105862306a36Sopenharmony_ci dev_dbg(fdev->dev, "request per-controller IRQ\n"); 105962306a36Sopenharmony_ci ret = request_irq(fdev->irq, fsldma_ctrl_irq, IRQF_SHARED, 106062306a36Sopenharmony_ci "fsldma-controller", fdev); 106162306a36Sopenharmony_ci return ret; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /* no per-controller IRQ, use the per-channel IRQs */ 106562306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 106662306a36Sopenharmony_ci chan = fdev->chan[i]; 106762306a36Sopenharmony_ci if (!chan) 106862306a36Sopenharmony_ci continue; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (!chan->irq) { 107162306a36Sopenharmony_ci chan_err(chan, "interrupts property missing in device tree\n"); 107262306a36Sopenharmony_ci ret = -ENODEV; 107362306a36Sopenharmony_ci goto out_unwind; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci chan_dbg(chan, "request per-channel IRQ\n"); 107762306a36Sopenharmony_ci ret = request_irq(chan->irq, fsldma_chan_irq, IRQF_SHARED, 107862306a36Sopenharmony_ci "fsldma-chan", chan); 107962306a36Sopenharmony_ci if (ret) { 108062306a36Sopenharmony_ci chan_err(chan, "unable to request per-channel IRQ\n"); 108162306a36Sopenharmony_ci goto out_unwind; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return 0; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ciout_unwind: 108862306a36Sopenharmony_ci for (/* none */; i >= 0; i--) { 108962306a36Sopenharmony_ci chan = fdev->chan[i]; 109062306a36Sopenharmony_ci if (!chan) 109162306a36Sopenharmony_ci continue; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (!chan->irq) 109462306a36Sopenharmony_ci continue; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci free_irq(chan->irq, chan); 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return ret; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 110362306a36Sopenharmony_ci/* OpenFirmware Subsystem */ 110462306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic int fsl_dma_chan_probe(struct fsldma_device *fdev, 110762306a36Sopenharmony_ci struct device_node *node, u32 feature, const char *compatible) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct fsldma_chan *chan; 111062306a36Sopenharmony_ci struct resource res; 111162306a36Sopenharmony_ci int err; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* alloc channel */ 111462306a36Sopenharmony_ci chan = kzalloc(sizeof(*chan), GFP_KERNEL); 111562306a36Sopenharmony_ci if (!chan) { 111662306a36Sopenharmony_ci err = -ENOMEM; 111762306a36Sopenharmony_ci goto out_return; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* ioremap registers for use */ 112162306a36Sopenharmony_ci chan->regs = of_iomap(node, 0); 112262306a36Sopenharmony_ci if (!chan->regs) { 112362306a36Sopenharmony_ci dev_err(fdev->dev, "unable to ioremap registers\n"); 112462306a36Sopenharmony_ci err = -ENOMEM; 112562306a36Sopenharmony_ci goto out_free_chan; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci err = of_address_to_resource(node, 0, &res); 112962306a36Sopenharmony_ci if (err) { 113062306a36Sopenharmony_ci dev_err(fdev->dev, "unable to find 'reg' property\n"); 113162306a36Sopenharmony_ci goto out_iounmap_regs; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci chan->feature = feature; 113562306a36Sopenharmony_ci if (!fdev->feature) 113662306a36Sopenharmony_ci fdev->feature = chan->feature; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* 113962306a36Sopenharmony_ci * If the DMA device's feature is different than the feature 114062306a36Sopenharmony_ci * of its channels, report the bug 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci WARN_ON(fdev->feature != chan->feature); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci chan->dev = fdev->dev; 114562306a36Sopenharmony_ci chan->id = (res.start & 0xfff) < 0x300 ? 114662306a36Sopenharmony_ci ((res.start - 0x100) & 0xfff) >> 7 : 114762306a36Sopenharmony_ci ((res.start - 0x200) & 0xfff) >> 7; 114862306a36Sopenharmony_ci if (chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) { 114962306a36Sopenharmony_ci dev_err(fdev->dev, "too many channels for device\n"); 115062306a36Sopenharmony_ci err = -EINVAL; 115162306a36Sopenharmony_ci goto out_iounmap_regs; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci fdev->chan[chan->id] = chan; 115562306a36Sopenharmony_ci tasklet_setup(&chan->tasklet, dma_do_tasklet); 115662306a36Sopenharmony_ci snprintf(chan->name, sizeof(chan->name), "chan%d", chan->id); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Initialize the channel */ 115962306a36Sopenharmony_ci dma_init(chan); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* Clear cdar registers */ 116262306a36Sopenharmony_ci set_cdar(chan, 0); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci switch (chan->feature & FSL_DMA_IP_MASK) { 116562306a36Sopenharmony_ci case FSL_DMA_IP_85XX: 116662306a36Sopenharmony_ci chan->toggle_ext_pause = fsl_chan_toggle_ext_pause; 116762306a36Sopenharmony_ci fallthrough; 116862306a36Sopenharmony_ci case FSL_DMA_IP_83XX: 116962306a36Sopenharmony_ci chan->toggle_ext_start = fsl_chan_toggle_ext_start; 117062306a36Sopenharmony_ci chan->set_src_loop_size = fsl_chan_set_src_loop_size; 117162306a36Sopenharmony_ci chan->set_dst_loop_size = fsl_chan_set_dst_loop_size; 117262306a36Sopenharmony_ci chan->set_request_count = fsl_chan_set_request_count; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci spin_lock_init(&chan->desc_lock); 117662306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->ld_pending); 117762306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->ld_running); 117862306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->ld_completed); 117962306a36Sopenharmony_ci chan->idle = true; 118062306a36Sopenharmony_ci#ifdef CONFIG_PM 118162306a36Sopenharmony_ci chan->pm_state = RUNNING; 118262306a36Sopenharmony_ci#endif 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci chan->common.device = &fdev->common; 118562306a36Sopenharmony_ci dma_cookie_init(&chan->common); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* find the IRQ line, if it exists in the device tree */ 118862306a36Sopenharmony_ci chan->irq = irq_of_parse_and_map(node, 0); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* Add the channel to DMA device channel list */ 119162306a36Sopenharmony_ci list_add_tail(&chan->common.device_node, &fdev->common.channels); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci dev_info(fdev->dev, "#%d (%s), irq %d\n", chan->id, compatible, 119462306a36Sopenharmony_ci chan->irq ? chan->irq : fdev->irq); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ciout_iounmap_regs: 119962306a36Sopenharmony_ci iounmap(chan->regs); 120062306a36Sopenharmony_ciout_free_chan: 120162306a36Sopenharmony_ci kfree(chan); 120262306a36Sopenharmony_ciout_return: 120362306a36Sopenharmony_ci return err; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic void fsl_dma_chan_remove(struct fsldma_chan *chan) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci irq_dispose_mapping(chan->irq); 120962306a36Sopenharmony_ci list_del(&chan->common.device_node); 121062306a36Sopenharmony_ci iounmap(chan->regs); 121162306a36Sopenharmony_ci kfree(chan); 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic int fsldma_of_probe(struct platform_device *op) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct fsldma_device *fdev; 121762306a36Sopenharmony_ci struct device_node *child; 121862306a36Sopenharmony_ci unsigned int i; 121962306a36Sopenharmony_ci int err; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci fdev = kzalloc(sizeof(*fdev), GFP_KERNEL); 122262306a36Sopenharmony_ci if (!fdev) { 122362306a36Sopenharmony_ci err = -ENOMEM; 122462306a36Sopenharmony_ci goto out_return; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci fdev->dev = &op->dev; 122862306a36Sopenharmony_ci INIT_LIST_HEAD(&fdev->common.channels); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* ioremap the registers for use */ 123162306a36Sopenharmony_ci fdev->regs = of_iomap(op->dev.of_node, 0); 123262306a36Sopenharmony_ci if (!fdev->regs) { 123362306a36Sopenharmony_ci dev_err(&op->dev, "unable to ioremap registers\n"); 123462306a36Sopenharmony_ci err = -ENOMEM; 123562306a36Sopenharmony_ci goto out_free; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* map the channel IRQ if it exists, but don't hookup the handler yet */ 123962306a36Sopenharmony_ci fdev->irq = irq_of_parse_and_map(op->dev.of_node, 0); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask); 124262306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, fdev->common.cap_mask); 124362306a36Sopenharmony_ci fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources; 124462306a36Sopenharmony_ci fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources; 124562306a36Sopenharmony_ci fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy; 124662306a36Sopenharmony_ci fdev->common.device_tx_status = fsl_tx_status; 124762306a36Sopenharmony_ci fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; 124862306a36Sopenharmony_ci fdev->common.device_config = fsl_dma_device_config; 124962306a36Sopenharmony_ci fdev->common.device_terminate_all = fsl_dma_device_terminate_all; 125062306a36Sopenharmony_ci fdev->common.dev = &op->dev; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci fdev->common.src_addr_widths = FSL_DMA_BUSWIDTHS; 125362306a36Sopenharmony_ci fdev->common.dst_addr_widths = FSL_DMA_BUSWIDTHS; 125462306a36Sopenharmony_ci fdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 125562306a36Sopenharmony_ci fdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci dma_set_mask(&(op->dev), DMA_BIT_MASK(36)); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci platform_set_drvdata(op, fdev); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* 126262306a36Sopenharmony_ci * We cannot use of_platform_bus_probe() because there is no 126362306a36Sopenharmony_ci * of_platform_bus_remove(). Instead, we manually instantiate every DMA 126462306a36Sopenharmony_ci * channel object. 126562306a36Sopenharmony_ci */ 126662306a36Sopenharmony_ci for_each_child_of_node(op->dev.of_node, child) { 126762306a36Sopenharmony_ci if (of_device_is_compatible(child, "fsl,eloplus-dma-channel")) { 126862306a36Sopenharmony_ci fsl_dma_chan_probe(fdev, child, 126962306a36Sopenharmony_ci FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN, 127062306a36Sopenharmony_ci "fsl,eloplus-dma-channel"); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (of_device_is_compatible(child, "fsl,elo-dma-channel")) { 127462306a36Sopenharmony_ci fsl_dma_chan_probe(fdev, child, 127562306a36Sopenharmony_ci FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN, 127662306a36Sopenharmony_ci "fsl,elo-dma-channel"); 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci /* 128162306a36Sopenharmony_ci * Hookup the IRQ handler(s) 128262306a36Sopenharmony_ci * 128362306a36Sopenharmony_ci * If we have a per-controller interrupt, we prefer that to the 128462306a36Sopenharmony_ci * per-channel interrupts to reduce the number of shared interrupt 128562306a36Sopenharmony_ci * handlers on the same IRQ line 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_ci err = fsldma_request_irqs(fdev); 128862306a36Sopenharmony_ci if (err) { 128962306a36Sopenharmony_ci dev_err(fdev->dev, "unable to request IRQs\n"); 129062306a36Sopenharmony_ci goto out_free_fdev; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci dma_async_device_register(&fdev->common); 129462306a36Sopenharmony_ci return 0; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ciout_free_fdev: 129762306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 129862306a36Sopenharmony_ci if (fdev->chan[i]) 129962306a36Sopenharmony_ci fsl_dma_chan_remove(fdev->chan[i]); 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci irq_dispose_mapping(fdev->irq); 130262306a36Sopenharmony_ci iounmap(fdev->regs); 130362306a36Sopenharmony_ciout_free: 130462306a36Sopenharmony_ci kfree(fdev); 130562306a36Sopenharmony_ciout_return: 130662306a36Sopenharmony_ci return err; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic int fsldma_of_remove(struct platform_device *op) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci struct fsldma_device *fdev; 131262306a36Sopenharmony_ci unsigned int i; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci fdev = platform_get_drvdata(op); 131562306a36Sopenharmony_ci dma_async_device_unregister(&fdev->common); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci fsldma_free_irqs(fdev); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 132062306a36Sopenharmony_ci if (fdev->chan[i]) 132162306a36Sopenharmony_ci fsl_dma_chan_remove(fdev->chan[i]); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci irq_dispose_mapping(fdev->irq); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci iounmap(fdev->regs); 132662306a36Sopenharmony_ci kfree(fdev); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return 0; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci#ifdef CONFIG_PM 133262306a36Sopenharmony_cistatic int fsldma_suspend_late(struct device *dev) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci struct fsldma_device *fdev = dev_get_drvdata(dev); 133562306a36Sopenharmony_ci struct fsldma_chan *chan; 133662306a36Sopenharmony_ci int i; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 133962306a36Sopenharmony_ci chan = fdev->chan[i]; 134062306a36Sopenharmony_ci if (!chan) 134162306a36Sopenharmony_ci continue; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 134462306a36Sopenharmony_ci if (unlikely(!chan->idle)) 134562306a36Sopenharmony_ci goto out; 134662306a36Sopenharmony_ci chan->regs_save.mr = get_mr(chan); 134762306a36Sopenharmony_ci chan->pm_state = SUSPENDED; 134862306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci return 0; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ciout: 135362306a36Sopenharmony_ci for (; i >= 0; i--) { 135462306a36Sopenharmony_ci chan = fdev->chan[i]; 135562306a36Sopenharmony_ci if (!chan) 135662306a36Sopenharmony_ci continue; 135762306a36Sopenharmony_ci chan->pm_state = RUNNING; 135862306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci return -EBUSY; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int fsldma_resume_early(struct device *dev) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci struct fsldma_device *fdev = dev_get_drvdata(dev); 136662306a36Sopenharmony_ci struct fsldma_chan *chan; 136762306a36Sopenharmony_ci u32 mode; 136862306a36Sopenharmony_ci int i; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) { 137162306a36Sopenharmony_ci chan = fdev->chan[i]; 137262306a36Sopenharmony_ci if (!chan) 137362306a36Sopenharmony_ci continue; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci spin_lock_bh(&chan->desc_lock); 137662306a36Sopenharmony_ci mode = chan->regs_save.mr 137762306a36Sopenharmony_ci & ~FSL_DMA_MR_CS & ~FSL_DMA_MR_CC & ~FSL_DMA_MR_CA; 137862306a36Sopenharmony_ci set_mr(chan, mode); 137962306a36Sopenharmony_ci chan->pm_state = RUNNING; 138062306a36Sopenharmony_ci spin_unlock_bh(&chan->desc_lock); 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci return 0; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_cistatic const struct dev_pm_ops fsldma_pm_ops = { 138762306a36Sopenharmony_ci .suspend_late = fsldma_suspend_late, 138862306a36Sopenharmony_ci .resume_early = fsldma_resume_early, 138962306a36Sopenharmony_ci}; 139062306a36Sopenharmony_ci#endif 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cistatic const struct of_device_id fsldma_of_ids[] = { 139362306a36Sopenharmony_ci { .compatible = "fsl,elo3-dma", }, 139462306a36Sopenharmony_ci { .compatible = "fsl,eloplus-dma", }, 139562306a36Sopenharmony_ci { .compatible = "fsl,elo-dma", }, 139662306a36Sopenharmony_ci {} 139762306a36Sopenharmony_ci}; 139862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsldma_of_ids); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic struct platform_driver fsldma_of_driver = { 140162306a36Sopenharmony_ci .driver = { 140262306a36Sopenharmony_ci .name = "fsl-elo-dma", 140362306a36Sopenharmony_ci .of_match_table = fsldma_of_ids, 140462306a36Sopenharmony_ci#ifdef CONFIG_PM 140562306a36Sopenharmony_ci .pm = &fsldma_pm_ops, 140662306a36Sopenharmony_ci#endif 140762306a36Sopenharmony_ci }, 140862306a36Sopenharmony_ci .probe = fsldma_of_probe, 140962306a36Sopenharmony_ci .remove = fsldma_of_remove, 141062306a36Sopenharmony_ci}; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 141362306a36Sopenharmony_ci/* Module Init / Exit */ 141462306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic __init int fsldma_init(void) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci pr_info("Freescale Elo series DMA driver\n"); 141962306a36Sopenharmony_ci return platform_driver_register(&fsldma_of_driver); 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic void __exit fsldma_exit(void) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci platform_driver_unregister(&fsldma_of_driver); 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cisubsys_initcall(fsldma_init); 142862306a36Sopenharmony_cimodule_exit(fsldma_exit); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Elo series DMA driver"); 143162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1432