incubator-nuttx/drivers/pci/pci_epc_mem.c

296 lines
8.4 KiB
C

/****************************************************************************
* drivers/pci/pci_epc_mem.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <strings.h>
#include <sys/param.h>
#include <nuttx/bits.h>
#include <nuttx/kmalloc.h>
#include <nuttx/lib/math32.h>
#include <nuttx/pci/pci_epc.h>
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: pci_epc_mem_find
*
* Description:
* Get the matching memory window
*
* Input Parameters:
* epc - The EPC device
* phys_addr - Virtual address alloced to be matched
*
* Returned Value:
* Memory window alloced if success, NULL if failed
****************************************************************************/
static FAR struct pci_epc_mem_s *
pci_epc_mem_find(FAR struct pci_epc_ctrl_s *epc,
uintptr_t phys_addr)
{
unsigned int i;
for (i = 0; i < epc->num_windows; i++)
{
if (phys_addr >= epc->mem[i].phys_base &&
phys_addr < epc->mem[i].phys_base + epc->mem[i].size)
{
return &epc->mem[i];
}
}
return NULL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pci_epc_mem_multi_init
*
* Description:
* This function is used to initialize the pci_epc_mem_s structure.
*
* Invoke to initialize the pci_epc_mem_s structure used by the
* endpoint functions to allocate mapped PCI address.
*
* Input Parameters:
* 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
*
* Returned Value:
* 0 if success, negative if failed
****************************************************************************/
int pci_epc_mem_multi_init(FAR struct pci_epc_ctrl_s *epc,
FAR const struct pci_epc_mem_window_s *windows,
unsigned int num_windows)
{
unsigned int i;
if (epc == NULL || windows == NULL)
{
return -EINVAL;
}
epc->mem = kmm_calloc(num_windows, sizeof(*epc->mem));
if (epc->mem == NULL)
{
return -ENOMEM;
}
for (i = 0; i < num_windows; i++)
{
size_t pages = windows[i].size / windows[i].page_size;
size_t bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
FAR unsigned long *bitmap = kmm_zalloc(bitmap_size);
if (bitmap == NULL)
{
goto err;
}
epc->mem[i].virt_base = windows[i].virt_base;
epc->mem[i].phys_base = windows[i].phys_base;
epc->mem[i].size = windows[i].size;
epc->mem[i].page_size = windows[i].page_size;
epc->mem[i].bitmap = bitmap;
epc->mem[i].pages = pages;
nxmutex_init(&epc->mem[i].lock);
}
epc->num_windows = num_windows;
return 0;
err:
while (i-- > 0)
{
nxmutex_destroy(&epc->mem[i].lock);
kmm_free(epc->mem[i].bitmap);
}
kmm_free(epc->mem);
epc->mem = NULL;
return -ENOMEM;
}
/****************************************************************************
* Name: pci_epc_mem_init
*
* Description:
* This function is used to initialize the PCI endpoint controller memory
* space.
*
* Input Parameters:
* epc - PCI EPC device
* virt - The virtual base address of the PCI address window
* phys - The phys base address of the PCI address window
* size - The PCI window size
* page_size - Size of each window page
*
* Returned Value:
* 0 if success, negative if failed
****************************************************************************/
int pci_epc_mem_init(FAR struct pci_epc_ctrl_s *epc, FAR void *virt,
uintptr_t phys, size_t size, size_t page_size)
{
struct pci_epc_mem_window_s window;
window.virt_base = virt;
window.phys_base = phys;
window.size = size;
window.page_size = page_size;
return pci_epc_mem_multi_init(epc, &window, 1);
}
/****************************************************************************
* Name: pci_epc_mem_exit
*
* Description:
* This function is used to cleanup the pci_epc_mem_s structure.
*
* Invoke to cleanup the pci_epc_mem_s structure allocated in
* pci_epc_mem_init().
*
* Input Parameters:
* epc - EPC device that invoked pci_epc_mem_exit
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_mem_exit(FAR struct pci_epc_ctrl_s *epc)
{
unsigned int i;
if (epc->num_windows == 0)
{
return;
}
for (i = 0; i < epc->num_windows; i++)
{
nxmutex_destroy(&epc->mem[i].lock);
kmm_free(epc->mem[i].bitmap);
}
kmm_free(epc->mem);
epc->mem = NULL;
epc->num_windows = 0;
}
/****************************************************************************
* Name: pci_epc_mem_alloc_addr
*
* Description:
* Allocate memory address from EPC addr space
*
* Invoke to allocate memory address from the EPC address space. This
* is usually done to map the remote RC address into the local system.
*
* Input Parameters:
* epc - The EPC device on which memory has to be allocated
* phys - The virtual addr
* size - The size of the address space that has to be allocated
*
* Returned Value:
* The memory address alloced if success, NULL if failed
****************************************************************************/
FAR void *pci_epc_mem_alloc_addr(FAR struct pci_epc_ctrl_s *epc,
FAR uintptr_t *phys, size_t size)
{
unsigned int i;
for (i = 0; i < epc->num_windows; i++)
{
FAR struct pci_epc_mem_s *mem = &epc->mem[i];
size_t pages = div_round_up(size, mem->page_size);
size_t pageno;
nxmutex_lock(&mem->lock);
pageno = bitmap_find_free_region(mem->bitmap, mem->pages, pages);
nxmutex_unlock(&mem->lock);
if (pageno != mem->pages)
{
*phys = mem->phys_base + pageno * mem->page_size;
return mem->virt_base + pageno * mem->page_size;
}
}
return NULL;
}
/****************************************************************************
* Name: pci_epc_mem_free_addr
*
* Description:
* Free the allocated memory address.
*
* Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
*
* Input Parameters:
* epc - The EPC device on which memory was allocated
* phys_addr - The allocated virtual address
* size - The size of the allocated address space
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_mem_free_addr(FAR struct pci_epc_ctrl_s *epc,
uintptr_t phys_addr, size_t size)
{
FAR struct pci_epc_mem_s *mem;
uintptr_t pageno;
size_t pages;
mem = pci_epc_mem_find(epc, phys_addr);
if (mem == NULL)
{
pcierr("Failed to get matching window\n");
return;
}
pageno = (phys_addr - mem->phys_base) / mem->page_size;
pages = div_round_up(size, mem->page_size);
nxmutex_lock(&mem->lock);
bitmap_release_region(mem->bitmap, pageno, pages);
nxmutex_unlock(&mem->lock);
}