18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Platform driver for the Synopsys DesignWare DMA Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Atmel Corporation 68c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 ST Microelectronics 78c2ecf20Sopenharmony_ci * Copyright (C) 2013 Intel Corporation 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Some parts of this driver are derived from the original dw_dmac. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/acpi.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "internal.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRV_NAME "dw_dmac" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int dw_probe(struct platform_device *pdev) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci const struct dw_dma_chip_pdata *match; 298c2ecf20Sopenharmony_ci struct dw_dma_chip_pdata *data; 308c2ecf20Sopenharmony_ci struct dw_dma_chip *chip; 318c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 328c2ecf20Sopenharmony_ci int err; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci match = device_get_match_data(dev); 358c2ecf20Sopenharmony_ci if (!match) 368c2ecf20Sopenharmony_ci return -ENODEV; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci data = devm_kmemdup(&pdev->dev, match, sizeof(*match), GFP_KERNEL); 398c2ecf20Sopenharmony_ci if (!data) 408c2ecf20Sopenharmony_ci return -ENOMEM; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 438c2ecf20Sopenharmony_ci if (!chip) 448c2ecf20Sopenharmony_ci return -ENOMEM; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci chip->irq = platform_get_irq(pdev, 0); 478c2ecf20Sopenharmony_ci if (chip->irq < 0) 488c2ecf20Sopenharmony_ci return chip->irq; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci chip->regs = devm_platform_ioremap_resource(pdev, 0); 518c2ecf20Sopenharmony_ci if (IS_ERR(chip->regs)) 528c2ecf20Sopenharmony_ci return PTR_ERR(chip->regs); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 558c2ecf20Sopenharmony_ci if (err) 568c2ecf20Sopenharmony_ci return err; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (!data->pdata) 598c2ecf20Sopenharmony_ci data->pdata = dev_get_platdata(dev); 608c2ecf20Sopenharmony_ci if (!data->pdata) 618c2ecf20Sopenharmony_ci data->pdata = dw_dma_parse_dt(pdev); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci chip->dev = dev; 648c2ecf20Sopenharmony_ci chip->id = pdev->id; 658c2ecf20Sopenharmony_ci chip->pdata = data->pdata; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci data->chip = chip; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci chip->clk = devm_clk_get_optional(chip->dev, "hclk"); 708c2ecf20Sopenharmony_ci if (IS_ERR(chip->clk)) 718c2ecf20Sopenharmony_ci return PTR_ERR(chip->clk); 728c2ecf20Sopenharmony_ci err = clk_prepare_enable(chip->clk); 738c2ecf20Sopenharmony_ci if (err) 748c2ecf20Sopenharmony_ci return err; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci err = data->probe(chip); 798c2ecf20Sopenharmony_ci if (err) 808c2ecf20Sopenharmony_ci goto err_dw_dma_probe; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dw_dma_of_controller_register(chip->dw); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci dw_dma_acpi_controller_register(chip->dw); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cierr_dw_dma_probe: 918c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 928c2ecf20Sopenharmony_ci clk_disable_unprepare(chip->clk); 938c2ecf20Sopenharmony_ci return err; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int dw_remove(struct platform_device *pdev) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev); 998c2ecf20Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 1008c2ecf20Sopenharmony_ci int ret; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci dw_dma_acpi_controller_free(chip->dw); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci dw_dma_of_controller_free(chip->dw); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = data->remove(chip); 1078c2ecf20Sopenharmony_ci if (ret) 1088c2ecf20Sopenharmony_ci dev_warn(chip->dev, "can't remove device properly: %d\n", ret); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 1118c2ecf20Sopenharmony_ci clk_disable_unprepare(chip->clk); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void dw_shutdown(struct platform_device *pdev) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev); 1198c2ecf20Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * We have to call do_dw_dma_disable() to stop any ongoing transfer. On 1238c2ecf20Sopenharmony_ci * some platforms we can't do that since DMA device is powered off. 1248c2ecf20Sopenharmony_ci * Moreover we have no possibility to check if the platform is affected 1258c2ecf20Sopenharmony_ci * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() 1268c2ecf20Sopenharmony_ci * unconditionally. On the other hand we can't use 1278c2ecf20Sopenharmony_ci * pm_runtime_suspended() because runtime PM framework is not fully 1288c2ecf20Sopenharmony_ci * used by the driver. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci pm_runtime_get_sync(chip->dev); 1318c2ecf20Sopenharmony_ci do_dw_dma_disable(chip); 1328c2ecf20Sopenharmony_ci pm_runtime_put_sync_suspend(chip->dev); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci clk_disable_unprepare(chip->clk); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1388c2ecf20Sopenharmony_cistatic const struct of_device_id dw_dma_of_id_table[] = { 1398c2ecf20Sopenharmony_ci { .compatible = "snps,dma-spear1340", .data = &dw_dma_chip_pdata }, 1408c2ecf20Sopenharmony_ci {} 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw_dma_of_id_table); 1438c2ecf20Sopenharmony_ci#endif 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 1468c2ecf20Sopenharmony_cistatic const struct acpi_device_id dw_dma_acpi_id_table[] = { 1478c2ecf20Sopenharmony_ci { "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata }, 1488c2ecf20Sopenharmony_ci { "80862286", (kernel_ulong_t)&dw_dma_chip_pdata }, 1498c2ecf20Sopenharmony_ci { "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata }, 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Elkhart Lake iDMA 32-bit (PSE DMA) */ 1528c2ecf20Sopenharmony_ci { "80864BB4", (kernel_ulong_t)&idma32_chip_pdata }, 1538c2ecf20Sopenharmony_ci { "80864BB5", (kernel_ulong_t)&idma32_chip_pdata }, 1548c2ecf20Sopenharmony_ci { "80864BB6", (kernel_ulong_t)&idma32_chip_pdata }, 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci { } 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table); 1598c2ecf20Sopenharmony_ci#endif 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int dw_suspend_late(struct device *dev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct dw_dma_chip_pdata *data = dev_get_drvdata(dev); 1668c2ecf20Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci do_dw_dma_disable(chip); 1698c2ecf20Sopenharmony_ci clk_disable_unprepare(chip->clk); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int dw_resume_early(struct device *dev) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct dw_dma_chip_pdata *data = dev_get_drvdata(dev); 1778c2ecf20Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = clk_prepare_enable(chip->clk); 1818c2ecf20Sopenharmony_ci if (ret) 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return do_dw_dma_enable(chip); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dw_dev_pm_ops = { 1908c2ecf20Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early) 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic struct platform_driver dw_driver = { 1948c2ecf20Sopenharmony_ci .probe = dw_probe, 1958c2ecf20Sopenharmony_ci .remove = dw_remove, 1968c2ecf20Sopenharmony_ci .shutdown = dw_shutdown, 1978c2ecf20Sopenharmony_ci .driver = { 1988c2ecf20Sopenharmony_ci .name = DRV_NAME, 1998c2ecf20Sopenharmony_ci .pm = &dw_dev_pm_ops, 2008c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(dw_dma_of_id_table), 2018c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), 2028c2ecf20Sopenharmony_ci }, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int __init dw_init(void) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci return platform_driver_register(&dw_driver); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_cisubsys_initcall(dw_init); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void __exit dw_exit(void) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci platform_driver_unregister(&dw_driver); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_cimodule_exit(dw_exit); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver"); 2198c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 220