[PATCH] Staging: VME: Correct TSI148 DMA handling on Intel platforms

Martyn Welch martyn.welch at ge.com
Wed Mar 21 15:33:14 UTC 2012


The DMA functionality fails to work on Intel based platforms. There are
two reasons for this:

1) Some recent Intel platforms have an IOMMU. Transferring the DMA
   descriptors, which were mapped using virt_to_phys(), failed. This
   patch updates the driver to use dma_map_single().

2) The DMA copies the link list descriptors from memory into big endian
   registers. On little endian systems this results in the values being
   byte swapped. This patch uses standard kernel functionality to ensure
   that the descriptors are stored in big endian format.

Signed-off-by: Martyn Welch <martyn.welch at ge.com>
---
 drivers/staging/vme/bridges/vme_tsi148.c |  169 ++++++++++++++++++------------
 drivers/staging/vme/bridges/vme_tsi148.h |    1 +
 2 files changed, 101 insertions(+), 69 deletions(-)

diff --git a/drivers/staging/vme/bridges/vme_tsi148.c b/drivers/staging/vme/bridges/vme_tsi148.c
index f505821..b7e0d15 100644
--- a/drivers/staging/vme/bridges/vme_tsi148.c
+++ b/drivers/staging/vme/bridges/vme_tsi148.c
@@ -29,6 +29,7 @@
 #include <linux/time.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/byteorder/generic.h>
 
 #include "../vme.h"
 #include "../vme_bridge.h"
@@ -1418,48 +1419,52 @@ static unsigned int tsi148_master_rmw(struct vme_master_resource *image,
 static int tsi148_dma_set_vme_src_attributes(struct device *dev, u32 *attr,
 	u32 aspace, u32 cycle, u32 dwidth)
 {
+	u32 val;
+
+	val = be32_to_cpu(*attr);
+
 	/* Setup 2eSST speeds */
 	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
 	case VME_2eSST160:
-		*attr |= TSI148_LCSR_DSAT_2eSSTM_160;
+		val |= TSI148_LCSR_DSAT_2eSSTM_160;
 		break;
 	case VME_2eSST267:
-		*attr |= TSI148_LCSR_DSAT_2eSSTM_267;
+		val |= TSI148_LCSR_DSAT_2eSSTM_267;
 		break;
 	case VME_2eSST320:
-		*attr |= TSI148_LCSR_DSAT_2eSSTM_320;
+		val |= TSI148_LCSR_DSAT_2eSSTM_320;
 		break;
 	}
 
 	/* Setup cycle types */
 	if (cycle & VME_SCT)
-		*attr |= TSI148_LCSR_DSAT_TM_SCT;
+		val |= TSI148_LCSR_DSAT_TM_SCT;
 
 	if (cycle & VME_BLT)
-		*attr |= TSI148_LCSR_DSAT_TM_BLT;
+		val |= TSI148_LCSR_DSAT_TM_BLT;
 
 	if (cycle & VME_MBLT)
-		*attr |= TSI148_LCSR_DSAT_TM_MBLT;
+		val |= TSI148_LCSR_DSAT_TM_MBLT;
 
 	if (cycle & VME_2eVME)
-		*attr |= TSI148_LCSR_DSAT_TM_2eVME;
+		val |= TSI148_LCSR_DSAT_TM_2eVME;
 
 	if (cycle & VME_2eSST)
-		*attr |= TSI148_LCSR_DSAT_TM_2eSST;
+		val |= TSI148_LCSR_DSAT_TM_2eSST;
 
 	if (cycle & VME_2eSSTB) {
 		dev_err(dev, "Currently not setting Broadcast Select "
 			"Registers\n");
-		*attr |= TSI148_LCSR_DSAT_TM_2eSSTB;
+		val |= TSI148_LCSR_DSAT_TM_2eSSTB;
 	}
 
 	/* Setup data width */
 	switch (dwidth) {
 	case VME_D16:
-		*attr |= TSI148_LCSR_DSAT_DBW_16;
+		val |= TSI148_LCSR_DSAT_DBW_16;
 		break;
 	case VME_D32:
-		*attr |= TSI148_LCSR_DSAT_DBW_32;
+		val |= TSI148_LCSR_DSAT_DBW_32;
 		break;
 	default:
 		dev_err(dev, "Invalid data width\n");
@@ -1469,31 +1474,31 @@ static int tsi148_dma_set_vme_src_attributes(struct device *dev, u32 *attr,
 	/* Setup address space */
 	switch (aspace) {
 	case VME_A16:
-		*attr |= TSI148_LCSR_DSAT_AMODE_A16;
+		val |= TSI148_LCSR_DSAT_AMODE_A16;
 		break;
 	case VME_A24:
-		*attr |= TSI148_LCSR_DSAT_AMODE_A24;
+		val |= TSI148_LCSR_DSAT_AMODE_A24;
 		break;
 	case VME_A32:
-		*attr |= TSI148_LCSR_DSAT_AMODE_A32;
+		val |= TSI148_LCSR_DSAT_AMODE_A32;
 		break;
 	case VME_A64:
-		*attr |= TSI148_LCSR_DSAT_AMODE_A64;
+		val |= TSI148_LCSR_DSAT_AMODE_A64;
 		break;
 	case VME_CRCSR:
-		*attr |= TSI148_LCSR_DSAT_AMODE_CRCSR;
+		val |= TSI148_LCSR_DSAT_AMODE_CRCSR;
 		break;
 	case VME_USER1:
-		*attr |= TSI148_LCSR_DSAT_AMODE_USER1;
+		val |= TSI148_LCSR_DSAT_AMODE_USER1;
 		break;
 	case VME_USER2:
-		*attr |= TSI148_LCSR_DSAT_AMODE_USER2;
+		val |= TSI148_LCSR_DSAT_AMODE_USER2;
 		break;
 	case VME_USER3:
-		*attr |= TSI148_LCSR_DSAT_AMODE_USER3;
+		val |= TSI148_LCSR_DSAT_AMODE_USER3;
 		break;
 	case VME_USER4:
-		*attr |= TSI148_LCSR_DSAT_AMODE_USER4;
+		val |= TSI148_LCSR_DSAT_AMODE_USER4;
 		break;
 	default:
 		dev_err(dev, "Invalid address space\n");
@@ -1502,9 +1507,11 @@ static int tsi148_dma_set_vme_src_attributes(struct device *dev, u32 *attr,
 	}
 
 	if (cycle & VME_SUPER)
-		*attr |= TSI148_LCSR_DSAT_SUP;
+		val |= TSI148_LCSR_DSAT_SUP;
 	if (cycle & VME_PROG)
-		*attr |= TSI148_LCSR_DSAT_PGM;
+		val |= TSI148_LCSR_DSAT_PGM;
+
+	*attr = cpu_to_be32(val);
 
 	return 0;
 }
@@ -1512,48 +1519,52 @@ static int tsi148_dma_set_vme_src_attributes(struct device *dev, u32 *attr,
 static int tsi148_dma_set_vme_dest_attributes(struct device *dev, u32 *attr,
 	u32 aspace, u32 cycle, u32 dwidth)
 {
+	u32 val;
+
+	val = be32_to_cpu(*attr);
+
 	/* Setup 2eSST speeds */
 	switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) {
 	case VME_2eSST160:
-		*attr |= TSI148_LCSR_DDAT_2eSSTM_160;
+		val |= TSI148_LCSR_DDAT_2eSSTM_160;
 		break;
 	case VME_2eSST267:
-		*attr |= TSI148_LCSR_DDAT_2eSSTM_267;
+		val |= TSI148_LCSR_DDAT_2eSSTM_267;
 		break;
 	case VME_2eSST320:
-		*attr |= TSI148_LCSR_DDAT_2eSSTM_320;
+		val |= TSI148_LCSR_DDAT_2eSSTM_320;
 		break;
 	}
 
 	/* Setup cycle types */
 	if (cycle & VME_SCT)
-		*attr |= TSI148_LCSR_DDAT_TM_SCT;
+		val |= TSI148_LCSR_DDAT_TM_SCT;
 
 	if (cycle & VME_BLT)
-		*attr |= TSI148_LCSR_DDAT_TM_BLT;
+		val |= TSI148_LCSR_DDAT_TM_BLT;
 
 	if (cycle & VME_MBLT)
-		*attr |= TSI148_LCSR_DDAT_TM_MBLT;
+		val |= TSI148_LCSR_DDAT_TM_MBLT;
 
 	if (cycle & VME_2eVME)
-		*attr |= TSI148_LCSR_DDAT_TM_2eVME;
+		val |= TSI148_LCSR_DDAT_TM_2eVME;
 
 	if (cycle & VME_2eSST)
-		*attr |= TSI148_LCSR_DDAT_TM_2eSST;
+		val |= TSI148_LCSR_DDAT_TM_2eSST;
 
 	if (cycle & VME_2eSSTB) {
 		dev_err(dev, "Currently not setting Broadcast Select "
 			"Registers\n");
-		*attr |= TSI148_LCSR_DDAT_TM_2eSSTB;
+		val |= TSI148_LCSR_DDAT_TM_2eSSTB;
 	}
 
 	/* Setup data width */
 	switch (dwidth) {
 	case VME_D16:
-		*attr |= TSI148_LCSR_DDAT_DBW_16;
+		val |= TSI148_LCSR_DDAT_DBW_16;
 		break;
 	case VME_D32:
-		*attr |= TSI148_LCSR_DDAT_DBW_32;
+		val |= TSI148_LCSR_DDAT_DBW_32;
 		break;
 	default:
 		dev_err(dev, "Invalid data width\n");
@@ -1563,31 +1574,31 @@ static int tsi148_dma_set_vme_dest_attributes(struct device *dev, u32 *attr,
 	/* Setup address space */
 	switch (aspace) {
 	case VME_A16:
-		*attr |= TSI148_LCSR_DDAT_AMODE_A16;
+		val |= TSI148_LCSR_DDAT_AMODE_A16;
 		break;
 	case VME_A24:
-		*attr |= TSI148_LCSR_DDAT_AMODE_A24;
+		val |= TSI148_LCSR_DDAT_AMODE_A24;
 		break;
 	case VME_A32:
-		*attr |= TSI148_LCSR_DDAT_AMODE_A32;
+		val |= TSI148_LCSR_DDAT_AMODE_A32;
 		break;
 	case VME_A64:
-		*attr |= TSI148_LCSR_DDAT_AMODE_A64;
+		val |= TSI148_LCSR_DDAT_AMODE_A64;
 		break;
 	case VME_CRCSR:
-		*attr |= TSI148_LCSR_DDAT_AMODE_CRCSR;
+		val |= TSI148_LCSR_DDAT_AMODE_CRCSR;
 		break;
 	case VME_USER1:
-		*attr |= TSI148_LCSR_DDAT_AMODE_USER1;
+		val |= TSI148_LCSR_DDAT_AMODE_USER1;
 		break;
 	case VME_USER2:
-		*attr |= TSI148_LCSR_DDAT_AMODE_USER2;
+		val |= TSI148_LCSR_DDAT_AMODE_USER2;
 		break;
 	case VME_USER3:
-		*attr |= TSI148_LCSR_DDAT_AMODE_USER3;
+		val |= TSI148_LCSR_DDAT_AMODE_USER3;
 		break;
 	case VME_USER4:
-		*attr |= TSI148_LCSR_DDAT_AMODE_USER4;
+		val |= TSI148_LCSR_DDAT_AMODE_USER4;
 		break;
 	default:
 		dev_err(dev, "Invalid address space\n");
@@ -1596,15 +1607,19 @@ static int tsi148_dma_set_vme_dest_attributes(struct device *dev, u32 *attr,
 	}
 
 	if (cycle & VME_SUPER)
-		*attr |= TSI148_LCSR_DDAT_SUP;
+		val |= TSI148_LCSR_DDAT_SUP;
 	if (cycle & VME_PROG)
-		*attr |= TSI148_LCSR_DDAT_PGM;
+		val |= TSI148_LCSR_DDAT_PGM;
+
+	*attr = cpu_to_be32(val);
 
 	return 0;
 }
 
 /*
  * Add a link list descriptor to the list
+ *
+ * Note: DMA engine expects the DMA descriptor to be big endian.
  */
 static int tsi148_dma_list_add(struct vme_dma_list *list,
 	struct vme_dma_attr *src, struct vme_dma_attr *dest, size_t count)
@@ -1614,7 +1629,6 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 	struct vme_dma_pattern *pattern_attr;
 	struct vme_dma_pci *pci_attr;
 	struct vme_dma_vme *vme_attr;
-	dma_addr_t desc_ptr;
 	int retval = 0;
 	struct vme_bridge *tsi148_bridge;
 
@@ -1648,15 +1662,17 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 	case VME_DMA_PATTERN:
 		pattern_attr = src->private;
 
-		entry->descriptor.dsal = pattern_attr->pattern;
-		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_PAT;
+		entry->descriptor.dsal = cpu_to_be32(pattern_attr->pattern);
+		entry->descriptor.dsat = cpu_to_be32(TSI148_LCSR_DSAT_TYP_PAT);
 		/* Default behaviour is 32 bit pattern */
 		if (pattern_attr->type & VME_DMA_PATTERN_BYTE)
-			entry->descriptor.dsat |= TSI148_LCSR_DSAT_PSZ;
+			entry->descriptor.dsat |=
+				cpu_to_be32(TSI148_LCSR_DSAT_PSZ);
 
 		/* It seems that the default behaviour is to increment */
 		if ((pattern_attr->type & VME_DMA_PATTERN_INCREMENT) == 0)
-			entry->descriptor.dsat |= TSI148_LCSR_DSAT_NIN;
+			entry->descriptor.dsat |=
+				cpu_to_be32(TSI148_LCSR_DSAT_NIN);
 
 		break;
 	case VME_DMA_PCI:
@@ -1664,18 +1680,18 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 
 		reg_split((unsigned long long)pci_attr->address, &address_high,
 			&address_low);
-		entry->descriptor.dsau = address_high;
-		entry->descriptor.dsal = address_low;
-		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_PCI;
+		entry->descriptor.dsau = cpu_to_be32(address_high);
+		entry->descriptor.dsal = cpu_to_be32(address_low);
+		entry->descriptor.dsat = cpu_to_be32(TSI148_LCSR_DSAT_TYP_PCI);
 		break;
 	case VME_DMA_VME:
 		vme_attr = src->private;
 
 		reg_split((unsigned long long)vme_attr->address, &address_high,
 			&address_low);
-		entry->descriptor.dsau = address_high;
-		entry->descriptor.dsal = address_low;
-		entry->descriptor.dsat = TSI148_LCSR_DSAT_TYP_VME;
+		entry->descriptor.dsau = cpu_to_be32(address_high);
+		entry->descriptor.dsal = cpu_to_be32(address_low);
+		entry->descriptor.dsat = cpu_to_be32(TSI148_LCSR_DSAT_TYP_VME);
 
 		retval = tsi148_dma_set_vme_src_attributes(
 			tsi148_bridge->parent, &entry->descriptor.dsat,
@@ -1692,7 +1708,7 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 
 	/* Assume last link - this will be over-written by adding another */
 	entry->descriptor.dnlau = 0;
-	entry->descriptor.dnlal = TSI148_LCSR_DNLAL_LLA;
+	entry->descriptor.dnlal = cpu_to_be32(TSI148_LCSR_DNLAL_LLA);
 
 
 	/* Fill out destination part */
@@ -1702,18 +1718,18 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 
 		reg_split((unsigned long long)pci_attr->address, &address_high,
 			&address_low);
-		entry->descriptor.ddau = address_high;
-		entry->descriptor.ddal = address_low;
-		entry->descriptor.ddat = TSI148_LCSR_DDAT_TYP_PCI;
+		entry->descriptor.ddau = cpu_to_be32(address_high);
+		entry->descriptor.ddal = cpu_to_be32(address_low);
+		entry->descriptor.ddat = cpu_to_be32(TSI148_LCSR_DDAT_TYP_PCI);
 		break;
 	case VME_DMA_VME:
 		vme_attr = dest->private;
 
 		reg_split((unsigned long long)vme_attr->address, &address_high,
 			&address_low);
-		entry->descriptor.ddau = address_high;
-		entry->descriptor.ddal = address_low;
-		entry->descriptor.ddat = TSI148_LCSR_DDAT_TYP_VME;
+		entry->descriptor.ddau = cpu_to_be32(address_high);
+		entry->descriptor.ddal = cpu_to_be32(address_low);
+		entry->descriptor.ddat = cpu_to_be32(TSI148_LCSR_DDAT_TYP_VME);
 
 		retval = tsi148_dma_set_vme_dest_attributes(
 			tsi148_bridge->parent, &entry->descriptor.ddat,
@@ -1729,7 +1745,7 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 	}
 
 	/* Fill out count */
-	entry->descriptor.dcnt = (u32)count;
+	entry->descriptor.dcnt = cpu_to_be32((u32)count);
 
 	/* Add to list */
 	list_add_tail(&entry->list, &list->entries);
@@ -1739,9 +1755,14 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 		prev = list_entry(entry->list.prev, struct tsi148_dma_entry,
 			list);
 		/* We need the bus address for the pointer */
-		desc_ptr = virt_to_bus(&entry->descriptor);
-		reg_split(desc_ptr, &prev->descriptor.dnlau,
-			&prev->descriptor.dnlal);
+		entry->dma_handle = dma_map_single(tsi148_bridge->parent,
+			&entry->descriptor,
+			sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE);
+
+		reg_split((unsigned long long)entry->dma_handle, &address_high,
+			&address_low);
+		prev->descriptor.dnlau = cpu_to_be32(address_high);
+		prev->descriptor.dnlal = cpu_to_be32(address_low);
 	}
 
 	return 0;
@@ -1784,7 +1805,6 @@ static int tsi148_dma_list_exec(struct vme_dma_list *list)
 	struct vme_dma_resource *ctrlr;
 	int channel, retval = 0;
 	struct tsi148_dma_entry *entry;
-	dma_addr_t bus_addr;
 	u32 bus_addr_high, bus_addr_low;
 	u32 val, dctlreg = 0;
 	struct vme_bridge *tsi148_bridge;
@@ -1817,23 +1837,29 @@ static int tsi148_dma_list_exec(struct vme_dma_list *list)
 	entry = list_first_entry(&list->entries, struct tsi148_dma_entry,
 		list);
 
-	bus_addr = virt_to_bus(&entry->descriptor);
+	entry->dma_handle = dma_map_single(tsi148_bridge->parent,
+		&entry->descriptor,
+		sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE);
 
 	mutex_unlock(&ctrlr->mtx);
 
-	reg_split(bus_addr, &bus_addr_high, &bus_addr_low);
+	reg_split(entry->dma_handle, &bus_addr_high, &bus_addr_low);
 
 	iowrite32be(bus_addr_high, bridge->base +
 		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAU);
 	iowrite32be(bus_addr_low, bridge->base +
 		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAL);
 
+	dctlreg = ioread32be(bridge->base +
+		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL);
+
 	/* Start the operation */
 	iowrite32be(dctlreg | TSI148_LCSR_DCTL_DGO, bridge->base +
 		TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL);
 
 	wait_event_interruptible(bridge->dma_queue[channel],
 		tsi148_dma_busy(ctrlr->parent, channel));
+
 	/*
 	 * Read status register, this register is valid until we kick off a
 	 * new transfer.
@@ -1864,10 +1890,15 @@ static int tsi148_dma_list_empty(struct vme_dma_list *list)
 	struct list_head *pos, *temp;
 	struct tsi148_dma_entry *entry;
 
+	struct vme_bridge *tsi148_bridge = list->parent->parent;
+
 	/* detach and free each entry */
 	list_for_each_safe(pos, temp, &list->entries) {
 		list_del(pos);
 		entry = list_entry(pos, struct tsi148_dma_entry, list);
+
+		dma_unmap_single(tsi148_bridge->parent, entry->dma_handle,
+			sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE);
 		kfree(entry);
 	}
 
diff --git a/drivers/staging/vme/bridges/vme_tsi148.h b/drivers/staging/vme/bridges/vme_tsi148.h
index a3ac2fe..00b1160 100644
--- a/drivers/staging/vme/bridges/vme_tsi148.h
+++ b/drivers/staging/vme/bridges/vme_tsi148.h
@@ -75,6 +75,7 @@ struct tsi148_dma_entry {
 	 */
 	struct tsi148_dma_descriptor descriptor;
 	struct list_head list;
+	dma_addr_t dma_handle;
 };
 
 /*
-- 
1.7.0.4




More information about the devel mailing list