[PATCH 2/2] Staging: VME: Ensure TSI148 link list descriptors are written big endian

Martyn Welch martyn.welch at ge.com
Thu Mar 22 13:27:30 UTC 2012


The DMA functionality fails to work on little endian processors, such as
found on x86 based platforms. The DMA engine 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 |  163 +++++++++++++++++-------------
 drivers/staging/vme/bridges/vme_tsi148.h |   20 ++--
 2 files changed, 103 insertions(+), 80 deletions(-)

diff --git a/drivers/staging/vme/bridges/vme_tsi148.c b/drivers/staging/vme/bridges/vme_tsi148.c
index cd3c821..ced5942 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"
@@ -1415,51 +1416,55 @@ static unsigned int tsi148_master_rmw(struct vme_master_resource *image,
 	return result;
 }
 
-static int tsi148_dma_set_vme_src_attributes(struct device *dev, u32 *attr,
+static int tsi148_dma_set_vme_src_attributes(struct device *dev, __be32 *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,58 +1507,64 @@ 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;
 }
 
-static int tsi148_dma_set_vme_dest_attributes(struct device *dev, u32 *attr,
+static int tsi148_dma_set_vme_dest_attributes(struct device *dev, __be32 *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,21 +1607,25 @@ 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)
 {
 	struct tsi148_dma_entry *entry, *prev;
-	u32 address_high, address_low;
+	u32 address_high, address_low, val;
 	struct vme_dma_pattern *pattern_attr;
 	struct vme_dma_pci *pci_attr;
 	struct vme_dma_vme *vme_attr;
@@ -1647,34 +1662,36 @@ 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);
+
+		val = 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;
+			val |= 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;
-
+			val |= TSI148_LCSR_DSAT_NIN;
+		entry->descriptor.dsat = cpu_to_be32(val);
 		break;
 	case VME_DMA_PCI:
 		pci_attr = src->private;
 
 		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,
@@ -1690,9 +1707,8 @@ 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.dnlau = cpu_to_be32(0);
+	entry->descriptor.dnlal = cpu_to_be32(TSI148_LCSR_DNLAL_LLA);
 
 	/* Fill out destination part */
 	switch (dest->type) {
@@ -1701,18 +1717,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,
@@ -1728,7 +1744,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);
@@ -1742,8 +1758,11 @@ static int tsi148_dma_list_add(struct vme_dma_list *list,
 			&entry->descriptor,
 			sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE);
 
-		reg_split((unsigned long long)entry->dma_handle,
-			&prev->descriptor.dnlau, &prev->descriptor.dnlal);
+		reg_split((unsigned long long)entry->dma_handle, &address_high,
+			&address_low);
+		entry->descriptor.dnlau = cpu_to_be32(address_high);
+		entry->descriptor.dnlal = cpu_to_be32(address_low);
+
 	}
 
 	return 0;
@@ -1831,12 +1850,16 @@ static int tsi148_dma_list_exec(struct vme_dma_list *list)
 	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.
diff --git a/drivers/staging/vme/bridges/vme_tsi148.h b/drivers/staging/vme/bridges/vme_tsi148.h
index 00b1160..f5ed143 100644
--- a/drivers/staging/vme/bridges/vme_tsi148.h
+++ b/drivers/staging/vme/bridges/vme_tsi148.h
@@ -56,16 +56,16 @@ struct tsi148_driver {
  *       correctly laid out - It must also be aligned on 64-bit boundaries.
  */
 struct tsi148_dma_descriptor {
-	u32 dsau;      /* Source Address */
-	u32 dsal;
-	u32 ddau;      /* Destination Address */
-	u32 ddal;
-	u32 dsat;      /* Source attributes */
-	u32 ddat;      /* Destination attributes */
-	u32 dnlau;     /* Next link address */
-	u32 dnlal;
-	u32 dcnt;      /* Byte count */
-	u32 ddbs;      /* 2eSST Broadcast select */
+	__be32 dsau;      /* Source Address */
+	__be32 dsal;
+	__be32 ddau;      /* Destination Address */
+	__be32 ddal;
+	__be32 dsat;      /* Source attributes */
+	__be32 ddat;      /* Destination attributes */
+	__be32 dnlau;     /* Next link address */
+	__be32 dnlal;
+	__be32 dcnt;      /* Byte count */
+	__be32 ddbs;      /* 2eSST Broadcast select */
 };
 
 struct tsi148_dma_entry {
-- 
1.7.0.4




More information about the devel mailing list