summaryrefslogtreecommitdiffstats
path: root/arch/arm/common/edma.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/common/edma.c')
-rw-r--r--arch/arm/common/edma.c305
1 files changed, 149 insertions, 156 deletions
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c
index 0b4c0ee59ed9..03692520812a 100644
--- a/arch/arm/common/edma.c
+++ b/arch/arm/common/edma.c
@@ -130,7 +130,7 @@ struct edma {
struct edma_soc_info *info;
int id;
-
+ bool unused_chan_list_done;
/* The edma_inuse bit for each PaRAM slot is clear unless the
* channel is in use ... by ARM or DSP, for QDMA, or whatever.
*/
@@ -264,7 +264,6 @@ static inline void clear_bits(int offset, int len, unsigned long *p)
}
/*****************************************************************************/
-static struct edma *edma_cc[EDMA_MAX_CC];
static int arch_num_cc;
/* dummy param set used to (re)initialize parameter RAM slots */
@@ -490,14 +489,18 @@ static irqreturn_t dma_ccerr_handler(int irq, void *data)
static int prepare_unused_channel_list(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
- int i, count, ctlr;
+ struct edma *cc = data;
+ int i, count;
struct of_phandle_args dma_spec;
if (dev->of_node) {
+ struct platform_device *dma_pdev;
+
count = of_property_count_strings(dev->of_node, "dma-names");
if (count < 0)
return 0;
for (i = 0; i < count; i++) {
+
if (of_parse_phandle_with_args(dev->of_node, "dmas",
"#dma-cells", i,
&dma_spec))
@@ -508,8 +511,12 @@ static int prepare_unused_channel_list(struct device *dev, void *data)
continue;
}
+ dma_pdev = of_find_device_by_node(dma_spec.np);
+ if (&dma_pdev->dev != cc->dev)
+ continue;
+
clear_bit(EDMA_CHAN_SLOT(dma_spec.args[0]),
- edma_cc[0]->edma_unused);
+ cc->edma_unused);
of_node_put(dma_spec.np);
}
return 0;
@@ -517,11 +524,11 @@ static int prepare_unused_channel_list(struct device *dev, void *data)
/* For non-OF case */
for (i = 0; i < pdev->num_resources; i++) {
- if ((pdev->resource[i].flags & IORESOURCE_DMA) &&
- (int)pdev->resource[i].start >= 0) {
- ctlr = EDMA_CTLR(pdev->resource[i].start);
+ struct resource *res = &pdev->resource[i];
+
+ if ((res->flags & IORESOURCE_DMA) && (int)res->start >= 0) {
clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start),
- edma_cc[ctlr]->edma_unused);
+ cc->edma_unused);
}
}
@@ -530,8 +537,6 @@ static int prepare_unused_channel_list(struct device *dev, void *data)
/*-----------------------------------------------------------------------*/
-static bool unused_chan_list_done;
-
/* Resource alloc/free: dma channels, parameter RAM slots */
/**
@@ -564,77 +569,73 @@ static bool unused_chan_list_done;
*
* Returns the number of the channel, else negative errno.
*/
-int edma_alloc_channel(int channel,
+int edma_alloc_channel(struct edma *cc, int channel,
void (*callback)(unsigned channel, u16 ch_status, void *data),
void *data,
enum dma_event_q eventq_no)
{
- unsigned i, done = 0, ctlr = 0;
+ unsigned done = 0;
int ret = 0;
- if (!unused_chan_list_done) {
+ if (!cc->unused_chan_list_done) {
/*
* Scan all the platform devices to find out the EDMA channels
* used and clear them in the unused list, making the rest
* available for ARM usage.
*/
- ret = bus_for_each_dev(&platform_bus_type, NULL, NULL,
- prepare_unused_channel_list);
+ ret = bus_for_each_dev(&platform_bus_type, NULL, cc,
+ prepare_unused_channel_list);
if (ret < 0)
return ret;
- unused_chan_list_done = true;
+ cc->unused_chan_list_done = true;
}
if (channel >= 0) {
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n",
+ __func__, cc->id, EDMA_CTLR(channel));
+ return -EINVAL;
+ }
channel = EDMA_CHAN_SLOT(channel);
}
if (channel < 0) {
- for (i = 0; i < arch_num_cc; i++) {
- channel = 0;
- for (;;) {
- channel = find_next_bit(edma_cc[i]->edma_unused,
- edma_cc[i]->num_channels,
- channel);
- if (channel == edma_cc[i]->num_channels)
- break;
- if (!test_and_set_bit(channel,
- edma_cc[i]->edma_inuse)) {
- done = 1;
- ctlr = i;
- break;
- }
- channel++;
- }
- if (done)
+ channel = 0;
+ for (;;) {
+ channel = find_next_bit(cc->edma_unused,
+ cc->num_channels, channel);
+ if (channel == cc->num_channels)
+ break;
+ if (!test_and_set_bit(channel, cc->edma_inuse)) {
+ done = 1;
break;
+ }
+ channel++;
}
if (!done)
return -ENOMEM;
- } else if (channel >= edma_cc[ctlr]->num_channels) {
+ } else if (channel >= cc->num_channels) {
return -EINVAL;
- } else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) {
+ } else if (test_and_set_bit(channel, cc->edma_inuse)) {
return -EBUSY;
}
/* ensure access through shadow region 0 */
- edma_or_array2(edma_cc[ctlr], EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f));
+ edma_or_array2(cc, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f));
/* ensure no events are pending */
- edma_stop(EDMA_CTLR_CHAN(ctlr, channel));
- memcpy_toio(edma_cc[ctlr]->base + PARM_OFFSET(channel), &dummy_paramset,
+ edma_stop(cc, EDMA_CTLR_CHAN(cc->id, channel));
+ memcpy_toio(cc->base + PARM_OFFSET(channel), &dummy_paramset,
PARM_SIZE);
if (callback)
- setup_dma_interrupt(edma_cc[ctlr],
- EDMA_CTLR_CHAN(ctlr, channel), callback,
- data);
+ setup_dma_interrupt(cc, EDMA_CTLR_CHAN(cc->id, channel),
+ callback, data);
- map_dmach_queue(edma_cc[ctlr], channel, eventq_no);
+ map_dmach_queue(cc, channel, eventq_no);
- return EDMA_CTLR_CHAN(ctlr, channel);
+ return EDMA_CTLR_CHAN(cc->id, channel);
}
EXPORT_SYMBOL(edma_alloc_channel);
@@ -650,22 +651,25 @@ EXPORT_SYMBOL(edma_alloc_channel);
* will not be reactivated by linking, chaining, or software calls to
* edma_start().
*/
-void edma_free_channel(unsigned channel)
+void edma_free_channel(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel >= edma_cc[ctlr]->num_channels)
+ if (channel >= cc->num_channels)
return;
- setup_dma_interrupt(edma_cc[ctlr], channel, NULL, NULL);
+ setup_dma_interrupt(cc, channel, NULL, NULL);
/* REVISIT should probably take out of shadow region 0 */
- memcpy_toio(edma_cc[ctlr]->base + PARM_OFFSET(channel), &dummy_paramset,
+ memcpy_toio(cc->base + PARM_OFFSET(channel), &dummy_paramset,
PARM_SIZE);
- clear_bit(channel, edma_cc[ctlr]->edma_inuse);
+ clear_bit(channel, cc->edma_inuse);
}
EXPORT_SYMBOL(edma_free_channel);
@@ -683,35 +687,29 @@ EXPORT_SYMBOL(edma_free_channel);
*
* Returns the number of the slot, else negative errno.
*/
-int edma_alloc_slot(unsigned ctlr, int slot)
+int edma_alloc_slot(struct edma *cc, int slot)
{
- if (!edma_cc[ctlr])
- return -EINVAL;
-
- if (slot >= 0)
+ if (slot > 0)
slot = EDMA_CHAN_SLOT(slot);
-
if (slot < 0) {
- slot = edma_cc[ctlr]->num_channels;
+ slot = cc->num_channels;
for (;;) {
- slot = find_next_zero_bit(edma_cc[ctlr]->edma_inuse,
- edma_cc[ctlr]->num_slots, slot);
- if (slot == edma_cc[ctlr]->num_slots)
+ slot = find_next_zero_bit(cc->edma_inuse, cc->num_slots,
+ slot);
+ if (slot == cc->num_slots)
return -ENOMEM;
- if (!test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse))
+ if (!test_and_set_bit(slot, cc->edma_inuse))
break;
}
- } else if (slot < edma_cc[ctlr]->num_channels ||
- slot >= edma_cc[ctlr]->num_slots) {
+ } else if (slot < cc->num_channels || slot >= cc->num_slots) {
return -EINVAL;
- } else if (test_and_set_bit(slot, edma_cc[ctlr]->edma_inuse)) {
+ } else if (test_and_set_bit(slot, cc->edma_inuse)) {
return -EBUSY;
}
- memcpy_toio(edma_cc[ctlr]->base + PARM_OFFSET(slot), &dummy_paramset,
- PARM_SIZE);
+ memcpy_toio(cc->base + PARM_OFFSET(slot), &dummy_paramset, PARM_SIZE);
- return EDMA_CTLR_CHAN(ctlr, slot);
+ return slot;
}
EXPORT_SYMBOL(edma_alloc_slot);
@@ -723,20 +721,15 @@ EXPORT_SYMBOL(edma_alloc_slot);
* Callers are responsible for ensuring the slot is inactive, and will
* not be activated.
*/
-void edma_free_slot(unsigned slot)
+void edma_free_slot(struct edma *cc, unsigned slot)
{
- unsigned ctlr;
- ctlr = EDMA_CTLR(slot);
slot = EDMA_CHAN_SLOT(slot);
-
- if (slot < edma_cc[ctlr]->num_channels ||
- slot >= edma_cc[ctlr]->num_slots)
+ if (slot < cc->num_channels || slot >= cc->num_slots)
return;
- memcpy_toio(edma_cc[ctlr]->base + PARM_OFFSET(slot), &dummy_paramset,
- PARM_SIZE);
- clear_bit(slot, edma_cc[ctlr]->edma_inuse);
+ memcpy_toio(cc->base + PARM_OFFSET(slot), &dummy_paramset, PARM_SIZE);
+ clear_bit(slot, cc->edma_inuse);
}
EXPORT_SYMBOL(edma_free_slot);
@@ -751,16 +744,15 @@ EXPORT_SYMBOL(edma_free_slot);
*
* Returns the position of the current active slot
*/
-dma_addr_t edma_get_position(unsigned slot, bool dst)
+dma_addr_t edma_get_position(struct edma *cc, unsigned slot, bool dst)
{
- u32 offs, ctlr = EDMA_CTLR(slot);
+ u32 offs;
slot = EDMA_CHAN_SLOT(slot);
-
offs = PARM_OFFSET(slot);
offs += dst ? PARM_DST : PARM_SRC;
- return edma_read(edma_cc[ctlr], offs);
+ return edma_read(cc, offs);
}
/**
@@ -770,21 +762,15 @@ dma_addr_t edma_get_position(unsigned slot, bool dst)
*
* The originating slot should not be part of any active DMA transfer.
*/
-void edma_link(unsigned from, unsigned to)
+void edma_link(struct edma *cc, unsigned from, unsigned to)
{
- unsigned ctlr_from, ctlr_to;
-
- ctlr_from = EDMA_CTLR(from);
from = EDMA_CHAN_SLOT(from);
- ctlr_to = EDMA_CTLR(to);
to = EDMA_CHAN_SLOT(to);
-
- if (from >= edma_cc[ctlr_from]->num_slots)
+ if (from >= cc->num_slots || to >= cc->num_slots)
return;
- if (to >= edma_cc[ctlr_to]->num_slots)
- return;
- edma_parm_modify(edma_cc[ctlr_from], PARM_LINK_BCNTRLD, from, 0xffff0000,
- PARM_OFFSET(to));
+
+ edma_parm_modify(cc, PARM_LINK_BCNTRLD, from, 0xffff0000,
+ PARM_OFFSET(to));
}
EXPORT_SYMBOL(edma_link);
@@ -802,16 +788,13 @@ EXPORT_SYMBOL(edma_link);
* calls to set up those parameters in small pieces, and provides
* complete control over all transfer options.
*/
-void edma_write_slot(unsigned slot, const struct edmacc_param *param)
+void edma_write_slot(struct edma *cc, unsigned slot,
+ const struct edmacc_param *param)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(slot);
slot = EDMA_CHAN_SLOT(slot);
-
- if (slot >= edma_cc[ctlr]->num_slots)
+ if (slot >= cc->num_slots)
return;
- memcpy_toio(edma_cc[ctlr]->base + PARM_OFFSET(slot), param, PARM_SIZE);
+ memcpy_toio(cc->base + PARM_OFFSET(slot), param, PARM_SIZE);
}
EXPORT_SYMBOL(edma_write_slot);
@@ -823,17 +806,12 @@ EXPORT_SYMBOL(edma_write_slot);
* Use this to read data from a parameter RAM slot, perhaps to
* save them as a template for later reuse.
*/
-void edma_read_slot(unsigned slot, struct edmacc_param *param)
+void edma_read_slot(struct edma *cc, unsigned slot, struct edmacc_param *param)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(slot);
slot = EDMA_CHAN_SLOT(slot);
-
- if (slot >= edma_cc[ctlr]->num_slots)
+ if (slot >= cc->num_slots)
return;
- memcpy_fromio(param, edma_cc[ctlr]->base + PARM_OFFSET(slot),
- PARM_SIZE);
+ memcpy_fromio(param, cc->base + PARM_OFFSET(slot), PARM_SIZE);
}
EXPORT_SYMBOL(edma_read_slot);
@@ -848,18 +826,19 @@ EXPORT_SYMBOL(edma_read_slot);
* This temporarily disables EDMA hardware events on the specified channel,
* preventing them from triggering new transfers on its behalf
*/
-void edma_pause(unsigned channel)
+void edma_pause(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel < edma_cc[ctlr]->num_channels) {
+ if (channel < cc->num_channels) {
unsigned int mask = BIT(channel & 0x1f);
- edma_shadow0_write_array(edma_cc[ctlr], SH_EECR, channel >> 5,
- mask);
+ edma_shadow0_write_array(cc, SH_EECR, channel >> 5, mask);
}
}
EXPORT_SYMBOL(edma_pause);
@@ -870,36 +849,39 @@ EXPORT_SYMBOL(edma_pause);
*
* This re-enables EDMA hardware events on the specified channel.
*/
-void edma_resume(unsigned channel)
+void edma_resume(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel < edma_cc[ctlr]->num_channels) {
+ if (channel < cc->num_channels) {
unsigned int mask = BIT(channel & 0x1f);
- edma_shadow0_write_array(edma_cc[ctlr], SH_EESR, channel >> 5,
- mask);
+ edma_shadow0_write_array(cc, SH_EESR, channel >> 5, mask);
}
}
EXPORT_SYMBOL(edma_resume);
-int edma_trigger_channel(unsigned channel)
+int edma_trigger_channel(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
unsigned int mask;
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return -EINVAL;
+ }
channel = EDMA_CHAN_SLOT(channel);
mask = BIT(channel & 0x1f);
- edma_shadow0_write_array(edma_cc[ctlr], SH_ESR, (channel >> 5), mask);
+ edma_shadow0_write_array(cc, SH_ESR, (channel >> 5), mask);
pr_debug("EDMA: ESR%d %08x\n", (channel >> 5),
- edma_shadow0_read_array(edma_cc[ctlr], SH_ESR,
- (channel >> 5)));
+ edma_shadow0_read_array(cc, SH_ESR, (channel >> 5)));
return 0;
}
EXPORT_SYMBOL(edma_trigger_channel);
@@ -915,15 +897,16 @@ EXPORT_SYMBOL(edma_trigger_channel);
*
* Returns zero on success, else negative errno.
*/
-int edma_start(unsigned channel)
+int edma_start(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return -EINVAL;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel < edma_cc[ctlr]->num_channels) {
- struct edma *cc = edma_cc[ctlr];
+ if (channel < cc->num_channels) {
int j = channel >> 5;
unsigned int mask = BIT(channel & 0x1f);
@@ -962,15 +945,16 @@ EXPORT_SYMBOL(edma_start);
* may not be resumed, and the channel's Parameter RAM should be
* reinitialized before being reused.
*/
-void edma_stop(unsigned channel)
+void edma_stop(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel < edma_cc[ctlr]->num_channels) {
- struct edma *cc = edma_cc[ctlr];
+ if (channel < cc->num_channels) {
int j = channel >> 5;
unsigned int mask = BIT(channel & 0x1f);
@@ -1005,15 +989,16 @@ EXPORT_SYMBOL(edma_stop);
*
*****************************************************************************/
-void edma_clean_channel(unsigned channel)
+void edma_clean_channel(struct edma *cc, unsigned channel)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel < edma_cc[ctlr]->num_channels) {
- struct edma *cc = edma_cc[ctlr];
+ if (channel < cc->num_channels) {
int j = (channel >> 5);
unsigned int mask = BIT(channel & 0x1f);
@@ -1037,26 +1022,35 @@ EXPORT_SYMBOL(edma_clean_channel);
*
* Can be used to move a channel to a selected event queue.
*/
-void edma_assign_channel_eventq(unsigned channel, enum dma_event_q eventq_no)
+void edma_assign_channel_eventq(struct edma *cc, unsigned channel,
+ enum dma_event_q eventq_no)
{
- unsigned ctlr;
-
- ctlr = EDMA_CTLR(channel);
+ if (cc->id != EDMA_CTLR(channel)) {
+ dev_err(cc->dev, "%s: ID mismatch for eDMA%d: %d\n", __func__,
+ cc->id, EDMA_CTLR(channel));
+ return;
+ }
channel = EDMA_CHAN_SLOT(channel);
- if (channel >= edma_cc[ctlr]->num_channels)
+ if (channel >= cc->num_channels)
return;
/* default to low priority queue */
if (eventq_no == EVENTQ_DEFAULT)
- eventq_no = edma_cc[ctlr]->default_queue;
- if (eventq_no >= edma_cc[ctlr]->num_tc)
+ eventq_no = cc->default_queue;
+ if (eventq_no >= cc->num_tc)
return;
- map_dmach_queue(edma_cc[ctlr], channel, eventq_no);
+ map_dmach_queue(cc, channel, eventq_no);
}
EXPORT_SYMBOL(edma_assign_channel_eventq);
+struct edma *edma_get_data(struct device *edma_dev)
+{
+ return dev_get_drvdata(edma_dev);
+}
+
+
static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata,
struct edma *edma_cc, int cc_id)
{
@@ -1278,11 +1272,10 @@ static int edma_probe(struct platform_device *pdev)
}
}
- edma_cc[dev_id] = devm_kzalloc(dev, sizeof(struct edma), GFP_KERNEL);
- if (!edma_cc[dev_id])
+ cc = devm_kzalloc(dev, sizeof(struct edma), GFP_KERNEL);
+ if (!cc)
return -ENOMEM;
- cc = edma_cc[dev_id];
cc->dev = dev;
cc->id = dev_id;
dev_set_drvdata(dev, cc);
OpenPOWER on IntegriCloud