xref: /linux/drivers/dma/dw/platform.c (revision fe364a7d95c24e07e9b3f2ab917f01d6d8330bba)
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 },
1409cade1a4SAndy Shevchenko 	{}
1419cade1a4SAndy Shevchenko };
1429cade1a4SAndy Shevchenko MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
1439cade1a4SAndy Shevchenko #endif
1449cade1a4SAndy Shevchenko 
1459cade1a4SAndy Shevchenko #ifdef CONFIG_ACPI
1469cade1a4SAndy Shevchenko static const struct acpi_device_id dw_dma_acpi_id_table[] = {
147b3757413SAndy Shevchenko 	{ "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata },
148b3757413SAndy Shevchenko 	{ "80862286", (kernel_ulong_t)&dw_dma_chip_pdata },
149b3757413SAndy Shevchenko 	{ "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata },
150f8d9ddbcSAndy Shevchenko 
151f8d9ddbcSAndy Shevchenko 	/* Elkhart Lake iDMA 32-bit (PSE DMA) */
152*fe364a7dSAndy Shevchenko 	{ "80864BB4", (kernel_ulong_t)&xbar_chip_pdata },
153*fe364a7dSAndy Shevchenko 	{ "80864BB5", (kernel_ulong_t)&xbar_chip_pdata },
154*fe364a7dSAndy Shevchenko 	{ "80864BB6", (kernel_ulong_t)&xbar_chip_pdata },
155f8d9ddbcSAndy Shevchenko 
1569cade1a4SAndy Shevchenko 	{ }
1579cade1a4SAndy Shevchenko };
158be480dcbSAndy Shevchenko MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
1599cade1a4SAndy Shevchenko #endif
1609cade1a4SAndy Shevchenko 
1619cade1a4SAndy Shevchenko #ifdef CONFIG_PM_SLEEP
1629cade1a4SAndy Shevchenko 
163067bd4fdSAndy Shevchenko static int dw_suspend_late(struct device *dev)
1649cade1a4SAndy Shevchenko {
165b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
166b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
1679cade1a4SAndy Shevchenko 
16869da8be9SAndy Shevchenko 	do_dw_dma_disable(chip);
169a15636e8SAndy Shevchenko 	clk_disable_unprepare(chip->clk);
170a15636e8SAndy Shevchenko 
171a15636e8SAndy Shevchenko 	return 0;
1729cade1a4SAndy Shevchenko }
1739cade1a4SAndy Shevchenko 
174067bd4fdSAndy Shevchenko static int dw_resume_early(struct device *dev)
1759cade1a4SAndy Shevchenko {
176b3757413SAndy Shevchenko 	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
177b3757413SAndy Shevchenko 	struct dw_dma_chip *chip = data->chip;
178702fce05SArvind Yadav 	int ret;
1799cade1a4SAndy Shevchenko 
180702fce05SArvind Yadav 	ret = clk_prepare_enable(chip->clk);
181702fce05SArvind Yadav 	if (ret)
182702fce05SArvind Yadav 		return ret;
183702fce05SArvind Yadav 
18469da8be9SAndy Shevchenko 	return do_dw_dma_enable(chip);
1859cade1a4SAndy Shevchenko }
1869cade1a4SAndy Shevchenko 
187067bd4fdSAndy Shevchenko #endif /* CONFIG_PM_SLEEP */
1889cade1a4SAndy Shevchenko 
1899cade1a4SAndy Shevchenko static const struct dev_pm_ops dw_dev_pm_ops = {
190067bd4fdSAndy Shevchenko 	SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early)
1919cade1a4SAndy Shevchenko };
1929cade1a4SAndy Shevchenko 
1939cade1a4SAndy Shevchenko static struct platform_driver dw_driver = {
1949cade1a4SAndy Shevchenko 	.probe		= dw_probe,
1959cade1a4SAndy Shevchenko 	.remove		= dw_remove,
1969cade1a4SAndy Shevchenko 	.shutdown       = dw_shutdown,
1979cade1a4SAndy Shevchenko 	.driver = {
198a104a45bSAndy Shevchenko 		.name	= DRV_NAME,
1999cade1a4SAndy Shevchenko 		.pm	= &dw_dev_pm_ops,
2009cade1a4SAndy Shevchenko 		.of_match_table = of_match_ptr(dw_dma_of_id_table),
2019cade1a4SAndy Shevchenko 		.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
2029cade1a4SAndy Shevchenko 	},
2039cade1a4SAndy Shevchenko };
2049cade1a4SAndy Shevchenko 
2059cade1a4SAndy Shevchenko static int __init dw_init(void)
2069cade1a4SAndy Shevchenko {
2079cade1a4SAndy Shevchenko 	return platform_driver_register(&dw_driver);
2089cade1a4SAndy Shevchenko }
2099cade1a4SAndy Shevchenko subsys_initcall(dw_init);
2109cade1a4SAndy Shevchenko 
2119cade1a4SAndy Shevchenko static void __exit dw_exit(void)
2129cade1a4SAndy Shevchenko {
2139cade1a4SAndy Shevchenko 	platform_driver_unregister(&dw_driver);
2149cade1a4SAndy Shevchenko }
2159cade1a4SAndy Shevchenko module_exit(dw_exit);
2169cade1a4SAndy Shevchenko 
2179cade1a4SAndy Shevchenko MODULE_LICENSE("GPL v2");
2189cade1a4SAndy Shevchenko MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");
219a104a45bSAndy Shevchenko MODULE_ALIAS("platform:" DRV_NAME);
220