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