diff options
Diffstat (limited to 'drivers/dma/mxs-dma.c')
| -rw-r--r-- | drivers/dma/mxs-dma.c | 109 | 
1 files changed, 75 insertions, 34 deletions
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 8f6d30d37c4..b48a79c2884 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -27,6 +27,7 @@  #include <linux/stmp_device.h>  #include <linux/of.h>  #include <linux/of_device.h> +#include <linux/of_dma.h>  #include <asm/irq.h> @@ -139,6 +140,8 @@ struct mxs_dma_engine {  	struct dma_device		dma_device;  	struct device_dma_parameters	dma_parms;  	struct mxs_dma_chan		mxs_chans[MXS_DMA_CHANNELS]; +	struct platform_device		*pdev; +	unsigned int			nr_channels;  };  struct mxs_dma_type { @@ -350,10 +353,8 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)  	struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;  	int ret; -	if (!data) -		return -EINVAL; - -	mxs_chan->chan_irq = data->chan_irq; +	if (data) +		mxs_chan->chan_irq = data->chan_irq;  	mxs_chan->ccw = dma_alloc_coherent(mxs_dma->dma_device.dev,  				CCW_BLOCK_SIZE, &mxs_chan->ccw_phys, @@ -665,8 +666,55 @@ err_out:  	return ret;  } +struct mxs_dma_filter_param { +	struct device_node *of_node; +	unsigned int chan_id; +}; + +static bool mxs_dma_filter_fn(struct dma_chan *chan, void *fn_param) +{ +	struct mxs_dma_filter_param *param = fn_param; +	struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); +	struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; +	int chan_irq; + +	if (mxs_dma->dma_device.dev->of_node != param->of_node) +		return false; + +	if (chan->chan_id != param->chan_id) +		return false; + +	chan_irq = platform_get_irq(mxs_dma->pdev, param->chan_id); +	if (chan_irq < 0) +		return false; + +	mxs_chan->chan_irq = chan_irq; + +	return true; +} + +struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec, +			       struct of_dma *ofdma) +{ +	struct mxs_dma_engine *mxs_dma = ofdma->of_dma_data; +	dma_cap_mask_t mask = mxs_dma->dma_device.cap_mask; +	struct mxs_dma_filter_param param; + +	if (dma_spec->args_count != 1) +		return NULL; + +	param.of_node = ofdma->of_node; +	param.chan_id = dma_spec->args[0]; + +	if (param.chan_id >= mxs_dma->nr_channels) +		return NULL; + +	return dma_request_channel(mask, mxs_dma_filter_fn, ¶m); +} +  static int __init mxs_dma_probe(struct platform_device *pdev)  { +	struct device_node *np = pdev->dev.of_node;  	const struct platform_device_id *id_entry;  	const struct of_device_id *of_id;  	const struct mxs_dma_type *dma_type; @@ -674,10 +722,16 @@ static int __init mxs_dma_probe(struct platform_device *pdev)  	struct resource *iores;  	int ret, i; -	mxs_dma = kzalloc(sizeof(*mxs_dma), GFP_KERNEL); +	mxs_dma = devm_kzalloc(&pdev->dev, sizeof(*mxs_dma), GFP_KERNEL);  	if (!mxs_dma)  		return -ENOMEM; +	ret = of_property_read_u32(np, "dma-channels", &mxs_dma->nr_channels); +	if (ret) { +		dev_err(&pdev->dev, "failed to read dma-channels\n"); +		return ret; +	} +  	of_id = of_match_device(mxs_dma_dt_ids, &pdev->dev);  	if (of_id)  		id_entry = of_id->data; @@ -689,24 +743,13 @@ static int __init mxs_dma_probe(struct platform_device *pdev)  	mxs_dma->dev_id = dma_type->id;  	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	mxs_dma->base = devm_ioremap_resource(&pdev->dev, iores); +	if (IS_ERR(mxs_dma->base)) +		return PTR_ERR(mxs_dma->base); -	if (!request_mem_region(iores->start, resource_size(iores), -				pdev->name)) { -		ret = -EBUSY; -		goto err_request_region; -	} - -	mxs_dma->base = ioremap(iores->start, resource_size(iores)); -	if (!mxs_dma->base) { -		ret = -ENOMEM; -		goto err_ioremap; -	} - -	mxs_dma->clk = clk_get(&pdev->dev, NULL); -	if (IS_ERR(mxs_dma->clk)) { -		ret = PTR_ERR(mxs_dma->clk); -		goto err_clk; -	} +	mxs_dma->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(mxs_dma->clk)) +		return PTR_ERR(mxs_dma->clk);  	dma_cap_set(DMA_SLAVE, mxs_dma->dma_device.cap_mask);  	dma_cap_set(DMA_CYCLIC, mxs_dma->dma_device.cap_mask); @@ -732,8 +775,9 @@ static int __init mxs_dma_probe(struct platform_device *pdev)  	ret = mxs_dma_init(mxs_dma);  	if (ret) -		goto err_init; +		return ret; +	mxs_dma->pdev = pdev;  	mxs_dma->dma_device.dev = &pdev->dev;  	/* mxs_dma gets 65535 bytes maximum sg size */ @@ -751,22 +795,19 @@ static int __init mxs_dma_probe(struct platform_device *pdev)  	ret = dma_async_device_register(&mxs_dma->dma_device);  	if (ret) {  		dev_err(mxs_dma->dma_device.dev, "unable to register\n"); -		goto err_init; +		return ret; +	} + +	ret = of_dma_controller_register(np, mxs_dma_xlate, mxs_dma); +	if (ret) { +		dev_err(mxs_dma->dma_device.dev, +			"failed to register controller\n"); +		dma_async_device_unregister(&mxs_dma->dma_device);  	}  	dev_info(mxs_dma->dma_device.dev, "initialized\n");  	return 0; - -err_init: -	clk_put(mxs_dma->clk); -err_clk: -	iounmap(mxs_dma->base); -err_ioremap: -	release_mem_region(iores->start, resource_size(iores)); -err_request_region: -	kfree(mxs_dma); -	return ret;  }  static struct platform_driver mxs_dma_driver = {  |