290 lines
8.1 KiB
C
290 lines
8.1 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 - Physical 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].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
|
||
|
* base - The physical 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, uintptr_t base,
|
||
|
size_t size, size_t page_size)
|
||
|
{
|
||
|
struct pci_epc_mem_window_s window;
|
||
|
|
||
|
window.phys_base = base;
|
||
|
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
|
||
|
* size - The size of the address space that has to be allocated
|
||
|
*
|
||
|
* Returned Value:
|
||
|
* The memory address alloced if success, 0 if failed
|
||
|
****************************************************************************/
|
||
|
|
||
|
uintptr_t pci_epc_mem_alloc_addr(FAR struct pci_epc_ctrl_s *epc, 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)
|
||
|
{
|
||
|
return mem->phys_base + pageno * mem->page_size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* 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 physical 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);
|
||
|
}
|
||
|
|