aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vme/bridges/vme_tsi148.c
diff options
context:
space:
mode:
authorMartyn Welch <martyn.welch@ge.com>2012-07-19 12:48:46 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-19 18:39:39 -0400
commit363e2e6f9e2d9ca53fb87e3988e2a225dc879aa4 (patch)
tree98e316e22bea15046819c1d44338b99bc48009fe /drivers/vme/bridges/vme_tsi148.c
parent59e32c490e476dba2938dde9fd4d4eaf5d0f8121 (diff)
VME: Prevent D16 cycles being split into 8-bit blocks
The memcpy_fromio() and memcpy_toio() functions use the __memcpy() function, at least on x86. This function carries out transfers smaller than 32 bits as multiple 8 bit transfers, causing a single (aligned) 16 bit transfer to be split into 2 8 bit transfers which may not be supported by the target VME device. The commit 53059aa05988761a738fa8bc082bbf3c5d4462d1 fixed this for the ca91cx42, however this was not fixed for the tsi148 at the time. This patch uses the same algorithm to fix the tsi148. Reported-by: Daniel Lambert <daniel.lambert@clermont.in2p3.fr> Signed-off-by: Martyn Welch <martyn.welch@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/vme/bridges/vme_tsi148.c')
-rw-r--r--drivers/vme/bridges/vme_tsi148.c85
1 files changed, 83 insertions, 2 deletions
diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c
index f6385f7a66d9..880d9242e349 100644
--- a/drivers/vme/bridges/vme_tsi148.c
+++ b/drivers/vme/bridges/vme_tsi148.c
@@ -1263,12 +1263,55 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
1263 u32 aspace, cycle, dwidth; 1263 u32 aspace, cycle, dwidth;
1264 struct vme_bus_error *vme_err = NULL; 1264 struct vme_bus_error *vme_err = NULL;
1265 struct vme_bridge *tsi148_bridge; 1265 struct vme_bridge *tsi148_bridge;
1266 void *addr = image->kern_base + offset;
1267 unsigned int done = 0;
1268 unsigned int count32;
1266 1269
1267 tsi148_bridge = image->parent; 1270 tsi148_bridge = image->parent;
1268 1271
1269 spin_lock(&image->lock); 1272 spin_lock(&image->lock);
1270 1273
1271 memcpy_fromio(buf, image->kern_base + offset, (unsigned int)count); 1274 /* The following code handles VME address alignment. We cannot use
1275 * memcpy_xxx directly here because it may cut small data transfers in
1276 * to 8-bit cycles, thus making D16 cycle impossible.
1277 * On the other hand, the bridge itself assures that the maximum data
1278 * cycle configured for the transfer is used and splits it
1279 * automatically for non-aligned addresses, so we don't want the
1280 * overhead of needlessly forcing small transfers for the entire cycle.
1281 */
1282 if ((uintptr_t)addr & 0x1) {
1283 *(u8 *)buf = ioread8(addr);
1284 done += 1;
1285 if (done == count)
1286 goto out;
1287 }
1288 if ((uintptr_t)addr & 0x2) {
1289 if ((count - done) < 2) {
1290 *(u8 *)(buf + done) = ioread8(addr + done);
1291 done += 1;
1292 goto out;
1293 } else {
1294 *(u16 *)(buf + done) = ioread16(addr + done);
1295 done += 2;
1296 }
1297 }
1298
1299 count32 = (count - done) & ~0x3;
1300 if (count32 > 0) {
1301 memcpy_fromio(buf + done, addr + done, count32);
1302 done += count32;
1303 }
1304
1305 if ((count - done) & 0x2) {
1306 *(u16 *)(buf + done) = ioread16(addr + done);
1307 done += 2;
1308 }
1309 if ((count - done) & 0x1) {
1310 *(u8 *)(buf + done) = ioread8(addr + done);
1311 done += 1;
1312 }
1313
1314out:
1272 retval = count; 1315 retval = count;
1273 1316
1274 if (!err_chk) 1317 if (!err_chk)
@@ -1301,6 +1344,9 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
1301 int retval = 0, enabled; 1344 int retval = 0, enabled;
1302 unsigned long long vme_base, size; 1345 unsigned long long vme_base, size;
1303 u32 aspace, cycle, dwidth; 1346 u32 aspace, cycle, dwidth;
1347 void *addr = image->kern_base + offset;
1348 unsigned int done = 0;
1349 unsigned int count32;
1304 1350
1305 struct vme_bus_error *vme_err = NULL; 1351 struct vme_bus_error *vme_err = NULL;
1306 struct vme_bridge *tsi148_bridge; 1352 struct vme_bridge *tsi148_bridge;
@@ -1312,7 +1358,42 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
1312 1358
1313 spin_lock(&image->lock); 1359 spin_lock(&image->lock);
1314 1360
1315 memcpy_toio(image->kern_base + offset, buf, (unsigned int)count); 1361 /* Here we apply for the same strategy we do in master_read
1362 * function in order to assure D16 cycle when required.
1363 */
1364 if ((uintptr_t)addr & 0x1) {
1365 iowrite8(*(u8 *)buf, addr);
1366 done += 1;
1367 if (done == count)
1368 goto out;
1369 }
1370 if ((uintptr_t)addr & 0x2) {
1371 if ((count - done) < 2) {
1372 iowrite8(*(u8 *)(buf + done), addr + done);
1373 done += 1;
1374 goto out;
1375 } else {
1376 iowrite16(*(u16 *)(buf + done), addr + done);
1377 done += 2;
1378 }
1379 }
1380
1381 count32 = (count - done) & ~0x3;
1382 if (count32 > 0) {
1383 memcpy_toio(addr + done, buf + done, count32);
1384 done += count32;
1385 }
1386
1387 if ((count - done) & 0x2) {
1388 iowrite16(*(u16 *)(buf + done), addr + done);
1389 done += 2;
1390 }
1391 if ((count - done) & 0x1) {
1392 iowrite8(*(u8 *)(buf + done), addr + done);
1393 done += 1;
1394 }
1395
1396out:
1316 retval = count; 1397 retval = count;
1317 1398
1318 /* 1399 /*