xref: /linux/drivers/dma/dw/platform.c (revision d5a8fe0fee54d830c47959f625ffc41d080ee526)
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 
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;
329cade1a4SAndy Shevchenko 	int err;
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 
5424353b8bSRussell King 	err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
5524353b8bSRussell King 	if (err)
5624353b8bSRussell King 		return err;
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);
72a15636e8SAndy Shevchenko 	err = clk_prepare_enable(chip->clk);
739cade1a4SAndy Shevchenko 	if (err)
749cade1a4SAndy Shevchenko 		return err;
759cade1a4SAndy Shevchenko 
766acf3998SAndy Shevchenko 	pm_runtime_enable(&pdev->dev);
776acf3998SAndy Shevchenko 
78b3757413SAndy Shevchenko 	err = data->probe(chip);
79a15636e8SAndy Shevchenko 	if (err)
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);
93a15636e8SAndy Shevchenko 	return err;
949cade1a4SAndy Shevchenko }
959cade1a4SAndy Shevchenko 
969cade1a4SAndy Shevchenko static int 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);
112a15636e8SAndy Shevchenko 
113a15636e8SAndy Shevchenko 	return 0;
1149cade1a4SAndy Shevchenko }
1159cade1a4SAndy Shevchenko 
1169cade1a4SAndy Shevchenko static void dw_shutdown(struct platform_device *pdev)
1179cade1a4SAndy Shevchenko {
118b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev);
119b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
1209cade1a4SAndy Shevchenko 
12132146588SAndy Shevchenko 	/*
12269da8be9SAndy Shevchenko 	 * We have to call do_dw_dma_disable() to stop any ongoing transfer. On
12332146588SAndy Shevchenko 	 * some platforms we can't do that since DMA device is powered off.
12432146588SAndy Shevchenko 	 * Moreover we have no possibility to check if the platform is affected
12532146588SAndy Shevchenko 	 * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
12632146588SAndy Shevchenko 	 * unconditionally. On the other hand we can't use
12732146588SAndy Shevchenko 	 * pm_runtime_suspended() because runtime PM framework is not fully
12832146588SAndy Shevchenko 	 * used by the driver.
12932146588SAndy Shevchenko 	 */
13032146588SAndy Shevchenko 	pm_runtime_get_sync(chip->dev);
13169da8be9SAndy Shevchenko 	do_dw_dma_disable(chip);
13232146588SAndy Shevchenko 	pm_runtime_put_sync_suspend(chip->dev);
13332146588SAndy Shevchenko 
134a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
1359cade1a4SAndy Shevchenko }
1369cade1a4SAndy Shevchenko 
1379cade1a4SAndy Shevchenko #ifdef CONFIG_OF
1389cade1a4SAndy Shevchenko static const struct of_device_id dw_dma_of_id_table[] = {
139b3757413SAndy Shevchenko 	{ .compatible = "snps,dma-spear1340", .data = &dw_dma_chip_pdata },
140*d5a8fe0fSMiquel Raynal 	{ .compatible = "renesas,rzn1-dma", .data = &dw_dma_chip_pdata },
1419cade1a4SAndy Shevchenko 	{}
1429cade1a4SAndy Shevchenko };
1439cade1a4SAndy Shevchenko MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
1449cade1a4SAndy Shevchenko #endif
1459cade1a4SAndy Shevchenko 
1469cade1a4SAndy Shevchenko #ifdef CONFIG_ACPI
1479cade1a4SAndy Shevchenko static const struct acpi_device_id dw_dma_acpi_id_table[] = {
148b3757413SAndy Shevchenko 	{ "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata },
149b3757413SAndy Shevchenko 	{ "80862286", (kernel_ulong_t)&dw_dma_chip_pdata },
150b3757413SAndy Shevchenko 	{ "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata },
151f8d9ddbcSAndy Shevchenko 
152f8d9ddbcSAndy Shevchenko 	/* Elkhart Lake iDMA 32-bit (PSE DMA) */
153fe364a7dSAndy Shevchenko 	{ "80864BB4", (kernel_ulong_t)&xbar_chip_pdata },
154fe364a7dSAndy Shevchenko 	{ "80864BB5", (kernel_ulong_t)&xbar_chip_pdata },
155fe364a7dSAndy Shevchenko 	{ "80864BB6", (kernel_ulong_t)&xbar_chip_pdata },
156f8d9ddbcSAndy Shevchenko 
1579cade1a4SAndy Shevchenko 	{ }
1589cade1a4SAndy Shevchenko };
159be480dcbSAndy Shevchenko MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
1609cade1a4SAndy Shevchenko #endif
1619cade1a4SAndy Shevchenko 
1629cade1a4SAndy Shevchenko #ifdef CONFIG_PM_SLEEP
1639cade1a4SAndy Shevchenko 
164067bd4fdSAndy Shevchenko static int dw_suspend_late(struct device *dev)
1659cade1a4SAndy Shevchenko {
166b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
167b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
1689cade1a4SAndy Shevchenko 
16969da8be9SAndy Shevchenko 	do_dw_dma_disable(chip);
170a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
171a15636e8SAndy Shevchenko 
172a15636e8SAndy Shevchenko 	return 0;
1739cade1a4SAndy Shevchenko }
1749cade1a4SAndy Shevchenko 
175067bd4fdSAndy Shevchenko static int dw_resume_early(struct device *dev)
1769cade1a4SAndy Shevchenko {
177b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
178b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
179702fce05SArvind Yadav 	int ret;
1809cade1a4SAndy Shevchenko 
181702fce05SArvind Yadav 	ret = clk_prepare_enable(chip->clk);
182702fce05SArvind Yadav 	if (ret)
183702fce05SArvind Yadav 		return ret;
184702fce05SArvind Yadav 
18569da8be9SAndy Shevchenko 	return do_dw_dma_enable(chip);
1869cade1a4SAndy Shevchenko }
1879cade1a4SAndy Shevchenko 
188067bd4fdSAndy Shevchenko #endif /* CONFIG_PM_SLEEP */
1899cade1a4SAndy Shevchenko 
1909cade1a4SAndy Shevchenko static const struct dev_pm_ops dw_dev_pm_ops = {
191067bd4fdSAndy Shevchenko 	SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early)
1929cade1a4SAndy Shevchenko };
1939cade1a4SAndy Shevchenko 
1949cade1a4SAndy Shevchenko static struct platform_driver dw_driver = {
1959cade1a4SAndy Shevchenko 	.probe		= dw_probe,
1969cade1a4SAndy Shevchenko 	.remove		= dw_remove,
1979cade1a4SAndy Shevchenko 	.shutdown       = dw_shutdown,
1989cade1a4SAndy Shevchenko 	.driver = {
199a104a45bSAndy Shevchenko 		.name	= DRV_NAME,
2009cade1a4SAndy Shevchenko 		.pm	= &dw_dev_pm_ops,
2019cade1a4SAndy Shevchenko 		.of_match_table = of_match_ptr(dw_dma_of_id_table),
2029cade1a4SAndy Shevchenko 		.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
2039cade1a4SAndy Shevchenko 	},
2049cade1a4SAndy Shevchenko };
2059cade1a4SAndy Shevchenko 
2069cade1a4SAndy Shevchenko static int __init dw_init(void)
2079cade1a4SAndy Shevchenko {
2089cade1a4SAndy Shevchenko 	return platform_driver_register(&dw_driver);
2099cade1a4SAndy Shevchenko }
2109cade1a4SAndy Shevchenko subsys_initcall(dw_init);
2119cade1a4SAndy Shevchenko 
2129cade1a4SAndy Shevchenko static void __exit dw_exit(void)
2139cade1a4SAndy Shevchenko {
2149cade1a4SAndy Shevchenko 	platform_driver_unregister(&dw_driver);
2159cade1a4SAndy Shevchenko }
2169cade1a4SAndy Shevchenko module_exit(dw_exit);
2179cade1a4SAndy Shevchenko 
2189cade1a4SAndy Shevchenko MODULE_LICENSE("GPL v2");
2199cade1a4SAndy Shevchenko MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");
220a104a45bSAndy Shevchenko MODULE_ALIAS("platform:" DRV_NAME);
221