misc: pci_endpoint_test: Use full pci-endpoint-test name in request_irq()
[linux-2.6-microblaze.git] / drivers / misc / pci_endpoint_test.c
index a5e3170..bc3ae4a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mutex.h>
 #include <linux/random.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 #include <linux/pci.h>
 #include <linux/pci_ids.h>
 
@@ -52,6 +53,7 @@
 #define STATUS_SRC_ADDR_INVALID                        BIT(7)
 #define STATUS_DST_ADDR_INVALID                        BIT(8)
 
+#define PCI_ENDPOINT_TEST_STATUS               0x8
 #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR       0x0c
 #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR       0x10
 
@@ -64,6 +66,9 @@
 #define PCI_ENDPOINT_TEST_IRQ_TYPE             0x24
 #define PCI_ENDPOINT_TEST_IRQ_NUMBER           0x28
 
+#define PCI_ENDPOINT_TEST_FLAGS                        0x2c
+#define FLAG_USE_DMA                           BIT(0)
+
 #define PCI_DEVICE_ID_TI_AM654                 0xb00c
 
 #define is_am654_pci_dev(pdev)         \
@@ -98,11 +103,13 @@ struct pci_endpoint_test {
        struct completion irq_raised;
        int             last_irq;
        int             num_irqs;
+       int             irq_type;
        /* mutex to protect the ioctls */
        struct mutex    mutex;
        struct miscdevice miscdev;
        enum pci_barno test_reg_bar;
        size_t alignment;
+       const char *name;
 };
 
 struct pci_endpoint_test_data {
@@ -157,6 +164,7 @@ static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
        struct pci_dev *pdev = test->pdev;
 
        pci_free_irq_vectors(pdev);
+       test->irq_type = IRQ_TYPE_UNDEFINED;
 }
 
 static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
@@ -191,6 +199,8 @@ static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
                irq = 0;
                res = false;
        }
+
+       test->irq_type = type;
        test->num_irqs = irq;
 
        return res;
@@ -218,7 +228,7 @@ static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
        for (i = 0; i < test->num_irqs; i++) {
                err = devm_request_irq(dev, pci_irq_vector(pdev, i),
                                       pci_endpoint_test_irqhandler,
-                                      IRQF_SHARED, DRV_MODULE_NAME, test);
+                                      IRQF_SHARED, test->name, test);
                if (err)
                        goto fail;
        }
@@ -315,11 +325,16 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
        return false;
 }
 
-static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
+static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
+                                  unsigned long arg)
 {
+       struct pci_endpoint_test_xfer_param param;
        bool ret = false;
        void *src_addr;
        void *dst_addr;
+       u32 flags = 0;
+       bool use_dma;
+       size_t size;
        dma_addr_t src_phys_addr;
        dma_addr_t dst_phys_addr;
        struct pci_dev *pdev = test->pdev;
@@ -330,25 +345,46 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
        dma_addr_t orig_dst_phys_addr;
        size_t offset;
        size_t alignment = test->alignment;
+       int irq_type = test->irq_type;
        u32 src_crc32;
        u32 dst_crc32;
+       int err;
 
+       err = copy_from_user(&param, (void __user *)arg, sizeof(param));
+       if (err) {
+               dev_err(dev, "Failed to get transfer param\n");
+               return false;
+       }
+
+       size = param.size;
        if (size > SIZE_MAX - alignment)
                goto err;
 
+       use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
+       if (use_dma)
+               flags |= FLAG_USE_DMA;
+
        if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
                dev_err(dev, "Invalid IRQ type option\n");
                goto err;
        }
 
-       orig_src_addr = dma_alloc_coherent(dev, size + alignment,
-                                          &orig_src_phys_addr, GFP_KERNEL);
+       orig_src_addr = kzalloc(size + alignment, GFP_KERNEL);
        if (!orig_src_addr) {
                dev_err(dev, "Failed to allocate source buffer\n");
                ret = false;
                goto err;
        }
 
+       get_random_bytes(orig_src_addr, size + alignment);
+       orig_src_phys_addr = dma_map_single(dev, orig_src_addr,
+                                           size + alignment, DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, orig_src_phys_addr)) {
+               dev_err(dev, "failed to map source buffer address\n");
+               ret = false;
+               goto err_src_phys_addr;
+       }
+
        if (alignment && !IS_ALIGNED(orig_src_phys_addr, alignment)) {
                src_phys_addr = PTR_ALIGN(orig_src_phys_addr, alignment);
                offset = src_phys_addr - orig_src_phys_addr;
@@ -364,15 +400,21 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
                                 upper_32_bits(src_phys_addr));
 
-       get_random_bytes(src_addr, size);
        src_crc32 = crc32_le(~0, src_addr, size);
 
-       orig_dst_addr = dma_alloc_coherent(dev, size + alignment,
-                                          &orig_dst_phys_addr, GFP_KERNEL);
+       orig_dst_addr = kzalloc(size + alignment, GFP_KERNEL);
        if (!orig_dst_addr) {
                dev_err(dev, "Failed to allocate destination address\n");
                ret = false;
-               goto err_orig_src_addr;
+               goto err_dst_addr;
+       }
+
+       orig_dst_phys_addr = dma_map_single(dev, orig_dst_addr,
+                                           size + alignment, DMA_FROM_DEVICE);
+       if (dma_mapping_error(dev, orig_dst_phys_addr)) {
+               dev_err(dev, "failed to map destination buffer address\n");
+               ret = false;
+               goto err_dst_phys_addr;
        }
 
        if (alignment && !IS_ALIGNED(orig_dst_phys_addr, alignment)) {
@@ -392,6 +434,7 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
                                 size);
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
@@ -399,24 +442,34 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
 
        wait_for_completion(&test->irq_raised);
 
+       dma_unmap_single(dev, orig_dst_phys_addr, size + alignment,
+                        DMA_FROM_DEVICE);
+
        dst_crc32 = crc32_le(~0, dst_addr, size);
        if (dst_crc32 == src_crc32)
                ret = true;
 
-       dma_free_coherent(dev, size + alignment, orig_dst_addr,
-                         orig_dst_phys_addr);
+err_dst_phys_addr:
+       kfree(orig_dst_addr);
+
+err_dst_addr:
+       dma_unmap_single(dev, orig_src_phys_addr, size + alignment,
+                        DMA_TO_DEVICE);
 
-err_orig_src_addr:
-       dma_free_coherent(dev, size + alignment, orig_src_addr,
-                         orig_src_phys_addr);
+err_src_phys_addr:
+       kfree(orig_src_addr);
 
 err:
        return ret;
 }
 
-static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
+static bool pci_endpoint_test_write(struct pci_endpoint_test *test,
+                                   unsigned long arg)
 {
+       struct pci_endpoint_test_xfer_param param;
        bool ret = false;
+       u32 flags = 0;
+       bool use_dma;
        u32 reg;
        void *addr;
        dma_addr_t phys_addr;
@@ -426,24 +479,47 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
        dma_addr_t orig_phys_addr;
        size_t offset;
        size_t alignment = test->alignment;
+       int irq_type = test->irq_type;
+       size_t size;
        u32 crc32;
+       int err;
+
+       err = copy_from_user(&param, (void __user *)arg, sizeof(param));
+       if (err != 0) {
+               dev_err(dev, "Failed to get transfer param\n");
+               return false;
+       }
 
+       size = param.size;
        if (size > SIZE_MAX - alignment)
                goto err;
 
+       use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
+       if (use_dma)
+               flags |= FLAG_USE_DMA;
+
        if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
                dev_err(dev, "Invalid IRQ type option\n");
                goto err;
        }
 
-       orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
-                                      GFP_KERNEL);
+       orig_addr = kzalloc(size + alignment, GFP_KERNEL);
        if (!orig_addr) {
                dev_err(dev, "Failed to allocate address\n");
                ret = false;
                goto err;
        }
 
+       get_random_bytes(orig_addr, size + alignment);
+
+       orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
+                                       DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, orig_phys_addr)) {
+               dev_err(dev, "failed to map source buffer address\n");
+               ret = false;
+               goto err_phys_addr;
+       }
+
        if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
                phys_addr =  PTR_ALIGN(orig_phys_addr, alignment);
                offset = phys_addr - orig_phys_addr;
@@ -453,8 +529,6 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
                addr = orig_addr;
        }
 
-       get_random_bytes(addr, size);
-
        crc32 = crc32_le(~0, addr, size);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM,
                                 crc32);
@@ -466,6 +540,7 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
 
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
@@ -477,15 +552,24 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
        if (reg & STATUS_READ_SUCCESS)
                ret = true;
 
-       dma_free_coherent(dev, size + alignment, orig_addr, orig_phys_addr);
+       dma_unmap_single(dev, orig_phys_addr, size + alignment,
+                        DMA_TO_DEVICE);
+
+err_phys_addr:
+       kfree(orig_addr);
 
 err:
        return ret;
 }
 
-static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
+static bool pci_endpoint_test_read(struct pci_endpoint_test *test,
+                                  unsigned long arg)
 {
+       struct pci_endpoint_test_xfer_param param;
        bool ret = false;
+       u32 flags = 0;
+       bool use_dma;
+       size_t size;
        void *addr;
        dma_addr_t phys_addr;
        struct pci_dev *pdev = test->pdev;
@@ -494,24 +578,44 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
        dma_addr_t orig_phys_addr;
        size_t offset;
        size_t alignment = test->alignment;
+       int irq_type = test->irq_type;
        u32 crc32;
+       int err;
+
+       err = copy_from_user(&param, (void __user *)arg, sizeof(param));
+       if (err) {
+               dev_err(dev, "Failed to get transfer param\n");
+               return false;
+       }
 
+       size = param.size;
        if (size > SIZE_MAX - alignment)
                goto err;
 
+       use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
+       if (use_dma)
+               flags |= FLAG_USE_DMA;
+
        if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) {
                dev_err(dev, "Invalid IRQ type option\n");
                goto err;
        }
 
-       orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr,
-                                      GFP_KERNEL);
+       orig_addr = kzalloc(size + alignment, GFP_KERNEL);
        if (!orig_addr) {
                dev_err(dev, "Failed to allocate destination address\n");
                ret = false;
                goto err;
        }
 
+       orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
+                                       DMA_FROM_DEVICE);
+       if (dma_mapping_error(dev, orig_phys_addr)) {
+               dev_err(dev, "failed to map source buffer address\n");
+               ret = false;
+               goto err_phys_addr;
+       }
+
        if (alignment && !IS_ALIGNED(orig_phys_addr, alignment)) {
                phys_addr = PTR_ALIGN(orig_phys_addr, alignment);
                offset = phys_addr - orig_phys_addr;
@@ -528,6 +632,7 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
 
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
 
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, flags);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
        pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
@@ -535,15 +640,26 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
 
        wait_for_completion(&test->irq_raised);
 
+       dma_unmap_single(dev, orig_phys_addr, size + alignment,
+                        DMA_FROM_DEVICE);
+
        crc32 = crc32_le(~0, addr, size);
        if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
                ret = true;
 
-       dma_free_coherent(dev, size + alignment, orig_addr, orig_phys_addr);
+err_phys_addr:
+       kfree(orig_addr);
 err:
        return ret;
 }
 
+static bool pci_endpoint_test_clear_irq(struct pci_endpoint_test *test)
+{
+       pci_endpoint_test_release_irq(test);
+       pci_endpoint_test_free_irq_vectors(test);
+       return true;
+}
+
 static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
                                      int req_irq_type)
 {
@@ -555,7 +671,7 @@ static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
                return false;
        }
 
-       if (irq_type == req_irq_type)
+       if (test->irq_type == req_irq_type)
                return true;
 
        pci_endpoint_test_release_irq(test);
@@ -567,12 +683,10 @@ static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
        if (!pci_endpoint_test_request_irq(test))
                goto err;
 
-       irq_type = req_irq_type;
        return true;
 
 err:
        pci_endpoint_test_free_irq_vectors(test);
-       irq_type = IRQ_TYPE_UNDEFINED;
        return false;
 }
 
@@ -616,6 +730,9 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
        case PCITEST_GET_IRQTYPE:
                ret = irq_type;
                break;
+       case PCITEST_CLEAR_IRQ:
+               ret = pci_endpoint_test_clear_irq(test);
+               break;
        }
 
 ret:
@@ -633,7 +750,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
 {
        int err;
        int id;
-       char name[20];
+       char name[24];
        enum pci_barno bar;
        void __iomem *base;
        struct device *dev = &pdev->dev;
@@ -652,6 +769,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
        test->test_reg_bar = 0;
        test->alignment = 0;
        test->pdev = pdev;
+       test->irq_type = IRQ_TYPE_UNDEFINED;
 
        if (no_msi)
                irq_type = IRQ_TYPE_LEGACY;
@@ -667,6 +785,12 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
        init_completion(&test->irq_raised);
        mutex_init(&test->mutex);
 
+       if ((dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)) != 0) &&
+           dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
+               dev_err(dev, "Cannot set DMA mask\n");
+               return -EINVAL;
+       }
+
        err = pci_enable_device(pdev);
        if (err) {
                dev_err(dev, "Cannot enable PCI device\n");
@@ -684,9 +808,6 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
        if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type))
                goto err_disable_irq;
 
-       if (!pci_endpoint_test_request_irq(test))
-               goto err_disable_irq;
-
        for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
                if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
                        base = pci_ioremap_bar(pdev, bar);
@@ -716,12 +837,21 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
        }
 
        snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
+       test->name = kstrdup(name, GFP_KERNEL);
+       if (!test->name) {
+               err = -ENOMEM;
+               goto err_ida_remove;
+       }
+
+       if (!pci_endpoint_test_request_irq(test))
+               goto err_kfree_test_name;
+
        misc_device = &test->miscdev;
        misc_device->minor = MISC_DYNAMIC_MINOR;
        misc_device->name = kstrdup(name, GFP_KERNEL);
        if (!misc_device->name) {
                err = -ENOMEM;
-               goto err_ida_remove;
+               goto err_release_irq;
        }
        misc_device->fops = &pci_endpoint_test_fops,
 
@@ -736,6 +866,12 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
 err_kfree_name:
        kfree(misc_device->name);
 
+err_release_irq:
+       pci_endpoint_test_release_irq(test);
+
+err_kfree_test_name:
+       kfree(test->name);
+
 err_ida_remove:
        ida_simple_remove(&pci_endpoint_test_ida, id);
 
@@ -744,7 +880,6 @@ err_iounmap:
                if (test->bar[bar])
                        pci_iounmap(pdev, test->bar[bar]);
        }
-       pci_endpoint_test_release_irq(test);
 
 err_disable_irq:
        pci_endpoint_test_free_irq_vectors(test);
@@ -770,6 +905,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
 
        misc_deregister(&test->miscdev);
        kfree(misc_device->name);
+       kfree(test->name);
        ida_simple_remove(&pci_endpoint_test_ida, id);
        for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
                if (test->bar[bar])
@@ -783,6 +919,12 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
+static const struct pci_endpoint_test_data default_data = {
+       .test_reg_bar = BAR_0,
+       .alignment = SZ_4K,
+       .irq_type = IRQ_TYPE_MSI,
+};
+
 static const struct pci_endpoint_test_data am654_data = {
        .test_reg_bar = BAR_2,
        .alignment = SZ_64K,
@@ -790,8 +932,12 @@ static const struct pci_endpoint_test_data am654_data = {
 };
 
 static const struct pci_device_id pci_endpoint_test_tbl[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
-       { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
+       { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x),
+         .driver_data = (kernel_ulong_t)&default_data,
+       },
+       { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x),
+         .driver_data = (kernel_ulong_t)&default_data,
+       },
        { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0) },
        { PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
        { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654),