162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DMA driver for STMicroelectronics STi FDMA controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Ludovic Barre <Ludovic.barre@st.com> 862306a36Sopenharmony_ci * Peter Griffin <peter.griffin@linaro.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of_device.h> 1462306a36Sopenharmony_ci#include <linux/of_dma.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/remoteproc.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "st_fdma.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic inline struct st_fdma_chan *to_st_fdma_chan(struct dma_chan *c) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci return container_of(c, struct st_fdma_chan, vchan.chan); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct st_fdma_desc *to_st_fdma_desc(struct virt_dma_desc *vd) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return container_of(vd, struct st_fdma_desc, vdesc); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int st_fdma_dreq_get(struct st_fdma_chan *fchan) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct st_fdma_dev *fdev = fchan->fdev; 3562306a36Sopenharmony_ci u32 req_line_cfg = fchan->cfg.req_line; 3662306a36Sopenharmony_ci u32 dreq_line; 3762306a36Sopenharmony_ci int try = 0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * dreq_mask is shared for n channels of fdma, so all accesses must be 4162306a36Sopenharmony_ci * atomic. if the dreq_mask is changed between ffz and set_bit, 4262306a36Sopenharmony_ci * we retry 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci do { 4562306a36Sopenharmony_ci if (fdev->dreq_mask == ~0L) { 4662306a36Sopenharmony_ci dev_err(fdev->dev, "No req lines available\n"); 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (try || req_line_cfg >= ST_FDMA_NR_DREQS) { 5162306a36Sopenharmony_ci dev_err(fdev->dev, "Invalid or used req line\n"); 5262306a36Sopenharmony_ci return -EINVAL; 5362306a36Sopenharmony_ci } else { 5462306a36Sopenharmony_ci dreq_line = req_line_cfg; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci try++; 5862306a36Sopenharmony_ci } while (test_and_set_bit(dreq_line, &fdev->dreq_mask)); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci dev_dbg(fdev->dev, "get dreq_line:%d mask:%#lx\n", 6162306a36Sopenharmony_ci dreq_line, fdev->dreq_mask); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return dreq_line; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void st_fdma_dreq_put(struct st_fdma_chan *fchan) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct st_fdma_dev *fdev = fchan->fdev; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci dev_dbg(fdev->dev, "put dreq_line:%#x\n", fchan->dreq_line); 7162306a36Sopenharmony_ci clear_bit(fchan->dreq_line, &fdev->dreq_mask); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void st_fdma_xfer_desc(struct st_fdma_chan *fchan) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 7762306a36Sopenharmony_ci unsigned long nbytes, ch_cmd, cmd; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci vdesc = vchan_next_desc(&fchan->vchan); 8062306a36Sopenharmony_ci if (!vdesc) 8162306a36Sopenharmony_ci return; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci fchan->fdesc = to_st_fdma_desc(vdesc); 8462306a36Sopenharmony_ci nbytes = fchan->fdesc->node[0].desc->nbytes; 8562306a36Sopenharmony_ci cmd = FDMA_CMD_START(fchan->vchan.chan.chan_id); 8662306a36Sopenharmony_ci ch_cmd = fchan->fdesc->node[0].pdesc | FDMA_CH_CMD_STA_START; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* start the channel for the descriptor */ 8962306a36Sopenharmony_ci fnode_write(fchan, nbytes, FDMA_CNTN_OFST); 9062306a36Sopenharmony_ci fchan_write(fchan, ch_cmd, FDMA_CH_CMD_OFST); 9162306a36Sopenharmony_ci writel(cmd, 9262306a36Sopenharmony_ci fchan->fdev->slim_rproc->peri + FDMA_CMD_SET_OFST); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci dev_dbg(fchan->fdev->dev, "start chan:%d\n", fchan->vchan.chan.chan_id); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void st_fdma_ch_sta_update(struct st_fdma_chan *fchan, 9862306a36Sopenharmony_ci unsigned long int_sta) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned long ch_sta, ch_err; 10162306a36Sopenharmony_ci int ch_id = fchan->vchan.chan.chan_id; 10262306a36Sopenharmony_ci struct st_fdma_dev *fdev = fchan->fdev; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ch_sta = fchan_read(fchan, FDMA_CH_CMD_OFST); 10562306a36Sopenharmony_ci ch_err = ch_sta & FDMA_CH_CMD_ERR_MASK; 10662306a36Sopenharmony_ci ch_sta &= FDMA_CH_CMD_STA_MASK; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (int_sta & FDMA_INT_STA_ERR) { 10962306a36Sopenharmony_ci dev_warn(fdev->dev, "chan:%d, error:%ld\n", ch_id, ch_err); 11062306a36Sopenharmony_ci fchan->status = DMA_ERROR; 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci switch (ch_sta) { 11562306a36Sopenharmony_ci case FDMA_CH_CMD_STA_PAUSED: 11662306a36Sopenharmony_ci fchan->status = DMA_PAUSED; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci case FDMA_CH_CMD_STA_RUNNING: 12062306a36Sopenharmony_ci fchan->status = DMA_IN_PROGRESS; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic irqreturn_t st_fdma_irq_handler(int irq, void *dev_id) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct st_fdma_dev *fdev = dev_id; 12862306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 12962306a36Sopenharmony_ci struct st_fdma_chan *fchan = &fdev->chans[0]; 13062306a36Sopenharmony_ci unsigned long int_sta, clr; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci int_sta = fdma_read(fdev, FDMA_INT_STA_OFST); 13362306a36Sopenharmony_ci clr = int_sta; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (; int_sta != 0 ; int_sta >>= 2, fchan++) { 13662306a36Sopenharmony_ci if (!(int_sta & (FDMA_INT_STA_CH | FDMA_INT_STA_ERR))) 13762306a36Sopenharmony_ci continue; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci spin_lock(&fchan->vchan.lock); 14062306a36Sopenharmony_ci st_fdma_ch_sta_update(fchan, int_sta); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (fchan->fdesc) { 14362306a36Sopenharmony_ci if (!fchan->fdesc->iscyclic) { 14462306a36Sopenharmony_ci list_del(&fchan->fdesc->vdesc.node); 14562306a36Sopenharmony_ci vchan_cookie_complete(&fchan->fdesc->vdesc); 14662306a36Sopenharmony_ci fchan->fdesc = NULL; 14762306a36Sopenharmony_ci fchan->status = DMA_COMPLETE; 14862306a36Sopenharmony_ci } else { 14962306a36Sopenharmony_ci vchan_cyclic_callback(&fchan->fdesc->vdesc); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Start the next descriptor (if available) */ 15362306a36Sopenharmony_ci if (!fchan->fdesc) 15462306a36Sopenharmony_ci st_fdma_xfer_desc(fchan); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_unlock(&fchan->vchan.lock); 15862306a36Sopenharmony_ci ret = IRQ_HANDLED; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci fdma_write(fdev, clr, FDMA_INT_CLR_OFST); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct dma_chan *st_fdma_of_xlate(struct of_phandle_args *dma_spec, 16762306a36Sopenharmony_ci struct of_dma *ofdma) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct st_fdma_dev *fdev = ofdma->of_dma_data; 17062306a36Sopenharmony_ci struct dma_chan *chan; 17162306a36Sopenharmony_ci struct st_fdma_chan *fchan; 17262306a36Sopenharmony_ci int ret; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (dma_spec->args_count < 1) 17562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (fdev->dma_device.dev->of_node != dma_spec->np) 17862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci ret = rproc_boot(fdev->slim_rproc->rproc); 18162306a36Sopenharmony_ci if (ret == -ENOENT) 18262306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 18362306a36Sopenharmony_ci else if (ret) 18462306a36Sopenharmony_ci return ERR_PTR(ret); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci chan = dma_get_any_slave_channel(&fdev->dma_device); 18762306a36Sopenharmony_ci if (!chan) 18862306a36Sopenharmony_ci goto err_chan; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci fchan = to_st_fdma_chan(chan); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci fchan->cfg.of_node = dma_spec->np; 19362306a36Sopenharmony_ci fchan->cfg.req_line = dma_spec->args[0]; 19462306a36Sopenharmony_ci fchan->cfg.req_ctrl = 0; 19562306a36Sopenharmony_ci fchan->cfg.type = ST_FDMA_TYPE_FREE_RUN; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (dma_spec->args_count > 1) 19862306a36Sopenharmony_ci fchan->cfg.req_ctrl = dma_spec->args[1] 19962306a36Sopenharmony_ci & FDMA_REQ_CTRL_CFG_MASK; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (dma_spec->args_count > 2) 20262306a36Sopenharmony_ci fchan->cfg.type = dma_spec->args[2]; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (fchan->cfg.type == ST_FDMA_TYPE_FREE_RUN) { 20562306a36Sopenharmony_ci fchan->dreq_line = 0; 20662306a36Sopenharmony_ci } else { 20762306a36Sopenharmony_ci fchan->dreq_line = st_fdma_dreq_get(fchan); 20862306a36Sopenharmony_ci if (IS_ERR_VALUE(fchan->dreq_line)) { 20962306a36Sopenharmony_ci chan = ERR_PTR(fchan->dreq_line); 21062306a36Sopenharmony_ci goto err_chan; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dev_dbg(fdev->dev, "xlate req_line:%d type:%d req_ctrl:%#lx\n", 21562306a36Sopenharmony_ci fchan->cfg.req_line, fchan->cfg.type, fchan->cfg.req_ctrl); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return chan; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cierr_chan: 22062306a36Sopenharmony_ci rproc_shutdown(fdev->slim_rproc->rproc); 22162306a36Sopenharmony_ci return chan; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void st_fdma_free_desc(struct virt_dma_desc *vdesc) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct st_fdma_desc *fdesc; 22862306a36Sopenharmony_ci int i; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci fdesc = to_st_fdma_desc(vdesc); 23162306a36Sopenharmony_ci for (i = 0; i < fdesc->n_nodes; i++) 23262306a36Sopenharmony_ci dma_pool_free(fdesc->fchan->node_pool, fdesc->node[i].desc, 23362306a36Sopenharmony_ci fdesc->node[i].pdesc); 23462306a36Sopenharmony_ci kfree(fdesc); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct st_fdma_desc *st_fdma_alloc_desc(struct st_fdma_chan *fchan, 23862306a36Sopenharmony_ci int sg_len) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct st_fdma_desc *fdesc; 24162306a36Sopenharmony_ci int i; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci fdesc = kzalloc(struct_size(fdesc, node, sg_len), GFP_NOWAIT); 24462306a36Sopenharmony_ci if (!fdesc) 24562306a36Sopenharmony_ci return NULL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci fdesc->fchan = fchan; 24862306a36Sopenharmony_ci fdesc->n_nodes = sg_len; 24962306a36Sopenharmony_ci for (i = 0; i < sg_len; i++) { 25062306a36Sopenharmony_ci fdesc->node[i].desc = dma_pool_alloc(fchan->node_pool, 25162306a36Sopenharmony_ci GFP_NOWAIT, &fdesc->node[i].pdesc); 25262306a36Sopenharmony_ci if (!fdesc->node[i].desc) 25362306a36Sopenharmony_ci goto err; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci return fdesc; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cierr: 25862306a36Sopenharmony_ci while (--i >= 0) 25962306a36Sopenharmony_ci dma_pool_free(fchan->node_pool, fdesc->node[i].desc, 26062306a36Sopenharmony_ci fdesc->node[i].pdesc); 26162306a36Sopenharmony_ci kfree(fdesc); 26262306a36Sopenharmony_ci return NULL; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int st_fdma_alloc_chan_res(struct dma_chan *chan) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Create the dma pool for descriptor allocation */ 27062306a36Sopenharmony_ci fchan->node_pool = dma_pool_create(dev_name(&chan->dev->device), 27162306a36Sopenharmony_ci fchan->fdev->dev, 27262306a36Sopenharmony_ci sizeof(struct st_fdma_hw_node), 27362306a36Sopenharmony_ci __alignof__(struct st_fdma_hw_node), 27462306a36Sopenharmony_ci 0); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!fchan->node_pool) { 27762306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "unable to allocate desc pool\n"); 27862306a36Sopenharmony_ci return -ENOMEM; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci dev_dbg(fchan->fdev->dev, "alloc ch_id:%d type:%d\n", 28262306a36Sopenharmony_ci fchan->vchan.chan.chan_id, fchan->cfg.type); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void st_fdma_free_chan_res(struct dma_chan *chan) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 29062306a36Sopenharmony_ci struct rproc *rproc = fchan->fdev->slim_rproc->rproc; 29162306a36Sopenharmony_ci unsigned long flags; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci dev_dbg(fchan->fdev->dev, "%s: freeing chan:%d\n", 29462306a36Sopenharmony_ci __func__, fchan->vchan.chan.chan_id); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (fchan->cfg.type != ST_FDMA_TYPE_FREE_RUN) 29762306a36Sopenharmony_ci st_fdma_dreq_put(fchan); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci spin_lock_irqsave(&fchan->vchan.lock, flags); 30062306a36Sopenharmony_ci fchan->fdesc = NULL; 30162306a36Sopenharmony_ci spin_unlock_irqrestore(&fchan->vchan.lock, flags); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci dma_pool_destroy(fchan->node_pool); 30462306a36Sopenharmony_ci fchan->node_pool = NULL; 30562306a36Sopenharmony_ci memset(&fchan->cfg, 0, sizeof(struct st_fdma_cfg)); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci rproc_shutdown(rproc); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *st_fdma_prep_dma_memcpy( 31162306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, 31262306a36Sopenharmony_ci size_t len, unsigned long flags) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct st_fdma_chan *fchan; 31562306a36Sopenharmony_ci struct st_fdma_desc *fdesc; 31662306a36Sopenharmony_ci struct st_fdma_hw_node *hw_node; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!len) 31962306a36Sopenharmony_ci return NULL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci fchan = to_st_fdma_chan(chan); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* We only require a single descriptor */ 32462306a36Sopenharmony_ci fdesc = st_fdma_alloc_desc(fchan, 1); 32562306a36Sopenharmony_ci if (!fdesc) { 32662306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "no memory for desc\n"); 32762306a36Sopenharmony_ci return NULL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci hw_node = fdesc->node[0].desc; 33162306a36Sopenharmony_ci hw_node->next = 0; 33262306a36Sopenharmony_ci hw_node->control = FDMA_NODE_CTRL_REQ_MAP_FREE_RUN; 33362306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_SRC_INCR; 33462306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_DST_INCR; 33562306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_INT_EON; 33662306a36Sopenharmony_ci hw_node->nbytes = len; 33762306a36Sopenharmony_ci hw_node->saddr = src; 33862306a36Sopenharmony_ci hw_node->daddr = dst; 33962306a36Sopenharmony_ci hw_node->generic.length = len; 34062306a36Sopenharmony_ci hw_node->generic.sstride = 0; 34162306a36Sopenharmony_ci hw_node->generic.dstride = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int config_reqctrl(struct st_fdma_chan *fchan, 34762306a36Sopenharmony_ci enum dma_transfer_direction direction) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci u32 maxburst = 0, addr = 0; 35062306a36Sopenharmony_ci enum dma_slave_buswidth width; 35162306a36Sopenharmony_ci int ch_id = fchan->vchan.chan.chan_id; 35262306a36Sopenharmony_ci struct st_fdma_dev *fdev = fchan->fdev; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci switch (direction) { 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 35762306a36Sopenharmony_ci fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_WNR; 35862306a36Sopenharmony_ci maxburst = fchan->scfg.src_maxburst; 35962306a36Sopenharmony_ci width = fchan->scfg.src_addr_width; 36062306a36Sopenharmony_ci addr = fchan->scfg.src_addr; 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 36462306a36Sopenharmony_ci fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_WNR; 36562306a36Sopenharmony_ci maxburst = fchan->scfg.dst_maxburst; 36662306a36Sopenharmony_ci width = fchan->scfg.dst_addr_width; 36762306a36Sopenharmony_ci addr = fchan->scfg.dst_addr; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci default: 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_OPCODE_MASK; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci switch (width) { 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 37962306a36Sopenharmony_ci fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST1; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 38362306a36Sopenharmony_ci fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST2; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 38762306a36Sopenharmony_ci fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST4; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_8_BYTES: 39162306a36Sopenharmony_ci fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST8; 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci default: 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_NUM_OPS_MASK; 39962306a36Sopenharmony_ci fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_NUM_OPS(maxburst-1); 40062306a36Sopenharmony_ci dreq_write(fchan, fchan->cfg.req_ctrl, FDMA_REQ_CTRL_OFST); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci fchan->cfg.dev_addr = addr; 40362306a36Sopenharmony_ci fchan->cfg.dir = direction; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci dev_dbg(fdev->dev, "chan:%d config_reqctrl:%#x req_ctrl:%#lx\n", 40662306a36Sopenharmony_ci ch_id, addr, fchan->cfg.req_ctrl); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void fill_hw_node(struct st_fdma_hw_node *hw_node, 41262306a36Sopenharmony_ci struct st_fdma_chan *fchan, 41362306a36Sopenharmony_ci enum dma_transfer_direction direction) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 41662306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_SRC_INCR; 41762306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_DST_STATIC; 41862306a36Sopenharmony_ci hw_node->daddr = fchan->cfg.dev_addr; 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_SRC_STATIC; 42162306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_DST_INCR; 42262306a36Sopenharmony_ci hw_node->saddr = fchan->cfg.dev_addr; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci hw_node->generic.sstride = 0; 42662306a36Sopenharmony_ci hw_node->generic.dstride = 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic inline struct st_fdma_chan *st_fdma_prep_common(struct dma_chan *chan, 43062306a36Sopenharmony_ci size_t len, enum dma_transfer_direction direction) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct st_fdma_chan *fchan; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!chan || !len) 43562306a36Sopenharmony_ci return NULL; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci fchan = to_st_fdma_chan(chan); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!is_slave_direction(direction)) { 44062306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "bad direction?\n"); 44162306a36Sopenharmony_ci return NULL; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return fchan; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *st_fdma_prep_dma_cyclic( 44862306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t buf_addr, size_t len, 44962306a36Sopenharmony_ci size_t period_len, enum dma_transfer_direction direction, 45062306a36Sopenharmony_ci unsigned long flags) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct st_fdma_chan *fchan; 45362306a36Sopenharmony_ci struct st_fdma_desc *fdesc; 45462306a36Sopenharmony_ci int sg_len, i; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci fchan = st_fdma_prep_common(chan, len, direction); 45762306a36Sopenharmony_ci if (!fchan) 45862306a36Sopenharmony_ci return NULL; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!period_len) 46162306a36Sopenharmony_ci return NULL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (config_reqctrl(fchan, direction)) { 46462306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "bad width or direction\n"); 46562306a36Sopenharmony_ci return NULL; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* the buffer length must be a multiple of period_len */ 46962306a36Sopenharmony_ci if (len % period_len != 0) { 47062306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "len is not multiple of period\n"); 47162306a36Sopenharmony_ci return NULL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci sg_len = len / period_len; 47562306a36Sopenharmony_ci fdesc = st_fdma_alloc_desc(fchan, sg_len); 47662306a36Sopenharmony_ci if (!fdesc) { 47762306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "no memory for desc\n"); 47862306a36Sopenharmony_ci return NULL; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci fdesc->iscyclic = true; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci for (i = 0; i < sg_len; i++) { 48462306a36Sopenharmony_ci struct st_fdma_hw_node *hw_node = fdesc->node[i].desc; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci hw_node->next = fdesc->node[(i + 1) % sg_len].pdesc; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci hw_node->control = 48962306a36Sopenharmony_ci FDMA_NODE_CTRL_REQ_MAP_DREQ(fchan->dreq_line); 49062306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_INT_EON; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci fill_hw_node(hw_node, fchan, direction); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) 49562306a36Sopenharmony_ci hw_node->saddr = buf_addr + (i * period_len); 49662306a36Sopenharmony_ci else 49762306a36Sopenharmony_ci hw_node->daddr = buf_addr + (i * period_len); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci hw_node->nbytes = period_len; 50062306a36Sopenharmony_ci hw_node->generic.length = period_len; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *st_fdma_prep_slave_sg( 50762306a36Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, 50862306a36Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 50962306a36Sopenharmony_ci unsigned long flags, void *context) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct st_fdma_chan *fchan; 51262306a36Sopenharmony_ci struct st_fdma_desc *fdesc; 51362306a36Sopenharmony_ci struct st_fdma_hw_node *hw_node; 51462306a36Sopenharmony_ci struct scatterlist *sg; 51562306a36Sopenharmony_ci int i; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci fchan = st_fdma_prep_common(chan, sg_len, direction); 51862306a36Sopenharmony_ci if (!fchan) 51962306a36Sopenharmony_ci return NULL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!sgl) 52262306a36Sopenharmony_ci return NULL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci fdesc = st_fdma_alloc_desc(fchan, sg_len); 52562306a36Sopenharmony_ci if (!fdesc) { 52662306a36Sopenharmony_ci dev_err(fchan->fdev->dev, "no memory for desc\n"); 52762306a36Sopenharmony_ci return NULL; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci fdesc->iscyclic = false; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 53362306a36Sopenharmony_ci hw_node = fdesc->node[i].desc; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci hw_node->next = fdesc->node[(i + 1) % sg_len].pdesc; 53662306a36Sopenharmony_ci hw_node->control = FDMA_NODE_CTRL_REQ_MAP_DREQ(fchan->dreq_line); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci fill_hw_node(hw_node, fchan, direction); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) 54162306a36Sopenharmony_ci hw_node->saddr = sg_dma_address(sg); 54262306a36Sopenharmony_ci else 54362306a36Sopenharmony_ci hw_node->daddr = sg_dma_address(sg); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci hw_node->nbytes = sg_dma_len(sg); 54662306a36Sopenharmony_ci hw_node->generic.length = sg_dma_len(sg); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* interrupt at end of last node */ 55062306a36Sopenharmony_ci hw_node->control |= FDMA_NODE_CTRL_INT_EON; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic size_t st_fdma_desc_residue(struct st_fdma_chan *fchan, 55662306a36Sopenharmony_ci struct virt_dma_desc *vdesc, 55762306a36Sopenharmony_ci bool in_progress) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct st_fdma_desc *fdesc = fchan->fdesc; 56062306a36Sopenharmony_ci size_t residue = 0; 56162306a36Sopenharmony_ci dma_addr_t cur_addr = 0; 56262306a36Sopenharmony_ci int i; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (in_progress) { 56562306a36Sopenharmony_ci cur_addr = fchan_read(fchan, FDMA_CH_CMD_OFST); 56662306a36Sopenharmony_ci cur_addr &= FDMA_CH_CMD_DATA_MASK; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci for (i = fchan->fdesc->n_nodes - 1 ; i >= 0; i--) { 57062306a36Sopenharmony_ci if (cur_addr == fdesc->node[i].pdesc) { 57162306a36Sopenharmony_ci residue += fnode_read(fchan, FDMA_CNTN_OFST); 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci residue += fdesc->node[i].desc->nbytes; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return residue; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic enum dma_status st_fdma_tx_status(struct dma_chan *chan, 58162306a36Sopenharmony_ci dma_cookie_t cookie, 58262306a36Sopenharmony_ci struct dma_tx_state *txstate) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 58562306a36Sopenharmony_ci struct virt_dma_desc *vd; 58662306a36Sopenharmony_ci enum dma_status ret; 58762306a36Sopenharmony_ci unsigned long flags; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 59062306a36Sopenharmony_ci if (ret == DMA_COMPLETE || !txstate) 59162306a36Sopenharmony_ci return ret; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci spin_lock_irqsave(&fchan->vchan.lock, flags); 59462306a36Sopenharmony_ci vd = vchan_find_desc(&fchan->vchan, cookie); 59562306a36Sopenharmony_ci if (fchan->fdesc && cookie == fchan->fdesc->vdesc.tx.cookie) 59662306a36Sopenharmony_ci txstate->residue = st_fdma_desc_residue(fchan, vd, true); 59762306a36Sopenharmony_ci else if (vd) 59862306a36Sopenharmony_ci txstate->residue = st_fdma_desc_residue(fchan, vd, false); 59962306a36Sopenharmony_ci else 60062306a36Sopenharmony_ci txstate->residue = 0; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci spin_unlock_irqrestore(&fchan->vchan.lock, flags); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return ret; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void st_fdma_issue_pending(struct dma_chan *chan) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 61062306a36Sopenharmony_ci unsigned long flags; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci spin_lock_irqsave(&fchan->vchan.lock, flags); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (vchan_issue_pending(&fchan->vchan) && !fchan->fdesc) 61562306a36Sopenharmony_ci st_fdma_xfer_desc(fchan); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci spin_unlock_irqrestore(&fchan->vchan.lock, flags); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int st_fdma_pause(struct dma_chan *chan) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci unsigned long flags; 62362306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 62462306a36Sopenharmony_ci int ch_id = fchan->vchan.chan.chan_id; 62562306a36Sopenharmony_ci unsigned long cmd = FDMA_CMD_PAUSE(ch_id); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci dev_dbg(fchan->fdev->dev, "pause chan:%d\n", ch_id); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci spin_lock_irqsave(&fchan->vchan.lock, flags); 63062306a36Sopenharmony_ci if (fchan->fdesc) 63162306a36Sopenharmony_ci fdma_write(fchan->fdev, cmd, FDMA_CMD_SET_OFST); 63262306a36Sopenharmony_ci spin_unlock_irqrestore(&fchan->vchan.lock, flags); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int st_fdma_resume(struct dma_chan *chan) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci unsigned long flags; 64062306a36Sopenharmony_ci unsigned long val; 64162306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 64262306a36Sopenharmony_ci int ch_id = fchan->vchan.chan.chan_id; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci dev_dbg(fchan->fdev->dev, "resume chan:%d\n", ch_id); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci spin_lock_irqsave(&fchan->vchan.lock, flags); 64762306a36Sopenharmony_ci if (fchan->fdesc) { 64862306a36Sopenharmony_ci val = fchan_read(fchan, FDMA_CH_CMD_OFST); 64962306a36Sopenharmony_ci val &= FDMA_CH_CMD_DATA_MASK; 65062306a36Sopenharmony_ci fchan_write(fchan, val, FDMA_CH_CMD_OFST); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci spin_unlock_irqrestore(&fchan->vchan.lock, flags); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int st_fdma_terminate_all(struct dma_chan *chan) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci unsigned long flags; 66062306a36Sopenharmony_ci LIST_HEAD(head); 66162306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 66262306a36Sopenharmony_ci int ch_id = fchan->vchan.chan.chan_id; 66362306a36Sopenharmony_ci unsigned long cmd = FDMA_CMD_PAUSE(ch_id); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci dev_dbg(fchan->fdev->dev, "terminate chan:%d\n", ch_id); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci spin_lock_irqsave(&fchan->vchan.lock, flags); 66862306a36Sopenharmony_ci fdma_write(fchan->fdev, cmd, FDMA_CMD_SET_OFST); 66962306a36Sopenharmony_ci fchan->fdesc = NULL; 67062306a36Sopenharmony_ci vchan_get_all_descriptors(&fchan->vchan, &head); 67162306a36Sopenharmony_ci spin_unlock_irqrestore(&fchan->vchan.lock, flags); 67262306a36Sopenharmony_ci vchan_dma_desc_free_list(&fchan->vchan, &head); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int st_fdma_slave_config(struct dma_chan *chan, 67862306a36Sopenharmony_ci struct dma_slave_config *slave_cfg) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct st_fdma_chan *fchan = to_st_fdma_chan(chan); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci memcpy(&fchan->scfg, slave_cfg, sizeof(fchan->scfg)); 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic const struct st_fdma_driverdata fdma_mpe31_stih407_11 = { 68762306a36Sopenharmony_ci .name = "STiH407", 68862306a36Sopenharmony_ci .id = 0, 68962306a36Sopenharmony_ci}; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic const struct st_fdma_driverdata fdma_mpe31_stih407_12 = { 69262306a36Sopenharmony_ci .name = "STiH407", 69362306a36Sopenharmony_ci .id = 1, 69462306a36Sopenharmony_ci}; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic const struct st_fdma_driverdata fdma_mpe31_stih407_13 = { 69762306a36Sopenharmony_ci .name = "STiH407", 69862306a36Sopenharmony_ci .id = 2, 69962306a36Sopenharmony_ci}; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic const struct of_device_id st_fdma_match[] = { 70262306a36Sopenharmony_ci { .compatible = "st,stih407-fdma-mpe31-11" 70362306a36Sopenharmony_ci , .data = &fdma_mpe31_stih407_11 }, 70462306a36Sopenharmony_ci { .compatible = "st,stih407-fdma-mpe31-12" 70562306a36Sopenharmony_ci , .data = &fdma_mpe31_stih407_12 }, 70662306a36Sopenharmony_ci { .compatible = "st,stih407-fdma-mpe31-13" 70762306a36Sopenharmony_ci , .data = &fdma_mpe31_stih407_13 }, 70862306a36Sopenharmony_ci {}, 70962306a36Sopenharmony_ci}; 71062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_fdma_match); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int st_fdma_parse_dt(struct platform_device *pdev, 71362306a36Sopenharmony_ci const struct st_fdma_driverdata *drvdata, 71462306a36Sopenharmony_ci struct st_fdma_dev *fdev) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci snprintf(fdev->fw_name, FW_NAME_SIZE, "fdma_%s_%d.elf", 71762306a36Sopenharmony_ci drvdata->name, drvdata->id); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return of_property_read_u32(pdev->dev.of_node, "dma-channels", 72062306a36Sopenharmony_ci &fdev->nr_channels); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci#define FDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 72362306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 72462306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ 72562306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void st_fdma_free(struct st_fdma_dev *fdev) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct st_fdma_chan *fchan; 73062306a36Sopenharmony_ci int i; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci for (i = 0; i < fdev->nr_channels; i++) { 73362306a36Sopenharmony_ci fchan = &fdev->chans[i]; 73462306a36Sopenharmony_ci list_del(&fchan->vchan.chan.device_node); 73562306a36Sopenharmony_ci tasklet_kill(&fchan->vchan.task); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int st_fdma_probe(struct platform_device *pdev) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct st_fdma_dev *fdev; 74262306a36Sopenharmony_ci const struct of_device_id *match; 74362306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 74462306a36Sopenharmony_ci const struct st_fdma_driverdata *drvdata; 74562306a36Sopenharmony_ci int ret, i; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci match = of_match_device((st_fdma_match), &pdev->dev); 74862306a36Sopenharmony_ci if (!match || !match->data) { 74962306a36Sopenharmony_ci dev_err(&pdev->dev, "No device match found\n"); 75062306a36Sopenharmony_ci return -ENODEV; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci drvdata = match->data; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci fdev = devm_kzalloc(&pdev->dev, sizeof(*fdev), GFP_KERNEL); 75662306a36Sopenharmony_ci if (!fdev) 75762306a36Sopenharmony_ci return -ENOMEM; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ret = st_fdma_parse_dt(pdev, drvdata, fdev); 76062306a36Sopenharmony_ci if (ret) { 76162306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to find platform data\n"); 76262306a36Sopenharmony_ci goto err; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci fdev->chans = devm_kcalloc(&pdev->dev, fdev->nr_channels, 76662306a36Sopenharmony_ci sizeof(struct st_fdma_chan), GFP_KERNEL); 76762306a36Sopenharmony_ci if (!fdev->chans) 76862306a36Sopenharmony_ci return -ENOMEM; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci fdev->dev = &pdev->dev; 77162306a36Sopenharmony_ci fdev->drvdata = drvdata; 77262306a36Sopenharmony_ci platform_set_drvdata(pdev, fdev); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci fdev->irq = platform_get_irq(pdev, 0); 77562306a36Sopenharmony_ci if (fdev->irq < 0) 77662306a36Sopenharmony_ci return -EINVAL; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, fdev->irq, st_fdma_irq_handler, 0, 77962306a36Sopenharmony_ci dev_name(&pdev->dev), fdev); 78062306a36Sopenharmony_ci if (ret) { 78162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq (%d)\n", ret); 78262306a36Sopenharmony_ci goto err; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci fdev->slim_rproc = st_slim_rproc_alloc(pdev, fdev->fw_name); 78662306a36Sopenharmony_ci if (IS_ERR(fdev->slim_rproc)) { 78762306a36Sopenharmony_ci ret = PTR_ERR(fdev->slim_rproc); 78862306a36Sopenharmony_ci dev_err(&pdev->dev, "slim_rproc_alloc failed (%d)\n", ret); 78962306a36Sopenharmony_ci goto err; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* Initialise list of FDMA channels */ 79362306a36Sopenharmony_ci INIT_LIST_HEAD(&fdev->dma_device.channels); 79462306a36Sopenharmony_ci for (i = 0; i < fdev->nr_channels; i++) { 79562306a36Sopenharmony_ci struct st_fdma_chan *fchan = &fdev->chans[i]; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci fchan->fdev = fdev; 79862306a36Sopenharmony_ci fchan->vchan.desc_free = st_fdma_free_desc; 79962306a36Sopenharmony_ci vchan_init(&fchan->vchan, &fdev->dma_device); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Initialise the FDMA dreq (reserve 0 & 31 for FDMA use) */ 80362306a36Sopenharmony_ci fdev->dreq_mask = BIT(0) | BIT(31); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, fdev->dma_device.cap_mask); 80662306a36Sopenharmony_ci dma_cap_set(DMA_CYCLIC, fdev->dma_device.cap_mask); 80762306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, fdev->dma_device.cap_mask); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci fdev->dma_device.dev = &pdev->dev; 81062306a36Sopenharmony_ci fdev->dma_device.device_alloc_chan_resources = st_fdma_alloc_chan_res; 81162306a36Sopenharmony_ci fdev->dma_device.device_free_chan_resources = st_fdma_free_chan_res; 81262306a36Sopenharmony_ci fdev->dma_device.device_prep_dma_cyclic = st_fdma_prep_dma_cyclic; 81362306a36Sopenharmony_ci fdev->dma_device.device_prep_slave_sg = st_fdma_prep_slave_sg; 81462306a36Sopenharmony_ci fdev->dma_device.device_prep_dma_memcpy = st_fdma_prep_dma_memcpy; 81562306a36Sopenharmony_ci fdev->dma_device.device_tx_status = st_fdma_tx_status; 81662306a36Sopenharmony_ci fdev->dma_device.device_issue_pending = st_fdma_issue_pending; 81762306a36Sopenharmony_ci fdev->dma_device.device_terminate_all = st_fdma_terminate_all; 81862306a36Sopenharmony_ci fdev->dma_device.device_config = st_fdma_slave_config; 81962306a36Sopenharmony_ci fdev->dma_device.device_pause = st_fdma_pause; 82062306a36Sopenharmony_ci fdev->dma_device.device_resume = st_fdma_resume; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci fdev->dma_device.src_addr_widths = FDMA_DMA_BUSWIDTHS; 82362306a36Sopenharmony_ci fdev->dma_device.dst_addr_widths = FDMA_DMA_BUSWIDTHS; 82462306a36Sopenharmony_ci fdev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 82562306a36Sopenharmony_ci fdev->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = dmaenginem_async_device_register(&fdev->dma_device); 82862306a36Sopenharmony_ci if (ret) { 82962306a36Sopenharmony_ci dev_err(&pdev->dev, 83062306a36Sopenharmony_ci "Failed to register DMA device (%d)\n", ret); 83162306a36Sopenharmony_ci goto err_rproc; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = of_dma_controller_register(np, st_fdma_of_xlate, fdev); 83562306a36Sopenharmony_ci if (ret) { 83662306a36Sopenharmony_ci dev_err(&pdev->dev, 83762306a36Sopenharmony_ci "Failed to register controller (%d)\n", ret); 83862306a36Sopenharmony_ci goto err_rproc; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci dev_info(&pdev->dev, "ST FDMA engine driver, irq:%d\n", fdev->irq); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cierr_rproc: 84662306a36Sopenharmony_ci st_fdma_free(fdev); 84762306a36Sopenharmony_ci st_slim_rproc_put(fdev->slim_rproc); 84862306a36Sopenharmony_cierr: 84962306a36Sopenharmony_ci return ret; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic int st_fdma_remove(struct platform_device *pdev) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct st_fdma_dev *fdev = platform_get_drvdata(pdev); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci devm_free_irq(&pdev->dev, fdev->irq, fdev); 85762306a36Sopenharmony_ci st_slim_rproc_put(fdev->slim_rproc); 85862306a36Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return 0; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic struct platform_driver st_fdma_platform_driver = { 86462306a36Sopenharmony_ci .driver = { 86562306a36Sopenharmony_ci .name = DRIVER_NAME, 86662306a36Sopenharmony_ci .of_match_table = st_fdma_match, 86762306a36Sopenharmony_ci }, 86862306a36Sopenharmony_ci .probe = st_fdma_probe, 86962306a36Sopenharmony_ci .remove = st_fdma_remove, 87062306a36Sopenharmony_ci}; 87162306a36Sopenharmony_cimodule_platform_driver(st_fdma_platform_driver); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 87462306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics FDMA engine driver"); 87562306a36Sopenharmony_ciMODULE_AUTHOR("Ludovic.barre <Ludovic.barre@st.com>"); 87662306a36Sopenharmony_ciMODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); 87762306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 878