These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / vme / vme.c
index 6bab2c4..72924b0 100644 (file)
@@ -177,8 +177,8 @@ size_t vme_get_size(struct vme_resource *resource)
 }
 EXPORT_SYMBOL(vme_get_size);
 
-static int vme_check_window(u32 aspace, unsigned long long vme_base,
-       unsigned long long size)
+int vme_check_window(u32 aspace, unsigned long long vme_base,
+                    unsigned long long size)
 {
        int retval = 0;
 
@@ -199,10 +199,8 @@ static int vme_check_window(u32 aspace, unsigned long long vme_base,
                        retval = -EFAULT;
                break;
        case VME_A64:
-               /*
-                * Any value held in an unsigned long long can be used as the
-                * base
-                */
+               if ((size != 0) && (vme_base > U64_MAX + 1 - size))
+                       retval = -EFAULT;
                break;
        case VME_CRCSR:
                if (((vme_base + size) > VME_CRCSR_MAX) ||
@@ -223,6 +221,40 @@ static int vme_check_window(u32 aspace, unsigned long long vme_base,
 
        return retval;
 }
+EXPORT_SYMBOL(vme_check_window);
+
+static u32 vme_get_aspace(int am)
+{
+       switch (am) {
+       case 0x29:
+       case 0x2D:
+               return VME_A16;
+       case 0x38:
+       case 0x39:
+       case 0x3A:
+       case 0x3B:
+       case 0x3C:
+       case 0x3D:
+       case 0x3E:
+       case 0x3F:
+               return VME_A24;
+       case 0x8:
+       case 0x9:
+       case 0xA:
+       case 0xB:
+       case 0xC:
+       case 0xD:
+       case 0xE:
+       case 0xF:
+               return VME_A32;
+       case 0x0:
+       case 0x1:
+       case 0x3:
+               return VME_A64;
+       }
+
+       return 0;
+}
 
 /*
  * Request a slave image with specific attributes, return some unique
@@ -991,6 +1023,63 @@ int vme_dma_free(struct vme_resource *resource)
 }
 EXPORT_SYMBOL(vme_dma_free);
 
+void vme_bus_error_handler(struct vme_bridge *bridge,
+                          unsigned long long address, int am)
+{
+       struct list_head *handler_pos = NULL;
+       struct vme_error_handler *handler;
+       int handler_triggered = 0;
+       u32 aspace = vme_get_aspace(am);
+
+       list_for_each(handler_pos, &bridge->vme_error_handlers) {
+               handler = list_entry(handler_pos, struct vme_error_handler,
+                                    list);
+               if ((aspace == handler->aspace) &&
+                   (address >= handler->start) &&
+                   (address < handler->end)) {
+                       if (!handler->num_errors)
+                               handler->first_error = address;
+                       if (handler->num_errors != UINT_MAX)
+                               handler->num_errors++;
+                       handler_triggered = 1;
+               }
+       }
+
+       if (!handler_triggered)
+               dev_err(bridge->parent,
+                       "Unhandled VME access error at address 0x%llx\n",
+                       address);
+}
+EXPORT_SYMBOL(vme_bus_error_handler);
+
+struct vme_error_handler *vme_register_error_handler(
+       struct vme_bridge *bridge, u32 aspace,
+       unsigned long long address, size_t len)
+{
+       struct vme_error_handler *handler;
+
+       handler = kmalloc(sizeof(*handler), GFP_KERNEL);
+       if (!handler)
+               return NULL;
+
+       handler->aspace = aspace;
+       handler->start = address;
+       handler->end = address + len;
+       handler->num_errors = 0;
+       handler->first_error = 0;
+       list_add_tail(&handler->list, &bridge->vme_error_handlers);
+
+       return handler;
+}
+EXPORT_SYMBOL(vme_register_error_handler);
+
+void vme_unregister_error_handler(struct vme_error_handler *handler)
+{
+       list_del(&handler->list);
+       kfree(handler);
+}
+EXPORT_SYMBOL(vme_unregister_error_handler);
+
 void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
 {
        void (*call)(int, int, void *);