162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright (c) 2013-2014 Freescale Semiconductor, Inc
462306a36Sopenharmony_ci// Copyright (c) 2017 Sysam, Angelo Dureghello  <angelo@sysam.it>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/dmaengine.h>
962306a36Sopenharmony_ci#include <linux/platform_device.h>
1062306a36Sopenharmony_ci#include <linux/platform_data/dma-mcf-edma.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "fsl-edma-common.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define EDMA_CHANNELS		64
1562306a36Sopenharmony_ci#define EDMA_MASK_CH(x)		((x) & GENMASK(5, 0))
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct fsl_edma_engine *mcf_edma = dev_id;
2062306a36Sopenharmony_ci	struct edma_regs *regs = &mcf_edma->regs;
2162306a36Sopenharmony_ci	unsigned int ch;
2262306a36Sopenharmony_ci	u64 intmap;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	intmap = ioread32(regs->inth);
2562306a36Sopenharmony_ci	intmap <<= 32;
2662306a36Sopenharmony_ci	intmap |= ioread32(regs->intl);
2762306a36Sopenharmony_ci	if (!intmap)
2862306a36Sopenharmony_ci		return IRQ_NONE;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	for (ch = 0; ch < mcf_edma->n_chans; ch++) {
3162306a36Sopenharmony_ci		if (intmap & BIT(ch)) {
3262306a36Sopenharmony_ci			iowrite8(EDMA_MASK_CH(ch), regs->cint);
3362306a36Sopenharmony_ci			fsl_edma_tx_chan_handler(&mcf_edma->chans[ch]);
3462306a36Sopenharmony_ci		}
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return IRQ_HANDLED;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic irqreturn_t mcf_edma_err_handler(int irq, void *dev_id)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct fsl_edma_engine *mcf_edma = dev_id;
4362306a36Sopenharmony_ci	struct edma_regs *regs = &mcf_edma->regs;
4462306a36Sopenharmony_ci	unsigned int err, ch;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	err = ioread32(regs->errl);
4762306a36Sopenharmony_ci	if (!err)
4862306a36Sopenharmony_ci		return IRQ_NONE;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	for (ch = 0; ch < (EDMA_CHANNELS / 2); ch++) {
5162306a36Sopenharmony_ci		if (err & BIT(ch)) {
5262306a36Sopenharmony_ci			fsl_edma_disable_request(&mcf_edma->chans[ch]);
5362306a36Sopenharmony_ci			iowrite8(EDMA_CERR_CERR(ch), regs->cerr);
5462306a36Sopenharmony_ci			fsl_edma_err_chan_handler(&mcf_edma->chans[ch]);
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	err = ioread32(regs->errh);
5962306a36Sopenharmony_ci	if (!err)
6062306a36Sopenharmony_ci		return IRQ_NONE;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	for (ch = (EDMA_CHANNELS / 2); ch < EDMA_CHANNELS; ch++) {
6362306a36Sopenharmony_ci		if (err & (BIT(ch - (EDMA_CHANNELS / 2)))) {
6462306a36Sopenharmony_ci			fsl_edma_disable_request(&mcf_edma->chans[ch]);
6562306a36Sopenharmony_ci			iowrite8(EDMA_CERR_CERR(ch), regs->cerr);
6662306a36Sopenharmony_ci			mcf_edma->chans[ch].status = DMA_ERROR;
6762306a36Sopenharmony_ci			mcf_edma->chans[ch].idle = true;
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return IRQ_HANDLED;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int mcf_edma_irq_init(struct platform_device *pdev,
7562306a36Sopenharmony_ci				struct fsl_edma_engine *mcf_edma)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int ret = 0, i;
7862306a36Sopenharmony_ci	struct resource *res;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev,
8162306a36Sopenharmony_ci				IORESOURCE_IRQ, "edma-tx-00-15");
8262306a36Sopenharmony_ci	if (!res)
8362306a36Sopenharmony_ci		return -1;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	for (ret = 0, i = res->start; i <= res->end; ++i)
8662306a36Sopenharmony_ci		ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma);
8762306a36Sopenharmony_ci	if (ret)
8862306a36Sopenharmony_ci		return ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev,
9162306a36Sopenharmony_ci			IORESOURCE_IRQ, "edma-tx-16-55");
9262306a36Sopenharmony_ci	if (!res)
9362306a36Sopenharmony_ci		return -1;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (ret = 0, i = res->start; i <= res->end; ++i)
9662306a36Sopenharmony_ci		ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma);
9762306a36Sopenharmony_ci	if (ret)
9862306a36Sopenharmony_ci		return ret;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	ret = platform_get_irq_byname(pdev, "edma-tx-56-63");
10162306a36Sopenharmony_ci	if (ret != -ENXIO) {
10262306a36Sopenharmony_ci		ret = request_irq(ret, mcf_edma_tx_handler,
10362306a36Sopenharmony_ci				  0, "eDMA", mcf_edma);
10462306a36Sopenharmony_ci		if (ret)
10562306a36Sopenharmony_ci			return ret;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ret = platform_get_irq_byname(pdev, "edma-err");
10962306a36Sopenharmony_ci	if (ret != -ENXIO) {
11062306a36Sopenharmony_ci		ret = request_irq(ret, mcf_edma_err_handler,
11162306a36Sopenharmony_ci				  0, "eDMA", mcf_edma);
11262306a36Sopenharmony_ci		if (ret)
11362306a36Sopenharmony_ci			return ret;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void mcf_edma_irq_free(struct platform_device *pdev,
12062306a36Sopenharmony_ci				struct fsl_edma_engine *mcf_edma)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int irq;
12362306a36Sopenharmony_ci	struct resource *res;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev,
12662306a36Sopenharmony_ci			IORESOURCE_IRQ, "edma-tx-00-15");
12762306a36Sopenharmony_ci	if (res) {
12862306a36Sopenharmony_ci		for (irq = res->start; irq <= res->end; irq++)
12962306a36Sopenharmony_ci			free_irq(irq, mcf_edma);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev,
13362306a36Sopenharmony_ci			IORESOURCE_IRQ, "edma-tx-16-55");
13462306a36Sopenharmony_ci	if (res) {
13562306a36Sopenharmony_ci		for (irq = res->start; irq <= res->end; irq++)
13662306a36Sopenharmony_ci			free_irq(irq, mcf_edma);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "edma-tx-56-63");
14062306a36Sopenharmony_ci	if (irq != -ENXIO)
14162306a36Sopenharmony_ci		free_irq(irq, mcf_edma);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "edma-err");
14462306a36Sopenharmony_ci	if (irq != -ENXIO)
14562306a36Sopenharmony_ci		free_irq(irq, mcf_edma);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct fsl_edma_drvdata mcf_data = {
14962306a36Sopenharmony_ci	.flags = FSL_EDMA_DRV_EDMA64,
15062306a36Sopenharmony_ci	.setup_irq = mcf_edma_irq_init,
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int mcf_edma_probe(struct platform_device *pdev)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct mcf_edma_platform_data *pdata;
15662306a36Sopenharmony_ci	struct fsl_edma_engine *mcf_edma;
15762306a36Sopenharmony_ci	struct edma_regs *regs;
15862306a36Sopenharmony_ci	int ret, i, chans;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
16162306a36Sopenharmony_ci	if (!pdata) {
16262306a36Sopenharmony_ci		dev_err(&pdev->dev, "no platform data supplied\n");
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (!pdata->dma_channels) {
16762306a36Sopenharmony_ci		dev_info(&pdev->dev, "setting default channel number to 64");
16862306a36Sopenharmony_ci		chans = 64;
16962306a36Sopenharmony_ci	} else {
17062306a36Sopenharmony_ci		chans = pdata->dma_channels;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	mcf_edma = devm_kzalloc(&pdev->dev, struct_size(mcf_edma, chans, chans),
17462306a36Sopenharmony_ci				GFP_KERNEL);
17562306a36Sopenharmony_ci	if (!mcf_edma)
17662306a36Sopenharmony_ci		return -ENOMEM;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	mcf_edma->n_chans = chans;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Set up drvdata for ColdFire edma */
18162306a36Sopenharmony_ci	mcf_edma->drvdata = &mcf_data;
18262306a36Sopenharmony_ci	mcf_edma->big_endian = 1;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	mutex_init(&mcf_edma->fsl_edma_mutex);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	mcf_edma->membase = devm_platform_ioremap_resource(pdev, 0);
18762306a36Sopenharmony_ci	if (IS_ERR(mcf_edma->membase))
18862306a36Sopenharmony_ci		return PTR_ERR(mcf_edma->membase);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	fsl_edma_setup_regs(mcf_edma);
19162306a36Sopenharmony_ci	regs = &mcf_edma->regs;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	INIT_LIST_HEAD(&mcf_edma->dma_dev.channels);
19462306a36Sopenharmony_ci	for (i = 0; i < mcf_edma->n_chans; i++) {
19562306a36Sopenharmony_ci		struct fsl_edma_chan *mcf_chan = &mcf_edma->chans[i];
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		mcf_chan->edma = mcf_edma;
19862306a36Sopenharmony_ci		mcf_chan->slave_id = i;
19962306a36Sopenharmony_ci		mcf_chan->idle = true;
20062306a36Sopenharmony_ci		mcf_chan->dma_dir = DMA_NONE;
20162306a36Sopenharmony_ci		mcf_chan->vchan.desc_free = fsl_edma_free_desc;
20262306a36Sopenharmony_ci		vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev);
20362306a36Sopenharmony_ci		mcf_chan->tcd = mcf_edma->membase + EDMA_TCD
20462306a36Sopenharmony_ci				+ i * sizeof(struct fsl_edma_hw_tcd);
20562306a36Sopenharmony_ci		iowrite32(0x0, &mcf_chan->tcd->csr);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	iowrite32(~0, regs->inth);
20962306a36Sopenharmony_ci	iowrite32(~0, regs->intl);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = mcf_edma->drvdata->setup_irq(pdev, mcf_edma);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		return ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, mcf_edma->dma_dev.cap_mask);
21662306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mcf_edma->dma_dev.cap_mask);
21762306a36Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, mcf_edma->dma_dev.cap_mask);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	mcf_edma->dma_dev.dev = &pdev->dev;
22062306a36Sopenharmony_ci	mcf_edma->dma_dev.device_alloc_chan_resources =
22162306a36Sopenharmony_ci			fsl_edma_alloc_chan_resources;
22262306a36Sopenharmony_ci	mcf_edma->dma_dev.device_free_chan_resources =
22362306a36Sopenharmony_ci			fsl_edma_free_chan_resources;
22462306a36Sopenharmony_ci	mcf_edma->dma_dev.device_config = fsl_edma_slave_config;
22562306a36Sopenharmony_ci	mcf_edma->dma_dev.device_prep_dma_cyclic =
22662306a36Sopenharmony_ci			fsl_edma_prep_dma_cyclic;
22762306a36Sopenharmony_ci	mcf_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
22862306a36Sopenharmony_ci	mcf_edma->dma_dev.device_tx_status = fsl_edma_tx_status;
22962306a36Sopenharmony_ci	mcf_edma->dma_dev.device_pause = fsl_edma_pause;
23062306a36Sopenharmony_ci	mcf_edma->dma_dev.device_resume = fsl_edma_resume;
23162306a36Sopenharmony_ci	mcf_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all;
23262306a36Sopenharmony_ci	mcf_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mcf_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS;
23562306a36Sopenharmony_ci	mcf_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS;
23662306a36Sopenharmony_ci	mcf_edma->dma_dev.directions =
23762306a36Sopenharmony_ci			BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	mcf_edma->dma_dev.filter.fn = mcf_edma_filter_fn;
24062306a36Sopenharmony_ci	mcf_edma->dma_dev.filter.map = pdata->slave_map;
24162306a36Sopenharmony_ci	mcf_edma->dma_dev.filter.mapcnt = pdata->slavecnt;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	platform_set_drvdata(pdev, mcf_edma);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ret = dma_async_device_register(&mcf_edma->dma_dev);
24662306a36Sopenharmony_ci	if (ret) {
24762306a36Sopenharmony_ci		dev_err(&pdev->dev,
24862306a36Sopenharmony_ci			"Can't register Freescale eDMA engine. (%d)\n", ret);
24962306a36Sopenharmony_ci		return ret;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Enable round robin arbitration */
25362306a36Sopenharmony_ci	iowrite32(EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int mcf_edma_remove(struct platform_device *pdev)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct fsl_edma_engine *mcf_edma = platform_get_drvdata(pdev);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	mcf_edma_irq_free(pdev, mcf_edma);
26362306a36Sopenharmony_ci	fsl_edma_cleanup_vchan(&mcf_edma->dma_dev);
26462306a36Sopenharmony_ci	dma_async_device_unregister(&mcf_edma->dma_dev);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic struct platform_driver mcf_edma_driver = {
27062306a36Sopenharmony_ci	.driver		= {
27162306a36Sopenharmony_ci		.name	= "mcf-edma",
27262306a36Sopenharmony_ci	},
27362306a36Sopenharmony_ci	.probe		= mcf_edma_probe,
27462306a36Sopenharmony_ci	.remove		= mcf_edma_remove,
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cibool mcf_edma_filter_fn(struct dma_chan *chan, void *param)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	if (chan->device->dev->driver == &mcf_edma_driver.driver) {
28062306a36Sopenharmony_ci		struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		return (mcf_chan->slave_id == (uintptr_t)param);
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return false;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ciEXPORT_SYMBOL(mcf_edma_filter_fn);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int __init mcf_edma_init(void)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	return platform_driver_register(&mcf_edma_driver);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_cisubsys_initcall(mcf_edma_init);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void __exit mcf_edma_exit(void)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	platform_driver_unregister(&mcf_edma_driver);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_cimodule_exit(mcf_edma_exit);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ciMODULE_ALIAS("platform:mcf-edma");
30262306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale eDMA engine driver, ColdFire family");
30362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
304