162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * drivers/dma/fsl_raid.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Freescale RAID Engine device driver 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: 762306a36Sopenharmony_ci * Harninder Rai <harninder.rai@freescale.com> 862306a36Sopenharmony_ci * Naveen Burmi <naveenburmi@freescale.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Rewrite: 1162306a36Sopenharmony_ci * Xuelin Shi <xuelin.shi@freescale.com> 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Copyright (c) 2010-2014 Freescale Semiconductor, Inc. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1662306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 1762306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 1862306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1962306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 2062306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 2162306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 2262306a36Sopenharmony_ci * * Neither the name of Freescale Semiconductor nor the 2362306a36Sopenharmony_ci * names of its contributors may be used to endorse or promote products 2462306a36Sopenharmony_ci * derived from this software without specific prior written permission. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * ALTERNATIVELY, this software may be distributed under the terms of the 2762306a36Sopenharmony_ci * GNU General Public License ("GPL") as published by the Free Software 2862306a36Sopenharmony_ci * Foundation, either version 2 of that License or (at your option) any 2962306a36Sopenharmony_ci * later version. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY 3262306a36Sopenharmony_ci * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 3362306a36Sopenharmony_ci * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 3462306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY 3562306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 3662306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 3762306a36Sopenharmony_ci * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 3862306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3962306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 4062306a36Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Theory of operation: 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * General capabilities: 4562306a36Sopenharmony_ci * RAID Engine (RE) block is capable of offloading XOR, memcpy and P/Q 4662306a36Sopenharmony_ci * calculations required in RAID5 and RAID6 operations. RE driver 4762306a36Sopenharmony_ci * registers with Linux's ASYNC layer as dma driver. RE hardware 4862306a36Sopenharmony_ci * maintains strict ordering of the requests through chained 4962306a36Sopenharmony_ci * command queueing. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Data flow: 5262306a36Sopenharmony_ci * Software RAID layer of Linux (MD layer) maintains RAID partitions, 5362306a36Sopenharmony_ci * strips, stripes etc. It sends requests to the underlying ASYNC layer 5462306a36Sopenharmony_ci * which further passes it to RE driver. ASYNC layer decides which request 5562306a36Sopenharmony_ci * goes to which job ring of RE hardware. For every request processed by 5662306a36Sopenharmony_ci * RAID Engine, driver gets an interrupt unless coalescing is set. The 5762306a36Sopenharmony_ci * per job ring interrupt handler checks the status register for errors, 5862306a36Sopenharmony_ci * clears the interrupt and leave the post interrupt processing to the irq 5962306a36Sopenharmony_ci * thread. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci#include <linux/interrupt.h> 6262306a36Sopenharmony_ci#include <linux/module.h> 6362306a36Sopenharmony_ci#include <linux/of.h> 6462306a36Sopenharmony_ci#include <linux/of_irq.h> 6562306a36Sopenharmony_ci#include <linux/of_platform.h> 6662306a36Sopenharmony_ci#include <linux/platform_device.h> 6762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 6862306a36Sopenharmony_ci#include <linux/dmapool.h> 6962306a36Sopenharmony_ci#include <linux/dmaengine.h> 7062306a36Sopenharmony_ci#include <linux/io.h> 7162306a36Sopenharmony_ci#include <linux/spinlock.h> 7262306a36Sopenharmony_ci#include <linux/slab.h> 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#include "dmaengine.h" 7562306a36Sopenharmony_ci#include "fsl_raid.h" 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define FSL_RE_MAX_XOR_SRCS 16 7862306a36Sopenharmony_ci#define FSL_RE_MAX_PQ_SRCS 16 7962306a36Sopenharmony_ci#define FSL_RE_MIN_DESCS 256 8062306a36Sopenharmony_ci#define FSL_RE_MAX_DESCS (4 * FSL_RE_MIN_DESCS) 8162306a36Sopenharmony_ci#define FSL_RE_FRAME_FORMAT 0x1 8262306a36Sopenharmony_ci#define FSL_RE_MAX_DATA_LEN (1024*1024) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define to_fsl_re_dma_desc(tx) container_of(tx, struct fsl_re_desc, async_tx) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Add descriptors into per chan software queue - submit_q */ 8762306a36Sopenharmony_cistatic dma_cookie_t fsl_re_tx_submit(struct dma_async_tx_descriptor *tx) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct fsl_re_desc *desc; 9062306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 9162306a36Sopenharmony_ci dma_cookie_t cookie; 9262306a36Sopenharmony_ci unsigned long flags; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci desc = to_fsl_re_dma_desc(tx); 9562306a36Sopenharmony_ci re_chan = container_of(tx->chan, struct fsl_re_chan, chan); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci spin_lock_irqsave(&re_chan->desc_lock, flags); 9862306a36Sopenharmony_ci cookie = dma_cookie_assign(tx); 9962306a36Sopenharmony_ci list_add_tail(&desc->node, &re_chan->submit_q); 10062306a36Sopenharmony_ci spin_unlock_irqrestore(&re_chan->desc_lock, flags); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return cookie; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Copy descriptor from per chan software queue into hardware job ring */ 10662306a36Sopenharmony_cistatic void fsl_re_issue_pending(struct dma_chan *chan) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 10962306a36Sopenharmony_ci int avail; 11062306a36Sopenharmony_ci struct fsl_re_desc *desc, *_desc; 11162306a36Sopenharmony_ci unsigned long flags; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci re_chan = container_of(chan, struct fsl_re_chan, chan); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_lock_irqsave(&re_chan->desc_lock, flags); 11662306a36Sopenharmony_ci avail = FSL_RE_SLOT_AVAIL( 11762306a36Sopenharmony_ci in_be32(&re_chan->jrregs->inbring_slot_avail)); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &re_chan->submit_q, node) { 12062306a36Sopenharmony_ci if (!avail) 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci list_move_tail(&desc->node, &re_chan->active_q); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci memcpy(&re_chan->inb_ring_virt_addr[re_chan->inb_count], 12662306a36Sopenharmony_ci &desc->hwdesc, sizeof(struct fsl_re_hw_desc)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci re_chan->inb_count = (re_chan->inb_count + 1) & 12962306a36Sopenharmony_ci FSL_RE_RING_SIZE_MASK; 13062306a36Sopenharmony_ci out_be32(&re_chan->jrregs->inbring_add_job, FSL_RE_ADD_JOB(1)); 13162306a36Sopenharmony_ci avail--; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci spin_unlock_irqrestore(&re_chan->desc_lock, flags); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void fsl_re_desc_done(struct fsl_re_desc *desc) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci dma_cookie_complete(&desc->async_tx); 13962306a36Sopenharmony_ci dma_descriptor_unmap(&desc->async_tx); 14062306a36Sopenharmony_ci dmaengine_desc_get_callback_invoke(&desc->async_tx, NULL); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void fsl_re_cleanup_descs(struct fsl_re_chan *re_chan) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct fsl_re_desc *desc, *_desc; 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spin_lock_irqsave(&re_chan->desc_lock, flags); 14962306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &re_chan->ack_q, node) { 15062306a36Sopenharmony_ci if (async_tx_test_ack(&desc->async_tx)) 15162306a36Sopenharmony_ci list_move_tail(&desc->node, &re_chan->free_q); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci spin_unlock_irqrestore(&re_chan->desc_lock, flags); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci fsl_re_issue_pending(&re_chan->chan); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void fsl_re_dequeue(struct tasklet_struct *t) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct fsl_re_chan *re_chan = from_tasklet(re_chan, t, irqtask); 16162306a36Sopenharmony_ci struct fsl_re_desc *desc, *_desc; 16262306a36Sopenharmony_ci struct fsl_re_hw_desc *hwdesc; 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci unsigned int count, oub_count; 16562306a36Sopenharmony_ci int found; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci fsl_re_cleanup_descs(re_chan); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci spin_lock_irqsave(&re_chan->desc_lock, flags); 17062306a36Sopenharmony_ci count = FSL_RE_SLOT_FULL(in_be32(&re_chan->jrregs->oubring_slot_full)); 17162306a36Sopenharmony_ci while (count--) { 17262306a36Sopenharmony_ci found = 0; 17362306a36Sopenharmony_ci hwdesc = &re_chan->oub_ring_virt_addr[re_chan->oub_count]; 17462306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &re_chan->active_q, 17562306a36Sopenharmony_ci node) { 17662306a36Sopenharmony_ci /* compare the hw dma addr to find the completed */ 17762306a36Sopenharmony_ci if (desc->hwdesc.lbea32 == hwdesc->lbea32 && 17862306a36Sopenharmony_ci desc->hwdesc.addr_low == hwdesc->addr_low) { 17962306a36Sopenharmony_ci found = 1; 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (found) { 18562306a36Sopenharmony_ci fsl_re_desc_done(desc); 18662306a36Sopenharmony_ci list_move_tail(&desc->node, &re_chan->ack_q); 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci dev_err(re_chan->dev, 18962306a36Sopenharmony_ci "found hwdesc not in sw queue, discard it\n"); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci oub_count = (re_chan->oub_count + 1) & FSL_RE_RING_SIZE_MASK; 19362306a36Sopenharmony_ci re_chan->oub_count = oub_count; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci out_be32(&re_chan->jrregs->oubring_job_rmvd, 19662306a36Sopenharmony_ci FSL_RE_RMVD_JOB(1)); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci spin_unlock_irqrestore(&re_chan->desc_lock, flags); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* Per Job Ring interrupt handler */ 20262306a36Sopenharmony_cistatic irqreturn_t fsl_re_isr(int irq, void *data) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 20562306a36Sopenharmony_ci u32 irqstate, status; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci re_chan = dev_get_drvdata((struct device *)data); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci irqstate = in_be32(&re_chan->jrregs->jr_interrupt_status); 21062306a36Sopenharmony_ci if (!irqstate) 21162306a36Sopenharmony_ci return IRQ_NONE; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * There's no way in upper layer (read MD layer) to recover from 21562306a36Sopenharmony_ci * error conditions except restart everything. In long term we 21662306a36Sopenharmony_ci * need to do something more than just crashing 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci if (irqstate & FSL_RE_ERROR) { 21962306a36Sopenharmony_ci status = in_be32(&re_chan->jrregs->jr_status); 22062306a36Sopenharmony_ci dev_err(re_chan->dev, "chan error irqstate: %x, status: %x\n", 22162306a36Sopenharmony_ci irqstate, status); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Clear interrupt */ 22562306a36Sopenharmony_ci out_be32(&re_chan->jrregs->jr_interrupt_status, FSL_RE_CLR_INTR); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci tasklet_schedule(&re_chan->irqtask); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return IRQ_HANDLED; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic enum dma_status fsl_re_tx_status(struct dma_chan *chan, 23362306a36Sopenharmony_ci dma_cookie_t cookie, 23462306a36Sopenharmony_ci struct dma_tx_state *txstate) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return dma_cookie_status(chan, cookie, txstate); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void fill_cfd_frame(struct fsl_re_cmpnd_frame *cf, u8 index, 24062306a36Sopenharmony_ci size_t length, dma_addr_t addr, bool final) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci u32 efrl = length & FSL_RE_CF_LENGTH_MASK; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci efrl |= final << FSL_RE_CF_FINAL_SHIFT; 24562306a36Sopenharmony_ci cf[index].efrl32 = efrl; 24662306a36Sopenharmony_ci cf[index].addr_high = upper_32_bits(addr); 24762306a36Sopenharmony_ci cf[index].addr_low = lower_32_bits(addr); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct fsl_re_desc *fsl_re_init_desc(struct fsl_re_chan *re_chan, 25162306a36Sopenharmony_ci struct fsl_re_desc *desc, 25262306a36Sopenharmony_ci void *cf, dma_addr_t paddr) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci desc->re_chan = re_chan; 25562306a36Sopenharmony_ci desc->async_tx.tx_submit = fsl_re_tx_submit; 25662306a36Sopenharmony_ci dma_async_tx_descriptor_init(&desc->async_tx, &re_chan->chan); 25762306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->node); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci desc->hwdesc.fmt32 = FSL_RE_FRAME_FORMAT << FSL_RE_HWDESC_FMT_SHIFT; 26062306a36Sopenharmony_ci desc->hwdesc.lbea32 = upper_32_bits(paddr); 26162306a36Sopenharmony_ci desc->hwdesc.addr_low = lower_32_bits(paddr); 26262306a36Sopenharmony_ci desc->cf_addr = cf; 26362306a36Sopenharmony_ci desc->cf_paddr = paddr; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci desc->cdb_addr = (void *)(cf + FSL_RE_CF_DESC_SIZE); 26662306a36Sopenharmony_ci desc->cdb_paddr = paddr + FSL_RE_CF_DESC_SIZE; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return desc; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic struct fsl_re_desc *fsl_re_chan_alloc_desc(struct fsl_re_chan *re_chan, 27262306a36Sopenharmony_ci unsigned long flags) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct fsl_re_desc *desc = NULL; 27562306a36Sopenharmony_ci void *cf; 27662306a36Sopenharmony_ci dma_addr_t paddr; 27762306a36Sopenharmony_ci unsigned long lock_flag; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci fsl_re_cleanup_descs(re_chan); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci spin_lock_irqsave(&re_chan->desc_lock, lock_flag); 28262306a36Sopenharmony_ci if (!list_empty(&re_chan->free_q)) { 28362306a36Sopenharmony_ci /* take one desc from free_q */ 28462306a36Sopenharmony_ci desc = list_first_entry(&re_chan->free_q, 28562306a36Sopenharmony_ci struct fsl_re_desc, node); 28662306a36Sopenharmony_ci list_del(&desc->node); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci desc->async_tx.flags = flags; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci spin_unlock_irqrestore(&re_chan->desc_lock, lock_flag); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!desc) { 29362306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 29462306a36Sopenharmony_ci if (!desc) 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci cf = dma_pool_alloc(re_chan->re_dev->cf_desc_pool, GFP_NOWAIT, 29862306a36Sopenharmony_ci &paddr); 29962306a36Sopenharmony_ci if (!cf) { 30062306a36Sopenharmony_ci kfree(desc); 30162306a36Sopenharmony_ci return NULL; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci desc = fsl_re_init_desc(re_chan, desc, cf, paddr); 30562306a36Sopenharmony_ci desc->async_tx.flags = flags; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci spin_lock_irqsave(&re_chan->desc_lock, lock_flag); 30862306a36Sopenharmony_ci re_chan->alloc_count++; 30962306a36Sopenharmony_ci spin_unlock_irqrestore(&re_chan->desc_lock, lock_flag); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return desc; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *fsl_re_prep_dma_genq( 31662306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, 31762306a36Sopenharmony_ci unsigned int src_cnt, const unsigned char *scf, size_t len, 31862306a36Sopenharmony_ci unsigned long flags) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 32162306a36Sopenharmony_ci struct fsl_re_desc *desc; 32262306a36Sopenharmony_ci struct fsl_re_xor_cdb *xor; 32362306a36Sopenharmony_ci struct fsl_re_cmpnd_frame *cf; 32462306a36Sopenharmony_ci u32 cdb; 32562306a36Sopenharmony_ci unsigned int i, j; 32662306a36Sopenharmony_ci unsigned int save_src_cnt = src_cnt; 32762306a36Sopenharmony_ci int cont_q = 0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci re_chan = container_of(chan, struct fsl_re_chan, chan); 33062306a36Sopenharmony_ci if (len > FSL_RE_MAX_DATA_LEN) { 33162306a36Sopenharmony_ci dev_err(re_chan->dev, "genq tx length %zu, max length %d\n", 33262306a36Sopenharmony_ci len, FSL_RE_MAX_DATA_LEN); 33362306a36Sopenharmony_ci return NULL; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci desc = fsl_re_chan_alloc_desc(re_chan, flags); 33762306a36Sopenharmony_ci if (desc <= 0) 33862306a36Sopenharmony_ci return NULL; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (scf && (flags & DMA_PREP_CONTINUE)) { 34162306a36Sopenharmony_ci cont_q = 1; 34262306a36Sopenharmony_ci src_cnt += 1; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Filling xor CDB */ 34662306a36Sopenharmony_ci cdb = FSL_RE_XOR_OPCODE << FSL_RE_CDB_OPCODE_SHIFT; 34762306a36Sopenharmony_ci cdb |= (src_cnt - 1) << FSL_RE_CDB_NRCS_SHIFT; 34862306a36Sopenharmony_ci cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT; 34962306a36Sopenharmony_ci cdb |= FSL_RE_INTR_ON_ERROR << FSL_RE_CDB_ERROR_SHIFT; 35062306a36Sopenharmony_ci cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT; 35162306a36Sopenharmony_ci xor = desc->cdb_addr; 35262306a36Sopenharmony_ci xor->cdb32 = cdb; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (scf) { 35562306a36Sopenharmony_ci /* compute q = src0*coef0^src1*coef1^..., * is GF(8) mult */ 35662306a36Sopenharmony_ci for (i = 0; i < save_src_cnt; i++) 35762306a36Sopenharmony_ci xor->gfm[i] = scf[i]; 35862306a36Sopenharmony_ci if (cont_q) 35962306a36Sopenharmony_ci xor->gfm[i++] = 1; 36062306a36Sopenharmony_ci } else { 36162306a36Sopenharmony_ci /* compute P, that is XOR all srcs */ 36262306a36Sopenharmony_ci for (i = 0; i < src_cnt; i++) 36362306a36Sopenharmony_ci xor->gfm[i] = 1; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Filling frame 0 of compound frame descriptor with CDB */ 36762306a36Sopenharmony_ci cf = desc->cf_addr; 36862306a36Sopenharmony_ci fill_cfd_frame(cf, 0, sizeof(*xor), desc->cdb_paddr, 0); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Fill CFD's 1st frame with dest buffer */ 37162306a36Sopenharmony_ci fill_cfd_frame(cf, 1, len, dest, 0); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Fill CFD's rest of the frames with source buffers */ 37462306a36Sopenharmony_ci for (i = 2, j = 0; j < save_src_cnt; i++, j++) 37562306a36Sopenharmony_ci fill_cfd_frame(cf, i, len, src[j], 0); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (cont_q) 37862306a36Sopenharmony_ci fill_cfd_frame(cf, i++, len, dest, 0); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Setting the final bit in the last source buffer frame in CFD */ 38162306a36Sopenharmony_ci cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return &desc->async_tx; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* 38762306a36Sopenharmony_ci * Prep function for P parity calculation.In RAID Engine terminology, 38862306a36Sopenharmony_ci * XOR calculation is called GenQ calculation done through GenQ command 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *fsl_re_prep_dma_xor( 39162306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, 39262306a36Sopenharmony_ci unsigned int src_cnt, size_t len, unsigned long flags) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci /* NULL let genq take all coef as 1 */ 39562306a36Sopenharmony_ci return fsl_re_prep_dma_genq(chan, dest, src, src_cnt, NULL, len, flags); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* 39962306a36Sopenharmony_ci * Prep function for P/Q parity calculation.In RAID Engine terminology, 40062306a36Sopenharmony_ci * P/Q calculation is called GenQQ done through GenQQ command 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *fsl_re_prep_dma_pq( 40362306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t *dest, dma_addr_t *src, 40462306a36Sopenharmony_ci unsigned int src_cnt, const unsigned char *scf, size_t len, 40562306a36Sopenharmony_ci unsigned long flags) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 40862306a36Sopenharmony_ci struct fsl_re_desc *desc; 40962306a36Sopenharmony_ci struct fsl_re_pq_cdb *pq; 41062306a36Sopenharmony_ci struct fsl_re_cmpnd_frame *cf; 41162306a36Sopenharmony_ci u32 cdb; 41262306a36Sopenharmony_ci u8 *p; 41362306a36Sopenharmony_ci int gfmq_len, i, j; 41462306a36Sopenharmony_ci unsigned int save_src_cnt = src_cnt; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci re_chan = container_of(chan, struct fsl_re_chan, chan); 41762306a36Sopenharmony_ci if (len > FSL_RE_MAX_DATA_LEN) { 41862306a36Sopenharmony_ci dev_err(re_chan->dev, "pq tx length is %zu, max length is %d\n", 41962306a36Sopenharmony_ci len, FSL_RE_MAX_DATA_LEN); 42062306a36Sopenharmony_ci return NULL; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * RE requires at least 2 sources, if given only one source, we pass the 42562306a36Sopenharmony_ci * second source same as the first one. 42662306a36Sopenharmony_ci * With only one source, generating P is meaningless, only generate Q. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci if (src_cnt == 1) { 42962306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 43062306a36Sopenharmony_ci dma_addr_t dma_src[2]; 43162306a36Sopenharmony_ci unsigned char coef[2]; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci dma_src[0] = *src; 43462306a36Sopenharmony_ci coef[0] = *scf; 43562306a36Sopenharmony_ci dma_src[1] = *src; 43662306a36Sopenharmony_ci coef[1] = 0; 43762306a36Sopenharmony_ci tx = fsl_re_prep_dma_genq(chan, dest[1], dma_src, 2, coef, len, 43862306a36Sopenharmony_ci flags); 43962306a36Sopenharmony_ci if (tx) 44062306a36Sopenharmony_ci desc = to_fsl_re_dma_desc(tx); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return tx; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * During RAID6 array creation, Linux's MD layer gets P and Q 44762306a36Sopenharmony_ci * calculated separately in two steps. But our RAID Engine has 44862306a36Sopenharmony_ci * the capability to calculate both P and Q with a single command 44962306a36Sopenharmony_ci * Hence to merge well with MD layer, we need to provide a hook 45062306a36Sopenharmony_ci * here and call re_jq_prep_dma_genq() function 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (flags & DMA_PREP_PQ_DISABLE_P) 45462306a36Sopenharmony_ci return fsl_re_prep_dma_genq(chan, dest[1], src, src_cnt, 45562306a36Sopenharmony_ci scf, len, flags); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (flags & DMA_PREP_CONTINUE) 45862306a36Sopenharmony_ci src_cnt += 3; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci desc = fsl_re_chan_alloc_desc(re_chan, flags); 46162306a36Sopenharmony_ci if (desc <= 0) 46262306a36Sopenharmony_ci return NULL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Filling GenQQ CDB */ 46562306a36Sopenharmony_ci cdb = FSL_RE_PQ_OPCODE << FSL_RE_CDB_OPCODE_SHIFT; 46662306a36Sopenharmony_ci cdb |= (src_cnt - 1) << FSL_RE_CDB_NRCS_SHIFT; 46762306a36Sopenharmony_ci cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT; 46862306a36Sopenharmony_ci cdb |= FSL_RE_BUFFER_OUTPUT << FSL_RE_CDB_BUFFER_SHIFT; 46962306a36Sopenharmony_ci cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci pq = desc->cdb_addr; 47262306a36Sopenharmony_ci pq->cdb32 = cdb; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci p = pq->gfm_q1; 47562306a36Sopenharmony_ci /* Init gfm_q1[] */ 47662306a36Sopenharmony_ci for (i = 0; i < src_cnt; i++) 47762306a36Sopenharmony_ci p[i] = 1; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Align gfm[] to 32bit */ 48062306a36Sopenharmony_ci gfmq_len = ALIGN(src_cnt, 4); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Init gfm_q2[] */ 48362306a36Sopenharmony_ci p += gfmq_len; 48462306a36Sopenharmony_ci for (i = 0; i < src_cnt; i++) 48562306a36Sopenharmony_ci p[i] = scf[i]; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Filling frame 0 of compound frame descriptor with CDB */ 48862306a36Sopenharmony_ci cf = desc->cf_addr; 48962306a36Sopenharmony_ci fill_cfd_frame(cf, 0, sizeof(struct fsl_re_pq_cdb), desc->cdb_paddr, 0); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* Fill CFD's 1st & 2nd frame with dest buffers */ 49262306a36Sopenharmony_ci for (i = 1, j = 0; i < 3; i++, j++) 49362306a36Sopenharmony_ci fill_cfd_frame(cf, i, len, dest[j], 0); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* Fill CFD's rest of the frames with source buffers */ 49662306a36Sopenharmony_ci for (i = 3, j = 0; j < save_src_cnt; i++, j++) 49762306a36Sopenharmony_ci fill_cfd_frame(cf, i, len, src[j], 0); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* PQ computation continuation */ 50062306a36Sopenharmony_ci if (flags & DMA_PREP_CONTINUE) { 50162306a36Sopenharmony_ci if (src_cnt - save_src_cnt == 3) { 50262306a36Sopenharmony_ci p[save_src_cnt] = 0; 50362306a36Sopenharmony_ci p[save_src_cnt + 1] = 0; 50462306a36Sopenharmony_ci p[save_src_cnt + 2] = 1; 50562306a36Sopenharmony_ci fill_cfd_frame(cf, i++, len, dest[0], 0); 50662306a36Sopenharmony_ci fill_cfd_frame(cf, i++, len, dest[1], 0); 50762306a36Sopenharmony_ci fill_cfd_frame(cf, i++, len, dest[1], 0); 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci dev_err(re_chan->dev, "PQ tx continuation error!\n"); 51062306a36Sopenharmony_ci return NULL; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Setting the final bit in the last source buffer frame in CFD */ 51562306a36Sopenharmony_ci cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return &desc->async_tx; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* 52162306a36Sopenharmony_ci * Prep function for memcpy. In RAID Engine, memcpy is done through MOVE 52262306a36Sopenharmony_ci * command. Logic of this function will need to be modified once multipage 52362306a36Sopenharmony_ci * support is added in Linux's MD/ASYNC Layer 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *fsl_re_prep_dma_memcpy( 52662306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 52762306a36Sopenharmony_ci size_t len, unsigned long flags) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 53062306a36Sopenharmony_ci struct fsl_re_desc *desc; 53162306a36Sopenharmony_ci size_t length; 53262306a36Sopenharmony_ci struct fsl_re_cmpnd_frame *cf; 53362306a36Sopenharmony_ci struct fsl_re_move_cdb *move; 53462306a36Sopenharmony_ci u32 cdb; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci re_chan = container_of(chan, struct fsl_re_chan, chan); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (len > FSL_RE_MAX_DATA_LEN) { 53962306a36Sopenharmony_ci dev_err(re_chan->dev, "cp tx length is %zu, max length is %d\n", 54062306a36Sopenharmony_ci len, FSL_RE_MAX_DATA_LEN); 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci desc = fsl_re_chan_alloc_desc(re_chan, flags); 54562306a36Sopenharmony_ci if (desc <= 0) 54662306a36Sopenharmony_ci return NULL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Filling move CDB */ 54962306a36Sopenharmony_ci cdb = FSL_RE_MOVE_OPCODE << FSL_RE_CDB_OPCODE_SHIFT; 55062306a36Sopenharmony_ci cdb |= FSL_RE_BLOCK_SIZE << FSL_RE_CDB_BLKSIZE_SHIFT; 55162306a36Sopenharmony_ci cdb |= FSL_RE_INTR_ON_ERROR << FSL_RE_CDB_ERROR_SHIFT; 55262306a36Sopenharmony_ci cdb |= FSL_RE_DATA_DEP << FSL_RE_CDB_DEPEND_SHIFT; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci move = desc->cdb_addr; 55562306a36Sopenharmony_ci move->cdb32 = cdb; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Filling frame 0 of CFD with move CDB */ 55862306a36Sopenharmony_ci cf = desc->cf_addr; 55962306a36Sopenharmony_ci fill_cfd_frame(cf, 0, sizeof(*move), desc->cdb_paddr, 0); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci length = min_t(size_t, len, FSL_RE_MAX_DATA_LEN); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Fill CFD's 1st frame with dest buffer */ 56462306a36Sopenharmony_ci fill_cfd_frame(cf, 1, length, dest, 0); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Fill CFD's 2nd frame with src buffer */ 56762306a36Sopenharmony_ci fill_cfd_frame(cf, 2, length, src, 1); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return &desc->async_tx; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int fsl_re_alloc_chan_resources(struct dma_chan *chan) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 57562306a36Sopenharmony_ci struct fsl_re_desc *desc; 57662306a36Sopenharmony_ci void *cf; 57762306a36Sopenharmony_ci dma_addr_t paddr; 57862306a36Sopenharmony_ci int i; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci re_chan = container_of(chan, struct fsl_re_chan, chan); 58162306a36Sopenharmony_ci for (i = 0; i < FSL_RE_MIN_DESCS; i++) { 58262306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 58362306a36Sopenharmony_ci if (!desc) 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci cf = dma_pool_alloc(re_chan->re_dev->cf_desc_pool, GFP_KERNEL, 58762306a36Sopenharmony_ci &paddr); 58862306a36Sopenharmony_ci if (!cf) { 58962306a36Sopenharmony_ci kfree(desc); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->node); 59462306a36Sopenharmony_ci fsl_re_init_desc(re_chan, desc, cf, paddr); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci list_add_tail(&desc->node, &re_chan->free_q); 59762306a36Sopenharmony_ci re_chan->alloc_count++; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci return re_chan->alloc_count; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void fsl_re_free_chan_resources(struct dma_chan *chan) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct fsl_re_chan *re_chan; 60562306a36Sopenharmony_ci struct fsl_re_desc *desc; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci re_chan = container_of(chan, struct fsl_re_chan, chan); 60862306a36Sopenharmony_ci while (re_chan->alloc_count--) { 60962306a36Sopenharmony_ci desc = list_first_entry(&re_chan->free_q, 61062306a36Sopenharmony_ci struct fsl_re_desc, 61162306a36Sopenharmony_ci node); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci list_del(&desc->node); 61462306a36Sopenharmony_ci dma_pool_free(re_chan->re_dev->cf_desc_pool, desc->cf_addr, 61562306a36Sopenharmony_ci desc->cf_paddr); 61662306a36Sopenharmony_ci kfree(desc); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!list_empty(&re_chan->free_q)) 62062306a36Sopenharmony_ci dev_err(re_chan->dev, "chan resource cannot be cleaned!\n"); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int fsl_re_chan_probe(struct platform_device *ofdev, 62462306a36Sopenharmony_ci struct device_node *np, u8 q, u32 off) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct device *dev, *chandev; 62762306a36Sopenharmony_ci struct fsl_re_drv_private *re_priv; 62862306a36Sopenharmony_ci struct fsl_re_chan *chan; 62962306a36Sopenharmony_ci struct dma_device *dma_dev; 63062306a36Sopenharmony_ci u32 ptr; 63162306a36Sopenharmony_ci u32 status; 63262306a36Sopenharmony_ci int ret = 0, rc; 63362306a36Sopenharmony_ci struct platform_device *chan_ofdev; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci dev = &ofdev->dev; 63662306a36Sopenharmony_ci re_priv = dev_get_drvdata(dev); 63762306a36Sopenharmony_ci dma_dev = &re_priv->dma_dev; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL); 64062306a36Sopenharmony_ci if (!chan) 64162306a36Sopenharmony_ci return -ENOMEM; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* create platform device for chan node */ 64462306a36Sopenharmony_ci chan_ofdev = of_platform_device_create(np, NULL, dev); 64562306a36Sopenharmony_ci if (!chan_ofdev) { 64662306a36Sopenharmony_ci dev_err(dev, "Not able to create ofdev for jr %d\n", q); 64762306a36Sopenharmony_ci ret = -EINVAL; 64862306a36Sopenharmony_ci goto err_free; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* read reg property from dts */ 65262306a36Sopenharmony_ci rc = of_property_read_u32(np, "reg", &ptr); 65362306a36Sopenharmony_ci if (rc) { 65462306a36Sopenharmony_ci dev_err(dev, "Reg property not found in jr %d\n", q); 65562306a36Sopenharmony_ci ret = -ENODEV; 65662306a36Sopenharmony_ci goto err_free; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci chan->jrregs = (struct fsl_re_chan_cfg *)((u8 *)re_priv->re_regs + 66062306a36Sopenharmony_ci off + ptr); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* read irq property from dts */ 66362306a36Sopenharmony_ci chan->irq = irq_of_parse_and_map(np, 0); 66462306a36Sopenharmony_ci if (!chan->irq) { 66562306a36Sopenharmony_ci dev_err(dev, "No IRQ defined for JR %d\n", q); 66662306a36Sopenharmony_ci ret = -ENODEV; 66762306a36Sopenharmony_ci goto err_free; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci snprintf(chan->name, sizeof(chan->name), "re_jr%02d", q); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci chandev = &chan_ofdev->dev; 67362306a36Sopenharmony_ci tasklet_setup(&chan->irqtask, fsl_re_dequeue); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ret = request_irq(chan->irq, fsl_re_isr, 0, chan->name, chandev); 67662306a36Sopenharmony_ci if (ret) { 67762306a36Sopenharmony_ci dev_err(dev, "Unable to register interrupt for JR %d\n", q); 67862306a36Sopenharmony_ci ret = -EINVAL; 67962306a36Sopenharmony_ci goto err_free; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci re_priv->re_jrs[q] = chan; 68362306a36Sopenharmony_ci chan->chan.device = dma_dev; 68462306a36Sopenharmony_ci chan->chan.private = chan; 68562306a36Sopenharmony_ci chan->dev = chandev; 68662306a36Sopenharmony_ci chan->re_dev = re_priv; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci spin_lock_init(&chan->desc_lock); 68962306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->ack_q); 69062306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->active_q); 69162306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->submit_q); 69262306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->free_q); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci chan->inb_ring_virt_addr = dma_pool_alloc(chan->re_dev->hw_desc_pool, 69562306a36Sopenharmony_ci GFP_KERNEL, &chan->inb_phys_addr); 69662306a36Sopenharmony_ci if (!chan->inb_ring_virt_addr) { 69762306a36Sopenharmony_ci dev_err(dev, "No dma memory for inb_ring_virt_addr\n"); 69862306a36Sopenharmony_ci ret = -ENOMEM; 69962306a36Sopenharmony_ci goto err_free; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci chan->oub_ring_virt_addr = dma_pool_alloc(chan->re_dev->hw_desc_pool, 70362306a36Sopenharmony_ci GFP_KERNEL, &chan->oub_phys_addr); 70462306a36Sopenharmony_ci if (!chan->oub_ring_virt_addr) { 70562306a36Sopenharmony_ci dev_err(dev, "No dma memory for oub_ring_virt_addr\n"); 70662306a36Sopenharmony_ci ret = -ENOMEM; 70762306a36Sopenharmony_ci goto err_free_1; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Program the Inbound/Outbound ring base addresses and size */ 71162306a36Sopenharmony_ci out_be32(&chan->jrregs->inbring_base_h, 71262306a36Sopenharmony_ci chan->inb_phys_addr & FSL_RE_ADDR_BIT_MASK); 71362306a36Sopenharmony_ci out_be32(&chan->jrregs->oubring_base_h, 71462306a36Sopenharmony_ci chan->oub_phys_addr & FSL_RE_ADDR_BIT_MASK); 71562306a36Sopenharmony_ci out_be32(&chan->jrregs->inbring_base_l, 71662306a36Sopenharmony_ci chan->inb_phys_addr >> FSL_RE_ADDR_BIT_SHIFT); 71762306a36Sopenharmony_ci out_be32(&chan->jrregs->oubring_base_l, 71862306a36Sopenharmony_ci chan->oub_phys_addr >> FSL_RE_ADDR_BIT_SHIFT); 71962306a36Sopenharmony_ci out_be32(&chan->jrregs->inbring_size, 72062306a36Sopenharmony_ci FSL_RE_RING_SIZE << FSL_RE_RING_SIZE_SHIFT); 72162306a36Sopenharmony_ci out_be32(&chan->jrregs->oubring_size, 72262306a36Sopenharmony_ci FSL_RE_RING_SIZE << FSL_RE_RING_SIZE_SHIFT); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Read LIODN value from u-boot */ 72562306a36Sopenharmony_ci status = in_be32(&chan->jrregs->jr_config_1) & FSL_RE_REG_LIODN_MASK; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* Program the CFG reg */ 72862306a36Sopenharmony_ci out_be32(&chan->jrregs->jr_config_1, 72962306a36Sopenharmony_ci FSL_RE_CFG1_CBSI | FSL_RE_CFG1_CBS0 | status); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci dev_set_drvdata(chandev, chan); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Enable RE/CHAN */ 73462306a36Sopenharmony_ci out_be32(&chan->jrregs->jr_command, FSL_RE_ENABLE); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cierr_free_1: 73962306a36Sopenharmony_ci dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr, 74062306a36Sopenharmony_ci chan->inb_phys_addr); 74162306a36Sopenharmony_cierr_free: 74262306a36Sopenharmony_ci return ret; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/* Probe function for RAID Engine */ 74662306a36Sopenharmony_cistatic int fsl_re_probe(struct platform_device *ofdev) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct fsl_re_drv_private *re_priv; 74962306a36Sopenharmony_ci struct device_node *np; 75062306a36Sopenharmony_ci struct device_node *child; 75162306a36Sopenharmony_ci u32 off; 75262306a36Sopenharmony_ci u8 ridx = 0; 75362306a36Sopenharmony_ci struct dma_device *dma_dev; 75462306a36Sopenharmony_ci struct resource *res; 75562306a36Sopenharmony_ci int rc; 75662306a36Sopenharmony_ci struct device *dev = &ofdev->dev; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci re_priv = devm_kzalloc(dev, sizeof(*re_priv), GFP_KERNEL); 75962306a36Sopenharmony_ci if (!re_priv) 76062306a36Sopenharmony_ci return -ENOMEM; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); 76362306a36Sopenharmony_ci if (!res) 76462306a36Sopenharmony_ci return -ENODEV; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* IOMAP the entire RAID Engine region */ 76762306a36Sopenharmony_ci re_priv->re_regs = devm_ioremap(dev, res->start, resource_size(res)); 76862306a36Sopenharmony_ci if (!re_priv->re_regs) 76962306a36Sopenharmony_ci return -EBUSY; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Program the RE mode */ 77262306a36Sopenharmony_ci out_be32(&re_priv->re_regs->global_config, FSL_RE_NON_DPAA_MODE); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Program Galois Field polynomial */ 77562306a36Sopenharmony_ci out_be32(&re_priv->re_regs->galois_field_config, FSL_RE_GFM_POLY); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci dev_info(dev, "version %x, mode %x, gfp %x\n", 77862306a36Sopenharmony_ci in_be32(&re_priv->re_regs->re_version_id), 77962306a36Sopenharmony_ci in_be32(&re_priv->re_regs->global_config), 78062306a36Sopenharmony_ci in_be32(&re_priv->re_regs->galois_field_config)); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci dma_dev = &re_priv->dma_dev; 78362306a36Sopenharmony_ci dma_dev->dev = dev; 78462306a36Sopenharmony_ci INIT_LIST_HEAD(&dma_dev->channels); 78562306a36Sopenharmony_ci dma_set_mask(dev, DMA_BIT_MASK(40)); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci dma_dev->device_alloc_chan_resources = fsl_re_alloc_chan_resources; 78862306a36Sopenharmony_ci dma_dev->device_tx_status = fsl_re_tx_status; 78962306a36Sopenharmony_ci dma_dev->device_issue_pending = fsl_re_issue_pending; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci dma_dev->max_xor = FSL_RE_MAX_XOR_SRCS; 79262306a36Sopenharmony_ci dma_dev->device_prep_dma_xor = fsl_re_prep_dma_xor; 79362306a36Sopenharmony_ci dma_cap_set(DMA_XOR, dma_dev->cap_mask); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci dma_dev->max_pq = FSL_RE_MAX_PQ_SRCS; 79662306a36Sopenharmony_ci dma_dev->device_prep_dma_pq = fsl_re_prep_dma_pq; 79762306a36Sopenharmony_ci dma_cap_set(DMA_PQ, dma_dev->cap_mask); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci dma_dev->device_prep_dma_memcpy = fsl_re_prep_dma_memcpy; 80062306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci dma_dev->device_free_chan_resources = fsl_re_free_chan_resources; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci re_priv->total_chans = 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci re_priv->cf_desc_pool = dmam_pool_create("fsl_re_cf_desc_pool", dev, 80762306a36Sopenharmony_ci FSL_RE_CF_CDB_SIZE, 80862306a36Sopenharmony_ci FSL_RE_CF_CDB_ALIGN, 0); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (!re_priv->cf_desc_pool) { 81162306a36Sopenharmony_ci dev_err(dev, "No memory for fsl re_cf desc pool\n"); 81262306a36Sopenharmony_ci return -ENOMEM; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci re_priv->hw_desc_pool = dmam_pool_create("fsl_re_hw_desc_pool", dev, 81662306a36Sopenharmony_ci sizeof(struct fsl_re_hw_desc) * FSL_RE_RING_SIZE, 81762306a36Sopenharmony_ci FSL_RE_FRAME_ALIGN, 0); 81862306a36Sopenharmony_ci if (!re_priv->hw_desc_pool) { 81962306a36Sopenharmony_ci dev_err(dev, "No memory for fsl re_hw desc pool\n"); 82062306a36Sopenharmony_ci return -ENOMEM; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci dev_set_drvdata(dev, re_priv); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Parse Device tree to find out the total number of JQs present */ 82662306a36Sopenharmony_ci for_each_compatible_node(np, NULL, "fsl,raideng-v1.0-job-queue") { 82762306a36Sopenharmony_ci rc = of_property_read_u32(np, "reg", &off); 82862306a36Sopenharmony_ci if (rc) { 82962306a36Sopenharmony_ci dev_err(dev, "Reg property not found in JQ node\n"); 83062306a36Sopenharmony_ci of_node_put(np); 83162306a36Sopenharmony_ci return -ENODEV; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci /* Find out the Job Rings present under each JQ */ 83462306a36Sopenharmony_ci for_each_child_of_node(np, child) { 83562306a36Sopenharmony_ci rc = of_device_is_compatible(child, 83662306a36Sopenharmony_ci "fsl,raideng-v1.0-job-ring"); 83762306a36Sopenharmony_ci if (rc) { 83862306a36Sopenharmony_ci fsl_re_chan_probe(ofdev, child, ridx++, off); 83962306a36Sopenharmony_ci re_priv->total_chans++; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci dma_async_device_register(dma_dev); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void fsl_re_remove_chan(struct fsl_re_chan *chan) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci tasklet_kill(&chan->irqtask); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr, 85462306a36Sopenharmony_ci chan->inb_phys_addr); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci dma_pool_free(chan->re_dev->hw_desc_pool, chan->oub_ring_virt_addr, 85762306a36Sopenharmony_ci chan->oub_phys_addr); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int fsl_re_remove(struct platform_device *ofdev) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct fsl_re_drv_private *re_priv; 86362306a36Sopenharmony_ci struct device *dev; 86462306a36Sopenharmony_ci int i; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci dev = &ofdev->dev; 86762306a36Sopenharmony_ci re_priv = dev_get_drvdata(dev); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* Cleanup chan related memory areas */ 87062306a36Sopenharmony_ci for (i = 0; i < re_priv->total_chans; i++) 87162306a36Sopenharmony_ci fsl_re_remove_chan(re_priv->re_jrs[i]); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Unregister the driver */ 87462306a36Sopenharmony_ci dma_async_device_unregister(&re_priv->dma_dev); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic const struct of_device_id fsl_re_ids[] = { 88062306a36Sopenharmony_ci { .compatible = "fsl,raideng-v1.0", }, 88162306a36Sopenharmony_ci {} 88262306a36Sopenharmony_ci}; 88362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_re_ids); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic struct platform_driver fsl_re_driver = { 88662306a36Sopenharmony_ci .driver = { 88762306a36Sopenharmony_ci .name = "fsl-raideng", 88862306a36Sopenharmony_ci .of_match_table = fsl_re_ids, 88962306a36Sopenharmony_ci }, 89062306a36Sopenharmony_ci .probe = fsl_re_probe, 89162306a36Sopenharmony_ci .remove = fsl_re_remove, 89262306a36Sopenharmony_ci}; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cimodule_platform_driver(fsl_re_driver); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ciMODULE_AUTHOR("Harninder Rai <harninder.rai@freescale.com>"); 89762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 89862306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale RAID Engine Device Driver"); 899