Skip to content

Commit

Permalink
Staging: VME: Ensure TSI148 link list descriptors are written big endian
Browse files Browse the repository at this point in the history
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@ge.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Martyn Welch authored and Greg Kroah-Hartman committed Apr 10, 2012
1 parent 3abc48a commit ac1a4f2
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 80 deletions.
163 changes: 93 additions & 70 deletions drivers/staging/vme/bridges/vme_tsi148.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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) {
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit ac1a4f2

Please sign in to comment.