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 + -
显示快捷键?