iop-adma.c
来自「linux 内核源代码」· C语言 代码 · 共 1,467 行 · 第 1/3 页
C
1,467 行
/* * offload engine driver for the Intel Xscale series of i/o processors * Copyright © 2006, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * *//* * This driver supports the asynchrounous DMA copy and RAID engines available * on the Intel Xscale(R) family of I/O Processors (IOP 32x, 33x, 134x) */#include <linux/init.h>#include <linux/module.h>#include <linux/async_tx.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/memory.h>#include <linux/ioport.h>#include <asm/arch/adma.h>#define to_iop_adma_chan(chan) container_of(chan, struct iop_adma_chan, common)#define to_iop_adma_device(dev) \ container_of(dev, struct iop_adma_device, common)#define tx_to_iop_adma_slot(tx) \ container_of(tx, struct iop_adma_desc_slot, async_tx)/** * iop_adma_free_slots - flags descriptor slots for reuse * @slot: Slot to free * Caller must hold &iop_chan->lock while calling this function */static void iop_adma_free_slots(struct iop_adma_desc_slot *slot){ int stride = slot->slots_per_op; while (stride--) { slot->slots_per_op = 0; slot = list_entry(slot->slot_node.next, struct iop_adma_desc_slot, slot_node); }}static dma_cookie_tiop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc, struct iop_adma_chan *iop_chan, dma_cookie_t cookie){ BUG_ON(desc->async_tx.cookie < 0); spin_lock_bh(&desc->async_tx.lock); if (desc->async_tx.cookie > 0) { cookie = desc->async_tx.cookie; desc->async_tx.cookie = 0; /* call the callback (must not sleep or submit new * operations to this channel) */ if (desc->async_tx.callback) desc->async_tx.callback( desc->async_tx.callback_param); /* unmap dma addresses * (unmap_single vs unmap_page?) */ if (desc->group_head && desc->unmap_len) { struct iop_adma_desc_slot *unmap = desc->group_head; struct device *dev = &iop_chan->device->pdev->dev; u32 len = unmap->unmap_len; u32 src_cnt = unmap->unmap_src_cnt; dma_addr_t addr = iop_desc_get_dest_addr(unmap, iop_chan); dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); while (src_cnt--) { addr = iop_desc_get_src_addr(unmap, iop_chan, src_cnt); dma_unmap_page(dev, addr, len, DMA_TO_DEVICE); } desc->group_head = NULL; } } /* run dependent operations */ async_tx_run_dependencies(&desc->async_tx); spin_unlock_bh(&desc->async_tx.lock); return cookie;}static intiop_adma_clean_slot(struct iop_adma_desc_slot *desc, struct iop_adma_chan *iop_chan){ /* the client is allowed to attach dependent operations * until 'ack' is set */ if (!desc->async_tx.ack) return 0; /* leave the last descriptor in the chain * so we can append to it */ if (desc->chain_node.next == &iop_chan->chain) return 1; dev_dbg(iop_chan->device->common.dev, "\tfree slot: %d slots_per_op: %d\n", desc->idx, desc->slots_per_op); list_del(&desc->chain_node); iop_adma_free_slots(desc); return 0;}static void __iop_adma_slot_cleanup(struct iop_adma_chan *iop_chan){ struct iop_adma_desc_slot *iter, *_iter, *grp_start = NULL; dma_cookie_t cookie = 0; u32 current_desc = iop_chan_get_current_descriptor(iop_chan); int busy = iop_chan_is_busy(iop_chan); int seen_current = 0, slot_cnt = 0, slots_per_op = 0; dev_dbg(iop_chan->device->common.dev, "%s\n", __FUNCTION__); /* free completed slots from the chain starting with * the oldest descriptor */ list_for_each_entry_safe(iter, _iter, &iop_chan->chain, chain_node) { pr_debug("\tcookie: %d slot: %d busy: %d " "this_desc: %#x next_desc: %#x ack: %d\n", iter->async_tx.cookie, iter->idx, busy, iter->async_tx.phys, iop_desc_get_next_desc(iter), iter->async_tx.ack); prefetch(_iter); prefetch(&_iter->async_tx); /* do not advance past the current descriptor loaded into the * hardware channel, subsequent descriptors are either in * process or have not been submitted */ if (seen_current) break; /* stop the search if we reach the current descriptor and the * channel is busy, or if it appears that the current descriptor * needs to be re-read (i.e. has been appended to) */ if (iter->async_tx.phys == current_desc) { BUG_ON(seen_current++); if (busy || iop_desc_get_next_desc(iter)) break; } /* detect the start of a group transaction */ if (!slot_cnt && !slots_per_op) { slot_cnt = iter->slot_cnt; slots_per_op = iter->slots_per_op; if (slot_cnt <= slots_per_op) { slot_cnt = 0; slots_per_op = 0; } } if (slot_cnt) { pr_debug("\tgroup++\n"); if (!grp_start) grp_start = iter; slot_cnt -= slots_per_op; } /* all the members of a group are complete */ if (slots_per_op != 0 && slot_cnt == 0) { struct iop_adma_desc_slot *grp_iter, *_grp_iter; int end_of_chain = 0; pr_debug("\tgroup end\n"); /* collect the total results */ if (grp_start->xor_check_result) { u32 zero_sum_result = 0; slot_cnt = grp_start->slot_cnt; grp_iter = grp_start; list_for_each_entry_from(grp_iter, &iop_chan->chain, chain_node) { zero_sum_result |= iop_desc_get_zero_result(grp_iter); pr_debug("\titer%d result: %d\n", grp_iter->idx, zero_sum_result); slot_cnt -= slots_per_op; if (slot_cnt == 0) break; } pr_debug("\tgrp_start->xor_check_result: %p\n", grp_start->xor_check_result); *grp_start->xor_check_result = zero_sum_result; } /* clean up the group */ slot_cnt = grp_start->slot_cnt; grp_iter = grp_start; list_for_each_entry_safe_from(grp_iter, _grp_iter, &iop_chan->chain, chain_node) { cookie = iop_adma_run_tx_complete_actions( grp_iter, iop_chan, cookie); slot_cnt -= slots_per_op; end_of_chain = iop_adma_clean_slot(grp_iter, iop_chan); if (slot_cnt == 0 || end_of_chain) break; } /* the group should be complete at this point */ BUG_ON(slot_cnt); slots_per_op = 0; grp_start = NULL; if (end_of_chain) break; else continue; } else if (slots_per_op) /* wait for group completion */ continue; /* write back zero sum results (single descriptor case) */ if (iter->xor_check_result && iter->async_tx.cookie) *iter->xor_check_result = iop_desc_get_zero_result(iter); cookie = iop_adma_run_tx_complete_actions( iter, iop_chan, cookie); if (iop_adma_clean_slot(iter, iop_chan)) break; } BUG_ON(!seen_current); iop_chan_idle(busy, iop_chan); if (cookie > 0) { iop_chan->completed_cookie = cookie; pr_debug("\tcompleted cookie %d\n", cookie); }}static voidiop_adma_slot_cleanup(struct iop_adma_chan *iop_chan){ spin_lock_bh(&iop_chan->lock); __iop_adma_slot_cleanup(iop_chan); spin_unlock_bh(&iop_chan->lock);}static void iop_adma_tasklet(unsigned long data){ struct iop_adma_chan *chan = (struct iop_adma_chan *) data; __iop_adma_slot_cleanup(chan);}static struct iop_adma_desc_slot *iop_adma_alloc_slots(struct iop_adma_chan *iop_chan, int num_slots, int slots_per_op){ struct iop_adma_desc_slot *iter, *_iter, *alloc_start = NULL; struct list_head chain = LIST_HEAD_INIT(chain); int slots_found, retry = 0; /* start search from the last allocated descrtiptor * if a contiguous allocation can not be found start searching * from the beginning of the list */retry: slots_found = 0; if (retry == 0) iter = iop_chan->last_used; else iter = list_entry(&iop_chan->all_slots, struct iop_adma_desc_slot, slot_node); list_for_each_entry_safe_continue( iter, _iter, &iop_chan->all_slots, slot_node) { prefetch(_iter); prefetch(&_iter->async_tx); if (iter->slots_per_op) { /* give up after finding the first busy slot * on the second pass through the list */ if (retry) break; slots_found = 0; continue; } /* start the allocation if the slot is correctly aligned */ if (!slots_found++) { if (iop_desc_is_aligned(iter, slots_per_op)) alloc_start = iter; else { slots_found = 0; continue; } } if (slots_found == num_slots) { struct iop_adma_desc_slot *alloc_tail = NULL; struct iop_adma_desc_slot *last_used = NULL; iter = alloc_start; while (num_slots) { int i; dev_dbg(iop_chan->device->common.dev, "allocated slot: %d " "(desc %p phys: %#x) slots_per_op %d\n", iter->idx, iter->hw_desc, iter->async_tx.phys, slots_per_op); /* pre-ack all but the last descriptor */ if (num_slots != slots_per_op) iter->async_tx.ack = 1; else iter->async_tx.ack = 0; list_add_tail(&iter->chain_node, &chain); alloc_tail = iter; iter->async_tx.cookie = 0; iter->slot_cnt = num_slots; iter->xor_check_result = NULL; for (i = 0; i < slots_per_op; i++) { iter->slots_per_op = slots_per_op - i; last_used = iter; iter = list_entry(iter->slot_node.next, struct iop_adma_desc_slot, slot_node); } num_slots -= slots_per_op; } alloc_tail->group_head = alloc_start; alloc_tail->async_tx.cookie = -EBUSY; list_splice(&chain, &alloc_tail->async_tx.tx_list); iop_chan->last_used = last_used; iop_desc_clear_next_desc(alloc_start); iop_desc_clear_next_desc(alloc_tail); return alloc_tail; } } if (!retry++) goto retry; /* try to free some slots if the allocation fails */ tasklet_schedule(&iop_chan->irq_tasklet); return NULL;}static dma_cookie_tiop_desc_assign_cookie(struct iop_adma_chan *iop_chan, struct iop_adma_desc_slot *desc){ dma_cookie_t cookie = iop_chan->common.cookie; cookie++; if (cookie < 0) cookie = 1; iop_chan->common.cookie = desc->async_tx.cookie = cookie; return cookie;}static void iop_adma_check_threshold(struct iop_adma_chan *iop_chan){ dev_dbg(iop_chan->device->common.dev, "pending: %d\n", iop_chan->pending); if (iop_chan->pending >= IOP_ADMA_THRESHOLD) { iop_chan->pending = 0; iop_chan_append(iop_chan); }}static dma_cookie_tiop_adma_tx_submit(struct dma_async_tx_descriptor *tx){ struct iop_adma_desc_slot *sw_desc = tx_to_iop_adma_slot(tx); struct iop_adma_chan *iop_chan = to_iop_adma_chan(tx->chan); struct iop_adma_desc_slot *grp_start, *old_chain_tail; int slot_cnt; int slots_per_op; dma_cookie_t cookie; grp_start = sw_desc->group_head; slot_cnt = grp_start->slot_cnt; slots_per_op = grp_start->slots_per_op; spin_lock_bh(&iop_chan->lock); cookie = iop_desc_assign_cookie(iop_chan, sw_desc); old_chain_tail = list_entry(iop_chan->chain.prev, struct iop_adma_desc_slot, chain_node); list_splice_init(&sw_desc->async_tx.tx_list, &old_chain_tail->chain_node); /* fix up the hardware chain */ iop_desc_set_next_desc(old_chain_tail, grp_start->async_tx.phys); /* 1/ don't add pre-chained descriptors * 2/ dummy read to flush next_desc write */ BUG_ON(iop_desc_get_next_desc(sw_desc)); /* increment the pending count by the number of slots * memcpy operations have a 1:1 (slot:operation) relation * other operations are heavier and will pop the threshold * more often. */ iop_chan->pending += slot_cnt; iop_adma_check_threshold(iop_chan); spin_unlock_bh(&iop_chan->lock); dev_dbg(iop_chan->device->common.dev, "%s cookie: %d slot: %d\n", __FUNCTION__, sw_desc->async_tx.cookie, sw_desc->idx); return cookie;}static voidiop_adma_set_dest(dma_addr_t addr, struct dma_async_tx_descriptor *tx, int index){ struct iop_adma_desc_slot *sw_desc = tx_to_iop_adma_slot(tx); struct iop_adma_chan *iop_chan = to_iop_adma_chan(tx->chan); /* to do: support transfers lengths > IOP_ADMA_MAX_BYTE_COUNT */ iop_desc_set_dest_addr(sw_desc->group_head, iop_chan, addr);}static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan);static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan);/* returns the number of allocated descriptors */static int iop_adma_alloc_chan_resources(struct dma_chan *chan){ char *hw_desc; int idx; struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); struct iop_adma_desc_slot *slot = NULL; int init = iop_chan->slots_allocated ? 0 : 1; struct iop_adma_platform_data *plat_data = iop_chan->device->pdev->dev.platform_data; int num_descs_in_pool = plat_data->pool_size/IOP_ADMA_SLOT_SIZE; /* Allocate descriptor slots */ do { idx = iop_chan->slots_allocated; if (idx == num_descs_in_pool) break; slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) { printk(KERN_INFO "IOP ADMA Channel only initialized" " %d descriptor slots", idx); break; } hw_desc = (char *) iop_chan->device->dma_desc_pool_virt; slot->hw_desc = (void *) &hw_desc[idx * IOP_ADMA_SLOT_SIZE]; dma_async_tx_descriptor_init(&slot->async_tx, chan); slot->async_tx.tx_submit = iop_adma_tx_submit; slot->async_tx.tx_set_dest = iop_adma_set_dest;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?