/* Valid only for channels 0 - 14, 15 has its own base address */ #define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
/* the max dma length for different channels */ #define MAX_DMA_LEN SZ_1G #define MAX_LITE_DMA_LEN (SZ_64K - 4)
staticinline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
{ /* lite and normal channels have different max frame length */ return c->is_lite_channel ? MAX_LITE_DMA_LEN : MAX_DMA_LEN;
}
/* how many frames of max_len size do we need to transfer len bytes */ staticinline size_t bcm2835_dma_frames_for_length(size_t len,
size_t max_len)
{ return DIV_ROUND_UP(len, max_len);
}
/* set the length taking lite-channel limitations into account */
control_block->length = min_t(u32, len, max_len);
/* finished if we have no period_length */ if (!period_len) return;
/* * period_len means: that we need to generate * transfers that are terminating at every * multiple of period_len - this is typically * used to set the interrupt flag in info * which is required during cyclic transfers
*/
/* have we filled in period_length yet? */ if (*total_len + control_block->length < period_len) { /* update number of bytes in this period so far */
*total_len += control_block->length; return;
}
/* calculate the length that remains to reach period_length */
control_block->length = period_len - *total_len;
/* reset total_length for next period */
*total_len = 0;
/* add extrainfo bits in info */
control_block->info |= finalextrainfo;
}
/** * bcm2835_dma_create_cb_chain - create a control block and fills data in * * @chan: the @dma_chan for which we run this * @direction: the direction in which we transfer * @cyclic: it is a cyclic transfer * @info: the default info bits to apply per controlblock * @frames: number of controlblocks to allocate * @src: the src address to assign (if the S_INC bit is set * in @info, then it gets incremented) * @dst: the dst address to assign (if the D_INC bit is set * in @info, then it gets incremented) * @buf_len: the full buffer length (may also be 0) * @period_len: the period length when to apply @finalextrainfo * in addition to the last transfer * this will also break some control-blocks early * @finalextrainfo: additional bits in last controlblock * (or when period_len is reached in case of cyclic) * @gfp: the GFP flag to use for allocation
*/ staticstruct bcm2835_desc *bcm2835_dma_create_cb_chain( struct dma_chan *chan, enum dma_transfer_direction direction, bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
dma_addr_t src, dma_addr_t dst, size_t buf_len,
size_t period_len, gfp_t gfp)
{ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
size_t len = buf_len, total_len;
size_t frame; struct bcm2835_desc *d; struct bcm2835_cb_entry *cb_entry; struct bcm2835_dma_cb *control_block;
if (!frames) return NULL;
/* allocate and setup the descriptor. */
d = kzalloc(struct_size(d, cb_list, frames), gfp); if (!d) return NULL;
d->c = c;
d->dir = direction;
d->cyclic = cyclic;
/* * Iterate over all frames, create a control block * for each frame and link them together.
*/ for (frame = 0, total_len = 0; frame < frames; d->frames++, frame++) {
cb_entry = &d->cb_list[frame];
cb_entry->cb = dma_pool_alloc(c->cb_pool, gfp,
&cb_entry->paddr); if (!cb_entry->cb) goto error_cb;
/* fill in the control block */
control_block = cb_entry->cb;
control_block->info = info;
control_block->src = src;
control_block->dst = dst;
control_block->stride = 0;
control_block->next = 0; /* set up length in control_block if requested */ if (buf_len) { /* calculate length honoring period_length */
bcm2835_dma_create_cb_set_length(
c, control_block,
len, period_len, &total_len,
cyclic ? finalextrainfo : 0);
/* calculate new remaining length */
len -= control_block->length;
}
/* link this the last controlblock */ if (frame)
d->cb_list[frame - 1].cb->next = cb_entry->paddr;
/* update src and dst and length */ if (src && (info & BCM2835_DMA_S_INC))
src += control_block->length; if (dst && (info & BCM2835_DMA_D_INC))
dst += control_block->length;
/* Length of total transfer */
d->size += control_block->length;
}
/* the last frame requires extra flags */
d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
/* detect a size mismatch */ if (buf_len && (d->size != buf_len)) goto error_cb;
/* * A zero control block address means the channel is idle. * (The ACTIVE flag in the CS register is not a reliable indicator.)
*/ if (!readl(chan_base + BCM2835_DMA_ADDR)) return;
/* Write 0 to the active bit - Pause the DMA */
writel(0, chan_base + BCM2835_DMA_CS);
/* Wait for any current AXI transfer to complete */ while ((readl(chan_base + BCM2835_DMA_CS) &
BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
cpu_relax();
/* Peripheral might be stuck and fail to signal AXI write responses */ if (!timeout)
dev_err(c->vc.chan.device->dev, "failed to complete outstanding writes\n");
/* check the shared interrupt */ if (c->irq_flags & IRQF_SHARED) { /* check if the interrupt is enabled */
flags = readl(c->chan_base + BCM2835_DMA_CS); /* if not set then we are not the reason for the irq */ if (!(flags & BCM2835_DMA_INT)) return IRQ_NONE;
}
spin_lock_irqsave(&c->vc.lock, flags);
/* * Clear the INT flag to receive further interrupts. Keep the channel * active in case the descriptor is cyclic or in case the client has * already terminated the descriptor and issued a new one. (May happen * if this IRQ handler is threaded.) If the channel is finished, it * will remain idle despite the ACTIVE flag being set.
*/
writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
c->chan_base + BCM2835_DMA_CS);
d = c->desc;
if (d) { if (d->cyclic) { /* call the cyclic callback */
vchan_cyclic_callback(&d->vd);
} elseif (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
vchan_cookie_complete(&c->desc->vd);
bcm2835_dma_start_desc(c);
}
}
/* * Control blocks are 256 bit in length and must start at a 256 bit * (32 byte) aligned address (BCM2835 ARM Peripherals, sec. 4.2.1.1).
*/
c->cb_pool = dma_pool_create(dev_name(dev), dev, sizeof(struct bcm2835_dma_cb), 32, 0); if (!c->cb_pool) {
dev_err(dev, "unable to allocate descriptor pool\n"); return -ENOMEM;
}
/* if src, dst or len is not given return with an error */ if (!src || !dst || !len) return NULL;
/* calculate number of frames */
frames = bcm2835_dma_frames_for_length(len, max_len);
/* allocate the CB chain - this also fills in the pointers */
d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
info, extra, frames,
src, dst, len, 0, GFP_KERNEL); if (!d) return NULL;
/* Grab configuration */ if (!is_slave_direction(direction)) {
dev_err(chan->device->dev, "%s: bad direction?\n", __func__); return NULL;
}
if (!buf_len) {
dev_err(chan->device->dev, "%s: bad buffer length (= 0)\n", __func__); return NULL;
}
if (flags & DMA_PREP_INTERRUPT)
extra |= BCM2835_DMA_INT_EN; else
period_len = buf_len;
/* * warn if buf_len is not a multiple of period_len - this may leed * to unexpected latencies for interrupts and thus audiable clicks
*/ if (buf_len % period_len)
dev_warn_once(chan->device->dev, "%s: buffer_length (%zd) is not a multiple of period_len (%zd)\n",
__func__, buf_len, period_len);
/* Setup DREQ channel */ if (c->dreq != 0)
info |= BCM2835_DMA_PER_MAP(c->dreq);
if (direction == DMA_DEV_TO_MEM) { if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) return NULL;
src = c->cfg.src_addr;
dst = buf_addr;
info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
} else { if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) return NULL;
dst = c->cfg.dst_addr;
src = buf_addr;
info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
/* non-lite channels can write zeroes w/o accessing memory */ if (buf_addr == od->zero_page && !c->is_lite_channel)
info |= BCM2835_DMA_S_IGNORE;
}
/* calculate number of frames */
frames = /* number of periods */
DIV_ROUND_UP(buf_len, period_len) * /* number of frames per period */
bcm2835_dma_frames_for_length(period_len, max_len);
/* * allocate the CB chain * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine * implementation calls prep_dma_cyclic with interrupts disabled.
*/
d = bcm2835_dma_create_cb_chain(chan, direction, true,
info, extra,
frames, src, dst, buf_len,
period_len, GFP_NOWAIT); if (!d) return NULL;
/* wrap around into a loop */
d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
/* check in DEBUG register if this is a LITE channel */ if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
BCM2835_DMA_DEBUG_LITE)
c->is_lite_channel = true;
od->zero_page = dma_map_page_attrs(od->ddev.dev, ZERO_PAGE(0), 0,
PAGE_SIZE, DMA_TO_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC); if (dma_mapping_error(od->ddev.dev, od->zero_page)) {
dev_err(&pdev->dev, "Failed to map zero page\n"); return -ENOMEM;
}
/* Request DMA channel mask from device tree */ if (of_property_read_u32(pdev->dev.of_node, "brcm,dma-channel-mask",
&chans_available)) {
dev_err(&pdev->dev, "Failed to get channel mask\n");
rc = -EINVAL; goto err_no_dma;
}
/* get irqs for each channel that we support */ for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { /* skip masked out channels */ if (!(chans_available & (1 << i))) {
irq[i] = -1; continue;
}
/* get the named irq */
snprintf(chan_name, sizeof(chan_name), "dma%i", i);
irq[i] = platform_get_irq_byname(pdev, chan_name); if (irq[i] >= 0) continue;
/* legacy device tree case handling */
dev_warn_once(&pdev->dev, "missing interrupt-names property in device tree - legacy interpretation is used\n"); /* * in case of channel >= 11 * use the 11th interrupt and that is shared
*/
irq[i] = platform_get_irq(pdev, i < 11 ? i : 11);
}
/* get irqs for each channel */ for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { /* skip channels without irq */ if (irq[i] < 0) continue;
/* check if there are other channels that also use this irq */
irq_flags = 0; for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++) if ((i != j) && (irq[j] == irq[i])) {
irq_flags = IRQF_SHARED; break;
}
/* initialize the channel */
rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags); if (rc) goto err_no_dma;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.