Commit 9cade1a4 authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Vinod Koul

dma: dw: split driver to library part and platform code

To simplify the driver development let's split driver to library and platform
code parts. It helps us to add PCI driver in future.
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Acked-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
[Fixed compile error and few checkpatch issues]
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent 61a76496
...@@ -15,7 +15,7 @@ obj-$(CONFIG_FSL_DMA) += fsldma.o ...@@ -15,7 +15,7 @@ obj-$(CONFIG_FSL_DMA) += fsldma.o
obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_MV_XOR) += mv_xor.o
obj-$(CONFIG_DW_DMAC) += dw/ obj-$(CONFIG_DW_DMAC_CORE) += dw/
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_MX3_IPU) += ipu/
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
# DMA engine configuration for dw # DMA engine configuration for dw
# #
config DW_DMAC config DW_DMAC_CORE
tristate "Synopsys DesignWare AHB DMA support" tristate "Synopsys DesignWare AHB DMA support"
depends on GENERIC_HARDIRQS depends on GENERIC_HARDIRQS
select DMA_ENGINE select DMA_ENGINE
config DW_DMAC
tristate "Synopsys DesignWare AHB DMA platform driver"
select DW_DMAC_CORE
default y if CPU_AT32AP7000 default y if CPU_AT32AP7000
help help
Support the Synopsys DesignWare AHB DMA controller. This Support the Synopsys DesignWare AHB DMA controller. This
...@@ -14,7 +18,7 @@ config DW_DMAC ...@@ -14,7 +18,7 @@ config DW_DMAC
config DW_DMAC_BIG_ENDIAN_IO config DW_DMAC_BIG_ENDIAN_IO
bool "Use big endian I/O register access" bool "Use big endian I/O register access"
default y if AVR32 default y if AVR32
depends on DW_DMAC depends on DW_DMAC_CORE
help help
Say yes here to use big endian I/O access when reading and writing Say yes here to use big endian I/O access when reading and writing
to the DMA controller registers. This is needed on some platforms, to the DMA controller registers. This is needed on some platforms,
......
obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o
dw_dmac_core-objs := core.o
obj-$(CONFIG_DW_DMAC) += dw_dmac.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o
dw_dmac-objs := platform.o
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2007-2008 Atmel Corporation
* Copyright (C) 2010-2011 ST Microelectronics * Copyright (C) 2010-2011 ST Microelectronics
* Copyright (C) 2013 Intel Corporation
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -19,17 +20,12 @@ ...@@ -19,17 +20,12 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/acpi_dma.h>
#include "../dmaengine.h" #include "../dmaengine.h"
#include "dw_dmac_regs.h" #include "internal.h"
/* /*
* This supports the Synopsys "DesignWare AHB Central DMA Controller", * This supports the Synopsys "DesignWare AHB Central DMA Controller",
...@@ -41,16 +37,6 @@ ...@@ -41,16 +37,6 @@
* which does not support descriptor writeback. * which does not support descriptor writeback.
*/ */
static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave)
{
return slave ? slave->dst_master : 0;
}
static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
{
return slave ? slave->src_master : 1;
}
static inline void dwc_set_masters(struct dw_dma_chan *dwc) static inline void dwc_set_masters(struct dw_dma_chan *dwc)
{ {
struct dw_dma *dw = to_dw_dma(dwc->chan.device); struct dw_dma *dw = to_dw_dma(dwc->chan.device);
...@@ -1225,99 +1211,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) ...@@ -1225,99 +1211,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
dev_vdbg(chan2dev(chan), "%s: done\n", __func__); dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
} }
/*----------------------------------------------------------------------*/
struct dw_dma_of_filter_args {
struct dw_dma *dw;
unsigned int req;
unsigned int src;
unsigned int dst;
};
static bool dw_dma_of_filter(struct dma_chan *chan, void *param)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_of_filter_args *fargs = param;
/* Ensure the device matches our channel */
if (chan->device != &fargs->dw->dma)
return false;
dwc->request_line = fargs->req;
dwc->src_master = fargs->src;
dwc->dst_master = fargs->dst;
return true;
}
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;
struct dw_dma_of_filter_args fargs = {
.dw = dw,
};
dma_cap_mask_t cap;
if (dma_spec->args_count != 3)
return NULL;
fargs.req = dma_spec->args[0];
fargs.src = dma_spec->args[1];
fargs.dst = dma_spec->args[2];
if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS ||
fargs.src >= dw->nr_masters ||
fargs.dst >= dw->nr_masters))
return NULL;
dma_cap_zero(cap);
dma_cap_set(DMA_SLAVE, cap);
/* TODO: there should be a simpler way to do this */
return dma_request_channel(cap, dw_dma_of_filter, &fargs);
}
#ifdef CONFIG_ACPI
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct acpi_dma_spec *dma_spec = param;
if (chan->device->dev != dma_spec->dev ||
chan->chan_id != dma_spec->chan_id)
return false;
dwc->request_line = dma_spec->slave_id;
dwc->src_master = dwc_get_sms(NULL);
dwc->dst_master = dwc_get_dms(NULL);
return true;
}
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 */
/* --------------------- Cyclic DMA API extensions -------------------- */ /* --------------------- Cyclic DMA API extensions -------------------- */
/** /**
...@@ -1598,101 +1491,24 @@ static void dw_dma_off(struct dw_dma *dw) ...@@ -1598,101 +1491,24 @@ static void dw_dma_off(struct dw_dma *dw)
dw->chan[i].initialized = false; dw->chan[i].initialized = false;
} }
#ifdef CONFIG_OF int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
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_platform_data *pdata;
struct resource *io;
struct dw_dma *dw; struct dw_dma *dw;
size_t size; size_t size;
void __iomem *regs;
bool autocfg; bool autocfg;
unsigned int dw_params; unsigned int dw_params;
unsigned int nr_channels; unsigned int nr_channels;
unsigned int max_blk_size = 0; unsigned int max_blk_size = 0;
int irq;
int err; int err;
int i; int i;
irq = platform_get_irq(pdev, 0); dw_params = dma_read_byaddr(chip->regs, DW_PARAMS);
if (irq < 0)
return irq;
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, io);
if (IS_ERR(regs))
return PTR_ERR(regs);
/* Apply default dma_mask if needed */
if (!pdev->dev.dma_mask) {
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
}
dw_params = dma_read_byaddr(regs, DW_PARAMS);
autocfg = dw_params >> DW_PARAMS_EN & 0x1; autocfg = dw_params >> DW_PARAMS_EN & 0x1;
dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params); dev_dbg(chip->dev, "DW_PARAMS: 0x%08x\n", dw_params);
pdata = dev_get_platdata(&pdev->dev);
if (!pdata)
pdata = dw_dma_parse_dt(pdev);
if (!pdata && autocfg) { if (!pdata && autocfg) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return -ENOMEM; return -ENOMEM;
...@@ -1709,16 +1525,17 @@ static int dw_probe(struct platform_device *pdev) ...@@ -1709,16 +1525,17 @@ static int dw_probe(struct platform_device *pdev)
nr_channels = pdata->nr_channels; nr_channels = pdata->nr_channels;
size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan);
dw = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); dw = devm_kzalloc(chip->dev, size, GFP_KERNEL);
if (!dw) if (!dw)
return -ENOMEM; return -ENOMEM;
dw->clk = devm_clk_get(&pdev->dev, "hclk"); dw->clk = devm_clk_get(chip->dev, "hclk");
if (IS_ERR(dw->clk)) if (IS_ERR(dw->clk))
return PTR_ERR(dw->clk); return PTR_ERR(dw->clk);
clk_prepare_enable(dw->clk); clk_prepare_enable(dw->clk);
dw->regs = regs; dw->regs = chip->regs;
chip->dw = dw;
/* Get hardware configuration parameters */ /* Get hardware configuration parameters */
if (autocfg) { if (autocfg) {
...@@ -1743,18 +1560,16 @@ static int dw_probe(struct platform_device *pdev) ...@@ -1743,18 +1560,16 @@ static int dw_probe(struct platform_device *pdev)
/* Disable BLOCK interrupts as well */ /* Disable BLOCK interrupts as well */
channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0, err = devm_request_irq(chip->dev, chip->irq, dw_dma_interrupt, 0,
"dw_dmac", dw); "dw_dmac", dw);
if (err) if (err)
return err; return err;
platform_set_drvdata(pdev, dw);
/* Create a pool of consistent memory blocks for hardware descriptors */ /* Create a pool of consistent memory blocks for hardware descriptors */
dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", chip->dev,
sizeof(struct dw_desc), 4, 0); sizeof(struct dw_desc), 4, 0);
if (!dw->desc_pool) { if (!dw->desc_pool) {
dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); dev_err(chip->dev, "No memory for descriptors dma pool\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -1795,11 +1610,11 @@ static int dw_probe(struct platform_device *pdev) ...@@ -1795,11 +1610,11 @@ static int dw_probe(struct platform_device *pdev)
/* Hardware configuration */ /* Hardware configuration */
if (autocfg) { if (autocfg) {
unsigned int dwc_params; unsigned int dwc_params;
void __iomem *addr = chip->regs + r * sizeof(u32);
dwc_params = dma_read_byaddr(regs + r * sizeof(u32), dwc_params = dma_read_byaddr(addr, DWC_PARAMS);
DWC_PARAMS);
dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, dev_dbg(chip->dev, "DWC_PARAMS[%d]: 0x%08x\n", i,
dwc_params); dwc_params);
/* Decode maximum block size for given channel. The /* Decode maximum block size for given channel. The
...@@ -1831,7 +1646,7 @@ static int dw_probe(struct platform_device *pdev) ...@@ -1831,7 +1646,7 @@ static int dw_probe(struct platform_device *pdev)
dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); dma_cap_set(DMA_SLAVE, dw->dma.cap_mask);
if (pdata->is_private) if (pdata->is_private)
dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask);
dw->dma.dev = &pdev->dev; dw->dma.dev = chip->dev;
dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources;
dw->dma.device_free_chan_resources = dwc_free_chan_resources; dw->dma.device_free_chan_resources = dwc_free_chan_resources;
...@@ -1845,32 +1660,20 @@ static int dw_probe(struct platform_device *pdev) ...@@ -1845,32 +1660,20 @@ static int dw_probe(struct platform_device *pdev)
dma_writel(dw, CFG, DW_CFG_DMA_EN); dma_writel(dw, CFG, DW_CFG_DMA_EN);
dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", dev_info(chip->dev, "DesignWare DMA Controller, %d channels\n",
nr_channels); nr_channels);
dma_async_device_register(&dw->dma); dma_async_device_register(&dw->dma);
if (pdev->dev.of_node) {
err = of_dma_controller_register(pdev->dev.of_node,
dw_dma_of_xlate, dw);
if (err)
dev_err(&pdev->dev,
"could not register of_dma_controller\n");
}
if (ACPI_HANDLE(&pdev->dev))
dw_dma_acpi_controller_register(dw);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(dw_dma_probe);
static int dw_remove(struct platform_device *pdev) int dw_dma_remove(struct dw_dma_chip *chip)
{ {
struct dw_dma *dw = platform_get_drvdata(pdev); struct dw_dma *dw = chip->dw;
struct dw_dma_chan *dwc, *_dwc; struct dw_dma_chan *dwc, *_dwc;
if (pdev->dev.of_node)
of_dma_controller_free(pdev->dev.of_node);
dw_dma_off(dw); dw_dma_off(dw);
dma_async_device_unregister(&dw->dma); dma_async_device_unregister(&dw->dma);
...@@ -1884,86 +1687,44 @@ static int dw_remove(struct platform_device *pdev) ...@@ -1884,86 +1687,44 @@ static int dw_remove(struct platform_device *pdev)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(dw_dma_remove);
static void dw_shutdown(struct platform_device *pdev) void dw_dma_shutdown(struct dw_dma_chip *chip)
{ {
struct dw_dma *dw = platform_get_drvdata(pdev); struct dw_dma *dw = chip->dw;
dw_dma_off(dw); dw_dma_off(dw);
clk_disable_unprepare(dw->clk); clk_disable_unprepare(dw->clk);
} }
EXPORT_SYMBOL_GPL(dw_dma_shutdown);
#ifdef CONFIG_PM_SLEEP
static int dw_suspend_noirq(struct device *dev) int dw_dma_suspend(struct dw_dma_chip *chip)
{ {
struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = chip->dw;
struct dw_dma *dw = platform_get_drvdata(pdev);
dw_dma_off(dw); dw_dma_off(dw);
clk_disable_unprepare(dw->clk); clk_disable_unprepare(dw->clk);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(dw_dma_suspend);
static int dw_resume_noirq(struct device *dev) int dw_dma_resume(struct dw_dma_chip *chip)
{ {
struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = chip->dw;
struct dw_dma *dw = platform_get_drvdata(pdev);
clk_prepare_enable(dw->clk); clk_prepare_enable(dw->clk);
dma_writel(dw, CFG, DW_CFG_DMA_EN); dma_writel(dw, CFG, DW_CFG_DMA_EN);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(dw_dma_resume);
static const struct dev_pm_ops dw_dev_pm_ops = { #endif /* CONFIG_PM_SLEEP */
.suspend_noirq = dw_suspend_noirq,
.resume_noirq = dw_resume_noirq,
.freeze_noirq = dw_suspend_noirq,
.thaw_noirq = dw_resume_noirq,
.restore_noirq = dw_resume_noirq,
.poweroff_noirq = dw_suspend_noirq,
};
#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 },
{ }
};
#endif
static struct platform_driver dw_driver = {
.probe = dw_probe,
.remove = dw_remove,
.shutdown = dw_shutdown,
.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_LICENSE("GPL v2");
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver");
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
/*
* Driver for the Synopsys DesignWare DMA Controller
*
* Copyright (C) 2013 Intel Corporation
*
* 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.
*/
#ifndef _DW_DMAC_INTERNAL_H
#define _DW_DMAC_INTERNAL_H
#include <linux/device.h>
#include <linux/dw_dmac.h>
#include "regs.h"
/**
* struct dw_dma_chip - representation of DesignWare DMA controller hardware
* @dev: struct device of the DMA controller
* @irq: irq line
* @regs: memory mapped I/O space
* @dw: struct dw_dma that is filed by dw_dma_probe()
*/
struct dw_dma_chip {
struct device *dev;
int irq;
void __iomem *regs;
struct dw_dma *dw;
};
/* Export to the platform drivers */
int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata);
int dw_dma_remove(struct dw_dma_chip *chip);
void dw_dma_shutdown(struct dw_dma_chip *chip);
#ifdef CONFIG_PM_SLEEP
int dw_dma_suspend(struct dw_dma_chip *chip);
int dw_dma_resume(struct dw_dma_chip *chip);
#endif /* CONFIG_PM_SLEEP */
/**
* dwc_get_dms - get destination master
* @slave: pointer to the custom slave configuration
*
* Returns destination master in the custom slave configuration if defined, or
* default value otherwise.
*/
static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave)
{
return slave ? slave->dst_master : 0;
}
/**
* dwc_get_sms - get source master
* @slave: pointer to the custom slave configuration
*
* Returns source master in the custom slave configuration if defined, or
* default value otherwise.
*/
static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
{
return slave ? slave->src_master : 1;
}
#endif /* _DW_DMAC_INTERNAL_H */
/*
* 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>
#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"
struct dw_dma_of_filter_args {
struct dw_dma *dw;
unsigned int req;
unsigned int src;
unsigned int dst;
};
static bool dw_dma_of_filter(struct dma_chan *chan, void *param)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_of_filter_args *fargs = param;
/* Ensure the device matches our channel */
if (chan->device != &fargs->dw->dma)
return false;
dwc->request_line = fargs->req;
dwc->src_master = fargs->src;
dwc->dst_master = fargs->dst;
return true;
}
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;
struct dw_dma_of_filter_args fargs = {
.dw = dw,
};
dma_cap_mask_t cap;
if (dma_spec->args_count != 3)
return NULL;
fargs.req = dma_spec->args[0];
fargs.src = dma_spec->args[1];
fargs.dst = dma_spec->args[2];
if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS ||
fargs.src >= dw->nr_masters ||
fargs.dst >= dw->nr_masters))
return NULL;
dma_cap_zero(cap);
dma_cap_set(DMA_SLAVE, cap);
/* TODO: there should be a simpler way to do this */
return dma_request_channel(cap, dw_dma_of_filter, &fargs);
}
#ifdef CONFIG_ACPI
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct acpi_dma_spec *dma_spec = param;
if (chan->device->dev != dma_spec->dev ||
chan->chan_id != dma_spec->chan_id)
return false;
dwc->request_line = dma_spec->slave_id;
dwc->src_master = dwc_get_sms(NULL);
dwc->dst_master = dwc_get_dms(NULL);
return true;
}
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);
/* Apply default dma_mask if needed */
if (!dev->dma_mask) {
dev->dma_mask = &dev->coherent_dma_mask;
dev->coherent_dma_mask = DMA_BIT_MASK(32);
}
pdata = dev_get_platdata(dev);
if (!pdata)
pdata = dw_dma_parse_dt(pdev);
chip->dev = dev;
err = dw_dma_probe(chip, pdata);
if (err)
return err;
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;
}
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);
return dw_dma_remove(chip);
}
static void dw_shutdown(struct platform_device *pdev)
{
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
dw_dma_shutdown(chip);
}
#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 },
{ }
};
#endif
#ifdef CONFIG_PM_SLEEP
static int dw_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
return dw_dma_suspend(chip);
}
static int dw_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
return dw_dma_resume(chip);
}
#else /* !CONFIG_PM_SLEEP */
#define dw_suspend_noirq NULL
#define dw_resume_noirq NULL
#endif /* !CONFIG_PM_SLEEP */
static const struct dev_pm_ops dw_dev_pm_ops = {
.suspend_noirq = dw_suspend_noirq,
.resume_noirq = dw_resume_noirq,
.freeze_noirq = dw_suspend_noirq,
.thaw_noirq = dw_resume_noirq,
.restore_noirq = dw_resume_noirq,
.poweroff_noirq = dw_suspend_noirq,
};
static struct platform_driver dw_driver = {
.probe = dw_probe,
.remove = dw_remove,
.shutdown = dw_shutdown,
.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");
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/interrupt.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/dw_dmac.h> #include <linux/dw_dmac.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment