/**************************************************************************** * 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 #include #include #include #include #include #include #include #include /**************************************************************************** * 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); }