platform.c 7.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Platform driver for the Synopsys DesignWare DMA Controller
 *
 * Copyright (C) 2007-2008 Atmel Corporation
 * Copyright (C) 2010-2011 ST Microelectronics
 * Copyright (C) 2013 Intel Corporation
 *
 * Some parts of this driver are derived from the original dw_dmac.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/device.h>
#include <linux/clk.h>
18
#include <linux/pm_runtime.h>
19 20 21 22 23 24 25 26 27 28 29 30 31 32
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/acpi.h>
#include <linux/acpi_dma.h>

#include "internal.h"

static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
					struct of_dma *ofdma)
{
	struct dw_dma *dw = ofdma->of_dma_data;
33 34
	struct dw_dma_slave slave = {
		.dma_dev = dw->dma.dev,
35 36 37 38 39 40
	};
	dma_cap_mask_t cap;

	if (dma_spec->args_count != 3)
		return NULL;

41 42 43 44
	slave.src_id = dma_spec->args[0];
	slave.dst_id = dma_spec->args[0];
	slave.src_master = dma_spec->args[1];
	slave.dst_master = dma_spec->args[2];
45

46 47 48 49
	if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS ||
		    slave.dst_id >= DW_DMA_MAX_NR_REQUESTS ||
		    slave.src_master >= dw->nr_masters ||
		    slave.dst_master >= dw->nr_masters))
50 51 52 53 54 55
		return NULL;

	dma_cap_zero(cap);
	dma_cap_set(DMA_SLAVE, cap);

	/* TODO: there should be a simpler way to do this */
56
	return dma_request_channel(cap, dw_dma_filter, &slave);
57 58 59 60 61 62
}

#ifdef CONFIG_ACPI
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
{
	struct acpi_dma_spec *dma_spec = param;
63 64 65 66 67 68 69
	struct dw_dma_slave slave = {
		.dma_dev = dma_spec->dev,
		.src_id = dma_spec->slave_id,
		.dst_id = dma_spec->slave_id,
		.src_master = 1,
		.dst_master = 0,
	};
70

71
	return dw_dma_filter(chan, &slave);
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
}

static void dw_dma_acpi_controller_register(struct dw_dma *dw)
{
	struct device *dev = dw->dma.dev;
	struct acpi_dma_filter_info *info;
	int ret;

	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
	if (!info)
		return;

	dma_cap_zero(info->dma_cap);
	dma_cap_set(DMA_SLAVE, info->dma_cap);
	info->filter_fn = dw_dma_acpi_filter;

	ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate,
						info);
	if (ret)
		dev_err(dev, "could not register acpi_dma_controller\n");
}
#else /* !CONFIG_ACPI */
static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {}
#endif /* !CONFIG_ACPI */

#ifdef CONFIG_OF
static struct dw_dma_platform_data *
dw_dma_parse_dt(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct dw_dma_platform_data *pdata;
	u32 tmp, arr[4];

	if (!np) {
		dev_err(&pdev->dev, "Missing DT data\n");
		return NULL;
	}

	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return NULL;

	if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
		return NULL;

	if (of_property_read_bool(np, "is_private"))
		pdata->is_private = true;

	if (!of_property_read_u32(np, "chan_allocation_order", &tmp))
		pdata->chan_allocation_order = (unsigned char)tmp;

	if (!of_property_read_u32(np, "chan_priority", &tmp))
		pdata->chan_priority = tmp;

	if (!of_property_read_u32(np, "block_size", &tmp))
		pdata->block_size = tmp;

	if (!of_property_read_u32(np, "dma-masters", &tmp)) {
		if (tmp > 4)
			return NULL;

		pdata->nr_masters = tmp;
	}

	if (!of_property_read_u32_array(np, "data_width", arr,
				pdata->nr_masters))
		for (tmp = 0; tmp < pdata->nr_masters; tmp++)
			pdata->data_width[tmp] = arr[tmp];

	return pdata;
}
#else
static inline struct dw_dma_platform_data *
dw_dma_parse_dt(struct platform_device *pdev)
{
	return NULL;
}
#endif

static int dw_probe(struct platform_device *pdev)
{
	struct dw_dma_chip *chip;
	struct device *dev = &pdev->dev;
	struct resource *mem;
	struct dw_dma_platform_data *pdata;
	int err;

	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->irq = platform_get_irq(pdev, 0);
	if (chip->irq < 0)
		return chip->irq;

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	chip->regs = devm_ioremap_resource(dev, mem);
	if (IS_ERR(chip->regs))
		return PTR_ERR(chip->regs);

172 173 174
	err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
	if (err)
		return err;
175 176 177 178 179 180 181

	pdata = dev_get_platdata(dev);
	if (!pdata)
		pdata = dw_dma_parse_dt(pdev);

	chip->dev = dev;

182 183 184 185
	chip->clk = devm_clk_get(chip->dev, "hclk");
	if (IS_ERR(chip->clk))
		return PTR_ERR(chip->clk);
	err = clk_prepare_enable(chip->clk);
186 187 188
	if (err)
		return err;

189 190
	pm_runtime_enable(&pdev->dev);

191 192 193 194
	err = dw_dma_probe(chip, pdata);
	if (err)
		goto err_dw_dma_probe;

195 196 197 198 199 200 201 202 203 204 205 206 207 208
	platform_set_drvdata(pdev, chip);

	if (pdev->dev.of_node) {
		err = of_dma_controller_register(pdev->dev.of_node,
						 dw_dma_of_xlate, chip->dw);
		if (err)
			dev_err(&pdev->dev,
				"could not register of_dma_controller\n");
	}

	if (ACPI_HANDLE(&pdev->dev))
		dw_dma_acpi_controller_register(chip->dw);

	return 0;
209 210

err_dw_dma_probe:
211
	pm_runtime_disable(&pdev->dev);
212 213
	clk_disable_unprepare(chip->clk);
	return err;
214 215 216 217 218 219 220 221 222
}

static int dw_remove(struct platform_device *pdev)
{
	struct dw_dma_chip *chip = platform_get_drvdata(pdev);

	if (pdev->dev.of_node)
		of_dma_controller_free(pdev->dev.of_node);

223
	dw_dma_remove(chip);
224
	pm_runtime_disable(&pdev->dev);
225 226 227
	clk_disable_unprepare(chip->clk);

	return 0;
228 229 230 231 232 233
}

static void dw_shutdown(struct platform_device *pdev)
{
	struct dw_dma_chip *chip = platform_get_drvdata(pdev);

234
	dw_dma_disable(chip);
235
	clk_disable_unprepare(chip->clk);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
}

#ifdef CONFIG_OF
static const struct of_device_id dw_dma_of_id_table[] = {
	{ .compatible = "snps,dma-spear1340" },
	{}
};
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
#endif

#ifdef CONFIG_ACPI
static const struct acpi_device_id dw_dma_acpi_id_table[] = {
	{ "INTL9C60", 0 },
	{ }
};
251
MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
252 253 254 255
#endif

#ifdef CONFIG_PM_SLEEP

256
static int dw_suspend_late(struct device *dev)
257 258 259 260
{
	struct platform_device *pdev = to_platform_device(dev);
	struct dw_dma_chip *chip = platform_get_drvdata(pdev);

261
	dw_dma_disable(chip);
262 263 264
	clk_disable_unprepare(chip->clk);

	return 0;
265 266
}

267
static int dw_resume_early(struct device *dev)
268 269 270 271
{
	struct platform_device *pdev = to_platform_device(dev);
	struct dw_dma_chip *chip = platform_get_drvdata(pdev);

272
	clk_prepare_enable(chip->clk);
273
	return dw_dma_enable(chip);
274 275
}

276
#endif /* CONFIG_PM_SLEEP */
277 278

static const struct dev_pm_ops dw_dev_pm_ops = {
279
	SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_suspend_late, dw_resume_early)
280 281 282 283 284
};

static struct platform_driver dw_driver = {
	.probe		= dw_probe,
	.remove		= dw_remove,
285
	.shutdown       = dw_shutdown,
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	.driver = {
		.name	= "dw_dmac",
		.pm	= &dw_dev_pm_ops,
		.of_match_table = of_match_ptr(dw_dma_of_id_table),
		.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
	},
};

static int __init dw_init(void)
{
	return platform_driver_register(&dw_driver);
}
subsys_initcall(dw_init);

static void __exit dw_exit(void)
{
	platform_driver_unregister(&dw_driver);
}
module_exit(dw_exit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");