2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2017-04-10 19:25:10 +05:30
/**
* PCI Endpoint * Controller * Address Space Management
*
* Copyright ( C ) 2017 Texas Instruments
* Author : Kishon Vijay Abraham I < kishon @ ti . com >
*/
# include <linux/io.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/pci-epc.h>
/**
2017-08-18 20:27:56 +05:30
* 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 - > page_size ) ;
size - - ;
size > > = page_shift ;
# if BITS_PER_LONG == 32
order = fls ( size ) ;
# else
order = fls64 ( size ) ;
# endif
return order ;
}
/**
* __pci_epc_mem_init ( ) - initialize the pci_epc_mem structure
2017-04-10 19:25:10 +05:30
* @ epc : the EPC device that invoked pci_epc_mem_init
* @ phys_base : the physical address of the base
* @ size : the size of the address space
2017-08-18 20:27:56 +05:30
* @ page_size : size of each page
2017-04-10 19:25:10 +05:30
*
* Invoke to initialize the pci_epc_mem structure used by the
* endpoint functions to allocate mapped PCI address .
*/
2017-08-18 20:27:56 +05:30
int __pci_epc_mem_init ( struct pci_epc * epc , phys_addr_t phys_base , size_t size ,
size_t page_size )
2017-04-10 19:25:10 +05:30
{
int ret ;
struct pci_epc_mem * mem ;
unsigned long * bitmap ;
2017-08-18 20:27:56 +05:30
unsigned int page_shift ;
int pages ;
int bitmap_size ;
if ( page_size < PAGE_SIZE )
page_size = PAGE_SIZE ;
page_shift = ilog2 ( page_size ) ;
pages = size > > page_shift ;
bitmap_size = BITS_TO_LONGS ( pages ) * sizeof ( long ) ;
2017-04-10 19:25:10 +05:30
mem = kzalloc ( sizeof ( * mem ) , GFP_KERNEL ) ;
if ( ! mem ) {
ret = - ENOMEM ;
goto err ;
}
bitmap = kzalloc ( bitmap_size , GFP_KERNEL ) ;
if ( ! bitmap ) {
ret = - ENOMEM ;
goto err_mem ;
}
mem - > bitmap = bitmap ;
mem - > phys_base = phys_base ;
2017-08-18 20:27:56 +05:30
mem - > page_size = page_size ;
2017-04-10 19:25:10 +05:30
mem - > pages = pages ;
mem - > size = size ;
epc - > mem = mem ;
return 0 ;
err_mem :
kfree ( mem ) ;
err :
return ret ;
}
2017-08-18 20:27:56 +05:30
EXPORT_SYMBOL_GPL ( __pci_epc_mem_init ) ;
2017-04-10 19:25:10 +05:30
/**
* 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 = epc - > mem ;
epc - > mem = NULL ;
kfree ( mem - > bitmap ) ;
kfree ( mem ) ;
}
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 )
{
int pageno ;
void __iomem * virt_addr ;
struct pci_epc_mem * mem = epc - > mem ;
2017-08-18 20:27:56 +05:30
unsigned int page_shift = ilog2 ( mem - > page_size ) ;
int order ;
size = ALIGN ( size , mem - > page_size ) ;
order = pci_epc_mem_get_order ( mem , size ) ;
2017-04-10 19:25:10 +05:30
pageno = bitmap_find_free_region ( mem - > bitmap , mem - > pages , order ) ;
if ( pageno < 0 )
return NULL ;
2017-08-18 20:27:56 +05:30
* phys_addr = mem - > phys_base + ( pageno < < page_shift ) ;
2017-04-10 19:25:10 +05:30
virt_addr = ioremap ( * phys_addr , size ) ;
if ( ! virt_addr )
bitmap_release_region ( mem - > bitmap , pageno , order ) ;
return virt_addr ;
}
EXPORT_SYMBOL_GPL ( pci_epc_mem_alloc_addr ) ;
/**
* 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 )
{
int pageno ;
struct pci_epc_mem * mem = epc - > mem ;
2017-08-18 20:27:56 +05:30
unsigned int page_shift = ilog2 ( mem - > page_size ) ;
int order ;
2017-04-10 19:25:10 +05:30
iounmap ( virt_addr ) ;
2017-08-18 20:27:56 +05:30
pageno = ( phys_addr - mem - > phys_base ) > > page_shift ;
size = ALIGN ( size , mem - > page_size ) ;
order = pci_epc_mem_get_order ( mem , size ) ;
2017-04-10 19:25:10 +05:30
bitmap_release_region ( mem - > bitmap , pageno , order ) ;
}
EXPORT_SYMBOL_GPL ( pci_epc_mem_free_addr ) ;
MODULE_DESCRIPTION ( " PCI EPC Address Space Management " ) ;
MODULE_AUTHOR ( " Kishon Vijay Abraham I <kishon@ti.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;