123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * PCI Endpoint *Controller* Address Space Management
- *
- * Copyright (C) 2017 Texas Instruments
- * Author: Kishon Vijay Abraham I <[email protected]>
- */
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/pci-epc.h>
- /**
- * pci_epc_mem_get_order() - determine the allocation order of a memory size
- * @mem: address space of the endpoint controller
- * @size: the size for which to get the order
- *
- * Reimplement get_order() for mem->page_size since the generic get_order
- * always gets order with a constant PAGE_SIZE.
- */
- static int pci_epc_mem_get_order(struct pci_epc_mem *mem, size_t size)
- {
- int order;
- unsigned int page_shift = ilog2(mem->window.page_size);
- size--;
- size >>= page_shift;
- #if BITS_PER_LONG == 32
- order = fls(size);
- #else
- order = fls64(size);
- #endif
- return order;
- }
- /**
- * pci_epc_multi_mem_init() - initialize the pci_epc_mem structure
- * @epc: the EPC device that invoked pci_epc_mem_init
- * @windows: pointer to windows supported by the device
- * @num_windows: number of windows device supports
- *
- * Invoke to initialize the pci_epc_mem structure used by the
- * endpoint functions to allocate mapped PCI address.
- */
- int pci_epc_multi_mem_init(struct pci_epc *epc,
- struct pci_epc_mem_window *windows,
- unsigned int num_windows)
- {
- struct pci_epc_mem *mem = NULL;
- unsigned long *bitmap = NULL;
- unsigned int page_shift;
- size_t page_size;
- int bitmap_size;
- int pages;
- int ret;
- int i;
- epc->num_windows = 0;
- if (!windows || !num_windows)
- return -EINVAL;
- epc->windows = kcalloc(num_windows, sizeof(*epc->windows), GFP_KERNEL);
- if (!epc->windows)
- return -ENOMEM;
- for (i = 0; i < num_windows; i++) {
- page_size = windows[i].page_size;
- if (page_size < PAGE_SIZE)
- page_size = PAGE_SIZE;
- page_shift = ilog2(page_size);
- pages = windows[i].size >> page_shift;
- bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
- mem = kzalloc(sizeof(*mem), GFP_KERNEL);
- if (!mem) {
- ret = -ENOMEM;
- i--;
- goto err_mem;
- }
- bitmap = kzalloc(bitmap_size, GFP_KERNEL);
- if (!bitmap) {
- ret = -ENOMEM;
- kfree(mem);
- i--;
- goto err_mem;
- }
- mem->window.phys_base = windows[i].phys_base;
- mem->window.size = windows[i].size;
- mem->window.page_size = page_size;
- mem->bitmap = bitmap;
- mem->pages = pages;
- mutex_init(&mem->lock);
- epc->windows[i] = mem;
- }
- epc->mem = epc->windows[0];
- epc->num_windows = num_windows;
- return 0;
- err_mem:
- for (; i >= 0; i--) {
- mem = epc->windows[i];
- kfree(mem->bitmap);
- kfree(mem);
- }
- kfree(epc->windows);
- return ret;
- }
- EXPORT_SYMBOL_GPL(pci_epc_multi_mem_init);
- int pci_epc_mem_init(struct pci_epc *epc, phys_addr_t base,
- size_t size, size_t page_size)
- {
- struct pci_epc_mem_window mem_window;
- mem_window.phys_base = base;
- mem_window.size = size;
- mem_window.page_size = page_size;
- return pci_epc_multi_mem_init(epc, &mem_window, 1);
- }
- EXPORT_SYMBOL_GPL(pci_epc_mem_init);
- /**
- * pci_epc_mem_exit() - cleanup the pci_epc_mem structure
- * @epc: the EPC device that invoked pci_epc_mem_exit
- *
- * Invoke to cleanup the pci_epc_mem structure allocated in
- * pci_epc_mem_init().
- */
- void pci_epc_mem_exit(struct pci_epc *epc)
- {
- struct pci_epc_mem *mem;
- int i;
- if (!epc->num_windows)
- return;
- for (i = 0; i < epc->num_windows; i++) {
- mem = epc->windows[i];
- kfree(mem->bitmap);
- kfree(mem);
- }
- kfree(epc->windows);
- epc->windows = NULL;
- epc->mem = NULL;
- epc->num_windows = 0;
- }
- EXPORT_SYMBOL_GPL(pci_epc_mem_exit);
- /**
- * pci_epc_mem_alloc_addr() - allocate memory address from EPC addr space
- * @epc: the EPC device on which memory has to be allocated
- * @phys_addr: populate the allocated physical address here
- * @size: the size of the address space that has to be allocated
- *
- * Invoke to allocate memory address from the EPC address space. This
- * is usually done to map the remote RC address into the local system.
- */
- void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
- phys_addr_t *phys_addr, size_t size)
- {
- void __iomem *virt_addr = NULL;
- struct pci_epc_mem *mem;
- unsigned int page_shift;
- size_t align_size;
- int pageno;
- int order;
- int i;
- for (i = 0; i < epc->num_windows; i++) {
- mem = epc->windows[i];
- mutex_lock(&mem->lock);
- align_size = ALIGN(size, mem->window.page_size);
- order = pci_epc_mem_get_order(mem, align_size);
- pageno = bitmap_find_free_region(mem->bitmap, mem->pages,
- order);
- if (pageno >= 0) {
- page_shift = ilog2(mem->window.page_size);
- *phys_addr = mem->window.phys_base +
- ((phys_addr_t)pageno << page_shift);
- virt_addr = ioremap(*phys_addr, align_size);
- if (!virt_addr) {
- bitmap_release_region(mem->bitmap,
- pageno, order);
- mutex_unlock(&mem->lock);
- continue;
- }
- mutex_unlock(&mem->lock);
- return virt_addr;
- }
- mutex_unlock(&mem->lock);
- }
- return virt_addr;
- }
- EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);
- static struct pci_epc_mem *pci_epc_get_matching_window(struct pci_epc *epc,
- phys_addr_t phys_addr)
- {
- struct pci_epc_mem *mem;
- int i;
- for (i = 0; i < epc->num_windows; i++) {
- mem = epc->windows[i];
- if (phys_addr >= mem->window.phys_base &&
- phys_addr < (mem->window.phys_base + mem->window.size))
- return mem;
- }
- return NULL;
- }
- /**
- * pci_epc_mem_free_addr() - free the allocated memory address
- * @epc: the EPC device on which memory was allocated
- * @phys_addr: the allocated physical address
- * @virt_addr: virtual address of the allocated mem space
- * @size: the size of the allocated address space
- *
- * Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
- */
- void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
- void __iomem *virt_addr, size_t size)
- {
- struct pci_epc_mem *mem;
- unsigned int page_shift;
- size_t page_size;
- int pageno;
- int order;
- mem = pci_epc_get_matching_window(epc, phys_addr);
- if (!mem) {
- pr_err("failed to get matching window\n");
- return;
- }
- page_size = mem->window.page_size;
- page_shift = ilog2(page_size);
- iounmap(virt_addr);
- pageno = (phys_addr - mem->window.phys_base) >> page_shift;
- size = ALIGN(size, page_size);
- order = pci_epc_mem_get_order(mem, size);
- mutex_lock(&mem->lock);
- bitmap_release_region(mem->bitmap, pageno, order);
- mutex_unlock(&mem->lock);
- }
- EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr);
- MODULE_DESCRIPTION("PCI EPC Address Space Management");
- MODULE_AUTHOR("Kishon Vijay Abraham I <[email protected]>");
- MODULE_LICENSE("GPL v2");
|