xref: /linux/drivers/dma/dw/platform.c (revision 922842a3bfbeff64dfebe7f01ce1f2ab01e4509d)
1b466a37fSAndy Shevchenko // SPDX-License-Identifier: GPL-2.0
29cade1a4SAndy Shevchenko /*
39cade1a4SAndy Shevchenko  * Platform driver for the Synopsys DesignWare DMA Controller
49cade1a4SAndy Shevchenko  *
59cade1a4SAndy Shevchenko  * Copyright (C) 2007-2008 Atmel Corporation
69cade1a4SAndy Shevchenko  * Copyright (C) 2010-2011 ST Microelectronics
79cade1a4SAndy Shevchenko  * Copyright (C) 2013 Intel Corporation
89cade1a4SAndy Shevchenko  *
99cade1a4SAndy Shevchenko  * Some parts of this driver are derived from the original dw_dmac.
109cade1a4SAndy Shevchenko  */
119cade1a4SAndy Shevchenko 
129cade1a4SAndy Shevchenko #include <linux/module.h>
139cade1a4SAndy Shevchenko #include <linux/device.h>
149cade1a4SAndy Shevchenko #include <linux/clk.h>
156acf3998SAndy Shevchenko #include <linux/pm_runtime.h>
169cade1a4SAndy Shevchenko #include <linux/platform_device.h>
179cade1a4SAndy Shevchenko #include <linux/dmaengine.h>
189cade1a4SAndy Shevchenko #include <linux/dma-mapping.h>
199cade1a4SAndy Shevchenko #include <linux/of.h>
209cade1a4SAndy Shevchenko #include <linux/acpi.h>
219cade1a4SAndy Shevchenko 
229cade1a4SAndy Shevchenko #include "internal.h"
239cade1a4SAndy Shevchenko 
24a104a45bSAndy Shevchenko #define DRV_NAME	"dw_dmac"
25a104a45bSAndy Shevchenko 
dw_probe(struct platform_device * pdev)269cade1a4SAndy Shevchenko static int dw_probe(struct platform_device *pdev)
279cade1a4SAndy Shevchenko {
28b3757413SAndy Shevchenko 	const struct dw_dma_chip_pdata *match;
29b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data;
309cade1a4SAndy Shevchenko 	struct dw_dma_chip *chip;
319cade1a4SAndy Shevchenko 	struct device *dev = &pdev->dev;
32*2ebc36b9SSerge Semin 	int ret;
339cade1a4SAndy Shevchenko 
34b3757413SAndy Shevchenko 	match = device_get_match_data(dev);
35b3757413SAndy Shevchenko 	if (!match)
36b3757413SAndy Shevchenko 		return -ENODEV;
37b3757413SAndy Shevchenko 
38b3757413SAndy Shevchenko 	data = devm_kmemdup(&pdev->dev, match, sizeof(*match), GFP_KERNEL);
39b3757413SAndy Shevchenko 	if (!data)
40b3757413SAndy Shevchenko 		return -ENOMEM;
41b3757413SAndy Shevchenko 
429cade1a4SAndy Shevchenko 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
439cade1a4SAndy Shevchenko 	if (!chip)
449cade1a4SAndy Shevchenko 		return -ENOMEM;
459cade1a4SAndy Shevchenko 
469cade1a4SAndy Shevchenko 	chip->irq = platform_get_irq(pdev, 0);
479cade1a4SAndy Shevchenko 	if (chip->irq < 0)
489cade1a4SAndy Shevchenko 		return chip->irq;
499cade1a4SAndy Shevchenko 
50a9c56721SAndy Shevchenko 	chip->regs = devm_platform_ioremap_resource(pdev, 0);
519cade1a4SAndy Shevchenko 	if (IS_ERR(chip->regs))
529cade1a4SAndy Shevchenko 		return PTR_ERR(chip->regs);
539cade1a4SAndy Shevchenko 
54*2ebc36b9SSerge Semin 	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
55*2ebc36b9SSerge Semin 	if (ret)
56*2ebc36b9SSerge Semin 		return ret;
579cade1a4SAndy Shevchenko 
58f8d9ddbcSAndy Shevchenko 	if (!data->pdata)
59f8d9ddbcSAndy Shevchenko 		data->pdata = dev_get_platdata(dev);
60f8d9ddbcSAndy Shevchenko 	if (!data->pdata)
61f8d9ddbcSAndy Shevchenko 		data->pdata = dw_dma_parse_dt(pdev);
629cade1a4SAndy Shevchenko 
639cade1a4SAndy Shevchenko 	chip->dev = dev;
6408d62f58SAndy Shevchenko 	chip->id = pdev->id;
65f8d9ddbcSAndy Shevchenko 	chip->pdata = data->pdata;
669cade1a4SAndy Shevchenko 
67b3757413SAndy Shevchenko 	data->chip = chip;
68b3757413SAndy Shevchenko 
69f27c2273SAndy Shevchenko 	chip->clk = devm_clk_get_optional(chip->dev, "hclk");
70a15636e8SAndy Shevchenko 	if (IS_ERR(chip->clk))
71a15636e8SAndy Shevchenko 		return PTR_ERR(chip->clk);
72*2ebc36b9SSerge Semin 	ret = clk_prepare_enable(chip->clk);
73*2ebc36b9SSerge Semin 	if (ret)
74*2ebc36b9SSerge Semin 		return ret;
759cade1a4SAndy Shevchenko 
766acf3998SAndy Shevchenko 	pm_runtime_enable(&pdev->dev);
776acf3998SAndy Shevchenko 
78*2ebc36b9SSerge Semin 	ret = data->probe(chip);
79*2ebc36b9SSerge Semin 	if (ret)
80a15636e8SAndy Shevchenko 		goto err_dw_dma_probe;
81a15636e8SAndy Shevchenko 
82b3757413SAndy Shevchenko 	platform_set_drvdata(pdev, data);
839cade1a4SAndy Shevchenko 
84f5e84eaeSAndy Shevchenko 	dw_dma_of_controller_register(chip->dw);
859cade1a4SAndy Shevchenko 
869cade1a4SAndy Shevchenko 	dw_dma_acpi_controller_register(chip->dw);
879cade1a4SAndy Shevchenko 
889cade1a4SAndy Shevchenko 	return 0;
89a15636e8SAndy Shevchenko 
90a15636e8SAndy Shevchenko err_dw_dma_probe:
916acf3998SAndy Shevchenko 	pm_runtime_disable(&pdev->dev);
92a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
93*2ebc36b9SSerge Semin 	return ret;
949cade1a4SAndy Shevchenko }
959cade1a4SAndy Shevchenko 
dw_remove(struct platform_device * pdev)9667572bfeSUwe Kleine-König static void dw_remove(struct platform_device *pdev)
979cade1a4SAndy Shevchenko {
98b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev);
99b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
100b3757413SAndy Shevchenko 	int ret;
1019cade1a4SAndy Shevchenko 
102e7b8514eSAndy Shevchenko 	dw_dma_acpi_controller_free(chip->dw);
103e7b8514eSAndy Shevchenko 
104f5e84eaeSAndy Shevchenko 	dw_dma_of_controller_free(chip->dw);
1059cade1a4SAndy Shevchenko 
106b3757413SAndy Shevchenko 	ret = data->remove(chip);
107b3757413SAndy Shevchenko 	if (ret)
108b3757413SAndy Shevchenko 		dev_warn(chip->dev, "can't remove device properly: %d\n", ret);
109b3757413SAndy Shevchenko 
1106acf3998SAndy Shevchenko 	pm_runtime_disable(&pdev->dev);
111a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
1129cade1a4SAndy Shevchenko }
1139cade1a4SAndy Shevchenko 
dw_shutdown(struct platform_device * pdev)1149cade1a4SAndy Shevchenko static void dw_shutdown(struct platform_device *pdev)
1159cade1a4SAndy Shevchenko {
116b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev);
117b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
1189cade1a4SAndy Shevchenko 
11932146588SAndy Shevchenko 	/*
12069da8be9SAndy Shevchenko 	 * We have to call do_dw_dma_disable() to stop any ongoing transfer. On
12132146588SAndy Shevchenko 	 * some platforms we can't do that since DMA device is powered off.
12232146588SAndy Shevchenko 	 * Moreover we have no possibility to check if the platform is affected
12332146588SAndy Shevchenko 	 * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
12432146588SAndy Shevchenko 	 * unconditionally. On the other hand we can't use
12532146588SAndy Shevchenko 	 * pm_runtime_suspended() because runtime PM framework is not fully
12632146588SAndy Shevchenko 	 * used by the driver.
12732146588SAndy Shevchenko 	 */
12832146588SAndy Shevchenko 	pm_runtime_get_sync(chip->dev);
12969da8be9SAndy Shevchenko 	do_dw_dma_disable(chip);
13032146588SAndy Shevchenko 	pm_runtime_put_sync_suspend(chip->dev);
13132146588SAndy Shevchenko 
132a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
1339cade1a4SAndy Shevchenko }
1349cade1a4SAndy Shevchenko 
1359cade1a4SAndy Shevchenko #ifdef CONFIG_OF
1369cade1a4SAndy Shevchenko static const struct of_device_id dw_dma_of_id_table[] = {
137b3757413SAndy Shevchenko 	{ .compatible = "snps,dma-spear1340", .data = &dw_dma_chip_pdata },
138d5a8fe0fSMiquel Raynal 	{ .compatible = "renesas,rzn1-dma", .data = &dw_dma_chip_pdata },
1399cade1a4SAndy Shevchenko 	{}
1409cade1a4SAndy Shevchenko };
1419cade1a4SAndy Shevchenko MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
1429cade1a4SAndy Shevchenko #endif
1439cade1a4SAndy Shevchenko 
1449cade1a4SAndy Shevchenko #ifdef CONFIG_ACPI
1459cade1a4SAndy Shevchenko static const struct acpi_device_id dw_dma_acpi_id_table[] = {
146b3757413SAndy Shevchenko 	{ "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata },
147b3757413SAndy Shevchenko 	{ "80862286", (kernel_ulong_t)&dw_dma_chip_pdata },
148b3757413SAndy Shevchenko 	{ "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata },
149f8d9ddbcSAndy Shevchenko 
150f8d9ddbcSAndy Shevchenko 	/* Elkhart Lake iDMA 32-bit (PSE DMA) */
151fe364a7dSAndy Shevchenko 	{ "80864BB4", (kernel_ulong_t)&xbar_chip_pdata },
152fe364a7dSAndy Shevchenko 	{ "80864BB5", (kernel_ulong_t)&xbar_chip_pdata },
153fe364a7dSAndy Shevchenko 	{ "80864BB6", (kernel_ulong_t)&xbar_chip_pdata },
154f8d9ddbcSAndy Shevchenko 
1559cade1a4SAndy Shevchenko 	{ }
1569cade1a4SAndy Shevchenko };
157be480dcbSAndy Shevchenko MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
1589cade1a4SAndy Shevchenko #endif
1599cade1a4SAndy Shevchenko 
1609cade1a4SAndy Shevchenko #ifdef CONFIG_PM_SLEEP
1619cade1a4SAndy Shevchenko 
dw_suspend_late(struct device * dev)162067bd4fdSAndy Shevchenko static int dw_suspend_late(struct device *dev)
1639cade1a4SAndy Shevchenko {
164b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
165b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
1669cade1a4SAndy Shevchenko 
16769da8be9SAndy Shevchenko 	do_dw_dma_disable(chip);
168a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
169a15636e8SAndy Shevchenko 
170a15636e8SAndy Shevchenko 	return 0;
1719cade1a4SAndy Shevchenko }
1729cade1a4SAndy Shevchenko 
dw_resume_early(struct device * dev)173067bd4fdSAndy Shevchenko static int dw_resume_early(struct device *dev)
1749cade1a4SAndy Shevchenko {
175b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
176b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
177702fce05SArvind Yadav 	int ret;
1789cade1a4SAndy Shevchenko 
179702fce05SArvind Yadav 	ret = clk_prepare_enable(chip->clk);
180702fce05SArvind Yadav 	if (ret)
181702fce05SArvind Yadav 		return ret;
182702fce05SArvind Yadav 
18369da8be9SAndy Shevchenko 	return do_dw_dma_enable(chip);
1849cade1a4SAndy Shevchenko }
1859cade1a4SAndy Shevchenko 
186067bd4fdSAndy Shevchenko #endif /* CONFIG_PM_SLEEP */
1879cade1a4SAndy Shevchenko 
1889cade1a4SAndy Shevchenko static const struct dev_pm_ops dw_dev_pm_ops = {
189067bd4fdSAndy Shevchenko 	SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early)
1909cade1a4SAndy Shevchenko };
1919cade1a4SAndy Shevchenko 
1929cade1a4SAndy Shevchenko static struct platform_driver dw_driver = {
1939cade1a4SAndy Shevchenko 	.probe		= dw_probe,
19467572bfeSUwe Kleine-König 	.remove_new	= dw_remove,
1959cade1a4SAndy Shevchenko 	.shutdown       = dw_shutdown,
1969cade1a4SAndy Shevchenko 	.driver = {
197a104a45bSAndy Shevchenko 		.name	= DRV_NAME,
1989cade1a4SAndy Shevchenko 		.pm	= &dw_dev_pm_ops,
1999cade1a4SAndy Shevchenko 		.of_match_table = of_match_ptr(dw_dma_of_id_table),
2009cade1a4SAndy Shevchenko 		.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
2019cade1a4SAndy Shevchenko 	},
2029cade1a4SAndy Shevchenko };
2039cade1a4SAndy Shevchenko 
dw_init(void)2049cade1a4SAndy Shevchenko static int __init dw_init(void)
2059cade1a4SAndy Shevchenko {
2069cade1a4SAndy Shevchenko 	return platform_driver_register(&dw_driver);
2079cade1a4SAndy Shevchenko }
2089cade1a4SAndy Shevchenko subsys_initcall(dw_init);
2099cade1a4SAndy Shevchenko 
dw_exit(void)2109cade1a4SAndy Shevchenko static void __exit dw_exit(void)
2119cade1a4SAndy Shevchenko {
2129cade1a4SAndy Shevchenko 	platform_driver_unregister(&dw_driver);
2139cade1a4SAndy Shevchenko }
2149cade1a4SAndy Shevchenko module_exit(dw_exit);
2159cade1a4SAndy Shevchenko 
2169cade1a4SAndy Shevchenko MODULE_LICENSE("GPL v2");
2179cade1a4SAndy Shevchenko MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");
218a104a45bSAndy Shevchenko MODULE_ALIAS("platform:" DRV_NAME);
219