diff options
Diffstat (limited to 'drivers/dma/pl330.c')
| -rw-r--r-- | drivers/dma/pl330.c | 107 | 
1 files changed, 56 insertions, 51 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index a562d24d20b..73fa9b7a10a 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -543,7 +543,9 @@ struct dma_pl330_chan {  	/* DMA-Engine Channel */  	struct dma_chan chan; -	/* List of to be xfered descriptors */ +	/* List of submitted descriptors */ +	struct list_head submitted_list; +	/* List of issued descriptors */  	struct list_head work_list;  	/* List of completed descriptors */  	struct list_head completed_list; @@ -578,12 +580,16 @@ struct dma_pl330_dmac {  	/* DMA-Engine Device */  	struct dma_device ddma; +	/* Holds info about sg limitations */ +	struct device_dma_parameters dma_parms; +  	/* Pool of descriptors available for the DMAC's channels */  	struct list_head desc_pool;  	/* To protect desc_pool manipulation */  	spinlock_t pool_lock;  	/* Peripheral channels connected to this DMAC */ +	unsigned int num_peripherals;  	struct dma_pl330_chan *peripherals; /* keep at end */  }; @@ -606,11 +612,6 @@ struct dma_pl330_desc {  	struct dma_pl330_chan *pchan;  }; -struct dma_pl330_filter_args { -	struct dma_pl330_dmac *pdmac; -	unsigned int chan_id; -}; -  static inline void _callback(struct pl330_req *r, enum pl330_op_err err)  {  	if (r && r->xfer_cb) @@ -2268,6 +2269,8 @@ static void pl330_tasklet(unsigned long data)  			list_move_tail(&desc->node, &pch->dmac->desc_pool);  		} +		dma_descriptor_unmap(&desc->txd); +  		if (callback) {  			spin_unlock_irqrestore(&pch->lock, flags);  			callback(callback_param); @@ -2296,16 +2299,6 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err)  	tasklet_schedule(&pch->task);  } -static bool pl330_dt_filter(struct dma_chan *chan, void *param) -{ -	struct dma_pl330_filter_args *fargs = param; - -	if (chan->device != &fargs->pdmac->ddma) -		return false; - -	return (chan->chan_id == fargs->chan_id); -} -  bool pl330_filter(struct dma_chan *chan, void *param)  {  	u8 *peri_id; @@ -2314,7 +2307,7 @@ bool pl330_filter(struct dma_chan *chan, void *param)  		return false;  	peri_id = chan->private; -	return *peri_id == (unsigned)param; +	return *peri_id == (unsigned long)param;  }  EXPORT_SYMBOL(pl330_filter); @@ -2323,23 +2316,16 @@ static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec,  {  	int count = dma_spec->args_count;  	struct dma_pl330_dmac *pdmac = ofdma->of_dma_data; -	struct dma_pl330_filter_args fargs; -	dma_cap_mask_t cap; - -	if (!pdmac) -		return NULL; +	unsigned int chan_id;  	if (count != 1)  		return NULL; -	fargs.pdmac = pdmac; -	fargs.chan_id = dma_spec->args[0]; - -	dma_cap_zero(cap); -	dma_cap_set(DMA_SLAVE, cap); -	dma_cap_set(DMA_CYCLIC, cap); +	chan_id = dma_spec->args[0]; +	if (chan_id >= pdmac->num_peripherals) +		return NULL; -	return dma_request_channel(cap, pl330_dt_filter, &fargs); +	return dma_get_slave_channel(&pdmac->peripherals[chan_id].chan);  }  static int pl330_alloc_chan_resources(struct dma_chan *chan) @@ -2383,6 +2369,11 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned  		pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);  		/* Mark all desc done */ +		list_for_each_entry(desc, &pch->submitted_list, node) { +			desc->status = FREE; +			dma_cookie_complete(&desc->txd); +		} +  		list_for_each_entry(desc, &pch->work_list , node) {  			desc->status = FREE;  			dma_cookie_complete(&desc->txd); @@ -2393,6 +2384,7 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned  			dma_cookie_complete(&desc->txd);  		} +		list_splice_tail_init(&pch->submitted_list, &pdmac->desc_pool);  		list_splice_tail_init(&pch->work_list, &pdmac->desc_pool);  		list_splice_tail_init(&pch->completed_list, &pdmac->desc_pool);  		spin_unlock_irqrestore(&pch->lock, flags); @@ -2451,7 +2443,14 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie,  static void pl330_issue_pending(struct dma_chan *chan)  { -	pl330_tasklet((unsigned long) to_pchan(chan)); +	struct dma_pl330_chan *pch = to_pchan(chan); +	unsigned long flags; + +	spin_lock_irqsave(&pch->lock, flags); +	list_splice_tail_init(&pch->submitted_list, &pch->work_list); +	spin_unlock_irqrestore(&pch->lock, flags); + +	pl330_tasklet((unsigned long)pch);  }  /* @@ -2478,11 +2477,11 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)  		dma_cookie_assign(&desc->txd); -		list_move_tail(&desc->node, &pch->work_list); +		list_move_tail(&desc->node, &pch->submitted_list);  	}  	cookie = dma_cookie_assign(&last->txd); -	list_add_tail(&last->node, &pch->work_list); +	list_add_tail(&last->node, &pch->submitted_list);  	spin_unlock_irqrestore(&pch->lock, flags);  	return cookie; @@ -2490,12 +2489,9 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)  static inline void _init_desc(struct dma_pl330_desc *desc)  { -	desc->pchan = NULL;  	desc->req.x = &desc->px;  	desc->req.token = desc;  	desc->rqcfg.swap = SWAP_NO; -	desc->rqcfg.privileged = 0; -	desc->rqcfg.insnaccess = 0;  	desc->rqcfg.scctl = SCCTRL0;  	desc->rqcfg.dcctl = DCCTRL0;  	desc->req.cfg = &desc->rqcfg; @@ -2515,7 +2511,7 @@ static int add_desc(struct dma_pl330_dmac *pdmac, gfp_t flg, int count)  	if (!pdmac)  		return 0; -	desc = kmalloc(count * sizeof(*desc), flg); +	desc = kcalloc(count, sizeof(*desc), flg);  	if (!desc)  		return 0; @@ -2885,6 +2881,7 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan,  	caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);  	caps->cmd_pause = false;  	caps->cmd_terminate = true; +	caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;  	return 0;  } @@ -2903,6 +2900,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)  	pdat = dev_get_platdata(&adev->dev); +	ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32)); +	if (ret) +		return ret; +  	/* Allocate a new DMAC and its Channels */  	pdmac = devm_kzalloc(&adev->dev, sizeof(*pdmac), GFP_KERNEL);  	if (!pdmac) { @@ -2922,16 +2923,23 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)  	amba_set_drvdata(adev, pdmac); -	irq = adev->irq[0]; -	ret = request_irq(irq, pl330_irq_handler, 0, -			dev_name(&adev->dev), pi); -	if (ret) -		return ret; +	for (i = 0; i < AMBA_NR_IRQS; i++) { +		irq = adev->irq[i]; +		if (irq) { +			ret = devm_request_irq(&adev->dev, irq, +					       pl330_irq_handler, 0, +					       dev_name(&adev->dev), pi); +			if (ret) +				return ret; +		} else { +			break; +		} +	}  	pi->pcfg.periph_id = adev->periphid;  	ret = pl330_add(pi);  	if (ret) -		goto probe_err1; +		return ret;  	INIT_LIST_HEAD(&pdmac->desc_pool);  	spin_lock_init(&pdmac->pool_lock); @@ -2949,6 +2957,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)  	else  		num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan); +	pdmac->num_peripherals = num_chan; +  	pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);  	if (!pdmac->peripherals) {  		ret = -ENOMEM; @@ -2963,6 +2973,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)  		else  			pch->chan.private = adev->dev.of_node; +		INIT_LIST_HEAD(&pch->submitted_list);  		INIT_LIST_HEAD(&pch->work_list);  		INIT_LIST_HEAD(&pch->completed_list);  		spin_lock_init(&pch->lock); @@ -3010,6 +3021,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)  			"unable to register DMA to the generic DT DMA helpers\n");  		}  	} + +	adev->dev.dma_parms = &pdmac->dma_parms; +  	/*  	 * This is the limit for transfers with a buswidth of 1, larger  	 * buswidths will have larger limits. @@ -3029,8 +3043,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)  	return 0;  probe_err3: -	amba_set_drvdata(adev, NULL); -  	/* Idle the DMAC */  	list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels,  			chan.device_node) { @@ -3044,8 +3056,6 @@ probe_err3:  	}  probe_err2:  	pl330_del(pi); -probe_err1: -	free_irq(irq, pi);  	return ret;  } @@ -3055,7 +3065,6 @@ static int pl330_remove(struct amba_device *adev)  	struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev);  	struct dma_pl330_chan *pch, *_p;  	struct pl330_info *pi; -	int irq;  	if (!pdmac)  		return 0; @@ -3064,7 +3073,6 @@ static int pl330_remove(struct amba_device *adev)  		of_dma_controller_free(adev->dev.of_node);  	dma_async_device_unregister(&pdmac->ddma); -	amba_set_drvdata(adev, NULL);  	/* Idle the DMAC */  	list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels, @@ -3082,9 +3090,6 @@ static int pl330_remove(struct amba_device *adev)  	pl330_del(pi); -	irq = adev->irq[0]; -	free_irq(irq, pi); -  	return 0;  }  | 
