162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Platform driver for the Synopsys DesignWare DMA Controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007-2008 Atmel Corporation 662306a36Sopenharmony_ci * Copyright (C) 2010-2011 ST Microelectronics 762306a36Sopenharmony_ci * Copyright (C) 2013 Intel Corporation 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Some parts of this driver are derived from the original dw_dmac. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/dmaengine.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/acpi.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "internal.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DRV_NAME "dw_dmac" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int dw_probe(struct platform_device *pdev) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci const struct dw_dma_chip_pdata *match; 2962306a36Sopenharmony_ci struct dw_dma_chip_pdata *data; 3062306a36Sopenharmony_ci struct dw_dma_chip *chip; 3162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 3262306a36Sopenharmony_ci int err; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci match = device_get_match_data(dev); 3562306a36Sopenharmony_ci if (!match) 3662306a36Sopenharmony_ci return -ENODEV; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci data = devm_kmemdup(&pdev->dev, match, sizeof(*match), GFP_KERNEL); 3962306a36Sopenharmony_ci if (!data) 4062306a36Sopenharmony_ci return -ENOMEM; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 4362306a36Sopenharmony_ci if (!chip) 4462306a36Sopenharmony_ci return -ENOMEM; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci chip->irq = platform_get_irq(pdev, 0); 4762306a36Sopenharmony_ci if (chip->irq < 0) 4862306a36Sopenharmony_ci return chip->irq; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci chip->regs = devm_platform_ioremap_resource(pdev, 0); 5162306a36Sopenharmony_ci if (IS_ERR(chip->regs)) 5262306a36Sopenharmony_ci return PTR_ERR(chip->regs); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 5562306a36Sopenharmony_ci if (err) 5662306a36Sopenharmony_ci return err; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!data->pdata) 5962306a36Sopenharmony_ci data->pdata = dev_get_platdata(dev); 6062306a36Sopenharmony_ci if (!data->pdata) 6162306a36Sopenharmony_ci data->pdata = dw_dma_parse_dt(pdev); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci chip->dev = dev; 6462306a36Sopenharmony_ci chip->id = pdev->id; 6562306a36Sopenharmony_ci chip->pdata = data->pdata; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci data->chip = chip; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci chip->clk = devm_clk_get_optional(chip->dev, "hclk"); 7062306a36Sopenharmony_ci if (IS_ERR(chip->clk)) 7162306a36Sopenharmony_ci return PTR_ERR(chip->clk); 7262306a36Sopenharmony_ci err = clk_prepare_enable(chip->clk); 7362306a36Sopenharmony_ci if (err) 7462306a36Sopenharmony_ci return err; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci err = data->probe(chip); 7962306a36Sopenharmony_ci if (err) 8062306a36Sopenharmony_ci goto err_dw_dma_probe; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci dw_dma_of_controller_register(chip->dw); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci dw_dma_acpi_controller_register(chip->dw); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cierr_dw_dma_probe: 9162306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 9262306a36Sopenharmony_ci clk_disable_unprepare(chip->clk); 9362306a36Sopenharmony_ci return err; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int dw_remove(struct platform_device *pdev) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev); 9962306a36Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 10062306a36Sopenharmony_ci int ret; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci dw_dma_acpi_controller_free(chip->dw); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci dw_dma_of_controller_free(chip->dw); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ret = data->remove(chip); 10762306a36Sopenharmony_ci if (ret) 10862306a36Sopenharmony_ci dev_warn(chip->dev, "can't remove device properly: %d\n", ret); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 11162306a36Sopenharmony_ci clk_disable_unprepare(chip->clk); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void dw_shutdown(struct platform_device *pdev) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev); 11962306a36Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * We have to call do_dw_dma_disable() to stop any ongoing transfer. On 12362306a36Sopenharmony_ci * some platforms we can't do that since DMA device is powered off. 12462306a36Sopenharmony_ci * Moreover we have no possibility to check if the platform is affected 12562306a36Sopenharmony_ci * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() 12662306a36Sopenharmony_ci * unconditionally. On the other hand we can't use 12762306a36Sopenharmony_ci * pm_runtime_suspended() because runtime PM framework is not fully 12862306a36Sopenharmony_ci * used by the driver. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci pm_runtime_get_sync(chip->dev); 13162306a36Sopenharmony_ci do_dw_dma_disable(chip); 13262306a36Sopenharmony_ci pm_runtime_put_sync_suspend(chip->dev); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci clk_disable_unprepare(chip->clk); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#ifdef CONFIG_OF 13862306a36Sopenharmony_cistatic const struct of_device_id dw_dma_of_id_table[] = { 13962306a36Sopenharmony_ci { .compatible = "snps,dma-spear1340", .data = &dw_dma_chip_pdata }, 14062306a36Sopenharmony_ci { .compatible = "renesas,rzn1-dma", .data = &dw_dma_chip_pdata }, 14162306a36Sopenharmony_ci {} 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw_dma_of_id_table); 14462306a36Sopenharmony_ci#endif 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#ifdef CONFIG_ACPI 14762306a36Sopenharmony_cistatic const struct acpi_device_id dw_dma_acpi_id_table[] = { 14862306a36Sopenharmony_ci { "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata }, 14962306a36Sopenharmony_ci { "80862286", (kernel_ulong_t)&dw_dma_chip_pdata }, 15062306a36Sopenharmony_ci { "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata }, 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Elkhart Lake iDMA 32-bit (PSE DMA) */ 15362306a36Sopenharmony_ci { "80864BB4", (kernel_ulong_t)&xbar_chip_pdata }, 15462306a36Sopenharmony_ci { "80864BB5", (kernel_ulong_t)&xbar_chip_pdata }, 15562306a36Sopenharmony_ci { "80864BB6", (kernel_ulong_t)&xbar_chip_pdata }, 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci { } 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table); 16062306a36Sopenharmony_ci#endif 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int dw_suspend_late(struct device *dev) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct dw_dma_chip_pdata *data = dev_get_drvdata(dev); 16762306a36Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci do_dw_dma_disable(chip); 17062306a36Sopenharmony_ci clk_disable_unprepare(chip->clk); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int dw_resume_early(struct device *dev) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct dw_dma_chip_pdata *data = dev_get_drvdata(dev); 17862306a36Sopenharmony_ci struct dw_dma_chip *chip = data->chip; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = clk_prepare_enable(chip->clk); 18262306a36Sopenharmony_ci if (ret) 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return do_dw_dma_enable(chip); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct dev_pm_ops dw_dev_pm_ops = { 19162306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early) 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic struct platform_driver dw_driver = { 19562306a36Sopenharmony_ci .probe = dw_probe, 19662306a36Sopenharmony_ci .remove = dw_remove, 19762306a36Sopenharmony_ci .shutdown = dw_shutdown, 19862306a36Sopenharmony_ci .driver = { 19962306a36Sopenharmony_ci .name = DRV_NAME, 20062306a36Sopenharmony_ci .pm = &dw_dev_pm_ops, 20162306a36Sopenharmony_ci .of_match_table = of_match_ptr(dw_dma_of_id_table), 20262306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int __init dw_init(void) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci return platform_driver_register(&dw_driver); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_cisubsys_initcall(dw_init); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void __exit dw_exit(void) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci platform_driver_unregister(&dw_driver); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_cimodule_exit(dw_exit); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21962306a36Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver"); 22062306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 221