summaryrefslogtreecommitdiffstats
path: root/drivers/vme
diff options
context:
space:
mode:
authorDmitry Kalinkin <dmitry.kalinkin@gmail.com>2015-09-17 19:01:44 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-04 14:50:57 -0400
commit0b0496625715374c7d0b747268c11528e8af32a3 (patch)
tree6f129e982f9f3325162afe0271414833597471e9 /drivers/vme
parent472f16f33c7d53515af83c805d4babd8a6c24a19 (diff)
vme: change bus error handling scheme
The current VME bus error handler adds errors to the bridge error list. vme_master_{read,write} then traverses that list to look for relevant errors. Such scheme didn't work well for accesses going through vme_master_mmap because they would also allocate a vme_bus_error, but have no way to do vme_clear_errors call to free that memory. This changes the error handling process to be other way around: now vme_master_{read,write} defines a window in VME address space that will catch possible errors. VME bus error interrupt only traverses these windows and marks those that had errors in them. Signed-off-by: Dmitry Kalinkin <dmitry.kalinkin@gmail.com> Cc: Igor Alekseev <igor.alekseev@itep.ru> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/vme')
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.c3
-rw-r--r--drivers/vme/bridges/vme_tsi148.c83
-rw-r--r--drivers/vme/vme.c92
-rw-r--r--drivers/vme/vme_bridge.h23
4 files changed, 91 insertions, 110 deletions
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index 1d41cf21319b..b79a74a98a23 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -204,8 +204,7 @@ static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge)
204 /* Need pdev */ 204 /* Need pdev */
205 pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); 205 pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev);
206 206
207 /* Initialise list for VME bus errors */ 207 INIT_LIST_HEAD(&ca91cx42_bridge->vme_error_handlers);
208 INIT_LIST_HEAD(&ca91cx42_bridge->vme_errors);
209 208
210 mutex_init(&ca91cx42_bridge->irq_mtx); 209 mutex_init(&ca91cx42_bridge->irq_mtx);
211 210
diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c
index 4520d985379f..d1e383bc1f17 100644
--- a/drivers/vme/bridges/vme_tsi148.c
+++ b/drivers/vme/bridges/vme_tsi148.c
@@ -314,8 +314,7 @@ static int tsi148_irq_init(struct vme_bridge *tsi148_bridge)
314 314
315 bridge = tsi148_bridge->driver_priv; 315 bridge = tsi148_bridge->driver_priv;
316 316
317 /* Initialise list for VME bus errors */ 317 INIT_LIST_HEAD(&tsi148_bridge->vme_error_handlers);
318 INIT_LIST_HEAD(&tsi148_bridge->vme_errors);
319 318
320 mutex_init(&tsi148_bridge->irq_mtx); 319 mutex_init(&tsi148_bridge->irq_mtx);
321 320
@@ -1187,7 +1186,7 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
1187 int retval, enabled; 1186 int retval, enabled;
1188 unsigned long long vme_base, size; 1187 unsigned long long vme_base, size;
1189 u32 aspace, cycle, dwidth; 1188 u32 aspace, cycle, dwidth;
1190 struct vme_bus_error *vme_err = NULL; 1189 struct vme_error_handler *handler;
1191 struct vme_bridge *tsi148_bridge; 1190 struct vme_bridge *tsi148_bridge;
1192 void __iomem *addr = image->kern_base + offset; 1191 void __iomem *addr = image->kern_base + offset;
1193 unsigned int done = 0; 1192 unsigned int done = 0;
@@ -1197,6 +1196,17 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
1197 1196
1198 spin_lock(&image->lock); 1197 spin_lock(&image->lock);
1199 1198
1199 if (err_chk) {
1200 __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace,
1201 &cycle, &dwidth);
1202 handler = vme_register_error_handler(tsi148_bridge, aspace,
1203 vme_base + offset, count);
1204 if (!handler) {
1205 spin_unlock(&image->lock);
1206 return -ENOMEM;
1207 }
1208 }
1209
1200 /* The following code handles VME address alignment. We cannot use 1210 /* The following code handles VME address alignment. We cannot use
1201 * memcpy_xxx here because it may cut data transfers in to 8-bit 1211 * memcpy_xxx here because it may cut data transfers in to 8-bit
1202 * cycles when D16 or D32 cycles are required on the VME bus. 1212 * cycles when D16 or D32 cycles are required on the VME bus.
@@ -1240,24 +1250,16 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
1240out: 1250out:
1241 retval = count; 1251 retval = count;
1242 1252
1243 if (!err_chk) 1253 if (err_chk) {
1244 goto skip_chk; 1254 if (handler->num_errors) {
1245 1255 dev_err(image->parent->parent,
1246 __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle, 1256 "First VME read error detected an at address 0x%llx\n",
1247 &dwidth); 1257 handler->first_error);
1248 1258 retval = handler->first_error - (vme_base + offset);
1249 vme_err = vme_find_error(tsi148_bridge, aspace, vme_base + offset, 1259 }
1250 count); 1260 vme_unregister_error_handler(handler);
1251 if (vme_err != NULL) {
1252 dev_err(image->parent->parent, "First VME read error detected "
1253 "an at address 0x%llx\n", vme_err->address);
1254 retval = vme_err->address - (vme_base + offset);
1255 /* Clear down save errors in this address range */
1256 vme_clear_errors(tsi148_bridge, aspace, vme_base + offset,
1257 count);
1258 } 1261 }
1259 1262
1260skip_chk:
1261 spin_unlock(&image->lock); 1263 spin_unlock(&image->lock);
1262 1264
1263 return retval; 1265 return retval;
@@ -1274,7 +1276,7 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
1274 unsigned int done = 0; 1276 unsigned int done = 0;
1275 unsigned int count32; 1277 unsigned int count32;
1276 1278
1277 struct vme_bus_error *vme_err = NULL; 1279 struct vme_error_handler *handler;
1278 struct vme_bridge *tsi148_bridge; 1280 struct vme_bridge *tsi148_bridge;
1279 struct tsi148_driver *bridge; 1281 struct tsi148_driver *bridge;
1280 1282
@@ -1284,6 +1286,17 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
1284 1286
1285 spin_lock(&image->lock); 1287 spin_lock(&image->lock);
1286 1288
1289 if (err_chk) {
1290 __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace,
1291 &cycle, &dwidth);
1292 handler = vme_register_error_handler(tsi148_bridge, aspace,
1293 vme_base + offset, count);
1294 if (!handler) {
1295 spin_unlock(&image->lock);
1296 return -ENOMEM;
1297 }
1298 }
1299
1287 /* Here we apply for the same strategy we do in master_read 1300 /* Here we apply for the same strategy we do in master_read
1288 * function in order to assure the correct cycles. 1301 * function in order to assure the correct cycles.
1289 */ 1302 */
@@ -1333,30 +1346,18 @@ out:
1333 * We check for saved errors in the written address range/space. 1346 * We check for saved errors in the written address range/space.
1334 */ 1347 */
1335 1348
1336 if (!err_chk) 1349 if (err_chk) {
1337 goto skip_chk; 1350 ioread16(bridge->flush_image->kern_base + 0x7F000);
1338
1339 /*
1340 * Get window info first, to maximise the time that the buffers may
1341 * fluch on their own
1342 */
1343 __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, &cycle,
1344 &dwidth);
1345
1346 ioread16(bridge->flush_image->kern_base + 0x7F000);
1347 1351
1348 vme_err = vme_find_error(tsi148_bridge, aspace, vme_base + offset, 1352 if (handler->num_errors) {
1349 count); 1353 dev_warn(tsi148_bridge->parent,
1350 if (vme_err != NULL) { 1354 "First VME write error detected an at address 0x%llx\n",
1351 dev_warn(tsi148_bridge->parent, "First VME write error detected" 1355 handler->first_error);
1352 " an at address 0x%llx\n", vme_err->address); 1356 retval = handler->first_error - (vme_base + offset);
1353 retval = vme_err->address - (vme_base + offset); 1357 }
1354 /* Clear down save errors in this address range */ 1358 vme_unregister_error_handler(handler);
1355 vme_clear_errors(tsi148_bridge, aspace, vme_base + offset,
1356 count);
1357 } 1359 }
1358 1360
1359skip_chk:
1360 spin_unlock(&image->lock); 1361 spin_unlock(&image->lock);
1361 1362
1362 return retval; 1363 return retval;
diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c
index 2b79cd2715da..7a10d926ebb4 100644
--- a/drivers/vme/vme.c
+++ b/drivers/vme/vme.c
@@ -1026,76 +1026,52 @@ EXPORT_SYMBOL(vme_dma_free);
1026void vme_bus_error_handler(struct vme_bridge *bridge, 1026void vme_bus_error_handler(struct vme_bridge *bridge,
1027 unsigned long long address, int am) 1027 unsigned long long address, int am)
1028{ 1028{
1029 struct vme_bus_error *error; 1029 struct list_head *handler_pos = NULL;
1030 1030 struct vme_error_handler *handler;
1031 error = kmalloc(sizeof(struct vme_bus_error), GFP_ATOMIC); 1031 u32 aspace = vme_get_aspace(am);
1032 if (error) { 1032
1033 error->aspace = vme_get_aspace(am); 1033 list_for_each(handler_pos, &bridge->vme_error_handlers) {
1034 error->address = address; 1034 handler = list_entry(handler_pos, struct vme_error_handler,
1035 list_add_tail(&error->list, &bridge->vme_errors); 1035 list);
1036 } else { 1036 if ((aspace == handler->aspace) &&
1037 dev_err(bridge->parent, 1037 (address >= handler->start) &&
1038 "Unable to alloc memory for VMEbus Error reporting\n"); 1038 (address < handler->end)) {
1039 if (!handler->num_errors)
1040 handler->first_error = address;
1041 if (handler->num_errors != UINT_MAX)
1042 handler->num_errors++;
1043 }
1039 } 1044 }
1040} 1045}
1041EXPORT_SYMBOL(vme_bus_error_handler); 1046EXPORT_SYMBOL(vme_bus_error_handler);
1042 1047
1043/* 1048struct vme_error_handler *vme_register_error_handler(
1044 * Find the first error in this address range 1049 struct vme_bridge *bridge, u32 aspace,
1045 */ 1050 unsigned long long address, size_t len)
1046struct vme_bus_error *vme_find_error(struct vme_bridge *bridge, u32 aspace,
1047 unsigned long long address, size_t count)
1048{ 1051{
1049 struct list_head *err_pos; 1052 struct vme_error_handler *handler;
1050 struct vme_bus_error *vme_err, *valid = NULL;
1051 unsigned long long bound;
1052
1053 bound = address + count;
1054 1053
1055 err_pos = NULL; 1054 handler = kmalloc(sizeof(*handler), GFP_KERNEL);
1056 /* Iterate through errors */ 1055 if (!handler)
1057 list_for_each(err_pos, &bridge->vme_errors) { 1056 return NULL;
1058 vme_err = list_entry(err_pos, struct vme_bus_error, list);
1059 if ((vme_err->aspace == aspace) &&
1060 (vme_err->address >= address) &&
1061 (vme_err->address < bound)) {
1062 1057
1063 valid = vme_err; 1058 handler->aspace = aspace;
1064 break; 1059 handler->start = address;
1065 } 1060 handler->end = address + len;
1066 } 1061 handler->num_errors = 0;
1062 handler->first_error = 0;
1063 list_add_tail(&handler->list, &bridge->vme_error_handlers);
1067 1064
1068 return valid; 1065 return handler;
1069} 1066}
1070EXPORT_SYMBOL(vme_find_error); 1067EXPORT_SYMBOL(vme_register_error_handler);
1071 1068
1072/* 1069void vme_unregister_error_handler(struct vme_error_handler *handler)
1073 * Clear errors in the provided address range.
1074 */
1075void vme_clear_errors(struct vme_bridge *bridge, u32 aspace,
1076 unsigned long long address, size_t count)
1077{ 1070{
1078 struct list_head *err_pos, *temp; 1071 list_del(&handler->list);
1079 struct vme_bus_error *vme_err; 1072 kfree(handler);
1080 unsigned long long bound;
1081
1082 bound = address + count;
1083
1084 err_pos = NULL;
1085 /* Iterate through errors */
1086 list_for_each_safe(err_pos, temp, &bridge->vme_errors) {
1087 vme_err = list_entry(err_pos, struct vme_bus_error, list);
1088
1089 if ((vme_err->aspace == aspace) &&
1090 (vme_err->address >= address) &&
1091 (vme_err->address < bound)) {
1092
1093 list_del(err_pos);
1094 kfree(vme_err);
1095 }
1096 }
1097} 1073}
1098EXPORT_SYMBOL(vme_clear_errors); 1074EXPORT_SYMBOL(vme_unregister_error_handler);
1099 1075
1100void vme_irq_handler(struct vme_bridge *bridge, int level, int statid) 1076void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
1101{ 1077{
diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h
index 92fbe18cbc42..397578a73883 100644
--- a/drivers/vme/vme_bridge.h
+++ b/drivers/vme/vme_bridge.h
@@ -75,10 +75,13 @@ struct vme_lm_resource {
75 int monitors; 75 int monitors;
76}; 76};
77 77
78struct vme_bus_error { 78struct vme_error_handler {
79 struct list_head list; 79 struct list_head list;
80 u32 aspace; 80 unsigned long long start; /* Beginning of error window */
81 unsigned long long address; 81 unsigned long long end; /* End of error window */
82 unsigned long long first_error; /* Address of the first error */
83 u32 aspace; /* Address space of error window*/
84 unsigned num_errors; /* Number of errors */
82}; 85};
83 86
84struct vme_callback { 87struct vme_callback {
@@ -106,8 +109,10 @@ struct vme_bridge {
106 struct list_head dma_resources; 109 struct list_head dma_resources;
107 struct list_head lm_resources; 110 struct list_head lm_resources;
108 111
109 struct list_head vme_errors; /* List for errors generated on VME */ 112 /* List for registered errors handlers */
110 struct list_head devices; /* List of devices on this bridge */ 113 struct list_head vme_error_handlers;
114 /* List of devices on this bridge */
115 struct list_head devices;
111 116
112 /* Bridge Info - XXX Move to private structure? */ 117 /* Bridge Info - XXX Move to private structure? */
113 struct device *parent; /* Parent device (eg. pdev->dev for PCI) */ 118 struct device *parent; /* Parent device (eg. pdev->dev for PCI) */
@@ -168,13 +173,13 @@ struct vme_bridge {
168 173
169void vme_bus_error_handler(struct vme_bridge *bridge, 174void vme_bus_error_handler(struct vme_bridge *bridge,
170 unsigned long long address, int am); 175 unsigned long long address, int am);
171struct vme_bus_error *vme_find_error(struct vme_bridge *bridge, u32 aspace,
172 unsigned long long address, size_t count);
173void vme_clear_errors(struct vme_bridge *bridge, u32 aspace,
174 unsigned long long address, size_t count);
175void vme_irq_handler(struct vme_bridge *, int, int); 176void vme_irq_handler(struct vme_bridge *, int, int);
176 177
177int vme_register_bridge(struct vme_bridge *); 178int vme_register_bridge(struct vme_bridge *);
178void vme_unregister_bridge(struct vme_bridge *); 179void vme_unregister_bridge(struct vme_bridge *);
180struct vme_error_handler *vme_register_error_handler(
181 struct vme_bridge *bridge, u32 aspace,
182 unsigned long long address, size_t len);
183void vme_unregister_error_handler(struct vme_error_handler *handler);
179 184
180#endif /* _VME_BRIDGE_H_ */ 185#endif /* _VME_BRIDGE_H_ */