162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright 2014-2015 Freescale 362306a36Sopenharmony_ci// Copyright 2018 NXP 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci/* 662306a36Sopenharmony_ci * Driver for NXP Layerscape Queue Direct Memory Access Controller 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: 962306a36Sopenharmony_ci * Wen He <wen.he_1@nxp.com> 1062306a36Sopenharmony_ci * Jiaheng Fan <jiaheng.fan@nxp.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_dma.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "virt-dma.h" 2262306a36Sopenharmony_ci#include "fsldma.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Register related definition */ 2562306a36Sopenharmony_ci#define FSL_QDMA_DMR 0x0 2662306a36Sopenharmony_ci#define FSL_QDMA_DSR 0x4 2762306a36Sopenharmony_ci#define FSL_QDMA_DEIER 0xe00 2862306a36Sopenharmony_ci#define FSL_QDMA_DEDR 0xe04 2962306a36Sopenharmony_ci#define FSL_QDMA_DECFDW0R 0xe10 3062306a36Sopenharmony_ci#define FSL_QDMA_DECFDW1R 0xe14 3162306a36Sopenharmony_ci#define FSL_QDMA_DECFDW2R 0xe18 3262306a36Sopenharmony_ci#define FSL_QDMA_DECFDW3R 0xe1c 3362306a36Sopenharmony_ci#define FSL_QDMA_DECFQIDR 0xe30 3462306a36Sopenharmony_ci#define FSL_QDMA_DECBR 0xe34 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define FSL_QDMA_BCQMR(x) (0xc0 + 0x100 * (x)) 3762306a36Sopenharmony_ci#define FSL_QDMA_BCQSR(x) (0xc4 + 0x100 * (x)) 3862306a36Sopenharmony_ci#define FSL_QDMA_BCQEDPA_SADDR(x) (0xc8 + 0x100 * (x)) 3962306a36Sopenharmony_ci#define FSL_QDMA_BCQDPA_SADDR(x) (0xcc + 0x100 * (x)) 4062306a36Sopenharmony_ci#define FSL_QDMA_BCQEEPA_SADDR(x) (0xd0 + 0x100 * (x)) 4162306a36Sopenharmony_ci#define FSL_QDMA_BCQEPA_SADDR(x) (0xd4 + 0x100 * (x)) 4262306a36Sopenharmony_ci#define FSL_QDMA_BCQIER(x) (0xe0 + 0x100 * (x)) 4362306a36Sopenharmony_ci#define FSL_QDMA_BCQIDR(x) (0xe4 + 0x100 * (x)) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define FSL_QDMA_SQDPAR 0x80c 4662306a36Sopenharmony_ci#define FSL_QDMA_SQEPAR 0x814 4762306a36Sopenharmony_ci#define FSL_QDMA_BSQMR 0x800 4862306a36Sopenharmony_ci#define FSL_QDMA_BSQSR 0x804 4962306a36Sopenharmony_ci#define FSL_QDMA_BSQICR 0x828 5062306a36Sopenharmony_ci#define FSL_QDMA_CQMR 0xa00 5162306a36Sopenharmony_ci#define FSL_QDMA_CQDSCR1 0xa08 5262306a36Sopenharmony_ci#define FSL_QDMA_CQDSCR2 0xa0c 5362306a36Sopenharmony_ci#define FSL_QDMA_CQIER 0xa10 5462306a36Sopenharmony_ci#define FSL_QDMA_CQEDR 0xa14 5562306a36Sopenharmony_ci#define FSL_QDMA_SQCCMR 0xa20 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Registers for bit and genmask */ 5862306a36Sopenharmony_ci#define FSL_QDMA_CQIDR_SQT BIT(15) 5962306a36Sopenharmony_ci#define QDMA_CCDF_FORMAT BIT(29) 6062306a36Sopenharmony_ci#define QDMA_CCDF_SER BIT(30) 6162306a36Sopenharmony_ci#define QDMA_SG_FIN BIT(30) 6262306a36Sopenharmony_ci#define QDMA_SG_LEN_MASK GENMASK(29, 0) 6362306a36Sopenharmony_ci#define QDMA_CCDF_MASK GENMASK(28, 20) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0) 6662306a36Sopenharmony_ci#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0) 6762306a36Sopenharmony_ci#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define FSL_QDMA_BCQIER_CQTIE BIT(15) 7062306a36Sopenharmony_ci#define FSL_QDMA_BCQIER_CQPEIE BIT(23) 7162306a36Sopenharmony_ci#define FSL_QDMA_BSQICR_ICEN BIT(31) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define FSL_QDMA_BSQICR_ICST(x) ((x) << 16) 7462306a36Sopenharmony_ci#define FSL_QDMA_CQIER_MEIE BIT(31) 7562306a36Sopenharmony_ci#define FSL_QDMA_CQIER_TEIE BIT(0) 7662306a36Sopenharmony_ci#define FSL_QDMA_SQCCMR_ENTER_WM BIT(21) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define FSL_QDMA_BCQMR_EN BIT(31) 7962306a36Sopenharmony_ci#define FSL_QDMA_BCQMR_EI BIT(30) 8062306a36Sopenharmony_ci#define FSL_QDMA_BCQMR_CD_THLD(x) ((x) << 20) 8162306a36Sopenharmony_ci#define FSL_QDMA_BCQMR_CQ_SIZE(x) ((x) << 16) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define FSL_QDMA_BCQSR_QF BIT(16) 8462306a36Sopenharmony_ci#define FSL_QDMA_BCQSR_XOFF BIT(0) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define FSL_QDMA_BSQMR_EN BIT(31) 8762306a36Sopenharmony_ci#define FSL_QDMA_BSQMR_DI BIT(30) 8862306a36Sopenharmony_ci#define FSL_QDMA_BSQMR_CQ_SIZE(x) ((x) << 16) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define FSL_QDMA_BSQSR_QE BIT(17) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define FSL_QDMA_DMR_DQD BIT(30) 9362306a36Sopenharmony_ci#define FSL_QDMA_DSR_DB BIT(31) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Size related definition */ 9662306a36Sopenharmony_ci#define FSL_QDMA_QUEUE_MAX 8 9762306a36Sopenharmony_ci#define FSL_QDMA_COMMAND_BUFFER_SIZE 64 9862306a36Sopenharmony_ci#define FSL_QDMA_DESCRIPTOR_BUFFER_SIZE 32 9962306a36Sopenharmony_ci#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN 64 10062306a36Sopenharmony_ci#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX 16384 10162306a36Sopenharmony_ci#define FSL_QDMA_QUEUE_NUM_MAX 8 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Field definition for CMD */ 10462306a36Sopenharmony_ci#define FSL_QDMA_CMD_RWTTYPE 0x4 10562306a36Sopenharmony_ci#define FSL_QDMA_CMD_LWC 0x2 10662306a36Sopenharmony_ci#define FSL_QDMA_CMD_RWTTYPE_OFFSET 28 10762306a36Sopenharmony_ci#define FSL_QDMA_CMD_NS_OFFSET 27 10862306a36Sopenharmony_ci#define FSL_QDMA_CMD_DQOS_OFFSET 24 10962306a36Sopenharmony_ci#define FSL_QDMA_CMD_WTHROTL_OFFSET 20 11062306a36Sopenharmony_ci#define FSL_QDMA_CMD_DSEN_OFFSET 19 11162306a36Sopenharmony_ci#define FSL_QDMA_CMD_LWC_OFFSET 16 11262306a36Sopenharmony_ci#define FSL_QDMA_CMD_PF BIT(17) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Field definition for Descriptor status */ 11562306a36Sopenharmony_ci#define QDMA_CCDF_STATUS_RTE BIT(5) 11662306a36Sopenharmony_ci#define QDMA_CCDF_STATUS_WTE BIT(4) 11762306a36Sopenharmony_ci#define QDMA_CCDF_STATUS_CDE BIT(2) 11862306a36Sopenharmony_ci#define QDMA_CCDF_STATUS_SDE BIT(1) 11962306a36Sopenharmony_ci#define QDMA_CCDF_STATUS_DDE BIT(0) 12062306a36Sopenharmony_ci#define QDMA_CCDF_STATUS_MASK (QDMA_CCDF_STATUS_RTE | \ 12162306a36Sopenharmony_ci QDMA_CCDF_STATUS_WTE | \ 12262306a36Sopenharmony_ci QDMA_CCDF_STATUS_CDE | \ 12362306a36Sopenharmony_ci QDMA_CCDF_STATUS_SDE | \ 12462306a36Sopenharmony_ci QDMA_CCDF_STATUS_DDE) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Field definition for Descriptor offset */ 12762306a36Sopenharmony_ci#define QDMA_CCDF_OFFSET 20 12862306a36Sopenharmony_ci#define QDMA_SDDF_CMD(x) (((u64)(x)) << 32) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* Field definition for safe loop count*/ 13162306a36Sopenharmony_ci#define FSL_QDMA_HALT_COUNT 1500 13262306a36Sopenharmony_ci#define FSL_QDMA_MAX_SIZE 16385 13362306a36Sopenharmony_ci#define FSL_QDMA_COMP_TIMEOUT 1000 13462306a36Sopenharmony_ci#define FSL_COMMAND_QUEUE_OVERFLLOW 10 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma_engine, x) \ 13762306a36Sopenharmony_ci (((fsl_qdma_engine)->block_offset) * (x)) 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * struct fsl_qdma_format - This is the struct holding describing compound 14162306a36Sopenharmony_ci * descriptor format with qDMA. 14262306a36Sopenharmony_ci * @status: Command status and enqueue status notification. 14362306a36Sopenharmony_ci * @cfg: Frame offset and frame format. 14462306a36Sopenharmony_ci * @addr_lo: Holding the compound descriptor of the lower 14562306a36Sopenharmony_ci * 32-bits address in memory 40-bit address. 14662306a36Sopenharmony_ci * @addr_hi: Same as above member, but point high 8-bits in 14762306a36Sopenharmony_ci * memory 40-bit address. 14862306a36Sopenharmony_ci * @__reserved1: Reserved field. 14962306a36Sopenharmony_ci * @cfg8b_w1: Compound descriptor command queue origin produced 15062306a36Sopenharmony_ci * by qDMA and dynamic debug field. 15162306a36Sopenharmony_ci * @data: Pointer to the memory 40-bit address, describes DMA 15262306a36Sopenharmony_ci * source information and DMA destination information. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistruct fsl_qdma_format { 15562306a36Sopenharmony_ci __le32 status; 15662306a36Sopenharmony_ci __le32 cfg; 15762306a36Sopenharmony_ci union { 15862306a36Sopenharmony_ci struct { 15962306a36Sopenharmony_ci __le32 addr_lo; 16062306a36Sopenharmony_ci u8 addr_hi; 16162306a36Sopenharmony_ci u8 __reserved1[2]; 16262306a36Sopenharmony_ci u8 cfg8b_w1; 16362306a36Sopenharmony_ci } __packed; 16462306a36Sopenharmony_ci __le64 data; 16562306a36Sopenharmony_ci }; 16662306a36Sopenharmony_ci} __packed; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* qDMA status notification pre information */ 16962306a36Sopenharmony_cistruct fsl_pre_status { 17062306a36Sopenharmony_ci u64 addr; 17162306a36Sopenharmony_ci u8 queue; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct fsl_pre_status, pre); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistruct fsl_qdma_chan { 17762306a36Sopenharmony_ci struct virt_dma_chan vchan; 17862306a36Sopenharmony_ci struct virt_dma_desc vdesc; 17962306a36Sopenharmony_ci enum dma_status status; 18062306a36Sopenharmony_ci struct fsl_qdma_engine *qdma; 18162306a36Sopenharmony_ci struct fsl_qdma_queue *queue; 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistruct fsl_qdma_queue { 18562306a36Sopenharmony_ci struct fsl_qdma_format *virt_head; 18662306a36Sopenharmony_ci struct fsl_qdma_format *virt_tail; 18762306a36Sopenharmony_ci struct list_head comp_used; 18862306a36Sopenharmony_ci struct list_head comp_free; 18962306a36Sopenharmony_ci struct dma_pool *comp_pool; 19062306a36Sopenharmony_ci struct dma_pool *desc_pool; 19162306a36Sopenharmony_ci spinlock_t queue_lock; 19262306a36Sopenharmony_ci dma_addr_t bus_addr; 19362306a36Sopenharmony_ci u32 n_cq; 19462306a36Sopenharmony_ci u32 id; 19562306a36Sopenharmony_ci struct fsl_qdma_format *cq; 19662306a36Sopenharmony_ci void __iomem *block_base; 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistruct fsl_qdma_comp { 20062306a36Sopenharmony_ci dma_addr_t bus_addr; 20162306a36Sopenharmony_ci dma_addr_t desc_bus_addr; 20262306a36Sopenharmony_ci struct fsl_qdma_format *virt_addr; 20362306a36Sopenharmony_ci struct fsl_qdma_format *desc_virt_addr; 20462306a36Sopenharmony_ci struct fsl_qdma_chan *qchan; 20562306a36Sopenharmony_ci struct virt_dma_desc vdesc; 20662306a36Sopenharmony_ci struct list_head list; 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistruct fsl_qdma_engine { 21062306a36Sopenharmony_ci struct dma_device dma_dev; 21162306a36Sopenharmony_ci void __iomem *ctrl_base; 21262306a36Sopenharmony_ci void __iomem *status_base; 21362306a36Sopenharmony_ci void __iomem *block_base; 21462306a36Sopenharmony_ci u32 n_chans; 21562306a36Sopenharmony_ci u32 n_queues; 21662306a36Sopenharmony_ci struct mutex fsl_qdma_mutex; 21762306a36Sopenharmony_ci int error_irq; 21862306a36Sopenharmony_ci int *queue_irq; 21962306a36Sopenharmony_ci u32 feature; 22062306a36Sopenharmony_ci struct fsl_qdma_queue *queue; 22162306a36Sopenharmony_ci struct fsl_qdma_queue **status; 22262306a36Sopenharmony_ci struct fsl_qdma_chan *chans; 22362306a36Sopenharmony_ci int block_number; 22462306a36Sopenharmony_ci int block_offset; 22562306a36Sopenharmony_ci int irq_base; 22662306a36Sopenharmony_ci int desc_allocated; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic inline u64 23162306a36Sopenharmony_ciqdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci return le64_to_cpu(ccdf->data) & (U64_MAX >> 24); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic inline void 23762306a36Sopenharmony_ciqdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci ccdf->addr_hi = upper_32_bits(addr); 24062306a36Sopenharmony_ci ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr)); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic inline u8 24462306a36Sopenharmony_ciqdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return ccdf->cfg8b_w1 & U8_MAX; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic inline int 25062306a36Sopenharmony_ciqdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >> QDMA_CCDF_OFFSET; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic inline void 25662306a36Sopenharmony_ciqdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci ccdf->cfg = cpu_to_le32(QDMA_CCDF_FORMAT | 25962306a36Sopenharmony_ci (offset << QDMA_CCDF_OFFSET)); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic inline int 26362306a36Sopenharmony_ciqdma_ccdf_get_status(const struct fsl_qdma_format *ccdf) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci return (le32_to_cpu(ccdf->status) & QDMA_CCDF_STATUS_MASK); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic inline void 26962306a36Sopenharmony_ciqdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int len) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int len) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len & QDMA_SG_LEN_MASK)); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci return FSL_DMA_IN(qdma, addr, 32); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void qdma_writel(struct fsl_qdma_engine *qdma, u32 val, 29062306a36Sopenharmony_ci void __iomem *addr) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci FSL_DMA_OUT(qdma, addr, val, 32); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci return container_of(chan, struct fsl_qdma_chan, vchan.chan); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci return container_of(vd, struct fsl_qdma_comp, vdesc); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void fsl_qdma_free_chan_resources(struct dma_chan *chan) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); 30862306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; 30962306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma; 31062306a36Sopenharmony_ci struct fsl_qdma_comp *comp_temp, *_comp_temp; 31162306a36Sopenharmony_ci unsigned long flags; 31262306a36Sopenharmony_ci LIST_HEAD(head); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 31562306a36Sopenharmony_ci vchan_get_all_descriptors(&fsl_chan->vchan, &head); 31662306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci vchan_dma_desc_free_list(&fsl_chan->vchan, &head); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!fsl_queue->comp_pool && !fsl_queue->desc_pool) 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci list_for_each_entry_safe(comp_temp, _comp_temp, 32462306a36Sopenharmony_ci &fsl_queue->comp_used, list) { 32562306a36Sopenharmony_ci dma_pool_free(fsl_queue->comp_pool, 32662306a36Sopenharmony_ci comp_temp->virt_addr, 32762306a36Sopenharmony_ci comp_temp->bus_addr); 32862306a36Sopenharmony_ci dma_pool_free(fsl_queue->desc_pool, 32962306a36Sopenharmony_ci comp_temp->desc_virt_addr, 33062306a36Sopenharmony_ci comp_temp->desc_bus_addr); 33162306a36Sopenharmony_ci list_del(&comp_temp->list); 33262306a36Sopenharmony_ci kfree(comp_temp); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci list_for_each_entry_safe(comp_temp, _comp_temp, 33662306a36Sopenharmony_ci &fsl_queue->comp_free, list) { 33762306a36Sopenharmony_ci dma_pool_free(fsl_queue->comp_pool, 33862306a36Sopenharmony_ci comp_temp->virt_addr, 33962306a36Sopenharmony_ci comp_temp->bus_addr); 34062306a36Sopenharmony_ci dma_pool_free(fsl_queue->desc_pool, 34162306a36Sopenharmony_ci comp_temp->desc_virt_addr, 34262306a36Sopenharmony_ci comp_temp->desc_bus_addr); 34362306a36Sopenharmony_ci list_del(&comp_temp->list); 34462306a36Sopenharmony_ci kfree(comp_temp); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci dma_pool_destroy(fsl_queue->comp_pool); 34862306a36Sopenharmony_ci dma_pool_destroy(fsl_queue->desc_pool); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci fsl_qdma->desc_allocated--; 35162306a36Sopenharmony_ci fsl_queue->comp_pool = NULL; 35262306a36Sopenharmony_ci fsl_queue->desc_pool = NULL; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp, 35662306a36Sopenharmony_ci dma_addr_t dst, dma_addr_t src, u32 len) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci u32 cmd; 35962306a36Sopenharmony_ci struct fsl_qdma_format *sdf, *ddf; 36062306a36Sopenharmony_ci struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ccdf = fsl_comp->virt_addr; 36362306a36Sopenharmony_ci csgf_desc = fsl_comp->virt_addr + 1; 36462306a36Sopenharmony_ci csgf_src = fsl_comp->virt_addr + 2; 36562306a36Sopenharmony_ci csgf_dest = fsl_comp->virt_addr + 3; 36662306a36Sopenharmony_ci sdf = fsl_comp->desc_virt_addr; 36762306a36Sopenharmony_ci ddf = fsl_comp->desc_virt_addr + 1; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci memset(fsl_comp->virt_addr, 0, FSL_QDMA_COMMAND_BUFFER_SIZE); 37062306a36Sopenharmony_ci memset(fsl_comp->desc_virt_addr, 0, FSL_QDMA_DESCRIPTOR_BUFFER_SIZE); 37162306a36Sopenharmony_ci /* Head Command Descriptor(Frame Descriptor) */ 37262306a36Sopenharmony_ci qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16); 37362306a36Sopenharmony_ci qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf)); 37462306a36Sopenharmony_ci qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf)); 37562306a36Sopenharmony_ci /* Status notification is enqueued to status queue. */ 37662306a36Sopenharmony_ci /* Compound Command Descriptor(Frame List Table) */ 37762306a36Sopenharmony_ci qdma_desc_addr_set64(csgf_desc, fsl_comp->desc_bus_addr); 37862306a36Sopenharmony_ci /* It must be 32 as Compound S/G Descriptor */ 37962306a36Sopenharmony_ci qdma_csgf_set_len(csgf_desc, 32); 38062306a36Sopenharmony_ci qdma_desc_addr_set64(csgf_src, src); 38162306a36Sopenharmony_ci qdma_csgf_set_len(csgf_src, len); 38262306a36Sopenharmony_ci qdma_desc_addr_set64(csgf_dest, dst); 38362306a36Sopenharmony_ci qdma_csgf_set_len(csgf_dest, len); 38462306a36Sopenharmony_ci /* This entry is the last entry. */ 38562306a36Sopenharmony_ci qdma_csgf_set_f(csgf_dest, len); 38662306a36Sopenharmony_ci /* Descriptor Buffer */ 38762306a36Sopenharmony_ci cmd = cpu_to_le32(FSL_QDMA_CMD_RWTTYPE << 38862306a36Sopenharmony_ci FSL_QDMA_CMD_RWTTYPE_OFFSET) | 38962306a36Sopenharmony_ci FSL_QDMA_CMD_PF; 39062306a36Sopenharmony_ci sdf->data = QDMA_SDDF_CMD(cmd); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci cmd = cpu_to_le32(FSL_QDMA_CMD_RWTTYPE << 39362306a36Sopenharmony_ci FSL_QDMA_CMD_RWTTYPE_OFFSET); 39462306a36Sopenharmony_ci cmd |= cpu_to_le32(FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET); 39562306a36Sopenharmony_ci ddf->data = QDMA_SDDF_CMD(cmd); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* 39962306a36Sopenharmony_ci * Pre-request full command descriptor for enqueue. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistatic int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue *queue) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int i; 40462306a36Sopenharmony_ci struct fsl_qdma_comp *comp_temp, *_comp_temp; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = 0; i < queue->n_cq + FSL_COMMAND_QUEUE_OVERFLLOW; i++) { 40762306a36Sopenharmony_ci comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL); 40862306a36Sopenharmony_ci if (!comp_temp) 40962306a36Sopenharmony_ci goto err_alloc; 41062306a36Sopenharmony_ci comp_temp->virt_addr = 41162306a36Sopenharmony_ci dma_pool_alloc(queue->comp_pool, GFP_KERNEL, 41262306a36Sopenharmony_ci &comp_temp->bus_addr); 41362306a36Sopenharmony_ci if (!comp_temp->virt_addr) 41462306a36Sopenharmony_ci goto err_dma_alloc; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci comp_temp->desc_virt_addr = 41762306a36Sopenharmony_ci dma_pool_alloc(queue->desc_pool, GFP_KERNEL, 41862306a36Sopenharmony_ci &comp_temp->desc_bus_addr); 41962306a36Sopenharmony_ci if (!comp_temp->desc_virt_addr) 42062306a36Sopenharmony_ci goto err_desc_dma_alloc; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci list_add_tail(&comp_temp->list, &queue->comp_free); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cierr_desc_dma_alloc: 42862306a36Sopenharmony_ci dma_pool_free(queue->comp_pool, comp_temp->virt_addr, 42962306a36Sopenharmony_ci comp_temp->bus_addr); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cierr_dma_alloc: 43262306a36Sopenharmony_ci kfree(comp_temp); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cierr_alloc: 43562306a36Sopenharmony_ci list_for_each_entry_safe(comp_temp, _comp_temp, 43662306a36Sopenharmony_ci &queue->comp_free, list) { 43762306a36Sopenharmony_ci if (comp_temp->virt_addr) 43862306a36Sopenharmony_ci dma_pool_free(queue->comp_pool, 43962306a36Sopenharmony_ci comp_temp->virt_addr, 44062306a36Sopenharmony_ci comp_temp->bus_addr); 44162306a36Sopenharmony_ci if (comp_temp->desc_virt_addr) 44262306a36Sopenharmony_ci dma_pool_free(queue->desc_pool, 44362306a36Sopenharmony_ci comp_temp->desc_virt_addr, 44462306a36Sopenharmony_ci comp_temp->desc_bus_addr); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci list_del(&comp_temp->list); 44762306a36Sopenharmony_ci kfree(comp_temp); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return -ENOMEM; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * Request a command descriptor for enqueue. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic struct fsl_qdma_comp 45762306a36Sopenharmony_ci*fsl_qdma_request_enqueue_desc(struct fsl_qdma_chan *fsl_chan) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci unsigned long flags; 46062306a36Sopenharmony_ci struct fsl_qdma_comp *comp_temp; 46162306a36Sopenharmony_ci int timeout = FSL_QDMA_COMP_TIMEOUT; 46262306a36Sopenharmony_ci struct fsl_qdma_queue *queue = fsl_chan->queue; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci while (timeout--) { 46562306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 46662306a36Sopenharmony_ci if (!list_empty(&queue->comp_free)) { 46762306a36Sopenharmony_ci comp_temp = list_first_entry(&queue->comp_free, 46862306a36Sopenharmony_ci struct fsl_qdma_comp, 46962306a36Sopenharmony_ci list); 47062306a36Sopenharmony_ci list_del(&comp_temp->list); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 47362306a36Sopenharmony_ci comp_temp->qchan = fsl_chan; 47462306a36Sopenharmony_ci return comp_temp; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 47762306a36Sopenharmony_ci udelay(1); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return NULL; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic struct fsl_qdma_queue 48462306a36Sopenharmony_ci*fsl_qdma_alloc_queue_resources(struct platform_device *pdev, 48562306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci int ret, len, i, j; 48862306a36Sopenharmony_ci int queue_num, block_number; 48962306a36Sopenharmony_ci unsigned int queue_size[FSL_QDMA_QUEUE_MAX]; 49062306a36Sopenharmony_ci struct fsl_qdma_queue *queue_head, *queue_temp; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci queue_num = fsl_qdma->n_queues; 49362306a36Sopenharmony_ci block_number = fsl_qdma->block_number; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (queue_num > FSL_QDMA_QUEUE_MAX) 49662306a36Sopenharmony_ci queue_num = FSL_QDMA_QUEUE_MAX; 49762306a36Sopenharmony_ci len = sizeof(*queue_head) * queue_num * block_number; 49862306a36Sopenharmony_ci queue_head = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 49962306a36Sopenharmony_ci if (!queue_head) 50062306a36Sopenharmony_ci return NULL; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ret = device_property_read_u32_array(&pdev->dev, "queue-sizes", 50362306a36Sopenharmony_ci queue_size, queue_num); 50462306a36Sopenharmony_ci if (ret) { 50562306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get queue-sizes.\n"); 50662306a36Sopenharmony_ci return NULL; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci for (j = 0; j < block_number; j++) { 50962306a36Sopenharmony_ci for (i = 0; i < queue_num; i++) { 51062306a36Sopenharmony_ci if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX || 51162306a36Sopenharmony_ci queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) { 51262306a36Sopenharmony_ci dev_err(&pdev->dev, 51362306a36Sopenharmony_ci "Get wrong queue-sizes.\n"); 51462306a36Sopenharmony_ci return NULL; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci queue_temp = queue_head + i + (j * queue_num); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci queue_temp->cq = 51962306a36Sopenharmony_ci dmam_alloc_coherent(&pdev->dev, 52062306a36Sopenharmony_ci sizeof(struct fsl_qdma_format) * 52162306a36Sopenharmony_ci queue_size[i], 52262306a36Sopenharmony_ci &queue_temp->bus_addr, 52362306a36Sopenharmony_ci GFP_KERNEL); 52462306a36Sopenharmony_ci if (!queue_temp->cq) 52562306a36Sopenharmony_ci return NULL; 52662306a36Sopenharmony_ci queue_temp->block_base = fsl_qdma->block_base + 52762306a36Sopenharmony_ci FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); 52862306a36Sopenharmony_ci queue_temp->n_cq = queue_size[i]; 52962306a36Sopenharmony_ci queue_temp->id = i; 53062306a36Sopenharmony_ci queue_temp->virt_head = queue_temp->cq; 53162306a36Sopenharmony_ci queue_temp->virt_tail = queue_temp->cq; 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * List for queue command buffer 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci INIT_LIST_HEAD(&queue_temp->comp_used); 53662306a36Sopenharmony_ci spin_lock_init(&queue_temp->queue_lock); 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci return queue_head; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic struct fsl_qdma_queue 54362306a36Sopenharmony_ci*fsl_qdma_prep_status_queue(struct platform_device *pdev) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci int ret; 54662306a36Sopenharmony_ci unsigned int status_size; 54762306a36Sopenharmony_ci struct fsl_qdma_queue *status_head; 54862306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci ret = of_property_read_u32(np, "status-sizes", &status_size); 55162306a36Sopenharmony_ci if (ret) { 55262306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get status-sizes.\n"); 55362306a36Sopenharmony_ci return NULL; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX || 55662306a36Sopenharmony_ci status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) { 55762306a36Sopenharmony_ci dev_err(&pdev->dev, "Get wrong status_size.\n"); 55862306a36Sopenharmony_ci return NULL; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci status_head = devm_kzalloc(&pdev->dev, 56162306a36Sopenharmony_ci sizeof(*status_head), GFP_KERNEL); 56262306a36Sopenharmony_ci if (!status_head) 56362306a36Sopenharmony_ci return NULL; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * Buffer for queue command 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci status_head->cq = dmam_alloc_coherent(&pdev->dev, 56962306a36Sopenharmony_ci sizeof(struct fsl_qdma_format) * 57062306a36Sopenharmony_ci status_size, 57162306a36Sopenharmony_ci &status_head->bus_addr, 57262306a36Sopenharmony_ci GFP_KERNEL); 57362306a36Sopenharmony_ci if (!status_head->cq) { 57462306a36Sopenharmony_ci devm_kfree(&pdev->dev, status_head); 57562306a36Sopenharmony_ci return NULL; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci status_head->n_cq = status_size; 57862306a36Sopenharmony_ci status_head->virt_head = status_head->cq; 57962306a36Sopenharmony_ci status_head->virt_tail = status_head->cq; 58062306a36Sopenharmony_ci status_head->comp_pool = NULL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return status_head; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci u32 reg; 58862306a36Sopenharmony_ci int i, j, count = FSL_QDMA_HALT_COUNT; 58962306a36Sopenharmony_ci void __iomem *block, *ctrl = fsl_qdma->ctrl_base; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Disable the command queue and wait for idle state. */ 59262306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); 59362306a36Sopenharmony_ci reg |= FSL_QDMA_DMR_DQD; 59462306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); 59562306a36Sopenharmony_ci for (j = 0; j < fsl_qdma->block_number; j++) { 59662306a36Sopenharmony_ci block = fsl_qdma->block_base + 59762306a36Sopenharmony_ci FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); 59862306a36Sopenharmony_ci for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++) 59962306a36Sopenharmony_ci qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i)); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci while (1) { 60262306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR); 60362306a36Sopenharmony_ci if (!(reg & FSL_QDMA_DSR_DB)) 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci if (count-- < 0) 60662306a36Sopenharmony_ci return -EBUSY; 60762306a36Sopenharmony_ci udelay(100); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci for (j = 0; j < fsl_qdma->block_number; j++) { 61162306a36Sopenharmony_ci block = fsl_qdma->block_base + 61262306a36Sopenharmony_ci FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Disable status queue. */ 61562306a36Sopenharmony_ci qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* 61862306a36Sopenharmony_ci * clear the command queue interrupt detect register for 61962306a36Sopenharmony_ci * all queues. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, 62262306a36Sopenharmony_ci block + FSL_QDMA_BCQIDR(0)); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int 62962306a36Sopenharmony_cifsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma, 63062306a36Sopenharmony_ci void *block, 63162306a36Sopenharmony_ci int id) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci bool duplicate; 63462306a36Sopenharmony_ci u32 reg, i, count; 63562306a36Sopenharmony_ci u8 completion_status; 63662306a36Sopenharmony_ci struct fsl_qdma_queue *temp_queue; 63762306a36Sopenharmony_ci struct fsl_qdma_format *status_addr; 63862306a36Sopenharmony_ci struct fsl_qdma_comp *fsl_comp = NULL; 63962306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue; 64062306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_status = fsl_qdma->status[id]; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci count = FSL_QDMA_MAX_SIZE; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci while (count--) { 64562306a36Sopenharmony_ci duplicate = 0; 64662306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR); 64762306a36Sopenharmony_ci if (reg & FSL_QDMA_BSQSR_QE) 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci status_addr = fsl_status->virt_head; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (qdma_ccdf_get_queue(status_addr) == 65362306a36Sopenharmony_ci __this_cpu_read(pre.queue) && 65462306a36Sopenharmony_ci qdma_ccdf_addr_get64(status_addr) == 65562306a36Sopenharmony_ci __this_cpu_read(pre.addr)) 65662306a36Sopenharmony_ci duplicate = 1; 65762306a36Sopenharmony_ci i = qdma_ccdf_get_queue(status_addr) + 65862306a36Sopenharmony_ci id * fsl_qdma->n_queues; 65962306a36Sopenharmony_ci __this_cpu_write(pre.addr, qdma_ccdf_addr_get64(status_addr)); 66062306a36Sopenharmony_ci __this_cpu_write(pre.queue, qdma_ccdf_get_queue(status_addr)); 66162306a36Sopenharmony_ci temp_queue = fsl_queue + i; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci spin_lock(&temp_queue->queue_lock); 66462306a36Sopenharmony_ci if (list_empty(&temp_queue->comp_used)) { 66562306a36Sopenharmony_ci if (!duplicate) { 66662306a36Sopenharmony_ci spin_unlock(&temp_queue->queue_lock); 66762306a36Sopenharmony_ci return -EAGAIN; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } else { 67062306a36Sopenharmony_ci fsl_comp = list_first_entry(&temp_queue->comp_used, 67162306a36Sopenharmony_ci struct fsl_qdma_comp, list); 67262306a36Sopenharmony_ci if (fsl_comp->bus_addr + 16 != 67362306a36Sopenharmony_ci __this_cpu_read(pre.addr)) { 67462306a36Sopenharmony_ci if (!duplicate) { 67562306a36Sopenharmony_ci spin_unlock(&temp_queue->queue_lock); 67662306a36Sopenharmony_ci return -EAGAIN; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (duplicate) { 68262306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); 68362306a36Sopenharmony_ci reg |= FSL_QDMA_BSQMR_DI; 68462306a36Sopenharmony_ci qdma_desc_addr_set64(status_addr, 0x0); 68562306a36Sopenharmony_ci fsl_status->virt_head++; 68662306a36Sopenharmony_ci if (fsl_status->virt_head == fsl_status->cq 68762306a36Sopenharmony_ci + fsl_status->n_cq) 68862306a36Sopenharmony_ci fsl_status->virt_head = fsl_status->cq; 68962306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); 69062306a36Sopenharmony_ci spin_unlock(&temp_queue->queue_lock); 69162306a36Sopenharmony_ci continue; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci list_del(&fsl_comp->list); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci completion_status = qdma_ccdf_get_status(status_addr); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); 69862306a36Sopenharmony_ci reg |= FSL_QDMA_BSQMR_DI; 69962306a36Sopenharmony_ci qdma_desc_addr_set64(status_addr, 0x0); 70062306a36Sopenharmony_ci fsl_status->virt_head++; 70162306a36Sopenharmony_ci if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq) 70262306a36Sopenharmony_ci fsl_status->virt_head = fsl_status->cq; 70362306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); 70462306a36Sopenharmony_ci spin_unlock(&temp_queue->queue_lock); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* The completion_status is evaluated here 70762306a36Sopenharmony_ci * (outside of spin lock) 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci if (completion_status) { 71062306a36Sopenharmony_ci /* A completion error occurred! */ 71162306a36Sopenharmony_ci if (completion_status & QDMA_CCDF_STATUS_WTE) { 71262306a36Sopenharmony_ci /* Write transaction error */ 71362306a36Sopenharmony_ci fsl_comp->vdesc.tx_result.result = 71462306a36Sopenharmony_ci DMA_TRANS_WRITE_FAILED; 71562306a36Sopenharmony_ci } else if (completion_status & QDMA_CCDF_STATUS_RTE) { 71662306a36Sopenharmony_ci /* Read transaction error */ 71762306a36Sopenharmony_ci fsl_comp->vdesc.tx_result.result = 71862306a36Sopenharmony_ci DMA_TRANS_READ_FAILED; 71962306a36Sopenharmony_ci } else { 72062306a36Sopenharmony_ci /* Command/source/destination 72162306a36Sopenharmony_ci * description error 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci fsl_comp->vdesc.tx_result.result = 72462306a36Sopenharmony_ci DMA_TRANS_ABORTED; 72562306a36Sopenharmony_ci dev_err(fsl_qdma->dma_dev.dev, 72662306a36Sopenharmony_ci "DMA status descriptor error %x\n", 72762306a36Sopenharmony_ci completion_status); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci spin_lock(&fsl_comp->qchan->vchan.lock); 73262306a36Sopenharmony_ci vchan_cookie_complete(&fsl_comp->vdesc); 73362306a36Sopenharmony_ci fsl_comp->qchan->status = DMA_COMPLETE; 73462306a36Sopenharmony_ci spin_unlock(&fsl_comp->qchan->vchan.lock); 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci unsigned int intr; 74362306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma = dev_id; 74462306a36Sopenharmony_ci void __iomem *status = fsl_qdma->status_base; 74562306a36Sopenharmony_ci unsigned int decfdw0r; 74662306a36Sopenharmony_ci unsigned int decfdw1r; 74762306a36Sopenharmony_ci unsigned int decfdw2r; 74862306a36Sopenharmony_ci unsigned int decfdw3r; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (intr) { 75362306a36Sopenharmony_ci decfdw0r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW0R); 75462306a36Sopenharmony_ci decfdw1r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW1R); 75562306a36Sopenharmony_ci decfdw2r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW2R); 75662306a36Sopenharmony_ci decfdw3r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW3R); 75762306a36Sopenharmony_ci dev_err(fsl_qdma->dma_dev.dev, 75862306a36Sopenharmony_ci "DMA transaction error! (%x: %x-%x-%x-%x)\n", 75962306a36Sopenharmony_ci intr, decfdw0r, decfdw1r, decfdw2r, decfdw3r); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); 76362306a36Sopenharmony_ci return IRQ_HANDLED; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci int id; 76962306a36Sopenharmony_ci unsigned int intr, reg; 77062306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma = dev_id; 77162306a36Sopenharmony_ci void __iomem *block, *ctrl = fsl_qdma->ctrl_base; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci id = irq - fsl_qdma->irq_base; 77462306a36Sopenharmony_ci if (id < 0 && id > fsl_qdma->block_number) { 77562306a36Sopenharmony_ci dev_err(fsl_qdma->dma_dev.dev, 77662306a36Sopenharmony_ci "irq %d is wrong irq_base is %d\n", 77762306a36Sopenharmony_ci irq, fsl_qdma->irq_base); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci block = fsl_qdma->block_base + 78162306a36Sopenharmony_ci FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, id); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0)); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if ((intr & FSL_QDMA_CQIDR_SQT) != 0) 78662306a36Sopenharmony_ci intr = fsl_qdma_queue_transfer_complete(fsl_qdma, block, id); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (intr != 0) { 78962306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); 79062306a36Sopenharmony_ci reg |= FSL_QDMA_DMR_DQD; 79162306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); 79262306a36Sopenharmony_ci qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0)); 79362306a36Sopenharmony_ci dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n"); 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Clear all detected events and interrupts. */ 79762306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, 79862306a36Sopenharmony_ci block + FSL_QDMA_BCQIDR(0)); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return IRQ_HANDLED; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int 80462306a36Sopenharmony_cifsl_qdma_irq_init(struct platform_device *pdev, 80562306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci int i; 80862306a36Sopenharmony_ci int cpu; 80962306a36Sopenharmony_ci int ret; 81062306a36Sopenharmony_ci char irq_name[32]; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci fsl_qdma->error_irq = 81362306a36Sopenharmony_ci platform_get_irq_byname(pdev, "qdma-error"); 81462306a36Sopenharmony_ci if (fsl_qdma->error_irq < 0) 81562306a36Sopenharmony_ci return fsl_qdma->error_irq; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq, 81862306a36Sopenharmony_ci fsl_qdma_error_handler, 0, 81962306a36Sopenharmony_ci "qDMA error", fsl_qdma); 82062306a36Sopenharmony_ci if (ret) { 82162306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n"); 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci for (i = 0; i < fsl_qdma->block_number; i++) { 82662306a36Sopenharmony_ci sprintf(irq_name, "qdma-queue%d", i); 82762306a36Sopenharmony_ci fsl_qdma->queue_irq[i] = 82862306a36Sopenharmony_ci platform_get_irq_byname(pdev, irq_name); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (fsl_qdma->queue_irq[i] < 0) 83162306a36Sopenharmony_ci return fsl_qdma->queue_irq[i]; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, 83462306a36Sopenharmony_ci fsl_qdma->queue_irq[i], 83562306a36Sopenharmony_ci fsl_qdma_queue_handler, 83662306a36Sopenharmony_ci 0, 83762306a36Sopenharmony_ci "qDMA queue", 83862306a36Sopenharmony_ci fsl_qdma); 83962306a36Sopenharmony_ci if (ret) { 84062306a36Sopenharmony_ci dev_err(&pdev->dev, 84162306a36Sopenharmony_ci "Can't register qDMA queue IRQ.\n"); 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci cpu = i % num_online_cpus(); 84662306a36Sopenharmony_ci ret = irq_set_affinity_hint(fsl_qdma->queue_irq[i], 84762306a36Sopenharmony_ci get_cpu_mask(cpu)); 84862306a36Sopenharmony_ci if (ret) { 84962306a36Sopenharmony_ci dev_err(&pdev->dev, 85062306a36Sopenharmony_ci "Can't set cpu %d affinity to IRQ %d.\n", 85162306a36Sopenharmony_ci cpu, 85262306a36Sopenharmony_ci fsl_qdma->queue_irq[i]); 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void fsl_qdma_irq_exit(struct platform_device *pdev, 86162306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci int i; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma); 86662306a36Sopenharmony_ci for (i = 0; i < fsl_qdma->block_number; i++) 86762306a36Sopenharmony_ci devm_free_irq(&pdev->dev, fsl_qdma->queue_irq[i], fsl_qdma); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci u32 reg; 87362306a36Sopenharmony_ci int i, j, ret; 87462306a36Sopenharmony_ci struct fsl_qdma_queue *temp; 87562306a36Sopenharmony_ci void __iomem *status = fsl_qdma->status_base; 87662306a36Sopenharmony_ci void __iomem *block, *ctrl = fsl_qdma->ctrl_base; 87762306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* Try to halt the qDMA engine first. */ 88062306a36Sopenharmony_ci ret = fsl_qdma_halt(fsl_qdma); 88162306a36Sopenharmony_ci if (ret) { 88262306a36Sopenharmony_ci dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!"); 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci for (i = 0; i < fsl_qdma->block_number; i++) { 88762306a36Sopenharmony_ci /* 88862306a36Sopenharmony_ci * Clear the command queue interrupt detect register for 88962306a36Sopenharmony_ci * all queues. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci block = fsl_qdma->block_base + 89362306a36Sopenharmony_ci FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, i); 89462306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR, 89562306a36Sopenharmony_ci block + FSL_QDMA_BCQIDR(0)); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci for (j = 0; j < fsl_qdma->block_number; j++) { 89962306a36Sopenharmony_ci block = fsl_qdma->block_base + 90062306a36Sopenharmony_ci FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j); 90162306a36Sopenharmony_ci for (i = 0; i < fsl_qdma->n_queues; i++) { 90262306a36Sopenharmony_ci temp = fsl_queue + i + (j * fsl_qdma->n_queues); 90362306a36Sopenharmony_ci /* 90462306a36Sopenharmony_ci * Initialize Command Queue registers to 90562306a36Sopenharmony_ci * point to the first 90662306a36Sopenharmony_ci * command descriptor in memory. 90762306a36Sopenharmony_ci * Dequeue Pointer Address Registers 90862306a36Sopenharmony_ci * Enqueue Pointer Address Registers 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci qdma_writel(fsl_qdma, temp->bus_addr, 91262306a36Sopenharmony_ci block + FSL_QDMA_BCQDPA_SADDR(i)); 91362306a36Sopenharmony_ci qdma_writel(fsl_qdma, temp->bus_addr, 91462306a36Sopenharmony_ci block + FSL_QDMA_BCQEPA_SADDR(i)); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Initialize the queue mode. */ 91762306a36Sopenharmony_ci reg = FSL_QDMA_BCQMR_EN; 91862306a36Sopenharmony_ci reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4); 91962306a36Sopenharmony_ci reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6); 92062306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i)); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * Workaround for erratum: ERR010812. 92562306a36Sopenharmony_ci * We must enable XOFF to avoid the enqueue rejection occurs. 92662306a36Sopenharmony_ci * Setting SQCCMR ENTER_WM to 0x20. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM, 93062306a36Sopenharmony_ci block + FSL_QDMA_SQCCMR); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* 93362306a36Sopenharmony_ci * Initialize status queue registers to point to the first 93462306a36Sopenharmony_ci * command descriptor in memory. 93562306a36Sopenharmony_ci * Dequeue Pointer Address Registers 93662306a36Sopenharmony_ci * Enqueue Pointer Address Registers 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr, 94062306a36Sopenharmony_ci block + FSL_QDMA_SQEPAR); 94162306a36Sopenharmony_ci qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr, 94262306a36Sopenharmony_ci block + FSL_QDMA_SQDPAR); 94362306a36Sopenharmony_ci /* Initialize status queue interrupt. */ 94462306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE, 94562306a36Sopenharmony_ci block + FSL_QDMA_BCQIER(0)); 94662306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN | 94762306a36Sopenharmony_ci FSL_QDMA_BSQICR_ICST(5) | 0x8000, 94862306a36Sopenharmony_ci block + FSL_QDMA_BSQICR); 94962306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE | 95062306a36Sopenharmony_ci FSL_QDMA_CQIER_TEIE, 95162306a36Sopenharmony_ci block + FSL_QDMA_CQIER); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Initialize the status queue mode. */ 95462306a36Sopenharmony_ci reg = FSL_QDMA_BSQMR_EN; 95562306a36Sopenharmony_ci reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2 95662306a36Sopenharmony_ci (fsl_qdma->status[j]->n_cq) - 6); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); 95962306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Initialize controller interrupt register. */ 96362306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); 96462306a36Sopenharmony_ci qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status + FSL_QDMA_DEIER); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR); 96762306a36Sopenharmony_ci reg &= ~FSL_QDMA_DMR_DQD; 96862306a36Sopenharmony_ci qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 97462306a36Sopenharmony_cifsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, 97562306a36Sopenharmony_ci dma_addr_t src, size_t len, unsigned long flags) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct fsl_qdma_comp *fsl_comp; 97862306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (!fsl_comp) 98362306a36Sopenharmony_ci return NULL; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci u32 reg; 99362306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 99462306a36Sopenharmony_ci struct fsl_qdma_comp *fsl_comp; 99562306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; 99662306a36Sopenharmony_ci void __iomem *block = fsl_queue->block_base; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQSR(fsl_queue->id)); 99962306a36Sopenharmony_ci if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF)) 100062306a36Sopenharmony_ci return; 100162306a36Sopenharmony_ci vdesc = vchan_next_desc(&fsl_chan->vchan); 100262306a36Sopenharmony_ci if (!vdesc) 100362306a36Sopenharmony_ci return; 100462306a36Sopenharmony_ci list_del(&vdesc->node); 100562306a36Sopenharmony_ci fsl_comp = to_fsl_qdma_comp(vdesc); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci memcpy(fsl_queue->virt_head++, 100862306a36Sopenharmony_ci fsl_comp->virt_addr, sizeof(struct fsl_qdma_format)); 100962306a36Sopenharmony_ci if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq) 101062306a36Sopenharmony_ci fsl_queue->virt_head = fsl_queue->cq; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci list_add_tail(&fsl_comp->list, &fsl_queue->comp_used); 101362306a36Sopenharmony_ci barrier(); 101462306a36Sopenharmony_ci reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQMR(fsl_queue->id)); 101562306a36Sopenharmony_ci reg |= FSL_QDMA_BCQMR_EI; 101662306a36Sopenharmony_ci qdma_writel(fsl_chan->qdma, reg, block + FSL_QDMA_BCQMR(fsl_queue->id)); 101762306a36Sopenharmony_ci fsl_chan->status = DMA_IN_PROGRESS; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic void fsl_qdma_free_desc(struct virt_dma_desc *vdesc) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci unsigned long flags; 102362306a36Sopenharmony_ci struct fsl_qdma_comp *fsl_comp; 102462306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci fsl_comp = to_fsl_qdma_comp(vdesc); 102762306a36Sopenharmony_ci fsl_queue = fsl_comp->qchan->queue; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci spin_lock_irqsave(&fsl_queue->queue_lock, flags); 103062306a36Sopenharmony_ci list_add_tail(&fsl_comp->list, &fsl_queue->comp_free); 103162306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic void fsl_qdma_issue_pending(struct dma_chan *chan) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci unsigned long flags; 103762306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); 103862306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci spin_lock_irqsave(&fsl_queue->queue_lock, flags); 104162306a36Sopenharmony_ci spin_lock(&fsl_chan->vchan.lock); 104262306a36Sopenharmony_ci if (vchan_issue_pending(&fsl_chan->vchan)) 104362306a36Sopenharmony_ci fsl_qdma_enqueue_desc(fsl_chan); 104462306a36Sopenharmony_ci spin_unlock(&fsl_chan->vchan.lock); 104562306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_queue->queue_lock, flags); 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic void fsl_qdma_synchronize(struct dma_chan *chan) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci vchan_synchronize(&fsl_chan->vchan); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic int fsl_qdma_terminate_all(struct dma_chan *chan) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci LIST_HEAD(head); 105862306a36Sopenharmony_ci unsigned long flags; 105962306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 106262306a36Sopenharmony_ci vchan_get_all_descriptors(&fsl_chan->vchan, &head); 106362306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 106462306a36Sopenharmony_ci vchan_dma_desc_free_list(&fsl_chan->vchan, &head); 106562306a36Sopenharmony_ci return 0; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic int fsl_qdma_alloc_chan_resources(struct dma_chan *chan) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci int ret; 107162306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); 107262306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma; 107362306a36Sopenharmony_ci struct fsl_qdma_queue *fsl_queue = fsl_chan->queue; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (fsl_queue->comp_pool && fsl_queue->desc_pool) 107662306a36Sopenharmony_ci return fsl_qdma->desc_allocated; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci INIT_LIST_HEAD(&fsl_queue->comp_free); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* 108162306a36Sopenharmony_ci * The dma pool for queue command buffer 108262306a36Sopenharmony_ci */ 108362306a36Sopenharmony_ci fsl_queue->comp_pool = 108462306a36Sopenharmony_ci dma_pool_create("comp_pool", 108562306a36Sopenharmony_ci chan->device->dev, 108662306a36Sopenharmony_ci FSL_QDMA_COMMAND_BUFFER_SIZE, 108762306a36Sopenharmony_ci 64, 0); 108862306a36Sopenharmony_ci if (!fsl_queue->comp_pool) 108962306a36Sopenharmony_ci return -ENOMEM; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci /* 109262306a36Sopenharmony_ci * The dma pool for Descriptor(SD/DD) buffer 109362306a36Sopenharmony_ci */ 109462306a36Sopenharmony_ci fsl_queue->desc_pool = 109562306a36Sopenharmony_ci dma_pool_create("desc_pool", 109662306a36Sopenharmony_ci chan->device->dev, 109762306a36Sopenharmony_ci FSL_QDMA_DESCRIPTOR_BUFFER_SIZE, 109862306a36Sopenharmony_ci 32, 0); 109962306a36Sopenharmony_ci if (!fsl_queue->desc_pool) 110062306a36Sopenharmony_ci goto err_desc_pool; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci ret = fsl_qdma_pre_request_enqueue_desc(fsl_queue); 110362306a36Sopenharmony_ci if (ret) { 110462306a36Sopenharmony_ci dev_err(chan->device->dev, 110562306a36Sopenharmony_ci "failed to alloc dma buffer for S/G descriptor\n"); 110662306a36Sopenharmony_ci goto err_mem; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci fsl_qdma->desc_allocated++; 111062306a36Sopenharmony_ci return fsl_qdma->desc_allocated; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cierr_mem: 111362306a36Sopenharmony_ci dma_pool_destroy(fsl_queue->desc_pool); 111462306a36Sopenharmony_cierr_desc_pool: 111562306a36Sopenharmony_ci dma_pool_destroy(fsl_queue->comp_pool); 111662306a36Sopenharmony_ci return -ENOMEM; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic int fsl_qdma_probe(struct platform_device *pdev) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci int ret, i; 112262306a36Sopenharmony_ci int blk_num, blk_off; 112362306a36Sopenharmony_ci u32 len, chans, queues; 112462306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan; 112562306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma; 112662306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ret = of_property_read_u32(np, "dma-channels", &chans); 112962306a36Sopenharmony_ci if (ret) { 113062306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get dma-channels.\n"); 113162306a36Sopenharmony_ci return ret; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci ret = of_property_read_u32(np, "block-offset", &blk_off); 113562306a36Sopenharmony_ci if (ret) { 113662306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get block-offset.\n"); 113762306a36Sopenharmony_ci return ret; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci ret = of_property_read_u32(np, "block-number", &blk_num); 114162306a36Sopenharmony_ci if (ret) { 114262306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get block-number.\n"); 114362306a36Sopenharmony_ci return ret; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci blk_num = min_t(int, blk_num, num_online_cpus()); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci len = sizeof(*fsl_qdma); 114962306a36Sopenharmony_ci fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 115062306a36Sopenharmony_ci if (!fsl_qdma) 115162306a36Sopenharmony_ci return -ENOMEM; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci len = sizeof(*fsl_chan) * chans; 115462306a36Sopenharmony_ci fsl_qdma->chans = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 115562306a36Sopenharmony_ci if (!fsl_qdma->chans) 115662306a36Sopenharmony_ci return -ENOMEM; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci len = sizeof(struct fsl_qdma_queue *) * blk_num; 115962306a36Sopenharmony_ci fsl_qdma->status = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 116062306a36Sopenharmony_ci if (!fsl_qdma->status) 116162306a36Sopenharmony_ci return -ENOMEM; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci len = sizeof(int) * blk_num; 116462306a36Sopenharmony_ci fsl_qdma->queue_irq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 116562306a36Sopenharmony_ci if (!fsl_qdma->queue_irq) 116662306a36Sopenharmony_ci return -ENOMEM; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci ret = of_property_read_u32(np, "fsl,dma-queues", &queues); 116962306a36Sopenharmony_ci if (ret) { 117062306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get queues.\n"); 117162306a36Sopenharmony_ci return ret; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci fsl_qdma->desc_allocated = 0; 117562306a36Sopenharmony_ci fsl_qdma->n_chans = chans; 117662306a36Sopenharmony_ci fsl_qdma->n_queues = queues; 117762306a36Sopenharmony_ci fsl_qdma->block_number = blk_num; 117862306a36Sopenharmony_ci fsl_qdma->block_offset = blk_off; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci mutex_init(&fsl_qdma->fsl_qdma_mutex); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci for (i = 0; i < fsl_qdma->block_number; i++) { 118362306a36Sopenharmony_ci fsl_qdma->status[i] = fsl_qdma_prep_status_queue(pdev); 118462306a36Sopenharmony_ci if (!fsl_qdma->status[i]) 118562306a36Sopenharmony_ci return -ENOMEM; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci fsl_qdma->ctrl_base = devm_platform_ioremap_resource(pdev, 0); 118862306a36Sopenharmony_ci if (IS_ERR(fsl_qdma->ctrl_base)) 118962306a36Sopenharmony_ci return PTR_ERR(fsl_qdma->ctrl_base); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci fsl_qdma->status_base = devm_platform_ioremap_resource(pdev, 1); 119262306a36Sopenharmony_ci if (IS_ERR(fsl_qdma->status_base)) 119362306a36Sopenharmony_ci return PTR_ERR(fsl_qdma->status_base); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci fsl_qdma->block_base = devm_platform_ioremap_resource(pdev, 2); 119662306a36Sopenharmony_ci if (IS_ERR(fsl_qdma->block_base)) 119762306a36Sopenharmony_ci return PTR_ERR(fsl_qdma->block_base); 119862306a36Sopenharmony_ci fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, fsl_qdma); 119962306a36Sopenharmony_ci if (!fsl_qdma->queue) 120062306a36Sopenharmony_ci return -ENOMEM; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci fsl_qdma->irq_base = platform_get_irq_byname(pdev, "qdma-queue0"); 120362306a36Sopenharmony_ci if (fsl_qdma->irq_base < 0) 120462306a36Sopenharmony_ci return fsl_qdma->irq_base; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci fsl_qdma->feature = of_property_read_bool(np, "big-endian"); 120762306a36Sopenharmony_ci INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci for (i = 0; i < fsl_qdma->n_chans; i++) { 121062306a36Sopenharmony_ci struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i]; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci fsl_chan->qdma = fsl_qdma; 121362306a36Sopenharmony_ci fsl_chan->queue = fsl_qdma->queue + i % (fsl_qdma->n_queues * 121462306a36Sopenharmony_ci fsl_qdma->block_number); 121562306a36Sopenharmony_ci fsl_chan->vchan.desc_free = fsl_qdma_free_desc; 121662306a36Sopenharmony_ci vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev); 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci fsl_qdma->dma_dev.dev = &pdev->dev; 122262306a36Sopenharmony_ci fsl_qdma->dma_dev.device_free_chan_resources = 122362306a36Sopenharmony_ci fsl_qdma_free_chan_resources; 122462306a36Sopenharmony_ci fsl_qdma->dma_dev.device_alloc_chan_resources = 122562306a36Sopenharmony_ci fsl_qdma_alloc_chan_resources; 122662306a36Sopenharmony_ci fsl_qdma->dma_dev.device_tx_status = dma_cookie_status; 122762306a36Sopenharmony_ci fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy; 122862306a36Sopenharmony_ci fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending; 122962306a36Sopenharmony_ci fsl_qdma->dma_dev.device_synchronize = fsl_qdma_synchronize; 123062306a36Sopenharmony_ci fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); 123362306a36Sopenharmony_ci if (ret) { 123462306a36Sopenharmony_ci dev_err(&pdev->dev, "dma_set_mask failure.\n"); 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci platform_set_drvdata(pdev, fsl_qdma); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci ret = fsl_qdma_reg_init(fsl_qdma); 124162306a36Sopenharmony_ci if (ret) { 124262306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n"); 124362306a36Sopenharmony_ci return ret; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci ret = fsl_qdma_irq_init(pdev, fsl_qdma); 124762306a36Sopenharmony_ci if (ret) 124862306a36Sopenharmony_ci return ret; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci ret = dma_async_device_register(&fsl_qdma->dma_dev); 125162306a36Sopenharmony_ci if (ret) { 125262306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't register NXP Layerscape qDMA engine.\n"); 125362306a36Sopenharmony_ci return ret; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci return 0; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic void fsl_qdma_cleanup_vchan(struct dma_device *dmadev) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci struct fsl_qdma_chan *chan, *_chan; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, 126462306a36Sopenharmony_ci &dmadev->channels, vchan.chan.device_node) { 126562306a36Sopenharmony_ci list_del(&chan->vchan.chan.device_node); 126662306a36Sopenharmony_ci tasklet_kill(&chan->vchan.task); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic int fsl_qdma_remove(struct platform_device *pdev) 127162306a36Sopenharmony_ci{ 127262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 127362306a36Sopenharmony_ci struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci fsl_qdma_irq_exit(pdev, fsl_qdma); 127662306a36Sopenharmony_ci fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev); 127762306a36Sopenharmony_ci of_dma_controller_free(np); 127862306a36Sopenharmony_ci dma_async_device_unregister(&fsl_qdma->dma_dev); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return 0; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic const struct of_device_id fsl_qdma_dt_ids[] = { 128462306a36Sopenharmony_ci { .compatible = "fsl,ls1021a-qdma", }, 128562306a36Sopenharmony_ci { /* sentinel */ } 128662306a36Sopenharmony_ci}; 128762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic struct platform_driver fsl_qdma_driver = { 129062306a36Sopenharmony_ci .driver = { 129162306a36Sopenharmony_ci .name = "fsl-qdma", 129262306a36Sopenharmony_ci .of_match_table = fsl_qdma_dt_ids, 129362306a36Sopenharmony_ci }, 129462306a36Sopenharmony_ci .probe = fsl_qdma_probe, 129562306a36Sopenharmony_ci .remove = fsl_qdma_remove, 129662306a36Sopenharmony_ci}; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cimodule_platform_driver(fsl_qdma_driver); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ciMODULE_ALIAS("platform:fsl-qdma"); 130162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 130262306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP Layerscape qDMA engine driver"); 1303