| 1 | |
| 2 | |
| 3 | * Platform driver for the Synopsys DesignWare DMA Controller
|
| 4 | *
|
| 5 | * Copyright (C) 2007-2008 Atmel Corporation
|
| 6 | * Copyright (C) 2010-2011 ST Microelectronics
|
| 7 | * Copyright (C) 2013 Intel Corporation
|
| 8 | *
|
| 9 | * Some parts of this driver are derived from the original dw_dmac.
|
| 10 |
|
| 11 |
|
| 12 | #include <linux/module.h>
|
| 13 | #include <linux/device.h>
|
| 14 | #include <linux/clk.h>
|
| 15 | #include <linux/pm_runtime.h>
|
| 16 | #include <linux/platform_device.h>
|
| 17 | #include <linux/dmaengine.h>
|
| 18 | #include <linux/dma-mapping.h>
|
| 19 | #include <linux/of.h>
|
| 20 | #include <linux/acpi.h>
|
| 21 |
|
| 22 | #include "internal.h"
|
| 23 |
|
| 24 | static int dw_probe(struct platform_device *pdev)
|
| 25 | {
|
| 26 | const struct dw_dma_chip_pdata *match;
|
| 27 | struct dw_dma_chip_pdata *data;
|
| 28 | struct dw_dma_chip *chip;
|
| 29 | struct device *dev = &pdev->dev;
|
| 30 | int ret;
|
| 31 |
|
| 32 | match = device_get_match_data(dev);
|
| 33 | if (!match)
|
| 34 | return -ENODEV;
|
| 35 |
|
| 36 | data = devm_kmemdup(&pdev->dev, match, sizeof(*match), GFP_KERNEL);
|
| 37 | if (!data)
|
| 38 | return -ENOMEM;
|
| 39 |
|
| 40 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
| 41 | if (!chip)
|
| 42 | return -ENOMEM;
|
| 43 |
|
| 44 | chip->irq = platform_get_irq(pdev, 0);
|
| 45 | if (chip->irq < 0)
|
| 46 | return chip->irq;
|
| 47 |
|
| 48 | chip->regs = devm_platform_ioremap_resource(pdev, 0);
|
| 49 | if (IS_ERR(chip->regs))
|
| 50 | return PTR_ERR(chip->regs);
|
| 51 |
|
| 52 | ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
| 53 | if (ret)
|
| 54 | return ret;
|
| 55 |
|
| 56 | if (!data->pdata)
|
| 57 | data->pdata = dev_get_platdata(dev);
|
| 58 | if (!data->pdata)
|
| 59 | data->pdata = dw_dma_parse_dt(pdev);
|
| 60 |
|
| 61 | chip->dev = dev;
|
| 62 | chip->id = pdev->id;
|
| 63 | chip->pdata = data->pdata;
|
| 64 |
|
| 65 | data->chip = chip;
|
| 66 |
|
| 67 | chip->clk = devm_clk_get_optional(chip->dev, "hclk");
|
| 68 | if (IS_ERR(chip->clk))
|
| 69 | return PTR_ERR(chip->clk);
|
| 70 | ret = clk_prepare_enable(chip->clk);
|
| 71 | if (ret)
|
| 72 | return ret;
|
| 73 |
|
| 74 | pm_runtime_enable(&pdev->dev);
|
| 75 |
|
| 76 | ret = data->probe(chip);
|
| 77 | if (ret)
|
| 78 | goto err_dw_dma_probe;
|
| 79 |
|
| 80 | platform_set_drvdata(pdev, data);
|
| 81 |
|
| 82 | dw_dma_of_controller_register(chip->dw);
|
| 83 |
|
| 84 | dw_dma_acpi_controller_register(chip->dw);
|
| 85 |
|
| 86 | return 0;
|
| 87 |
|
| 88 | err_dw_dma_probe:
|
| 89 | pm_runtime_disable(&pdev->dev);
|
| 90 | clk_disable_unprepare(chip->clk);
|
| 91 | return ret;
|
| 92 | }
|
| 93 |
|
| 94 | static void dw_remove(struct platform_device *pdev)
|
| 95 | {
|
| 96 | struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev);
|
| 97 | struct dw_dma_chip *chip = data->chip;
|
| 98 | int ret;
|
| 99 |
|
| 100 | dw_dma_acpi_controller_free(chip->dw);
|
| 101 |
|
| 102 | dw_dma_of_controller_free(chip->dw);
|
| 103 |
|
| 104 | ret = data->remove(chip);
|
| 105 | if (ret)
|
| 106 | dev_warn(chip->dev, "can't remove device properly: %d\n", ret);
|
| 107 |
|
| 108 | pm_runtime_disable(&pdev->dev);
|
| 109 | clk_disable_unprepare(chip->clk);
|
| 110 | }
|
| 111 |
|
| 112 | static void dw_shutdown(struct platform_device *pdev)
|
| 113 | {
|
| 114 | struct dw_dma_chip_pdata *data = platform_get_drvdata(pdev);
|
| 115 | struct dw_dma_chip *chip = data->chip;
|
| 116 |
|
| 117 | |
| 118 | * We have to call do_dw_dma_disable() to stop any ongoing transfer. On
|
| 119 | * some platforms we can't do that since DMA device is powered off.
|
| 120 | * Moreover we have no possibility to check if the platform is affected
|
| 121 | * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
|
| 122 | * unconditionally. On the other hand we can't use
|
| 123 | * pm_runtime_suspended() because runtime PM framework is not fully
|
| 124 | * used by the driver.
|
| 125 |
|
| 126 | pm_runtime_get_sync(chip->dev);
|
| 127 | do_dw_dma_disable(chip);
|
| 128 | pm_runtime_put_sync_suspend(chip->dev);
|
| 129 |
|
| 130 | clk_disable_unprepare(chip->clk);
|
| 131 | }
|
| 132 |
|
| 133 | #ifdef CONFIG_OF
|
| 134 | static const struct of_device_id dw_dma_of_id_table[] = {
|
| 135 | { .compatible = "snps,dma-spear1340", .data = &dw_dma_chip_pdata },
|
| 136 | { .compatible = "renesas,rzn1-dma", .data = &dw_dma_chip_pdata },
|
| 137 | {}
|
| 138 | };
|
| 139 | MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
|
| 140 | #endif
|
| 141 |
|
| 142 | #ifdef CONFIG_ACPI
|
| 143 | static const struct acpi_device_id dw_dma_acpi_id_table[] = {
|
| 144 | { "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata },
|
| 145 | { "80862286", (kernel_ulong_t)&dw_dma_chip_pdata },
|
| 146 | { "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata },
|
| 147 |
|
| 148 |
|
| 149 | { "80864BB4", (kernel_ulong_t)&xbar_chip_pdata },
|
| 150 | { "80864BB5", (kernel_ulong_t)&xbar_chip_pdata },
|
| 151 | { "80864BB6", (kernel_ulong_t)&xbar_chip_pdata },
|
| 152 |
|
| 153 | { }
|
| 154 | };
|
| 155 | MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
|
| 156 | #endif
|
| 157 |
|
| 158 | static int dw_suspend_late(struct device *dev)
|
| 159 | {
|
| 160 | struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
|
| 161 | struct dw_dma_chip *chip = data->chip;
|
| 162 |
|
| 163 | do_dw_dma_disable(chip);
|
| 164 | clk_disable_unprepare(chip->clk);
|
| 165 |
|
| 166 | return 0;
|
| 167 | }
|
| 168 |
|
| 169 | static int dw_resume_early(struct device *dev)
|
| 170 | {
|
| 171 | struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
|
| 172 | struct dw_dma_chip *chip = data->chip;
|
| 173 | int ret;
|
| 174 |
|
| 175 | ret = clk_prepare_enable(chip->clk);
|
| 176 | if (ret)
|
| 177 | return ret;
|
| 178 |
|
| 179 | return do_dw_dma_enable(chip);
|
| 180 | }
|
| 181 |
|
| 182 | static const struct dev_pm_ops dw_dev_pm_ops = {
|
| 183 | LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early)
|
| 184 | };
|
| 185 |
|
| 186 | static struct platform_driver dw_driver = {
|
| 187 | .probe = dw_probe,
|
| 188 | .remove = dw_remove,
|
| 189 | .shutdown = dw_shutdown,
|
| 190 | .driver = {
|
| 191 | .name = "dw_dmac",
|
| 192 | .pm = pm_sleep_ptr(&dw_dev_pm_ops),
|
| 193 | .of_match_table = of_match_ptr(dw_dma_of_id_table),
|
| 194 | .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
|
| 195 | },
|
| 196 | };
|
| 197 |
|
| 198 | static int __init dw_init(void)
|
| 199 | {
|
| 200 | return platform_driver_register(&dw_driver);
|
| 201 | }
|
| 202 | subsys_initcall(dw_init);
|
| 203 |
|
| 204 | static void __exit dw_exit(void)
|
| 205 | {
|
| 206 | platform_driver_unregister(&dw_driver);
|
| 207 | }
|
| 208 | module_exit(dw_exit);
|
| 209 |
|
| 210 | MODULE_LICENSE("GPL v2");
|
| 211 | MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");
|