// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) Ericsson AB 2007-2008 * Copyright (C) ST-Ericsson SA 2008-2010 * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
*/
/** * struct stedma40_platform_data - Configuration struct for the dma device. * * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW * which avoids HW bug that exists in some versions of the controller. * SoftLLI introduces relink overhead that could impact performance for * certain use cases. * @num_of_soft_lli_chans: The number of channels that needs to be configured * to use SoftLLI. * @use_esram_lcla: flag for mapping the lcla into esram region * @num_of_memcpy_chans: The number of channels reserved for memcpy. * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid * for 'multiple of 4' channels, like 8.
*/ struct stedma40_platform_data { int disabled_channels[STEDMA40_MAX_PHYS]; int *soft_lli_chans; int num_of_soft_lli_chans; bool use_esram_lcla; int num_of_memcpy_chans; int num_of_phy_chans;
};
/** * enum d40_command - The different commands and/or statuses. * * @D40_DMA_STOP: DMA channel command STOP or status STOPPED, * @D40_DMA_RUN: The DMA channel is RUNNING of the command RUN. * @D40_DMA_SUSPEND_REQ: Request the DMA to SUSPEND as soon as possible. * @D40_DMA_SUSPENDED: The DMA channel is SUSPENDED.
*/ enum d40_command {
D40_DMA_STOP = 0,
D40_DMA_RUN = 1,
D40_DMA_SUSPEND_REQ = 2,
D40_DMA_SUSPENDED = 3
};
/* * enum d40_events - The different Event Enables for the event lines. * * @D40_DEACTIVATE_EVENTLINE: De-activate Event line, stopping the logical chan. * @D40_ACTIVATE_EVENTLINE: Activate the Event line, to start a logical chan. * @D40_SUSPEND_REQ_EVENTLINE: Requesting for suspending a event line. * @D40_ROUND_EVENTLINE: Status check for event line.
*/
/* * These are the registers that has to be saved and later restored * when the DMA hw is powered off. * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works.
*/ static __maybe_unused u32 d40_backup_regs[] = {
D40_DREG_LCPA,
D40_DREG_LCLA,
D40_DREG_PRMSE,
D40_DREG_PRMSO,
D40_DREG_PRMOE,
D40_DREG_PRMOO,
};
/* * since 9540 and 8540 has the same HW revision * use v4a for 9540 or earlier * use v4b for 8540 or later * HW revision: * DB8500ed has revision 0 * DB8500v1 has revision 2 * DB8500v2 has revision 3 * AP9540v1 has revision 4 * DB8540v1 has revision 4 * TODO: Check if all these registers have to be saved/restored on dma40 v4a
*/ static u32 d40_backup_regs_v4a[] = {
D40_DREG_PSEG1,
D40_DREG_PSEG2,
D40_DREG_PSEG3,
D40_DREG_PSEG4,
D40_DREG_PCEG1,
D40_DREG_PCEG2,
D40_DREG_PCEG3,
D40_DREG_PCEG4,
D40_DREG_RSEG1,
D40_DREG_RSEG2,
D40_DREG_RSEG3,
D40_DREG_RSEG4,
D40_DREG_RCEG1,
D40_DREG_RCEG2,
D40_DREG_RCEG3,
D40_DREG_RCEG4,
};
/** * struct d40_interrupt_lookup - lookup table for interrupt handler * * @src: Interrupt mask register. * @clr: Interrupt clear register. * @is_error: true if this is an error interrupt. * @offset: start delta in the lookup_log_chans in d40_base. If equals to * D40_PHY_CHAN, the lookup_phy_chans shall be used instead.
*/ struct d40_interrupt_lookup {
u32 src;
u32 clr; bool is_error; int offset;
};
/** * struct d40_reg_val - simple lookup struct * * @reg: The register. * @val: The value that belongs to the register in reg.
*/ struct d40_reg_val { unsignedint reg; unsignedint val;
};
static __initdata struct d40_reg_val dma_init_reg_v4a[] = { /* Clock every part of the DMA block from start */
{ .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL},
/** * struct d40_lli_pool - Structure for keeping LLIs in memory * * @base: Pointer to memory area when the pre_alloc_lli's are not large * enough, IE bigger than the most common case, 1 dst and 1 src. NULL if * pre_alloc_lli is used. * @dma_addr: DMA address, if mapped * @size: The size in bytes of the memory at base or the size of pre_alloc_lli. * @pre_alloc_lli: Pre allocated area for the most common case of transfers, * one buffer to one buffer.
*/ struct d40_lli_pool { void *base; int size;
dma_addr_t dma_addr; /* Space for dst and src, plus an extra for padding */
u8 pre_alloc_lli[3 * sizeof(struct d40_phy_lli)];
};
/** * struct d40_desc - A descriptor is one DMA job. * * @lli_phy: LLI settings for physical channel. Both src and dst= * points into the lli_pool, to base if lli_len > 1 or to pre_alloc_lli if * lli_len equals one. * @lli_log: Same as above but for logical channels. * @lli_pool: The pool with two entries pre-allocated. * @lli_len: Number of llis of current descriptor. * @lli_current: Number of transferred llis. * @lcla_alloc: Number of LCLA entries allocated. * @txd: DMA engine struct. Used for among other things for communication * during a transfer. * @node: List entry. * @is_in_client_list: true if the client owns this descriptor. * @cyclic: true if this is a cyclic job * * This descriptor is used for both logical and physical transfers.
*/ struct d40_desc { /* LLI physical */ struct d40_phy_lli_bidir lli_phy; /* LLI logical */ struct d40_log_lli_bidir lli_log;
struct d40_lli_pool lli_pool; int lli_len; int lli_current; int lcla_alloc;
/** * struct d40_lcla_pool - LCLA pool settings and data. * * @base: The virtual address of LCLA. 18 bit aligned. * @dma_addr: DMA address, if mapped * @base_unaligned: The original kmalloc pointer, if kmalloc is used. * This pointer is only there for clean-up on error. * @pages: The number of pages needed for all physical channels. * Only used later for clean-up on error * @lock: Lock to protect the content in this struct. * @alloc_map: big map over which LCLA entry is own by which job.
*/ struct d40_lcla_pool { void *base;
dma_addr_t dma_addr; void *base_unaligned; int pages;
spinlock_t lock; struct d40_desc **alloc_map;
};
/** * struct d40_phy_res - struct for handling eventlines mapped to physical * channels. * * @lock: A lock protection this entity. * @reserved: True if used by secure world or otherwise. * @num: The physical channel number of this entity. * @allocated_src: Bit mapped to show which src event line's are mapped to * this physical channel. Can also be free or physically allocated. * @allocated_dst: Same as for src but is dst. * allocated_dst and allocated_src uses the D40_ALLOC* defines as well as * event line number. * @use_soft_lli: To mark if the linked lists of channel are managed by SW.
*/ struct d40_phy_res {
spinlock_t lock; bool reserved; int num;
u32 allocated_src;
u32 allocated_dst; bool use_soft_lli;
};
struct d40_base;
/** * struct d40_chan - Struct that describes a channel. * * @lock: A spinlock to protect this struct. * @log_num: The logical number, if any of this channel. * @pending_tx: The number of pending transfers. Used between interrupt handler * and tasklet. * @busy: Set to true when transfer is ongoing on this channel. * @phy_chan: Pointer to physical channel which this instance runs on. If this * point is NULL, then the channel is not allocated. * @chan: DMA engine handle. * @tasklet: Tasklet that gets scheduled from interrupt context to complete a * transfer and call client callback. * @client: Cliented owned descriptor list. * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. * @done: Completed jobs * @queue: Queued jobs. * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. * @slave_config: DMA slave configuration. * @configured: whether the dma_cfg configuration is valid * @base: Pointer to the device instance struct. * @src_def_cfg: Default cfg register setting for src. * @dst_def_cfg: Default cfg register setting for dst. * @log_def: Default logical channel settings. * @lcpa: Pointer to dst and src lcpa settings. * @runtime_addr: runtime configured address. * @runtime_direction: runtime configured direction. * * This struct can either "be" a logical or a physical channel.
*/ struct d40_chan {
spinlock_t lock; int log_num; int pending_tx; bool busy; struct d40_phy_res *phy_chan; struct dma_chan chan; struct tasklet_struct tasklet; struct list_head client; struct list_head pending_queue; struct list_head active; struct list_head done; struct list_head queue; struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; struct dma_slave_config slave_config; bool configured; struct d40_base *base; /* Default register configurations */
u32 src_def_cfg;
u32 dst_def_cfg; struct d40_def_lcsp log_def; struct d40_log_lli_full *lcpa; /* Runtime reconfiguration */
dma_addr_t runtime_addr; enum dma_transfer_direction runtime_direction;
};
/** * struct d40_gen_dmac - generic values to represent u8500/u8540 DMA * controller * * @backup: the pointer to the registers address array for backup * @backup_size: the size of the registers address array for backup * @realtime_en: the realtime enable register * @realtime_clear: the realtime clear register * @high_prio_en: the high priority enable register * @high_prio_clear: the high priority clear register * @interrupt_en: the interrupt enable register * @interrupt_clear: the interrupt clear register * @il: the pointer to struct d40_interrupt_lookup * @il_size: the size of d40_interrupt_lookup array * @init_reg: the pointer to the struct d40_reg_val * @init_reg_size: the size of d40_reg_val array
*/ struct d40_gen_dmac {
u32 *backup;
u32 backup_size;
u32 realtime_en;
u32 realtime_clear;
u32 high_prio_en;
u32 high_prio_clear;
u32 interrupt_en;
u32 interrupt_clear; struct d40_interrupt_lookup *il;
u32 il_size; struct d40_reg_val *init_reg;
u32 init_reg_size;
};
/** * struct d40_base - The big global struct, one for each probe'd instance. * * @interrupt_lock: Lock used to make sure one interrupt is handle a time. * @execmd_lock: Lock for execute command usage since several channels share * the same physical register. * @dev: The device structure. * @virtbase: The virtual base address of the DMA's register. * @rev: silicon revision detected. * @clk: Pointer to the DMA clock structure. * @irq: The IRQ number. * @num_memcpy_chans: The number of channels used for memcpy (mem-to-mem * transfers). * @num_phy_chans: The number of physical channels. Read from HW. This * is the number of available channels for this driver, not counting "Secure * mode" allocated physical channels. * @num_log_chans: The number of logical channels. Calculated from * num_phy_chans. * @dma_both: dma_device channels that can do both memcpy and slave transfers. * @dma_slave: dma_device channels that can do only do slave transfers. * @dma_memcpy: dma_device channels that can do only do memcpy transfers. * @phy_chans: Room for all possible physical channels in system. * @log_chans: Room for all possible logical channels in system. * @lookup_log_chans: Used to map interrupt number to logical channel. Points * to log_chans entries. * @lookup_phy_chans: Used to map interrupt number to physical channel. Points * to phy_chans entries. * @plat_data: Pointer to provided platform_data which is the driver * configuration. * @lcpa_regulator: Pointer to hold the regulator for the esram bank for lcla. * @phy_res: Vector containing all physical channels. * @lcla_pool: lcla pool settings and data. * @lcpa_base: The virtual mapped address of LCPA. * @phy_lcpa: The physical address of the LCPA. * @lcpa_size: The size of the LCPA area. * @desc_slab: cache for descriptors. * @reg_val_backup: Here the values of some hardware registers are stored * before the DMA is powered off. They are restored when the power is back on. * @reg_val_backup_v4: Backup of registers that only exits on dma40 v3 and * later * @reg_val_backup_chan: Backup data for standard channel parameter registers. * @regs_interrupt: Scratch space for registers during interrupt. * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off. * @gen_dmac: the struct for generic registers values to represent u8500/8540 * DMA controller
*/ struct d40_base {
spinlock_t interrupt_lock;
spinlock_t execmd_lock; struct device *dev; void __iomem *virtbase;
u8 rev:4; struct clk *clk; int irq; int num_memcpy_chans; int num_phy_chans; int num_log_chans; struct dma_device dma_both; struct dma_device dma_slave; struct dma_device dma_memcpy; struct d40_chan *phy_chans; struct d40_chan *log_chans; struct d40_chan **lookup_log_chans; struct d40_chan **lookup_phy_chans; struct stedma40_platform_data *plat_data; struct regulator *lcpa_regulator; /* Physical half channels */ struct d40_phy_res *phy_res; struct d40_lcla_pool lcla_pool; void *lcpa_base;
dma_addr_t phy_lcpa;
resource_size_t lcpa_size; struct kmem_cache *desc_slab;
u32 reg_val_backup[BACKUP_REGS_SZ];
u32 reg_val_backup_v4[BACKUP_REGS_SZ_MAX];
u32 *reg_val_backup_chan;
u32 *regs_interrupt;
u16 gcc_pwr_off_mask; struct d40_gen_dmac gen_dmac;
};
/* * Allocate both src and dst at the same time, therefore the half * start on 1 since 0 can't be used since zero is used as end marker.
*/ for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i;
if (!d40c->base->lcla_pool.alloc_map[idx]) {
d40c->base->lcla_pool.alloc_map[idx] = d40d;
d40d->lcla_alloc++;
ret = i; break;
}
}
writel(lli_src->reg_cfg, base + D40_CHAN_REG_SSCFG);
writel(lli_src->reg_elt, base + D40_CHAN_REG_SSELT);
writel(lli_src->reg_ptr, base + D40_CHAN_REG_SSPTR);
writel(lli_src->reg_lnk, base + D40_CHAN_REG_SSLNK);
writel(lli_dst->reg_cfg, base + D40_CHAN_REG_SDCFG);
writel(lli_dst->reg_elt, base + D40_CHAN_REG_SDELT);
writel(lli_dst->reg_ptr, base + D40_CHAN_REG_SDPTR);
writel(lli_dst->reg_lnk, base + D40_CHAN_REG_SDLNK);
}
/* * We may have partially running cyclic transfers, in case we did't get * enough LCLA entries.
*/
linkback = cyclic && lli_current == 0;
/* * For linkback, we need one LCLA even with only one link, because we * can't link back to the one in LCPA space
*/ if (linkback || (lli_len - lli_current > 1)) { /* * If the channel is expected to use only soft_lli don't * allocate a lcla. This is to avoid a HW issue that exists * in some controller during a peripheral to memory transfer * that uses linked lists.
*/ if (!(chan->phy_chan->use_soft_lli &&
chan->dma_cfg.dir == DMA_DEV_TO_MEM))
curr_lcla = d40_lcla_alloc_one(chan, desc);
first_lcla = curr_lcla;
}
/* * For linkback, we normally load the LCPA in the loop since we need to * link it to the second LCLA and not the first. However, if we * couldn't even get a first LCLA, then we have to run in LCPA and * reload manually.
*/ if (!linkback || curr_lcla == -EINVAL) { unsignedint flags = 0;
if (cyclic || next_lcla == -EINVAL)
flags |= LLI_TERM_INT;
if (linkback && curr_lcla == first_lcla) { /* First link goes in both LCPA and LCLA */
d40_log_lli_lcpa_write(chan->lcpa,
&lli->dst[lli_current],
&lli->src[lli_current],
next_lcla, flags);
}
/* * One unused LCLA in the cyclic case if the very first * next_lcla fails...
*/
d40_log_lli_lcla_write(lcla,
&lli->dst[lli_current],
&lli->src[lli_current],
next_lcla, flags);
/* * Cache maintenance is not needed if lcla is * mapped in esram
*/ if (!use_esram_lcla) {
dma_sync_single_range_for_device(chan->base->dev,
pool->dma_addr, lcla_offset,
2 * sizeof(struct d40_log_lli),
DMA_TO_DEVICE);
}
curr_lcla = next_lcla;
/* remove desc from current queue and add it to the pending_queue */ staticvoid d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
{
d40_desc_remove(desc);
desc->is_in_client_list = false;
list_add_tail(&desc->node, &d40c->pending_queue);
}
staticint d40_psize_2_burst_size(bool is_log, int psize)
{ if (is_log) { if (psize == STEDMA40_PSIZE_LOG_1) return 1;
} else { if (psize == STEDMA40_PSIZE_PHY_1) return 1;
}
return 2 << psize;
}
/* * The dma only supports transmitting packages up to * STEDMA40_MAX_SEG_SIZE * data_width, where data_width is stored in Bytes. * * Calculate the total number of dma elements required to send the entire sg list.
*/ staticint d40_size_2_dmalen(int size, u32 data_width1, u32 data_width2)
{ int dmalen;
u32 max_w = max(data_width1, data_width2);
u32 min_w = min(data_width1, data_width2);
u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE * min_w, max_w);
if (seg_max > STEDMA40_MAX_SEG_SIZE)
seg_max -= max_w;
staticint d40_sg_2_dmalen(struct scatterlist *sgl, int sg_len,
u32 data_width1, u32 data_width2)
{ struct scatterlist *sg; int i; int len = 0; int ret;
for_each_sg(sgl, sg, sg_len, i) {
ret = d40_size_2_dmalen(sg_dma_len(sg),
data_width1, data_width2); if (ret < 0) return ret;
len += ret;
} return len;
}
staticint __d40_execute_command_phy(struct d40_chan *d40c, enum d40_command command)
{
u32 status; int i; void __iomem *active_reg; int ret = 0; unsignedlong flags;
u32 wmask;
if (command == D40_DMA_STOP) {
ret = __d40_execute_command_phy(d40c, D40_DMA_SUSPEND_REQ); if (ret) return ret;
}
for (i = 0 ; i < D40_SUSPEND_MAX_IT; i++) {
status = (readl(active_reg) &
D40_CHAN_POS_MASK(d40c->phy_chan->num)) >>
D40_CHAN_POS(d40c->phy_chan->num);
cpu_relax(); /* * Reduce the number of bus accesses while * waiting for the DMA to suspend.
*/
udelay(3);
if (status == D40_DMA_STOP ||
status == D40_DMA_SUSPENDED) break;
}
if (i == D40_SUSPEND_MAX_IT) {
chan_err(d40c, "unable to suspend the chl %d (log: %d) status %x\n",
d40c->phy_chan->num, d40c->log_num,
status);
dump_stack();
ret = -EBUSY;
}
for (tries = 0 ; tries < D40_SUSPEND_MAX_IT; tries++) {
status = (readl(addr) & D40_EVENTLINE_MASK(event)) >>
D40_EVENTLINE_POS(event);
cpu_relax(); /* * Reduce the number of bus accesses while * waiting for the DMA to suspend.
*/
udelay(3);
if (status == D40_DEACTIVATE_EVENTLINE) break;
}
if (tries == D40_SUSPEND_MAX_IT) {
chan_err(d40c, "unable to stop the event_line chl %d (log: %d)" "status %x\n", d40c->phy_chan->num,
d40c->log_num, status);
} break;
case D40_ACTIVATE_EVENTLINE: /* * The hardware sometimes doesn't register the enable when src and dst * event lines are active on the same logical channel. Retry to ensure * it does. Usually only one retry is sufficient.
*/
tries = 100; while (--tries) {
writel((D40_ACTIVATE_EVENTLINE <<
D40_EVENTLINE_POS(event)) |
~D40_EVENTLINE_MASK(event), addr);
if (readl(addr) & D40_EVENTLINE_MASK(event)) break;
}
/* If bytes left to transfer or linked tx resume job */ if (d40_residue(d40c) || d40_tx_is_linked(d40c))
res = d40_channel_execute_command(d40c, D40_DMA_RUN);
/* Start queued jobs, if any */
d40d = d40_first_queued(d40c);
if (d40d != NULL) { if (!d40c->busy) {
d40c->busy = true;
pm_runtime_get_sync(d40c->base->dev);
}
/* Remove from queue */
d40_desc_remove(d40d);
/* Add to active queue */
d40_desc_submit(d40c, d40d);
/* Initiate DMA job */
d40_desc_load(d40c, d40d);
/* Start dma job */
err = d40_start(d40c);
if (err) return NULL;
}
return d40d;
}
/* called from interrupt context */ staticvoid dma_tc_handle(struct d40_chan *d40c)
{ struct d40_desc *d40d;
/* Get first active entry from list */
d40d = d40_first_active_get(d40c);
if (d40d == NULL) return;
if (d40d->cyclic) { /* * If this was a paritially loaded list, we need to reloaded * it, and only when the list is completed. We need to check * for done because the interrupt will hit for every link, and * not just the last one.
*/ if (d40d->lli_current < d40d->lli_len
&& !d40_tx_is_linked(d40c)
&& !d40_residue(d40c)) {
d40_lcla_free_all(d40c, d40d);
d40_desc_load(d40c, d40d);
(void) d40_start(d40c);
/* Get first entry from the done list */
d40d = d40_first_done(d40c); if (d40d == NULL) { /* Check if we have reached here for cyclic job */
d40d = d40_first_active_get(d40c); if (d40d == NULL || !d40d->cyclic) goto check_pending_tx;
}
if (!d40d->cyclic)
dma_cookie_complete(&d40d->txd);
/* * If terminating a channel pending_tx is set to zero. * This prevents any finished active jobs to return to the client.
*/ if (d40c->pending_tx == 0) {
spin_unlock_irqrestore(&d40c->lock, flags); return;
}
staticint d40_validate_conf(struct d40_chan *d40c, struct stedma40_chan_cfg *conf)
{ int res = 0; bool is_log = conf->mode == STEDMA40_MODE_LOGICAL;
if (!conf->dir) {
chan_err(d40c, "Invalid direction.\n");
res = -EINVAL;
}
if ((is_log && conf->dev_type > d40c->base->num_log_chans) ||
(!is_log && conf->dev_type > d40c->base->num_phy_chans) ||
(conf->dev_type < 0)) {
chan_err(d40c, "Invalid device type (%d)\n", conf->dev_type);
res = -EINVAL;
}
if (conf->dir == DMA_DEV_TO_DEV) { /* * DMAC HW supports it. Will be added to this driver, * in case any dma client requires it.
*/
chan_err(d40c, "periph to periph not supported\n");
res = -EINVAL;
}
if (d40_psize_2_burst_size(is_log, conf->src_info.psize) *
conf->src_info.data_width !=
d40_psize_2_burst_size(is_log, conf->dst_info.psize) *
conf->dst_info.data_width) { /* * The DMAC hardware only supports * src (burst x width) == dst (burst x width)
*/
chan_err(d40c, "src (burst x width) != dst (burst x width)\n");
res = -EINVAL;
}
staticint d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
{ int dev_type = d40c->dma_cfg.dev_type; int event_group; int event_line; struct d40_phy_res *phys; int i; int j; int log_num; int num_phy_chans; bool is_src; bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL;
/* * Spread logical channels across all available physical rather * than pack every logical channel at the first available phy * channels.
*/ if (is_src) { for (i = phy_num; i < phy_num + 2; i++) { if (d40_alloc_mask_set(&phys[i], is_src,
event_line, is_log,
first_phy_user)) goto found_log;
}
} else { for (i = phy_num + 1; i >= phy_num; i--) { if (d40_alloc_mask_set(&phys[i], is_src,
event_line, is_log,
first_phy_user)) goto found_log;
}
}
} return -EINVAL;
if (chan_is_logical(chan))
ret = d40_prep_sg_log(chan, desc, sg_src, sg_dst,
sg_len, src_dev_addr, dst_dev_addr); else
ret = d40_prep_sg_phy(chan, desc, sg_src, sg_dst,
sg_len, src_dev_addr, dst_dev_addr);
if (ret) {
chan_err(chan, "Failed to prepare %s sg job: %d\n",
chan_is_logical(chan) ? "log" : "phy", ret); goto free_desc;
}
/* * add descriptor to the prepare queue in order to be able * to free them later in terminate_all
*/
list_add_tail(&desc->node, &chan->prepare_queue);
rtreg = realtime ? dmac->realtime_en : dmac->realtime_clear; /* * Due to a hardware bug, in some cases a logical channel triggered by * a high priority destination event line can generate extra packet * transactions. * * The workaround is to not set the high priority level for the * destination event lines that trigger logical channels.
*/ if (!src && chan_is_logical(d40c))
highprio = false;
/* If no dma configuration is set use default configuration (memcpy) */ if (!d40c->configured) {
err = d40_config_memcpy(d40c); if (err) {
chan_err(d40c, "Failed to configure memcpy channel\n"); goto mark_last_busy;
}
}
err = d40_allocate_channel(d40c, &is_free_phy); if (err) {
chan_err(d40c, "Failed to allocate channel\n");
d40c->configured = false; goto mark_last_busy;
}
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.