diff options
Diffstat (limited to 'arch/arm/common')
-rw-r--r-- | arch/arm/common/edma.c | 305 |
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); |