static int ca91cx42_dma_list_add()

in bridges/vme_ca91cx42.c [1018:1160]


static int ca91cx42_dma_list_add(struct vme_dma_list *list,
	struct vme_dma_attr *src, struct vme_dma_attr *dest, size_t count)
{
	struct ca91cx42_dma_entry *entry, *prev;
	struct vme_dma_pci *pci_attr;
	struct vme_dma_vme *vme_attr;
	dma_addr_t desc_ptr;
	int retval = 0;
	struct device *dev;

	dev = list->parent->parent->parent;

	/* XXX descriptor must be aligned on 64-bit boundaries */
	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
	if (!entry) {
		retval = -ENOMEM;
		goto err_mem;
	}

	/* Test descriptor alignment */
	if ((unsigned long)&entry->descriptor & CA91CX42_DCPP_M) {
		dev_err(dev, "Descriptor not aligned to 16 byte boundary as "
			"required: %p\n", &entry->descriptor);
		retval = -EINVAL;
		goto err_align;
	}

	memset(&entry->descriptor, 0, sizeof(entry->descriptor));

	if (dest->type == VME_DMA_VME) {
		entry->descriptor.dctl |= CA91CX42_DCTL_L2V;
		vme_attr = dest->private;
		pci_attr = src->private;
	} else {
		vme_attr = src->private;
		pci_attr = dest->private;
	}

	/* Check we can do fulfill required attributes */
	if ((vme_attr->aspace & ~(VME_A16 | VME_A24 | VME_A32 | VME_USER1 |
		VME_USER2)) != 0) {

		dev_err(dev, "Unsupported cycle type\n");
		retval = -EINVAL;
		goto err_aspace;
	}

	if ((vme_attr->cycle & ~(VME_SCT | VME_BLT | VME_SUPER | VME_USER |
		VME_PROG | VME_DATA)) != 0) {

		dev_err(dev, "Unsupported cycle type\n");
		retval = -EINVAL;
		goto err_cycle;
	}

	/* Check to see if we can fulfill source and destination */
	if (!(((src->type == VME_DMA_PCI) && (dest->type == VME_DMA_VME)) ||
		((src->type == VME_DMA_VME) && (dest->type == VME_DMA_PCI)))) {

		dev_err(dev, "Cannot perform transfer with this "
			"source-destination combination\n");
		retval = -EINVAL;
		goto err_direct;
	}

	/* Setup cycle types */
	if (vme_attr->cycle & VME_BLT)
		entry->descriptor.dctl |= CA91CX42_DCTL_VCT_BLT;

	/* Setup data width */
	switch (vme_attr->dwidth) {
	case VME_D8:
		entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D8;
		break;
	case VME_D16:
		entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D16;
		break;
	case VME_D32:
		entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D32;
		break;
	case VME_D64:
		entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D64;
		break;
	default:
		dev_err(dev, "Invalid data width\n");
		return -EINVAL;
	}

	/* Setup address space */
	switch (vme_attr->aspace) {
	case VME_A16:
		entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A16;
		break;
	case VME_A24:
		entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A24;
		break;
	case VME_A32:
		entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A32;
		break;
	case VME_USER1:
		entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER1;
		break;
	case VME_USER2:
		entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER2;
		break;
	default:
		dev_err(dev, "Invalid address space\n");
		return -EINVAL;
		break;
	}

	if (vme_attr->cycle & VME_SUPER)
		entry->descriptor.dctl |= CA91CX42_DCTL_SUPER_SUPR;
	if (vme_attr->cycle & VME_PROG)
		entry->descriptor.dctl |= CA91CX42_DCTL_PGM_PGM;

	entry->descriptor.dtbc = count;
	entry->descriptor.dla = pci_attr->address;
	entry->descriptor.dva = vme_attr->address;
	entry->descriptor.dcpp = CA91CX42_DCPP_NULL;

	/* Add to list */
	list_add_tail(&entry->list, &list->entries);

	/* Fill out previous descriptors "Next Address" */
	if (entry->list.prev != &list->entries) {
		prev = list_entry(entry->list.prev, struct ca91cx42_dma_entry,
			list);
		/* We need the bus address for the pointer */
		desc_ptr = virt_to_bus(&entry->descriptor);
		prev->descriptor.dcpp = desc_ptr & ~CA91CX42_DCPP_M;
	}

	return 0;

err_cycle:
err_aspace:
err_direct:
err_align:
	kfree(entry);
err_mem:
	return retval;
}