Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / i2o / memory.c
diff --git a/kernel/drivers/staging/i2o/memory.c b/kernel/drivers/staging/i2o/memory.c
new file mode 100644 (file)
index 0000000..78b702c
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *     Functions to handle I2O memory
+ *
+ *     Pulled from the inlines in i2o headers and uninlined
+ *
+ *
+ *     This program is free software; you can redistribute it and/or modify it
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation; either version 2 of the License, or (at your
+ *     option) any later version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+/* Protects our 32/64bit mask switching */
+static DEFINE_MUTEX(mem_lock);
+
+/**
+ *     i2o_sg_tablesize - Calculate the maximum number of elements in a SGL
+ *     @c: I2O controller for which the calculation should be done
+ *     @body_size: maximum body size used for message in 32-bit words.
+ *
+ *     Return the maximum number of SG elements in a SG list.
+ */
+u16 i2o_sg_tablesize(struct i2o_controller *c, u16 body_size)
+{
+       i2o_status_block *sb = c->status_block.virt;
+       u16 sg_count =
+           (sb->inbound_frame_size - sizeof(struct i2o_message) / 4) -
+           body_size;
+
+       if (c->pae_support) {
+               /*
+                * for 64-bit a SG attribute element must be added and each
+                * SG element needs 12 bytes instead of 8.
+                */
+               sg_count -= 2;
+               sg_count /= 3;
+       } else
+               sg_count /= 2;
+
+       if (c->short_req && (sg_count > 8))
+               sg_count = 8;
+
+       return sg_count;
+}
+EXPORT_SYMBOL_GPL(i2o_sg_tablesize);
+
+
+/**
+ *     i2o_dma_map_single - Map pointer to controller and fill in I2O message.
+ *     @c: I2O controller
+ *     @ptr: pointer to the data which should be mapped
+ *     @size: size of data in bytes
+ *     @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE
+ *     @sg_ptr: pointer to the SG list inside the I2O message
+ *
+ *     This function does all necessary DMA handling and also writes the I2O
+ *     SGL elements into the I2O message. For details on DMA handling see also
+ *     dma_map_single(). The pointer sg_ptr will only be set to the end of the
+ *     SG list if the allocation was successful.
+ *
+ *     Returns DMA address which must be checked for failures using
+ *     dma_mapping_error().
+ */
+dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr,
+                                           size_t size,
+                                           enum dma_data_direction direction,
+                                           u32 ** sg_ptr)
+{
+       u32 sg_flags;
+       u32 *mptr = *sg_ptr;
+       dma_addr_t dma_addr;
+
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               sg_flags = 0xd4000000;
+               break;
+       case DMA_FROM_DEVICE:
+               sg_flags = 0xd0000000;
+               break;
+       default:
+               return 0;
+       }
+
+       dma_addr = dma_map_single(&c->pdev->dev, ptr, size, direction);
+       if (!dma_mapping_error(&c->pdev->dev, dma_addr)) {
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+               if ((sizeof(dma_addr_t) > 4) && c->pae_support) {
+                       *mptr++ = cpu_to_le32(0x7C020002);
+                       *mptr++ = cpu_to_le32(PAGE_SIZE);
+               }
+#endif
+
+               *mptr++ = cpu_to_le32(sg_flags | size);
+               *mptr++ = cpu_to_le32(i2o_dma_low(dma_addr));
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+               if ((sizeof(dma_addr_t) > 4) && c->pae_support)
+                       *mptr++ = cpu_to_le32(i2o_dma_high(dma_addr));
+#endif
+               *sg_ptr = mptr;
+       }
+       return dma_addr;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_map_single);
+
+/**
+ *     i2o_dma_map_sg - Map a SG List to controller and fill in I2O message.
+ *     @c: I2O controller
+ *     @sg: SG list to be mapped
+ *     @sg_count: number of elements in the SG list
+ *     @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE
+ *     @sg_ptr: pointer to the SG list inside the I2O message
+ *
+ *     This function does all necessary DMA handling and also writes the I2O
+ *     SGL elements into the I2O message. For details on DMA handling see also
+ *     dma_map_sg(). The pointer sg_ptr will only be set to the end of the SG
+ *     list if the allocation was successful.
+ *
+ *     Returns 0 on failure or 1 on success.
+ */
+int i2o_dma_map_sg(struct i2o_controller *c, struct scatterlist *sg,
+           int sg_count, enum dma_data_direction direction, u32 ** sg_ptr)
+{
+       u32 sg_flags;
+       u32 *mptr = *sg_ptr;
+
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               sg_flags = 0x14000000;
+               break;
+       case DMA_FROM_DEVICE:
+               sg_flags = 0x10000000;
+               break;
+       default:
+               return 0;
+       }
+
+       sg_count = dma_map_sg(&c->pdev->dev, sg, sg_count, direction);
+       if (!sg_count)
+               return 0;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+       if ((sizeof(dma_addr_t) > 4) && c->pae_support) {
+               *mptr++ = cpu_to_le32(0x7C020002);
+               *mptr++ = cpu_to_le32(PAGE_SIZE);
+       }
+#endif
+
+       while (sg_count-- > 0) {
+               if (!sg_count)
+                       sg_flags |= 0xC0000000;
+               *mptr++ = cpu_to_le32(sg_flags | sg_dma_len(sg));
+               *mptr++ = cpu_to_le32(i2o_dma_low(sg_dma_address(sg)));
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+               if ((sizeof(dma_addr_t) > 4) && c->pae_support)
+                       *mptr++ = cpu_to_le32(i2o_dma_high(sg_dma_address(sg)));
+#endif
+               sg = sg_next(sg);
+       }
+       *sg_ptr = mptr;
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_map_sg);
+
+/**
+ *     i2o_dma_alloc - Allocate DMA memory
+ *     @dev: struct device pointer to the PCI device of the I2O controller
+ *     @addr: i2o_dma struct which should get the DMA buffer
+ *     @len: length of the new DMA memory
+ *
+ *     Allocate a coherent DMA memory and write the pointers into addr.
+ *
+ *     Returns 0 on success or -ENOMEM on failure.
+ */
+int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, size_t len)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       int dma_64 = 0;
+
+       mutex_lock(&mem_lock);
+       if ((sizeof(dma_addr_t) > 4) && (pdev->dma_mask == DMA_BIT_MASK(64))) {
+               dma_64 = 1;
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+                       mutex_unlock(&mem_lock);
+                       return -ENOMEM;
+               }
+       }
+
+       addr->virt = dma_alloc_coherent(dev, len, &addr->phys, GFP_KERNEL);
+
+       if ((sizeof(dma_addr_t) > 4) && dma_64)
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+                       printk(KERN_WARNING "i2o: unable to set 64-bit DMA");
+       mutex_unlock(&mem_lock);
+
+       if (!addr->virt)
+               return -ENOMEM;
+
+       memset(addr->virt, 0, len);
+       addr->len = len;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_alloc);
+
+
+/**
+ *     i2o_dma_free - Free DMA memory
+ *     @dev: struct device pointer to the PCI device of the I2O controller
+ *     @addr: i2o_dma struct which contains the DMA buffer
+ *
+ *     Free a coherent DMA memory and set virtual address of addr to NULL.
+ */
+void i2o_dma_free(struct device *dev, struct i2o_dma *addr)
+{
+       if (addr->virt) {
+               if (addr->phys)
+                       dma_free_coherent(dev, addr->len, addr->virt,
+                                         addr->phys);
+               else
+                       kfree(addr->virt);
+               addr->virt = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(i2o_dma_free);
+
+
+/**
+ *     i2o_dma_realloc - Realloc DMA memory
+ *     @dev: struct device pointer to the PCI device of the I2O controller
+ *     @addr: pointer to a i2o_dma struct DMA buffer
+ *     @len: new length of memory
+ *
+ *     If there was something allocated in the addr, free it first. If len > 0
+ *     than try to allocate it and write the addresses back to the addr
+ *     structure. If len == 0 set the virtual address to NULL.
+ *
+ *     Returns the 0 on success or negative error code on failure.
+ */
+int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, size_t len)
+{
+       i2o_dma_free(dev, addr);
+
+       if (len)
+               return i2o_dma_alloc(dev, addr, len);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_realloc);
+
+/*
+ *     i2o_pool_alloc - Allocate an slab cache and mempool
+ *     @mempool: pointer to struct i2o_pool to write data into.
+ *     @name: name which is used to identify cache
+ *     @size: size of each object
+ *     @min_nr: minimum number of objects
+ *
+ *     First allocates a slab cache with name and size. Then allocates a
+ *     mempool which uses the slab cache for allocation and freeing.
+ *
+ *     Returns 0 on success or negative error code on failure.
+ */
+int i2o_pool_alloc(struct i2o_pool *pool, const char *name,
+                                size_t size, int min_nr)
+{
+       pool->name = kstrdup(name, GFP_KERNEL);
+       if (!pool->name)
+               goto exit;
+
+       pool->slab =
+           kmem_cache_create(pool->name, size, 0, SLAB_HWCACHE_ALIGN, NULL);
+       if (!pool->slab)
+               goto free_name;
+
+       pool->mempool = mempool_create_slab_pool(min_nr, pool->slab);
+       if (!pool->mempool)
+               goto free_slab;
+
+       return 0;
+
+free_slab:
+       kmem_cache_destroy(pool->slab);
+
+free_name:
+       kfree(pool->name);
+
+exit:
+       return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(i2o_pool_alloc);
+
+/*
+ *     i2o_pool_free - Free slab cache and mempool again
+ *     @mempool: pointer to struct i2o_pool which should be freed
+ *
+ *     Note that you have to return all objects to the mempool again before
+ *     calling i2o_pool_free().
+ */
+void i2o_pool_free(struct i2o_pool *pool)
+{
+       mempool_destroy(pool->mempool);
+       kmem_cache_destroy(pool->slab);
+       kfree(pool->name);
+};
+EXPORT_SYMBOL_GPL(i2o_pool_free);